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
+
+
+
+
+
+