diff --git a/src/Umbraco.Core/DelegateEqualityComparer.cs b/src/Umbraco.Core/DelegateEqualityComparer.cs new file mode 100644 index 0000000000..140ea016d1 --- /dev/null +++ b/src/Umbraco.Core/DelegateEqualityComparer.cs @@ -0,0 +1,60 @@ +using System; +using System.Collections.Generic; + +namespace Umbraco.Core +{ + /// + /// A custom equality comparer that excepts a delegate to do the comparison operation + /// + /// + public class DelegateEqualityComparer : IEqualityComparer + { + private readonly Func _equals; + private readonly Func _getHashcode; + + #region Implementation of IEqualityComparer + + public DelegateEqualityComparer(Func equals, Func getHashcode) + { + _getHashcode = getHashcode; + _equals = equals; + } + + public static DelegateEqualityComparer CompareMember(Func memberExpression) where TMember : IEquatable + { + return new DelegateEqualityComparer( + (x, y) => memberExpression.Invoke(x).Equals((TMember)memberExpression.Invoke(y)), + x => + { + var invoked = memberExpression.Invoke(x); + return !ReferenceEquals(invoked, default(TMember)) ? invoked.GetHashCode() : 0; + }); + } + + /// + /// Determines whether the specified objects are equal. + /// + /// + /// true if the specified objects are equal; otherwise, false. + /// + /// The first object of type to compare.The second object of type to compare. + public bool Equals(T x, T y) + { + return _equals.Invoke(x, y); + } + + /// + /// Returns a hash code for the specified object. + /// + /// + /// A hash code for the specified object. + /// + /// The for which a hash code is to be returned.The type of is a reference type and is null. + public int GetHashCode(T obj) + { + return _getHashcode.Invoke(obj); + } + + #endregion + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/EnumerableExtensions.cs b/src/Umbraco.Core/EnumerableExtensions.cs new file mode 100644 index 0000000000..3fa4847a26 --- /dev/null +++ b/src/Umbraco.Core/EnumerableExtensions.cs @@ -0,0 +1,269 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using System.Text; + +namespace Umbraco.Core +{ + /// + /// Extensions for enumerable sources + /// + public static class EnumerableExtensions + { + public static IEnumerable> InGroupsOf(this IEnumerable source, int groupSize) + { + var i = 0; + var length = source.Count(); + + while ((i * groupSize) < length) + { + yield return source.Skip(i * groupSize).Take(groupSize); + i++; + } + } + + + /// The distinct by. + /// The source. + /// The key selector. + /// Source type + /// Key type + /// the unique list + public static IEnumerable DistinctBy(this IEnumerable source, Func keySelector) + where TKey : IEquatable + { + return source.Distinct(DelegateEqualityComparer.CompareMember(keySelector)); + } + + /// + /// Returns a sequence of length whose elements are the result of invoking . + /// + /// + /// The factory. + /// The count. + /// + public static IEnumerable Range(Func factory, int count) + { + for (int i = 1; i <= count; i++) + { + yield return factory.Invoke(i - 1); + } + } + + /// The if not null. + /// The items. + /// The action. + /// The type + public static void IfNotNull(this IEnumerable items, Action action) where TItem : class + { + if (items != null) + { + foreach (TItem item in items) + { + item.IfNotNull(action); + } + } + } + + /// The for each. + /// The items. + /// The func. + /// item type + /// Result type + /// the Results + public static TResult[] ForEach(this IEnumerable items, Func func) + { + return items.Select(func).ToArray(); + } + + /// The for each. + /// The items. + /// The action. + /// Item type + /// list of TItem + public static IEnumerable ForEach(this IEnumerable items, Action action) + { + if (items != null) + { + foreach (TItem item in items) + { + action(item); + } + } + + return items; + } + + /// The flatten list. + /// The items. + /// The select child. + /// Item type + /// list of TItem + [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "By design")] + public static IEnumerable FlattenList(this IEnumerable items, Func> selectChild) + { + IEnumerable children = items != null && items.Any() + ? items.SelectMany(selectChild).FlattenList(selectChild) + : Enumerable.Empty(); + + if (items != null) + { + return items.Concat(children); + } + + return null; + } + + /// + /// Returns true if all items in the other collection exist in this collection + /// + /// + /// + /// + /// + public static bool ContainsAll(this IEnumerable source, IEnumerable other) + { + var matches = true; + foreach (var i in other) + { + matches = source.Contains(i); + if (!matches) break; + } + return matches; + } + + /// + /// Returns true if the source contains any of the items in the other list + /// + /// + /// + /// + /// + public static bool ContainsAny(this IEnumerable source, IEnumerable other) + { + return other.Any(i => source.Contains(i)); + } + + /// + /// Removes all matching items from an . + /// + /// + /// The list. + /// The predicate. + /// + public static void RemoveAll(this IList list, Func predicate) + { + for (var i = 0; i < list.Count; i++) + { + if (predicate(list[i])) + { + list.RemoveAt(i--); + } + } + } + + /// + /// Removes all matching items from an . + /// + /// + /// The list. + /// The predicate. + /// + public static void RemoveAll(this ICollection list, Func predicate) + { + var matches = list.Where(predicate).ToArray(); + foreach (var match in matches) + { + list.Remove(match); + } + } + + public static IEnumerable SelectRecursive( + this IEnumerable source, + Func> recursiveSelector, int maxRecusionDepth = 100) + { + var stack = new Stack>(); + stack.Push(source.GetEnumerator()); + + try + { + while (stack.Count > 0) + { + if (stack.Count > maxRecusionDepth) + throw new InvalidOperationException("Maximum recursion depth reached of " + maxRecusionDepth); + + if (stack.Peek().MoveNext()) + { + var current = stack.Peek().Current; + + yield return current; + + stack.Push(recursiveSelector(current).GetEnumerator()); + } + else + { + stack.Pop().Dispose(); + } + } + } + finally + { + while (stack.Count > 0) + { + stack.Pop().Dispose(); + } + } + } + + /// + /// Filters a sequence of values to ignore those which are null. + /// + /// + /// The coll. + /// + /// + public static IEnumerable WhereNotNull(this IEnumerable coll) where T : class + { + return coll.Where(x => x != null); + } + + public static IEnumerable ForAllThatAre(this IEnumerable sequence, Action projection) + where TActual : class + { + return sequence.Select( + x => + { + if (typeof(TActual).IsAssignableFrom(x.GetType())) + { + var casted = x as TActual; + projection.Invoke(casted); + } + return x; + }); + } + + ///Finds the index of the first item matching an expression in an enumerable. + ///The enumerable to search. + ///The expression to test the items against. + ///The index of the first matching item, or -1 if no items match. + public static int FindIndex(this IEnumerable items, Func predicate) + { + if (items == null) throw new ArgumentNullException("items"); + if (predicate == null) throw new ArgumentNullException("predicate"); + + var retVal = 0; + foreach (var item in items) + { + if (predicate(item)) return retVal; + retVal++; + } + return -1; + } + ///Finds the index of the first occurence of an item in an enumerable. + ///The enumerable to search. + ///The item to find. + ///The index of the first matching item, or -1 if the item was not found. + public static int IndexOf(this IEnumerable items, T item) { return items.FindIndex(i => EqualityComparer.Default.Equals(item, i)); } + } +} diff --git a/src/Umbraco.Core/IfExtensions.cs b/src/Umbraco.Core/IfExtensions.cs new file mode 100644 index 0000000000..13c1a11f36 --- /dev/null +++ b/src/Umbraco.Core/IfExtensions.cs @@ -0,0 +1,67 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Umbraco.Core +{ + + /// + /// Extension methods for 'If' checking like checking If something is null or not null + /// + public static class IfExtensions + { + + /// The if not null. + /// The item. + /// The action. + /// The type + public static void IfNotNull(this TItem item, Action action) where TItem : class + { + if (item != null) + { + action(item); + } + } + + /// The if true. + /// The predicate. + /// The action. + public static void IfTrue(this bool predicate, Action action) + { + if (predicate) + { + action(); + } + } + + /// + /// Checks if the item is not null, and if so returns an action on that item, or a default value + /// + /// the result type + /// The type + /// The item. + /// The action. + /// The default value. + /// + public static TResult IfNotNull(this TItem item, Func action, TResult defaultValue = default(TResult)) + where TItem : class + { + return item != null ? action(item) : defaultValue; + } + + /// + /// Checks if the value is null, if it is it returns the value specified, otherwise returns the non-null value + /// + /// + /// + /// + /// + public static TItem IfNull(this TItem item, Func action) + where TItem : class + { + return item ?? action(item); + } + + } +} diff --git a/src/Umbraco.Core/StringAliasCaseType.cs b/src/Umbraco.Core/StringAliasCaseType.cs new file mode 100644 index 0000000000..420c504897 --- /dev/null +++ b/src/Umbraco.Core/StringAliasCaseType.cs @@ -0,0 +1,9 @@ +namespace Umbraco.Core +{ + public enum StringAliasCaseType + { + PascalCase, + CamelCase, + Unchanged + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/StringExtensions.cs b/src/Umbraco.Core/StringExtensions.cs new file mode 100644 index 0000000000..4469af3176 --- /dev/null +++ b/src/Umbraco.Core/StringExtensions.cs @@ -0,0 +1,606 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Diagnostics.CodeAnalysis; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Security.Cryptography; +using System.Text; +using System.Text.RegularExpressions; +using System.Web; + +namespace Umbraco.Core +{ + /// + /// String extension methods + /// + public static class StringExtensions + { + /// + /// Trims the specified value from a string; accepts a string input whereas the in-built implementation only accepts char or char[]. + /// + /// The value. + /// For removing. + /// + public static string Trim(this string value, string forRemoving) + { + if (string.IsNullOrEmpty(value)) return value; + return value.TrimEnd(forRemoving).TrimStart(forRemoving); + } + + public static string TrimEnd(this string value, string forRemoving) + { + if (string.IsNullOrEmpty(value)) return value; + while (value.EndsWith(forRemoving, StringComparison.InvariantCultureIgnoreCase)) + { + value = value.Remove(value.LastIndexOf(forRemoving, StringComparison.InvariantCultureIgnoreCase)); + } + return value; + } + + public static string TrimStart(this string value, string forRemoving) + { + if (string.IsNullOrEmpty(value)) return value; + while (value.StartsWith(forRemoving, StringComparison.InvariantCultureIgnoreCase)) + { + value = value.Substring(forRemoving.Length); + } + return value; + } + + public static string EnsureStartsWith(this string input, string toStartWith) + { + if (input.StartsWith(toStartWith)) return input; + return toStartWith + input.TrimStart(toStartWith.ToArray()); // Ensure each char is removed first from input, e.g. ~/ plus /Path will equal ~/Path not ~//Path + } + + public static bool IsLowerCase(this char ch) + { + return ch.ToString(CultureInfo.InvariantCulture) == ch.ToString(CultureInfo.InvariantCulture).ToLower(); + } + + /// Is null or white space. + /// The str. + /// The is null or white space. + public static bool IsNullOrWhiteSpace(this string str) + { + return (str == null) || (str.Trim().Length == 0); + } + + public static string IfNullOrWhiteSpace(this string str, string defaultValue) + { + return str.IsNullOrWhiteSpace() ? defaultValue : str; + } + + /// The to delimited list. + /// The list. + /// The delimiter. + /// the list + [SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed", Justification = "By design")] + public static IList ToDelimitedList(this string list, string delimiter = ",") + { + var delimiters = new[] { delimiter }; + return !list.IsNullOrWhiteSpace() + ? list.Split(delimiters, StringSplitOptions.RemoveEmptyEntries) + .Select(i => i.Trim()) + .ToList() + : new List(); + } + + /// enum try parse. + /// The str type. + /// The ignore case. + /// The result. + /// The type + /// The enum try parse. + [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "By Design")] + [SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", MessageId = "2#", Justification = "By Design")] + public static bool EnumTryParse(this string strType, bool ignoreCase, out T result) + { + try + { + result = (T)Enum.Parse(typeof(T), strType, ignoreCase); + return true; + } + catch + { + result = default(T); + return false; + } + } + + /// + /// Strips all html from a string. + /// + /// The text. + /// Returns the string without any html tags. + public static string StripHtml(this string text) + { + const string pattern = @"<(.|\n)*?>"; + return Regex.Replace(text, pattern, String.Empty); + } + + /// + /// Converts string to a URL alias. + /// + /// The value. + /// The char replacements. + /// if set to true replace double dashes. + /// if set to true strip non ASCII. + /// if set to true URL encode. + /// + /// + /// This ensures that ONLY ascii chars are allowed and of those ascii chars, only digits and lowercase chars, all + /// punctuation, etc... are stripped out, however this method allows you to pass in string's to replace with the + /// specified replacement character before the string is converted to ascii and it has invalid characters stripped out. + /// This allows you to replace strings like & , etc.. with your replacement character before the automatic + /// reduction. + /// + public static string ToUrlAlias(this string value, IDictionary charReplacements, bool replaceDoubleDashes, bool stripNonAscii, bool urlEncode) + { + //first to lower case + value = value.ToLowerInvariant(); + + //then replacement chars + value = charReplacements.Aggregate(value, (current, kvp) => current.Replace(kvp.Key, kvp.Value)); + + //then convert to only ascii, this will remove the rest of any invalid chars + if (stripNonAscii) + { + value = Encoding.ASCII.GetString( + Encoding.Convert( + Encoding.UTF8, + Encoding.GetEncoding( + Encoding.ASCII.EncodingName, + new EncoderReplacementFallback(String.Empty), + new DecoderExceptionFallback()), + Encoding.UTF8.GetBytes(value))); + + //remove all characters that do not fall into the following categories (apart from the replacement val) + var validCodeRanges = + //digits + Enumerable.Range(48, 10).Concat( + //lowercase chars + Enumerable.Range(97, 26)); + + var sb = new StringBuilder(); + foreach (var c in value.Where(c => charReplacements.Values.Contains(c.ToString()) || validCodeRanges.Contains(c))) + { + sb.Append(c); + } + + value = sb.ToString(); + } + + //trim dashes from end + value = value.Trim('-', '_'); + + //replace double occurances of - or _ + value = replaceDoubleDashes ? Regex.Replace(value, @"([-_]){2,}", "$1", RegexOptions.Compiled) : value; + + //url encode result + return urlEncode ? HttpUtility.UrlEncode(value) : value; + } + + /// + /// Converts a string for use with an entity alias which is camel case and without invalid characters + /// + /// The phrase. + /// By default this is camel case + /// if set to true [remove spaces]. + /// + public static string ToUmbracoAlias(this string phrase, StringAliasCaseType caseType = StringAliasCaseType.CamelCase, bool removeSpaces = false) + { + if (string.IsNullOrEmpty(phrase)) return string.Empty; + + //convert case first + var tmp = phrase.ConvertCase(caseType); + + //remove non-alphanumeric chars + var result = Regex.Replace(tmp, @"[^a-zA-Z0-9\s\.-]+", "", RegexOptions.Compiled); + + if (removeSpaces) + result = result.Replace(" ", ""); + + return result; + } + + /// + /// Converts the phrase to specified convention. + /// + /// + /// The cases. + /// string + public static string ConvertCase(this string phrase, StringAliasCaseType cases) + { + var splittedPhrase = Regex.Split(phrase, @"[^a-zA-Z0-9\']", RegexOptions.Compiled); + + if (cases == StringAliasCaseType.Unchanged) + return string.Join("", splittedPhrase); + + //var splittedPhrase = phrase.Split(' ', '-', '.'); + var sb = new StringBuilder(); + + foreach (var splittedPhraseChars in splittedPhrase.Select(s => s.ToCharArray())) + { + if (splittedPhraseChars.Length > 0) + { + splittedPhraseChars[0] = ((new String(splittedPhraseChars[0], 1)).ToUpper().ToCharArray())[0]; + } + sb.Append(new String(splittedPhraseChars)); + } + + var result = sb.ToString(); + + if (cases == StringAliasCaseType.CamelCase) + { + if (result.Length > 1) + { + var pattern = new Regex("^([A-Z]*)([A-Z].*)$", RegexOptions.Singleline | RegexOptions.Compiled); + var match = pattern.Match(result); + if (match.Success) + { + result = match.Groups[1].Value.ToLower() + match.Groups[2].Value; + + return result.Substring(0, 1).ToLower() + result.Substring(1); + } + + return result; + } + + return result.ToLower(); + } + + return result; + } + + /// + /// Encodes as GUID. + /// + /// The input. + /// + public static Guid EncodeAsGuid(this string input) + { + if (string.IsNullOrWhiteSpace(input)) throw new ArgumentNullException("input"); + + var convertToHex = input.ConvertToHex(); + var hexLength = convertToHex.Length < 32 ? convertToHex.Length : 32; + var hex = convertToHex.Substring(0, hexLength).PadLeft(32, '0'); + var output = Guid.Empty; + return Guid.TryParse(hex, out output) ? output : Guid.Empty; + } + + /// + /// Converts to hex. + /// + /// The input. + /// + public static string ConvertToHex(this string input) + { + if (String.IsNullOrEmpty(input)) return String.Empty; + + var sb = new StringBuilder(input.Length); + foreach (char c in input) + { + int tmp = c; + sb.AppendFormat("{0:x2}", Convert.ToUInt32(c)); + } + return sb.ToString(); + } + + /// + /// Encodes a string to a safe URL base64 string + /// + /// + /// + public static string ToUrlBase64(this string input) + { + if (input == null) throw new ArgumentNullException("input"); + + if (String.IsNullOrEmpty(input)) return String.Empty; + + var bytes = Encoding.UTF8.GetBytes(input); + return UrlTokenEncode(bytes); + //return Convert.ToBase64String(bytes).Replace(".", "-").Replace("/", "_").Replace("=", ","); + } + + /// + /// Decodes a URL safe base64 string back + /// + /// + /// + public static string FromUrlBase64(this string input) + { + if (input == null) throw new ArgumentNullException("input"); + + //if (input.IsInvalidBase64()) return null; + + try + { + //var decodedBytes = Convert.FromBase64String(input.Replace("-", ".").Replace("_", "/").Replace(",", "=")); + byte[] decodedBytes = UrlTokenDecode(input); + return decodedBytes != null ? Encoding.UTF8.GetString(decodedBytes) : null; + } + catch (FormatException ex) + { + return null; + } + } + + /// + /// formats the string with invariant culture + /// + /// The format. + /// The args. + /// + public static string InvariantFormat(this string format, params object[] args) + { + return String.Format(CultureInfo.InvariantCulture, format, args); + } + + /// + /// Compares 2 strings with invariant culture and case ignored + /// + /// The compare. + /// The compare to. + /// + public static bool InvariantEquals(this string compare, string compareTo) + { + return String.Equals(compare, compareTo, StringComparison.InvariantCultureIgnoreCase); + } + + public static bool InvariantContains(this IEnumerable compare, string compareTo) + { + return compare.Contains(compareTo, new DelegateEqualityComparer((source, dest) => source.Equals(dest, StringComparison.InvariantCultureIgnoreCase), x => x.GetHashCode())); + } + + /// + /// Determines if the string is a Guid + /// + /// + /// + /// + public static bool IsGuid(this string str, bool withHyphens) + { + var isGuid = false; + + if (!String.IsNullOrEmpty(str)) + { + Regex guidRegEx; + if (withHyphens) + { + guidRegEx = new Regex(@"^(\{{0,1}([0-9a-fA-F]){8}-([0-9a-fA-F]){4}-([0-9a-fA-F]){4}-([0-9a-fA-F]){4}-([0-9a-fA-F]){12}\}{0,1})$"); + } + else + { + guidRegEx = new Regex(@"^(\{{0,1}([0-9a-fA-F]){8}([0-9a-fA-F]){4}([0-9a-fA-F]){4}([0-9a-fA-F]){4}([0-9a-fA-F]){12}\}{0,1})$"); + } + isGuid = guidRegEx.IsMatch(str); + } + + return isGuid; + } + + /// + /// Tries to parse a string into the supplied type by finding and using the Type's "Parse" method + /// + /// + /// + /// + public static T ParseInto(this string val) + { + return (T)val.ParseInto(typeof(T)); + } + + /// + /// Tries to parse a string into the supplied type by finding and using the Type's "Parse" method + /// + /// + /// + /// + public static object ParseInto(this string val, Type type) + { + if (!String.IsNullOrEmpty(val)) + { + TypeConverter tc = TypeDescriptor.GetConverter(type); + return tc.ConvertFrom(val); + } + return val; + } + + /// + /// Converts the string to MD5 + /// + /// referrs to itself + /// the md5 hashed string + public static string ToMd5(this string stringToConvert) + { + //create an instance of the MD5CryptoServiceProvider + var md5Provider = new MD5CryptoServiceProvider(); + + //convert our string into byte array + var byteArray = Encoding.UTF8.GetBytes(stringToConvert); + + //get the hashed values created by our MD5CryptoServiceProvider + var hashedByteArray = md5Provider.ComputeHash(byteArray); + + //create a StringBuilder object + var stringBuilder = new StringBuilder(); + + //loop to each each byte + foreach (var b in hashedByteArray) + { + //append it to our StringBuilder + stringBuilder.Append(b.ToString("x2").ToLower()); + } + + //return the hashed value + return stringBuilder.ToString(); + } + + + /// + /// Decodes a string that was encoded with UrlTokenEncode + /// + /// + /// + internal static byte[] UrlTokenDecode(string input) + { + if (input == null) + { + throw new ArgumentNullException("input"); + } + int length = input.Length; + if (length < 1) + { + return new byte[0]; + } + int num2 = input[length - 1] - '0'; + if ((num2 < 0) || (num2 > 10)) + { + return null; + } + char[] inArray = new char[(length - 1) + num2]; + for (int i = 0; i < (length - 1); i++) + { + char ch = input[i]; + switch (ch) + { + case '-': + inArray[i] = '+'; + break; + + case '_': + inArray[i] = '/'; + break; + + default: + inArray[i] = ch; + break; + } + } + for (int j = length - 1; j < inArray.Length; j++) + { + inArray[j] = '='; + } + return Convert.FromBase64CharArray(inArray, 0, inArray.Length); + } + + /// + /// Encodes a string so that it is 'safe' for URLs, files, etc.. + /// + /// + /// + internal static string UrlTokenEncode(byte[] input) + { + if (input == null) + { + throw new ArgumentNullException("input"); + } + if (input.Length < 1) + { + return String.Empty; + } + string str = null; + int index = 0; + char[] chArray = null; + str = Convert.ToBase64String(input); + if (str == null) + { + return null; + } + index = str.Length; + while (index > 0) + { + if (str[index - 1] != '=') + { + break; + } + index--; + } + chArray = new char[index + 1]; + chArray[index] = (char)((0x30 + str.Length) - index); + for (int i = 0; i < index; i++) + { + char ch = str[i]; + switch (ch) + { + case '+': + chArray[i] = '-'; + break; + + case '/': + chArray[i] = '_'; + break; + + case '=': + chArray[i] = ch; + break; + + default: + chArray[i] = ch; + break; + } + } + return new string(chArray); + } + + /// + /// Ensures that the folder path endds with a DirectorySeperatorChar + /// + /// + /// + public static string NormaliseDirectoryPath(this string currentFolder) + { + currentFolder = currentFolder + .IfNull(x => String.Empty) + .TrimEnd(Path.DirectorySeparatorChar) + Path.DirectorySeparatorChar; + return currentFolder; + } + + + /// + /// Truncates the specified text string. + /// + /// The text. + /// Length of the max. + /// The suffix. + /// + public static string Truncate(this string text, int maxLength, string suffix = "...") + { + // replaces the truncated string to a ... + var truncatedString = text; + + if (maxLength <= 0) return truncatedString; + var strLength = maxLength - suffix.Length; + + if (strLength <= 0) return truncatedString; + + if (text == null || text.Length <= maxLength) return truncatedString; + + truncatedString = text.Substring(0, strLength); + truncatedString = truncatedString.TrimEnd(); + truncatedString += suffix; + + return truncatedString; + } + + /// + /// Strips carrage returns and line feeds from the specified text. + /// + /// The input. + /// + public static string StripNewLines(this string input) + { + return input.Replace("\r", "").Replace("\n", ""); + } + + public static string OrIfNullOrWhiteSpace(this string input, string alternative) + { + return !string.IsNullOrWhiteSpace(input) + ? input + : alternative; + } + } +} diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index 4882d98267..b45944b80c 100644 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -33,6 +33,7 @@ + @@ -43,7 +44,13 @@ Properties\SolutionInfo.cs + + + + + +