2012-07-17 03:51:34 +06:00
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 ;
2013-01-17 14:53:55 -01:00
using System.Web.Security ;
2014-05-09 14:06:15 +10:00
using Newtonsoft.Json ;
2012-12-28 19:03:15 -01:00
using Umbraco.Core.Configuration ;
2017-05-30 15:46:25 +02:00
using Umbraco.Core.Composing ;
2018-05-02 18:15:22 +02:00
using Umbraco.Core.Exceptions ;
2015-07-16 10:40:43 +02:00
using Umbraco.Core.IO ;
2018-06-05 12:25:06 +01:00
using Umbraco.Core.Strings ;
2012-07-17 03:51:34 +06:00
namespace Umbraco.Core
{
2012-07-31 00:02:27 +06:00
2012-07-17 03:51:34 +06:00
///<summary>
/// String extension methods
///</summary>
public static class StringExtensions
{
2013-02-06 05:56:49 +06:00
2013-09-07 10:24:52 +02:00
private static readonly char [ ] ToCSharpHexDigitLower = "0123456789abcdef" . ToCharArray ( ) ;
private static readonly char [ ] ToCSharpEscapeChars ;
2014-08-12 14:18:49 +02:00
2013-09-07 10:24:52 +02:00
static StringExtensions ( )
{
var escapes = new [ ] { "\aa" , "\bb" , "\ff" , "\nn" , "\rr" , "\tt" , "\vv" , "\"\"" , "\\\\" , "??" , "\00" } ;
ToCSharpEscapeChars = new char [ escapes . Max ( e = > e [ 0 ] ) + 1 ] ;
foreach ( var escape in escapes )
ToCSharpEscapeChars [ escape [ 0 ] ] = escape [ 1 ] ;
}
2017-08-24 21:24:14 +02:00
/// <summary>
/// Convert a path to node ids in the order from right to left (deepest to shallowest)
/// </summary>
/// <param name="path"></param>
/// <returns></returns>
internal static int [ ] GetIdsFromPathReversed ( this string path )
{
var nodeIds = path . Split ( new [ ] { ',' } , StringSplitOptions . RemoveEmptyEntries )
. Select ( x = > x . TryConvertTo < int > ( ) )
. Where ( x = > x . Success )
. Select ( x = > x . Result )
. Reverse ( )
. ToArray ( ) ;
return nodeIds ;
2017-09-23 10:08:18 +02:00
}
2017-08-24 21:24:14 +02:00
2016-02-03 13:43:51 +01:00
/// <summary>
/// Removes new lines and tabs
/// </summary>
/// <param name="txt"></param>
/// <returns></returns>
2016-02-04 18:09:17 +01:00
internal static string StripWhitespace ( this string txt )
2016-02-03 13:43:51 +01:00
{
2016-02-04 18:09:17 +01:00
return Regex . Replace ( txt , @"\s" , string . Empty ) ;
2016-02-03 13:43:51 +01:00
}
2014-06-18 17:11:47 +10:00
internal static string StripFileExtension ( this string fileName )
{
//filenames cannot contain line breaks
if ( fileName . Contains ( Environment . NewLine ) | | fileName . Contains ( "\r" ) | | fileName . Contains ( "\n" ) ) return fileName ;
var lastIndex = fileName . LastIndexOf ( '.' ) ;
if ( lastIndex > 0 )
{
var ext = fileName . Substring ( lastIndex ) ;
//file extensions cannot contain whitespace
if ( ext . Contains ( " " ) ) return fileName ;
return string . Format ( "{0}" , fileName . Substring ( 0 , fileName . IndexOf ( ext , StringComparison . Ordinal ) ) ) ;
}
return fileName ;
2014-08-12 14:18:49 +02:00
2014-06-18 17:11:47 +10:00
}
2015-07-16 10:40:43 +02:00
/// <summary>
/// Based on the input string, this will detect if the strnig is a JS path or a JS snippet.
/// If a path cannot be determined, then it is assumed to be a snippet the original text is returned
/// with an invalid attempt, otherwise a valid attempt is returned with the resolved path
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
/// <remarks>
/// This is only used for legacy purposes for the Action.JsSource stuff and shouldn't be needed in v8
/// </remarks>
internal static Attempt < string > DetectIsJavaScriptPath ( this string input )
{
//validate that this is a url, if it is not, we'll assume that it is a text block and render it as a text
//block instead.
var isValid = true ;
if ( Uri . IsWellFormedUriString ( input , UriKind . RelativeOrAbsolute ) )
{
//ok it validates, but so does alert('hello'); ! so we need to do more checks
//here are the valid chars in a url without escaping
if ( Regex . IsMatch ( input , @"[^a-zA-Z0-9-._~:/?#\[\]@!$&'\(\)*\+,%;=]" ) )
isValid = false ;
//we'll have to be smarter and just check for certain js patterns now too!
var jsPatterns = new [ ] { @"\+\s*\=" , @"\);" , @"function\s*\(" , @"!=" , @"==" } ;
if ( jsPatterns . Any ( p = > Regex . IsMatch ( input , p ) ) )
isValid = false ;
if ( isValid )
{
var resolvedUrlResult = IOHelper . TryResolveUrl ( input ) ;
//if the resolution was success, return it, otherwise just return the path, we've detected
// it's a path but maybe it's relative and resolution has failed, etc... in which case we're just
// returning what was given to us.
2016-05-26 17:12:04 +02:00
return resolvedUrlResult . Success
? resolvedUrlResult
2015-07-16 10:40:43 +02:00
: Attempt . Succeed ( input ) ;
}
}
return Attempt . Fail ( input ) ;
}
2013-08-21 17:54:30 +10:00
/// <summary>
2016-05-26 17:12:04 +02:00
/// This tries to detect a json string, this is not a fail safe way but it is quicker than doing
2013-08-21 17:54:30 +10:00
/// a try/catch when deserializing when it is not json.
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
2017-05-12 14:49:44 +02:00
public static bool DetectIsJson ( this string input )
2013-08-21 17:54:30 +10:00
{
2018-04-04 13:11:12 +10:00
if ( input . IsNullOrWhiteSpace ( ) ) return false ;
2013-08-21 17:54:30 +10:00
input = input . Trim ( ) ;
2014-04-14 18:32:28 +10:00
return ( input . StartsWith ( "{" ) & & input . EndsWith ( "}" ) )
| | ( input . StartsWith ( "[" ) & & input . EndsWith ( "]" ) ) ;
2013-08-21 17:54:30 +10:00
}
2016-10-19 13:50:18 +02:00
internal static readonly Lazy < Regex > Whitespace = new Lazy < Regex > ( ( ) = > new Regex ( @"\s+" , RegexOptions . Compiled ) ) ;
internal static readonly string [ ] JsonEmpties = { "[]" , "{}" } ;
2014-08-15 13:21:00 -06:00
internal static bool DetectIsEmptyJson ( this string input )
{
2016-10-19 13:50:18 +02:00
return JsonEmpties . Contains ( Whitespace . Value . Replace ( input , string . Empty ) ) ;
2014-08-15 13:21:00 -06:00
}
2014-05-09 14:06:15 +10:00
/// <summary>
/// Returns a JObject/JArray instance if the string can be converted to json, otherwise returns the string
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
internal static object ConvertToJsonIfPossible ( this string input )
{
if ( input . DetectIsJson ( ) = = false )
{
return input ;
}
try
{
var obj = JsonConvert . DeserializeObject ( input ) ;
return obj ;
}
2018-05-03 14:58:34 +02:00
catch ( Exception )
2014-05-09 14:06:15 +10:00
{
return input ;
}
}
2015-02-13 14:50:16 +11:00
internal static string ReplaceNonAlphanumericChars ( this string input , string replacement )
{
//any character that is not alphanumeric, convert to a hyphen
var mName = input ;
foreach ( var c in mName . ToCharArray ( ) . Where ( c = > ! char . IsLetterOrDigit ( c ) ) )
{
mName = mName . Replace ( c . ToString ( CultureInfo . InvariantCulture ) , replacement ) ;
}
return mName ;
}
2013-08-16 12:25:38 +10:00
internal static string ReplaceNonAlphanumericChars ( this string input , char replacement )
{
2015-03-04 12:16:28 +01:00
var inputArray = input . ToCharArray ( ) ;
var outputArray = new char [ input . Length ] ;
for ( var i = 0 ; i < inputArray . Length ; i + + )
outputArray [ i ] = char . IsLetterOrDigit ( inputArray [ i ] ) ? inputArray [ i ] : replacement ;
return new string ( outputArray ) ;
2013-08-16 12:25:38 +10:00
}
2015-09-10 11:16:21 +02:00
private static readonly char [ ] CleanForXssChars = "*?(){}[];:%<>/\\|&'\"" . ToCharArray ( ) ;
2013-12-04 12:42:40 +11:00
/// <summary>
/// Cleans string to aid in preventing xss attacks.
/// </summary>
/// <param name="input"></param>
2015-09-09 21:41:06 +02:00
/// <param name="ignoreFromClean"></param>
2013-12-04 12:42:40 +11:00
/// <returns></returns>
2016-11-08 09:55:24 +01:00
public static string CleanForXss ( this string input , params char [ ] ignoreFromClean )
2013-12-04 12:42:40 +11:00
{
//remove any html
input = input . StripHtml ( ) ;
//strip out any potential chars involved with XSS
2015-09-10 11:16:21 +02:00
return input . ExceptChars ( new HashSet < char > ( CleanForXssChars . Except ( ignoreFromClean ) ) ) ;
2013-12-04 12:42:40 +11:00
}
2013-08-13 13:47:57 +10:00
public static string ExceptChars ( this string str , HashSet < char > toExclude )
{
var sb = new StringBuilder ( str . Length ) ;
foreach ( var c in str . Where ( c = > toExclude . Contains ( c ) = = false ) )
{
sb . Append ( c ) ;
}
return sb . ToString ( ) ;
}
2013-07-16 18:23:20 +10:00
/// <summary>
/// Returns a stream from a string
/// </summary>
/// <param name="s"></param>
/// <returns></returns>
internal static Stream GenerateStreamFromString ( this string s )
{
var stream = new MemoryStream ( ) ;
var writer = new StreamWriter ( stream ) ;
writer . Write ( s ) ;
writer . Flush ( ) ;
stream . Position = 0 ;
return stream ;
}
2013-07-09 18:51:45 +10:00
/// <summary>
/// This will append the query string to the URL
/// </summary>
/// <param name="url"></param>
/// <param name="queryStrings"></param>
/// <returns></returns>
/// <remarks>
/// This methods ensures that the resulting URL is structured correctly, that there's only one '?' and that things are
2016-05-26 17:12:04 +02:00
/// delimited properly with '&'
2013-07-09 18:51:45 +10:00
/// </remarks>
internal static string AppendQueryStringToUrl ( this string url , params string [ ] queryStrings )
{
//remove any prefixed '&' or '?'
for ( var i = 0 ; i < queryStrings . Length ; i + + )
{
2014-08-12 14:18:49 +02:00
queryStrings [ i ] = queryStrings [ i ] . TrimStart ( '?' , '&' ) . TrimEnd ( '&' ) ;
2013-07-09 18:51:45 +10:00
}
var nonEmpty = queryStrings . Where ( x = > ! x . IsNullOrWhiteSpace ( ) ) . ToArray ( ) ;
if ( url . Contains ( "?" ) )
{
return url + string . Join ( "&" , nonEmpty ) . EnsureStartsWith ( '&' ) ;
}
return url + string . Join ( "&" , nonEmpty ) . EnsureStartsWith ( '?' ) ;
}
2014-08-12 14:18:49 +02:00
/// <summary>
/// Encrypt the string using the MachineKey in medium trust
/// </summary>
/// <param name="value">The string value to be encrypted.</param>
/// <returns>The encrypted string.</returns>
public static string EncryptWithMachineKey ( this string value )
2013-01-17 14:53:55 -01:00
{
2014-08-12 14:18:49 +02:00
if ( value = = null )
return null ;
2013-01-15 20:07:07 +00:00
2014-08-12 14:18:49 +02:00
string valueToEncrypt = value ;
List < string > parts = new List < string > ( ) ;
2013-01-15 20:07:07 +00:00
2014-08-12 14:18:49 +02:00
const int EncrpytBlockSize = 500 ;
2013-01-15 20:07:07 +00:00
2014-08-12 14:18:49 +02:00
while ( valueToEncrypt . Length > EncrpytBlockSize )
{
parts . Add ( valueToEncrypt . Substring ( 0 , EncrpytBlockSize ) ) ;
valueToEncrypt = valueToEncrypt . Remove ( 0 , EncrpytBlockSize ) ;
}
2013-01-15 20:07:07 +00:00
2014-08-12 14:18:49 +02:00
if ( valueToEncrypt . Length > 0 )
{
parts . Add ( valueToEncrypt ) ;
}
2013-01-15 20:07:07 +00:00
2014-08-12 14:18:49 +02:00
StringBuilder encrpytedValue = new StringBuilder ( ) ;
2013-01-15 20:07:07 +00:00
2014-08-12 14:18:49 +02:00
foreach ( var part in parts )
{
var encrpytedBlock = FormsAuthentication . Encrypt ( new FormsAuthenticationTicket ( 0 , string . Empty , DateTime . Now , DateTime . MaxValue , false , part ) ) ;
encrpytedValue . AppendLine ( encrpytedBlock ) ;
}
2013-01-15 20:07:07 +00:00
2014-08-12 14:18:49 +02:00
return encrpytedValue . ToString ( ) . TrimEnd ( ) ;
2013-01-17 14:53:55 -01:00
}
2013-02-15 08:42:08 -01:00
/// <summary>
2014-08-12 14:18:49 +02:00
/// Decrypt the encrypted string using the Machine key in medium trust
/// </summary>
/// <param name="value">The string value to be decrypted</param>
/// <returns>The decrypted string.</returns>
public static string DecryptWithMachineKey ( this string value )
2013-01-17 14:53:55 -01:00
{
2014-08-12 14:18:49 +02:00
if ( value = = null )
return null ;
2013-01-15 20:07:07 +00:00
2014-08-12 14:18:49 +02:00
string [ ] parts = value . Split ( '\n' ) ;
2013-01-15 20:07:07 +00:00
2014-08-12 14:18:49 +02:00
StringBuilder decryptedValue = new StringBuilder ( ) ;
2013-01-15 20:07:07 +00:00
2014-08-12 14:18:49 +02:00
foreach ( var part in parts )
{
decryptedValue . Append ( FormsAuthentication . Decrypt ( part . TrimEnd ( ) ) . UserData ) ;
}
2013-01-15 20:07:07 +00:00
2014-08-12 14:18:49 +02:00
return decryptedValue . ToString ( ) ;
2013-01-17 14:53:55 -01:00
}
2014-08-12 14:18:49 +02:00
2013-01-17 14:53:55 -01:00
//this is from SqlMetal and just makes it a bit of fun to allow pluralisation
public static string MakePluralName ( this string name )
{
2014-05-16 14:33:03 +02:00
if ( ( name . EndsWith ( "x" , StringComparison . OrdinalIgnoreCase ) | | name . EndsWith ( "ch" , StringComparison . OrdinalIgnoreCase ) ) | | ( name . EndsWith ( "s" , StringComparison . OrdinalIgnoreCase ) | | name . EndsWith ( "sh" , StringComparison . OrdinalIgnoreCase ) ) )
2013-01-17 14:53:55 -01:00
{
name = name + "es" ;
return name ;
}
if ( ( name . EndsWith ( "y" , StringComparison . OrdinalIgnoreCase ) & & ( name . Length > 1 ) ) & & ! IsVowel ( name [ name . Length - 2 ] ) )
{
name = name . Remove ( name . Length - 1 , 1 ) ;
name = name + "ies" ;
return name ;
}
if ( ! name . EndsWith ( "s" , StringComparison . OrdinalIgnoreCase ) )
{
name = name + "s" ;
}
return name ;
}
public static bool IsVowel ( this char c )
{
switch ( c )
{
case 'O' :
case 'U' :
case 'Y' :
case 'A' :
case 'E' :
case 'I' :
case 'o' :
case 'u' :
case 'y' :
case 'a' :
case 'e' :
case 'i' :
return true ;
}
return false ;
}
2012-08-18 11:03:30 +06:00
2012-07-17 03:51:34 +06:00
/// <summary>
/// Trims the specified value from a string; accepts a string input whereas the in-built implementation only accepts char or char[].
/// </summary>
/// <param name="value">The value.</param>
/// <param name="forRemoving">For removing.</param>
/// <returns></returns>
public static string Trim ( this string value , string forRemoving )
{
if ( string . IsNullOrEmpty ( value ) ) return value ;
return value . TrimEnd ( forRemoving ) . TrimStart ( forRemoving ) ;
}
2013-01-17 14:53:55 -01:00
public static string EncodeJsString ( this string s )
{
var sb = new StringBuilder ( ) ;
foreach ( var c in s )
{
switch ( c )
{
case '\"' :
sb . Append ( "\\\"" ) ;
break ;
case '\\' :
sb . Append ( "\\\\" ) ;
break ;
case '\b' :
sb . Append ( "\\b" ) ;
break ;
case '\f' :
sb . Append ( "\\f" ) ;
break ;
case '\n' :
sb . Append ( "\\n" ) ;
break ;
case '\r' :
sb . Append ( "\\r" ) ;
break ;
case '\t' :
sb . Append ( "\\t" ) ;
break ;
default :
int i = ( int ) c ;
if ( i < 32 | | i > 127 )
{
sb . AppendFormat ( "\\u{0:X04}" , i ) ;
}
else
{
sb . Append ( c ) ;
}
break ;
}
}
return sb . ToString ( ) ;
}
2012-07-31 00:02:27 +06:00
2012-07-17 03:51:34 +06:00
public static string TrimEnd ( this string value , string forRemoving )
{
if ( string . IsNullOrEmpty ( value ) ) return value ;
2013-03-15 11:28:05 +06:00
if ( string . IsNullOrEmpty ( forRemoving ) ) return value ;
2012-07-17 03:51:34 +06:00
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 ;
2013-03-15 11:28:05 +06:00
if ( string . IsNullOrEmpty ( forRemoving ) ) return value ;
2012-07-17 03:51:34 +06:00
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 ;
Updates PartialView & PartialViewMacros models/services/repositories, streamlines their operations, fixes up other underlying problems with the FileRepository, fixes tree syncing for partial views, partial view macros and scripts, fixes scripts being created in folders, allows partial views and partial view macros to be managed and created in folders, fixes FileUnitOfWork to use a queue, publicizes some internal test classes, fixes tree syncing when dealing with invariant case, adds correct validation to the create dialogs of scripts and partial views (and partial view macros)
2014-10-22 16:44:45 +10:00
return toStartWith + input . TrimStart ( toStartWith ) ;
2012-07-17 03:51:34 +06:00
}
2013-01-17 14:53:55 -01:00
public static string EnsureStartsWith ( this string input , char value )
{
2013-02-26 01:13:31 +06:00
return input . StartsWith ( value . ToString ( CultureInfo . InvariantCulture ) ) ? input : value + input ;
2013-01-17 14:53:55 -01:00
}
2012-07-22 14:01:18 -02:00
2013-01-17 14:53:55 -01:00
public static string EnsureEndsWith ( this string input , char value )
{
2013-02-26 01:13:31 +06:00
return input . EndsWith ( value . ToString ( CultureInfo . InvariantCulture ) ) ? input : input + value ;
2013-01-17 14:53:55 -01:00
}
2012-07-22 14:01:18 -02:00
2015-01-15 11:49:32 +11:00
public static string EnsureEndsWith ( this string input , string toEndWith )
{
return input . EndsWith ( toEndWith . ToString ( CultureInfo . InvariantCulture ) ) ? input : input + toEndWith ;
}
2012-07-17 03:51:34 +06:00
public static bool IsLowerCase ( this char ch )
{
2013-02-26 01:13:31 +06:00
return ch . ToString ( CultureInfo . InvariantCulture ) = = ch . ToString ( CultureInfo . InvariantCulture ) . ToLowerInvariant ( ) ;
2012-07-17 03:51:34 +06:00
}
2013-01-17 14:53:55 -01:00
public static bool IsUpperCase ( this char ch )
{
2013-02-26 01:13:31 +06:00
return ch . ToString ( CultureInfo . InvariantCulture ) = = ch . ToString ( CultureInfo . InvariantCulture ) . ToUpperInvariant ( ) ;
2013-01-17 14:53:55 -01:00
}
2012-08-17 14:02:29 +06:00
2018-06-05 12:25:06 +01:00
/// <summary>Indicates whether a specified string is null, empty, or
/// consists only of white-space characters.</summary>
/// <param name="value">The value to check.</param>
/// <returns>Returns <see langword="true"/> if the value is null,
/// empty, or consists only of white-space characters, otherwise
/// returns <see langword="false"/>.</returns>
public static bool IsNullOrWhiteSpace ( this string value ) = > string . IsNullOrWhiteSpace ( value ) ;
2012-07-17 03:51:34 +06:00
public static string IfNullOrWhiteSpace ( this string str , string defaultValue )
{
return str . IsNullOrWhiteSpace ( ) ? defaultValue : str ;
}
/// <summary>The to delimited list.</summary>
/// <param name="list">The list.</param>
/// <param name="delimiter">The delimiter.</param>
/// <returns>the list</returns>
[SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed", Justification = "By design")]
public static IList < string > 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 < string > ( ) ;
}
/// <summary>enum try parse.</summary>
/// <param name="strType">The str type.</param>
/// <param name="ignoreCase">The ignore case.</param>
/// <param name="result">The result.</param>
/// <typeparam name="T">The type</typeparam>
/// <returns>The enum try parse.</returns>
[SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "By Design")]
[SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", MessageId = "2#", Justification = "By Design")]
public static bool EnumTryParse < T > ( 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 ;
}
}
2012-10-04 12:38:43 -02:00
/// <summary>
/// Parse string to Enum
/// </summary>
/// <typeparam name="T">The enum type</typeparam>
/// <param name="strType">The string to parse</param>
/// <param name="ignoreCase">The ignore case</param>
/// <returns>The parsed enum</returns>
[SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "By Design")]
[SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", MessageId = "2#", Justification = "By Design")]
public static T EnumParse < T > ( this string strType , bool ignoreCase )
{
return ( T ) Enum . Parse ( typeof ( T ) , strType , ignoreCase ) ;
}
2012-07-17 03:51:34 +06:00
/// <summary>
/// Strips all html from a string.
/// </summary>
/// <param name="text">The text.</param>
/// <returns>Returns the string without any html tags.</returns>
public static string StripHtml ( this string text )
{
const string pattern = @"<(.|\n)*?>" ;
return Regex . Replace ( text , pattern , String . Empty ) ;
}
/// <summary>
/// Encodes as GUID.
/// </summary>
/// <param name="input">The input.</param>
/// <returns></returns>
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 ;
}
/// <summary>
/// Converts to hex.
/// </summary>
/// <param name="input">The input.</param>
/// <returns></returns>
public static string ConvertToHex ( this string input )
{
2015-02-10 16:33:59 +11:00
if ( string . IsNullOrEmpty ( input ) ) return string . Empty ;
2012-07-17 03:51:34 +06:00
var sb = new StringBuilder ( input . Length ) ;
2015-02-10 16:33:59 +11:00
foreach ( var c in input )
2012-07-17 03:51:34 +06:00
{
sb . AppendFormat ( "{0:x2}" , Convert . ToUInt32 ( c ) ) ;
}
return sb . ToString ( ) ;
}
2015-02-10 16:33:59 +11:00
public static string DecodeFromHex ( this string hexValue )
{
var strValue = "" ;
while ( hexValue . Length > 0 )
{
strValue + = Convert . ToChar ( Convert . ToUInt32 ( hexValue . Substring ( 0 , 2 ) , 16 ) ) . ToString ( ) ;
hexValue = hexValue . Substring ( 2 , hexValue . Length - 2 ) ;
}
return strValue ;
}
2012-07-17 03:51:34 +06:00
///<summary>
/// Encodes a string to a safe URL base64 string
///</summary>
///<param name="input"></param>
///<returns></returns>
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("=", ",");
}
/// <summary>
/// Decodes a URL safe base64 string back
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
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 ;
}
2018-05-03 14:58:34 +02:00
catch ( FormatException )
2012-07-17 03:51:34 +06:00
{
return null ;
}
}
/// <summary>
/// formats the string with invariant culture
/// </summary>
/// <param name="format">The format.</param>
/// <param name="args">The args.</param>
/// <returns></returns>
public static string InvariantFormat ( this string format , params object [ ] args )
{
return String . Format ( CultureInfo . InvariantCulture , format , args ) ;
}
2013-07-30 13:29:05 +10:00
/// <summary>
/// Converts an integer to an invariant formatted string
/// </summary>
/// <param name="str"></param>
/// <returns></returns>
public static string ToInvariantString ( this int str )
{
return str . ToString ( CultureInfo . InvariantCulture ) ;
}
2016-04-28 18:39:52 +02:00
public static string ToInvariantString ( this long str )
{
return str . ToString ( CultureInfo . InvariantCulture ) ;
}
2012-07-17 03:51:34 +06:00
/// <summary>
/// Compares 2 strings with invariant culture and case ignored
/// </summary>
/// <param name="compare">The compare.</param>
/// <param name="compareTo">The compare to.</param>
/// <returns></returns>
public static bool InvariantEquals ( this string compare , string compareTo )
{
return String . Equals ( compare , compareTo , StringComparison . InvariantCultureIgnoreCase ) ;
}
2013-01-17 14:53:55 -01:00
public static bool InvariantStartsWith ( this string compare , string compareTo )
{
return compare . StartsWith ( compareTo , StringComparison . InvariantCultureIgnoreCase ) ;
}
2012-08-07 02:33:08 +06:00
2013-12-18 17:22:00 +11:00
public static bool InvariantEndsWith ( this string compare , string compareTo )
{
return compare . EndsWith ( compareTo , StringComparison . InvariantCultureIgnoreCase ) ;
2013-01-17 14:53:55 -01:00
}
2012-08-07 02:33:08 +06:00
2012-07-24 10:06:07 -01:00
public static bool InvariantContains ( this string compare , string compareTo )
{
return compare . IndexOf ( compareTo , StringComparison . OrdinalIgnoreCase ) > = 0 ;
}
2012-07-17 03:51:34 +06:00
public static bool InvariantContains ( this IEnumerable < string > compare , string compareTo )
{
2014-03-17 11:24:01 +11:00
return compare . Contains ( compareTo , StringComparer . InvariantCultureIgnoreCase ) ;
2012-07-17 03:51:34 +06:00
}
2016-05-26 17:12:04 +02:00
public static int InvariantIndexOf ( this string s , string value )
{
return s . IndexOf ( value , StringComparison . OrdinalIgnoreCase ) ;
}
public static int InvariantLastIndexOf ( this string s , string value )
{
return s . LastIndexOf ( value , StringComparison . OrdinalIgnoreCase ) ;
}
2016-08-05 19:28:46 +02:00
[Obsolete("Use Guid.TryParse instead")]
[EditorBrowsable(EditorBrowsableState.Never)]
2012-07-17 03:51:34 +06:00
public static bool IsGuid ( this string str , bool withHyphens )
{
2016-08-05 19:28:46 +02:00
Guid g ;
return Guid . TryParse ( str , out g ) ;
2012-07-17 03:51:34 +06:00
}
/// <summary>
/// Tries to parse a string into the supplied type by finding and using the Type's "Parse" method
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="val"></param>
/// <returns></returns>
public static T ParseInto < T > ( this string val )
{
return ( T ) val . ParseInto ( typeof ( T ) ) ;
}
/// <summary>
/// Tries to parse a string into the supplied type by finding and using the Type's "Parse" method
/// </summary>
/// <param name="val"></param>
/// <param name="type"></param>
/// <returns></returns>
public static object ParseInto ( this string val , Type type )
{
2016-08-05 19:28:46 +02:00
if ( string . IsNullOrEmpty ( val ) = = false )
2012-07-17 03:51:34 +06:00
{
TypeConverter tc = TypeDescriptor . GetConverter ( type ) ;
return tc . ConvertFrom ( val ) ;
}
return val ;
}
2017-08-24 21:24:14 +02:00
/// <summary>
/// Generates a hash of a string based on the FIPS compliance setting.
/// </summary>
/// <param name="str">Referrs to itself</param>
/// <returns>The hashed string</returns>
public static string GenerateHash ( this string str )
{
return CryptoConfig . AllowOnlyFipsAlgorithms
? str . ToSHA1 ( )
: str . ToMd5 ( ) ;
}
2012-07-17 03:51:34 +06:00
/// <summary>
/// Converts the string to MD5
/// </summary>
2017-08-24 21:24:14 +02:00
/// <param name="stringToConvert">Referrs to itself</param>
/// <returns>The MD5 hashed string</returns>
2012-07-17 03:51:34 +06:00
public static string ToMd5 ( this string stringToConvert )
{
2017-08-24 21:24:14 +02:00
return stringToConvert . GenerateHash ( "MD5" ) ;
2012-07-17 03:51:34 +06:00
}
2016-08-05 18:55:48 +02:00
/// <summary>
/// Converts the string to SHA1
/// </summary>
/// <param name="stringToConvert">referrs to itself</param>
2017-08-24 21:24:14 +02:00
/// <returns>The SHA1 hashed string</returns>
2016-08-05 18:55:48 +02:00
public static string ToSHA1 ( this string stringToConvert )
{
2017-08-24 21:24:14 +02:00
return stringToConvert . GenerateHash ( "SHA1" ) ;
}
2016-08-05 18:55:48 +02:00
2017-08-24 21:24:14 +02:00
/// <summary>Generate a hash of a string based on the hashType passed in
/// </summary>
/// <param name="str">Referrs to itself</param>
/// <param name="hashType">String with the hash type. See remarks section of the CryptoConfig Class in MSDN docs for a list of possible values.</param>
/// <returns>The hashed string</returns>
private static string GenerateHash ( this string str , string hashType )
{
//create an instance of the correct hashing provider based on the type passed in
var hasher = HashAlgorithm . Create ( hashType ) ;
if ( hasher = = null ) throw new InvalidOperationException ( "No hashing type found by name " + hashType ) ;
using ( hasher )
{
//convert our string into byte array
var byteArray = Encoding . UTF8 . GetBytes ( str ) ;
2016-08-05 18:55:48 +02:00
2017-08-24 21:24:14 +02:00
//get the hashed values created by our selected provider
var hashedByteArray = hasher . ComputeHash ( byteArray ) ;
2012-07-17 03:51:34 +06:00
2017-08-24 21:24:14 +02:00
//create a StringBuilder object
var stringBuilder = new StringBuilder ( ) ;
2012-07-17 03:51:34 +06:00
2017-08-24 21:24:14 +02:00
//loop to each each byte
foreach ( var b in hashedByteArray )
{
//append it to our StringBuilder
2017-09-15 18:22:19 +02:00
stringBuilder . Append ( b . ToString ( "x2" ) ) ;
2017-08-24 21:24:14 +02:00
}
2012-07-17 03:51:34 +06:00
2017-08-24 21:24:14 +02:00
//return the hashed value
return stringBuilder . ToString ( ) ;
}
}
2012-07-17 03:51:34 +06:00
/// <summary>
/// Decodes a string that was encoded with UrlTokenEncode
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
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 ) ;
}
/// <summary>
/// Encodes a string so that it is 'safe' for URLs, files, etc..
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
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 ) ;
}
/// <summary>
2017-09-15 18:22:19 +02:00
/// Ensures that the folder path ends with a DirectorySeperatorChar
2012-07-17 03:51:34 +06:00
/// </summary>
/// <param name="currentFolder"></param>
/// <returns></returns>
public static string NormaliseDirectoryPath ( this string currentFolder )
{
currentFolder = currentFolder
. IfNull ( x = > String . Empty )
. TrimEnd ( Path . DirectorySeparatorChar ) + Path . DirectorySeparatorChar ;
return currentFolder ;
}
2013-01-17 14:53:55 -01:00
2012-07-17 03:51:34 +06:00
/// <summary>
/// Truncates the specified text string.
/// </summary>
/// <param name="text">The text.</param>
/// <param name="maxLength">Length of the max.</param>
/// <param name="suffix">The suffix.</param>
/// <returns></returns>
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 ;
}
/// <summary>
/// Strips carrage returns and line feeds from the specified text.
/// </summary>
/// <param name="input">The input.</param>
/// <returns></returns>
public static string StripNewLines ( this string input )
{
return input . Replace ( "\r" , "" ) . Replace ( "\n" , "" ) ;
}
2017-11-10 11:27:12 +01:00
/// <summary>
/// Converts to single line by replacing line breaks with spaces.
/// </summary>
public static string ToSingleLine ( this string text )
{
if ( string . IsNullOrEmpty ( text ) ) return text ;
text = text . Replace ( "\r\n" , " " ) ; // remove CRLF
text = text . Replace ( "\r" , " " ) ; // remove CR
text = text . Replace ( "\n" , " " ) ; // remove LF
return text ;
}
2012-07-17 03:51:34 +06:00
public static string OrIfNullOrWhiteSpace ( this string input , string alternative )
{
return ! string . IsNullOrWhiteSpace ( input )
? input
: alternative ;
}
2012-12-28 19:03:15 -01:00
2013-02-26 18:12:22 -01:00
/// <summary>
/// Returns a copy of the string with the first character converted to uppercase.
/// </summary>
/// <param name="input">The string.</param>
/// <returns>The converted string.</returns>
public static string ToFirstUpper ( this string input )
{
return string . IsNullOrWhiteSpace ( input )
? input
: input . Substring ( 0 , 1 ) . ToUpper ( ) + input . Substring ( 1 ) ;
}
/// <summary>
/// Returns a copy of the string with the first character converted to lowercase.
/// </summary>
/// <param name="input">The string.</param>
/// <returns>The converted string.</returns>
public static string ToFirstLower ( this string input )
{
return string . IsNullOrWhiteSpace ( input )
? input
: input . Substring ( 0 , 1 ) . ToLower ( ) + input . Substring ( 1 ) ;
}
/// <summary>
/// Returns a copy of the string with the first character converted to uppercase using the casing rules of the specified culture.
/// </summary>
/// <param name="input">The string.</param>
/// <param name="culture">The culture.</param>
/// <returns>The converted string.</returns>
public static string ToFirstUpper ( this string input , CultureInfo culture )
{
return string . IsNullOrWhiteSpace ( input )
? input
: input . Substring ( 0 , 1 ) . ToUpper ( culture ) + input . Substring ( 1 ) ;
}
/// <summary>
/// Returns a copy of the string with the first character converted to lowercase using the casing rules of the specified culture.
/// </summary>
/// <param name="input">The string.</param>
/// <param name="culture">The culture.</param>
/// <returns>The converted string.</returns>
public static string ToFirstLower ( this string input , CultureInfo culture )
{
return string . IsNullOrWhiteSpace ( input )
? input
: input . Substring ( 0 , 1 ) . ToLower ( culture ) + input . Substring ( 1 ) ;
}
/// <summary>
/// Returns a copy of the string with the first character converted to uppercase using the casing rules of the invariant culture.
/// </summary>
/// <param name="input">The string.</param>
/// <returns>The converted string.</returns>
public static string ToFirstUpperInvariant ( this string input )
{
return string . IsNullOrWhiteSpace ( input )
? input
: input . Substring ( 0 , 1 ) . ToUpperInvariant ( ) + input . Substring ( 1 ) ;
}
/// <summary>
/// Returns a copy of the string with the first character converted to lowercase using the casing rules of the invariant culture.
/// </summary>
/// <param name="input">The string.</param>
/// <returns>The converted string.</returns>
public static string ToFirstLowerInvariant ( this string input )
{
return string . IsNullOrWhiteSpace ( input )
? input
: input . Substring ( 0 , 1 ) . ToLowerInvariant ( ) + input . Substring ( 1 ) ;
}
2013-02-07 13:30:50 -01:00
/// <summary>
/// Returns a new string in which all occurences of specified strings are replaced by other specified strings.
/// </summary>
/// <param name="text">The string to filter.</param>
/// <param name="replacements">The replacements definition.</param>
/// <returns>The filtered string.</returns>
public static string ReplaceMany ( this string text , IDictionary < string , string > replacements )
{
2018-04-27 10:26:32 +10:00
if ( text = = null ) throw new ArgumentNullException ( nameof ( text ) ) ;
if ( replacements = = null ) throw new ArgumentNullException ( nameof ( replacements ) ) ;
foreach ( KeyValuePair < string , string > item in replacements )
text = text . Replace ( item . Key , item . Value ) ;
return text ;
2013-02-07 13:30:50 -01:00
}
2013-03-11 14:58:07 -01:00
/// <summary>
/// Returns a new string in which all occurences of specified characters are replaced by a specified character.
/// </summary>
/// <param name="text">The string to filter.</param>
/// <param name="chars">The characters to replace.</param>
/// <param name="replacement">The replacement character.</param>
/// <returns>The filtered string.</returns>
public static string ReplaceMany ( this string text , char [ ] chars , char replacement )
2018-05-02 17:50:58 +02:00
{
2018-04-27 10:26:32 +10:00
if ( text = = null ) throw new ArgumentNullException ( nameof ( text ) ) ;
if ( chars = = null ) throw new ArgumentNullException ( nameof ( chars ) ) ;
for ( int i = 0 ; i < chars . Length ; i + + )
text = text . Replace ( chars [ i ] , replacement ) ;
return text ;
2018-05-02 17:50:58 +02:00
}
2013-03-11 14:58:07 -01:00
2013-02-15 08:42:08 -01:00
// FORMAT STRINGS
2016-05-26 17:12:04 +02:00
2013-01-22 08:44:06 -01:00
/// <summary>
2013-02-07 13:30:50 -01:00
/// Cleans a string to produce a string that can safely be used in an alias.
2013-01-22 08:44:06 -01:00
/// </summary>
2013-02-07 13:30:50 -01:00
/// <param name="alias">The text to filter.</param>
/// <returns>The safe alias.</returns>
2013-01-22 08:44:06 -01:00
public static string ToSafeAlias ( this string alias )
{
2016-08-24 19:30:33 +02:00
return Current . ShortStringHelper . CleanStringForSafeAlias ( alias ) ;
2013-02-07 13:30:50 -01:00
}
2013-01-22 08:44:06 -01:00
2013-11-04 15:00:13 +01:00
/// <summary>
/// Cleans a string to produce a string that can safely be used in an alias.
/// </summary>
/// <param name="alias">The text to filter.</param>
2013-11-05 09:00:23 +01:00
/// <param name="camel">A value indicating that we want to camel-case the alias.</param>
2013-11-04 15:00:13 +01:00
/// <returns>The safe alias.</returns>
2013-11-05 09:00:23 +01:00
public static string ToSafeAlias ( this string alias , bool camel )
2013-11-04 15:00:13 +01:00
{
2016-08-24 19:30:33 +02:00
var a = Current . ShortStringHelper . CleanStringForSafeAlias ( alias ) ;
2013-11-05 09:00:23 +01:00
if ( string . IsNullOrWhiteSpace ( a ) | | camel = = false ) return a ;
return char . ToLowerInvariant ( a [ 0 ] ) + a . Substring ( 1 ) ;
2013-11-04 15:00:13 +01:00
}
2013-02-07 13:30:50 -01:00
/// <summary>
/// Cleans a string, in the context of a specified culture, to produce a string that can safely be used in an alias.
/// </summary>
/// <param name="alias">The text to filter.</param>
/// <param name="culture">The culture.</param>
/// <returns>The safe alias.</returns>
2018-04-30 15:11:01 +02:00
public static string ToSafeAlias ( this string alias , string culture )
2013-02-07 13:30:50 -01:00
{
2016-08-24 19:30:33 +02:00
return Current . ShortStringHelper . CleanStringForSafeAlias ( alias , culture ) ;
2013-01-22 08:44:06 -01:00
}
2013-02-07 13:30:50 -01:00
/// <summary>
/// Cleans (but only if required) a string to produce a string that can safely be used in an alias.
/// </summary>
/// <param name="alias">The text to filter.</param>
/// <returns>The safe alias.</returns>
/// <remarks>Checks <c>UmbracoSettings.ForceSafeAliases</c> to determine whether it should filter the text.</remarks>
2013-01-22 08:44:06 -01:00
public static string ToSafeAliasWithForcingCheck ( this string alias )
{
2013-09-25 19:23:41 +10:00
return UmbracoConfig . For . UmbracoSettings ( ) . Content . ForceSafeAliases ? alias . ToSafeAlias ( ) : alias ;
2013-01-22 08:44:06 -01:00
}
2013-02-15 08:42:08 -01:00
/// <summary>
2013-02-07 13:30:50 -01:00
/// Cleans (but only if required) a string, in the context of a specified culture, to produce a string that can safely be used in an alias.
2013-02-15 08:42:08 -01:00
/// </summary>
2013-02-07 13:30:50 -01:00
/// <param name="alias">The text to filter.</param>
/// <param name="culture">The culture.</param>
/// <returns>The safe alias.</returns>
/// <remarks>Checks <c>UmbracoSettings.ForceSafeAliases</c> to determine whether it should filter the text.</remarks>
2018-04-30 15:11:01 +02:00
public static string ToSafeAliasWithForcingCheck ( this string alias , string culture )
2013-02-15 08:42:08 -01:00
{
2013-09-25 19:23:41 +10:00
return UmbracoConfig . For . UmbracoSettings ( ) . Content . ForceSafeAliases ? alias . ToSafeAlias ( culture ) : alias ;
2013-02-07 13:30:50 -01:00
}
2013-02-15 08:42:08 -01:00
2013-02-07 13:30:50 -01:00
// the new methods to get a url segment
2014-08-12 14:18:49 +02:00
2013-02-15 08:42:08 -01:00
/// <summary>
2013-02-07 13:30:50 -01:00
/// Cleans a string to produce a string that can safely be used in an url segment.
2013-02-15 08:42:08 -01:00
/// </summary>
2013-02-07 13:30:50 -01:00
/// <param name="text">The text to filter.</param>
/// <returns>The safe url segment.</returns>
public static string ToUrlSegment ( this string text )
2013-02-15 08:42:08 -01:00
{
2018-05-02 17:50:58 +02:00
if ( string . IsNullOrWhiteSpace ( text ) ) throw new ArgumentNullOrEmptyException ( nameof ( text ) ) ;
2016-08-24 19:30:33 +02:00
return Current . ShortStringHelper . CleanStringForUrlSegment ( text ) ;
2013-02-07 13:30:50 -01:00
}
2013-02-15 08:42:08 -01:00
2013-02-07 13:30:50 -01:00
/// <summary>
/// Cleans a string, in the context of a specified culture, to produce a string that can safely be used in an url segment.
/// </summary>
/// <param name="text">The text to filter.</param>
/// <param name="culture">The culture.</param>
/// <returns>The safe url segment.</returns>
2018-04-28 16:34:43 +02:00
public static string ToUrlSegment ( this string text , string culture )
2018-05-02 17:50:58 +02:00
{
if ( string . IsNullOrWhiteSpace ( text ) ) throw new ArgumentNullOrEmptyException ( nameof ( text ) ) ;
2016-08-24 19:30:33 +02:00
return Current . ShortStringHelper . CleanStringForUrlSegment ( text , culture ) ;
2013-02-07 13:30:50 -01:00
}
2013-02-15 08:42:08 -01:00
2013-02-07 13:30:50 -01:00
// the new methods to clean a string (to alias, url segment...)
2013-02-15 08:42:08 -01:00
2013-02-07 13:30:50 -01:00
/// <summary>
/// Cleans a string.
/// </summary>
/// <param name="text">The text to clean.</param>
2016-05-26 17:12:04 +02:00
/// <param name="stringType">A flag indicating the target casing and encoding of the string. By default,
2013-02-07 13:30:50 -01:00
/// strings are cleaned up to camelCase and Ascii.</param>
/// <returns>The clean string.</returns>
2016-08-24 19:30:33 +02:00
/// <remarks>The string is cleaned in the context of the ICurrent.ShortStringHelper default culture.</remarks>
2013-10-03 17:49:38 +02:00
public static string ToCleanString ( this string text , CleanStringType stringType )
2013-02-07 13:30:50 -01:00
{
2016-08-24 19:30:33 +02:00
return Current . ShortStringHelper . CleanString ( text , stringType ) ;
2013-02-07 13:30:50 -01:00
}
2013-02-15 08:42:08 -01:00
2013-02-07 13:30:50 -01:00
/// <summary>
/// Cleans a string, using a specified separator.
/// </summary>
/// <param name="text">The text to clean.</param>
2016-05-26 17:12:04 +02:00
/// <param name="stringType">A flag indicating the target casing and encoding of the string. By default,
2013-02-07 13:30:50 -01:00
/// strings are cleaned up to camelCase and Ascii.</param>
/// <param name="separator">The separator.</param>
/// <returns>The clean string.</returns>
2016-08-24 19:30:33 +02:00
/// <remarks>The string is cleaned in the context of the ICurrent.ShortStringHelper default culture.</remarks>
2013-10-03 17:49:38 +02:00
public static string ToCleanString ( this string text , CleanStringType stringType , char separator )
2013-02-07 13:30:50 -01:00
{
2016-08-24 19:30:33 +02:00
return Current . ShortStringHelper . CleanString ( text , stringType , separator ) ;
2013-02-07 13:30:50 -01:00
}
2013-02-15 08:42:08 -01:00
2013-02-07 13:30:50 -01:00
/// <summary>
/// Cleans a string in the context of a specified culture.
/// </summary>
/// <param name="text">The text to clean.</param>
2016-05-26 17:12:04 +02:00
/// <param name="stringType">A flag indicating the target casing and encoding of the string. By default,
2013-02-07 13:30:50 -01:00
/// strings are cleaned up to camelCase and Ascii.</param>
/// <param name="culture">The culture.</param>
/// <returns>The clean string.</returns>
2018-04-30 15:11:01 +02:00
public static string ToCleanString ( this string text , CleanStringType stringType , string culture )
2013-02-07 13:30:50 -01:00
{
2016-08-24 19:30:33 +02:00
return Current . ShortStringHelper . CleanString ( text , stringType , culture ) ;
2013-02-07 13:30:50 -01:00
}
2013-02-15 08:42:08 -01:00
2013-02-07 13:30:50 -01:00
/// <summary>
/// Cleans a string in the context of a specified culture, using a specified separator.
/// </summary>
/// <param name="text">The text to clean.</param>
2016-05-26 17:12:04 +02:00
/// <param name="stringType">A flag indicating the target casing and encoding of the string. By default,
2013-02-07 13:30:50 -01:00
/// strings are cleaned up to camelCase and Ascii.</param>
/// <param name="separator">The separator.</param>
/// <param name="culture">The culture.</param>
/// <returns>The clean string.</returns>
2018-04-30 15:11:01 +02:00
public static string ToCleanString ( this string text , CleanStringType stringType , char separator , string culture )
2013-02-07 13:30:50 -01:00
{
2016-08-24 19:30:33 +02:00
return Current . ShortStringHelper . CleanString ( text , stringType , separator , culture ) ;
2013-02-15 08:42:08 -01:00
}
2016-08-24 19:30:33 +02:00
// note: LegacyCurrent.ShortStringHelper will produce 100% backward-compatible output for SplitPascalCasing.
// other helpers may not. DefaultCurrent.ShortStringHelper produces better, but non-compatible, results.
2013-02-07 13:30:50 -01:00
2013-02-15 08:42:08 -01:00
/// <summary>
2015-06-15 16:12:46 +02:00
/// Splits a Pascal cased string into a phrase separated by spaces.
2013-02-15 08:42:08 -01:00
/// </summary>
2013-02-07 13:30:50 -01:00
/// <param name="phrase">The text to split.</param>
/// <returns>The splitted text.</returns>
2013-02-15 08:42:08 -01:00
public static string SplitPascalCasing ( this string phrase )
{
2016-08-24 19:30:33 +02:00
return Current . ShortStringHelper . SplitPascalCasing ( phrase , ' ' ) ;
2013-02-15 08:42:08 -01:00
}
2013-03-11 14:58:07 -01:00
2015-12-22 16:07:45 +01:00
//NOTE: Not sure what this actually does but is used a few places, need to figure it out and then move to StringExtensions and obsolete.
// it basically is yet another version of SplitPascalCasing
// plugging string extensions here to be 99% compatible
// the only diff. is with numbers, Number6Is was "Number6 Is", and the new string helper does it too,
// but the legacy one does "Number6Is"... assuming it is not a big deal.
internal static string SpaceCamelCasing ( this string phrase )
{
return phrase . Length < 2 ? phrase : phrase . SplitPascalCasing ( ) . ToFirstUpperInvariant ( ) ;
}
2013-03-11 14:58:07 -01:00
/// <summary>
/// Cleans a string, in the context of the invariant culture, to produce a string that can safely be used as a filename,
/// both internally (on disk) and externally (as a url).
/// </summary>
/// <param name="text">The text to filter.</param>
/// <returns>The safe filename.</returns>
public static string ToSafeFileName ( this string text )
{
2016-08-24 19:30:33 +02:00
return Current . ShortStringHelper . CleanStringForSafeFileName ( text ) ;
2013-03-11 14:58:07 -01:00
}
/// <summary>
/// Cleans a string, in the context of the invariant culture, to produce a string that can safely be used as a filename,
/// both internally (on disk) and externally (as a url).
/// </summary>
/// <param name="text">The text to filter.</param>
/// <param name="culture">The culture.</param>
/// <returns>The safe filename.</returns>
2018-04-30 15:11:01 +02:00
public static string ToSafeFileName ( this string text , string culture )
2013-03-11 14:58:07 -01:00
{
2016-08-24 19:30:33 +02:00
return Current . ShortStringHelper . CleanStringForSafeFileName ( text , culture ) ;
2013-03-11 14:58:07 -01:00
}
2014-08-12 14:18:49 +02:00
2013-06-19 18:11:28 +02:00
/// <summary>
2016-05-26 17:12:04 +02:00
/// An extension method that returns a new string in which all occurrences of a
2013-06-19 18:11:28 +02:00
/// specified string in the current instance are replaced with another specified string.
/// StringComparison specifies the type of search to use for the specified string.
/// </summary>
/// <param name="source">Current instance of the string</param>
/// <param name="oldString">Specified string to replace</param>
/// <param name="newString">Specified string to inject</param>
/// <param name="stringComparison">String Comparison object to specify search type</param>
/// <returns>Updated string</returns>
public static string Replace ( this string source , string oldString , string newString , StringComparison stringComparison )
{
2014-04-29 13:54:26 +02:00
// This initialisation ensures the first check starts at index zero of the source. On successive checks for
// a match, the source is skipped to immediately after the last replaced occurrence for efficiency
2016-05-26 17:12:04 +02:00
// and to avoid infinite loops when oldString and newString compare equal.
2014-04-29 13:54:26 +02:00
int index = - 1 * newString . Length ;
2013-06-19 18:11:28 +02:00
2014-04-29 13:54:26 +02:00
// Determine if there are any matches left in source, starting from just after the result of replacing the last match.
2014-08-12 14:18:49 +02:00
while ( ( index = source . IndexOf ( oldString , index + newString . Length , stringComparison ) ) > = 0 )
2013-06-19 18:11:28 +02:00
{
2014-04-29 13:03:52 +02:00
// Remove the old text.
2013-06-19 18:11:28 +02:00
source = source . Remove ( index , oldString . Length ) ;
2014-04-29 13:03:52 +02:00
// Add the replacemenet text.
2013-06-19 18:11:28 +02:00
source = source . Insert ( index , newString ) ;
}
return source ;
}
2013-09-07 10:24:52 +02:00
/// <summary>
/// Converts a literal string into a C# expression.
/// </summary>
/// <param name="s">Current instance of the string.</param>
/// <returns>The string in a C# format.</returns>
public static string ToCSharpString ( this string s )
{
if ( s = = null ) return "<null>" ;
// http://stackoverflow.com/questions/323640/can-i-convert-a-c-sharp-string-value-to-an-escaped-string-literal
var sb = new StringBuilder ( s . Length + 2 ) ;
for ( var rp = 0 ; rp < s . Length ; rp + + )
{
var c = s [ rp ] ;
if ( c < ToCSharpEscapeChars . Length & & '\0' ! = ToCSharpEscapeChars [ c ] )
sb . Append ( '\\' ) . Append ( ToCSharpEscapeChars [ c ] ) ;
else if ( '~' > = c & & c > = ' ' )
sb . Append ( c ) ;
else
sb . Append ( @"\x" )
. Append ( ToCSharpHexDigitLower [ c > > 12 & 0x0F ] )
. Append ( ToCSharpHexDigitLower [ c > > 8 & 0x0F ] )
. Append ( ToCSharpHexDigitLower [ c > > 4 & 0x0F ] )
. Append ( ToCSharpHexDigitLower [ c & 0x0F ] ) ;
}
return sb . ToString ( ) ;
// requires full trust
/ *
using ( var writer = new StringWriter ( ) )
using ( var provider = CodeDomProvider . CreateProvider ( "CSharp" ) )
{
provider . GenerateCodeFromExpression ( new CodePrimitiveExpression ( s ) , writer , null ) ;
return writer . ToString ( ) . Replace ( string . Format ( "\" +{0}\t\"" , Environment . NewLine ) , "" ) ;
}
* /
}
2016-03-29 14:29:27 +02:00
public static string EscapeRegexSpecialCharacters ( this string text )
{
var regexSpecialCharacters = new Dictionary < string , string >
{
{ "." , @"\." } ,
{ "(" , @"\(" } ,
{ ")" , @"\)" } ,
{ "]" , @"\]" } ,
{ "[" , @"\[" } ,
{ "{" , @"\{" } ,
{ "}" , @"\}" } ,
{ "?" , @"\?" } ,
{ "!" , @"\!" } ,
{ "$" , @"\$" } ,
{ "^" , @"\^" } ,
{ "+" , @"\+" } ,
{ "*" , @"\*" } ,
{ "|" , @"\|" } ,
{ "<" , @"\<" } ,
{ ">" , @"\>" }
} ;
return ReplaceMany ( text , regexSpecialCharacters ) ;
}
2018-05-10 14:28:16 +01:00
/// <summary>
/// Checks whether a string "haystack" contains within it any of the strings in the "needles" collection and returns true if it does or false if it doesn't
/// </summary>
/// <param name="haystack">The string to check</param>
/// <param name="needles">The collection of strings to check are contained within the first string</param>
/// <param name="comparison">The type of comparision to perform - defaults to <see cref="StringComparison.CurrentCulture"/></param>
/// <returns>True if any of the needles are contained with haystack; otherwise returns false</returns>
/// Added fix to ensure the comparison is used - see http://issues.umbraco.org/issue/U4-11313
2014-04-16 17:01:26 +10:00
public static bool ContainsAny ( this string haystack , IEnumerable < string > needles , StringComparison comparison = StringComparison . CurrentCulture )
{
2018-05-10 14:28:16 +01:00
if ( haystack = = null )
throw new ArgumentNullException ( "haystack" ) ;
if ( string . IsNullOrEmpty ( haystack ) | | needles = = null | | ! needles . Any ( ) )
2014-04-16 17:01:26 +10:00
{
2018-05-10 14:28:16 +01:00
return false ;
2014-04-16 17:01:26 +10:00
}
2018-05-10 14:28:16 +01:00
return needles . Any ( value = > haystack . IndexOf ( value , comparison ) > = 0 ) ;
2014-04-16 17:01:26 +10:00
}
2014-04-17 14:23:37 +10:00
public static bool CsvContains ( this string csv , string value )
{
if ( string . IsNullOrEmpty ( csv ) )
{
return false ;
}
var idCheckList = csv . Split ( new [ ] { "," } , StringSplitOptions . RemoveEmptyEntries ) ;
return idCheckList . Contains ( value ) ;
}
2014-08-12 14:18:49 +02:00
// From: http://stackoverflow.com/a/961504/5018
// filters control characters but allows only properly-formed surrogate sequences
2016-10-19 13:50:18 +02:00
private static readonly Lazy < Regex > InvalidXmlChars = new Lazy < Regex > ( ( ) = >
2014-08-12 14:18:49 +02:00
new Regex (
@"(?<![\uD800-\uDBFF])[\uDC00-\uDFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|[\x00-\x08\x0B\x0C\x0E-\x1F\x7F-\x9F\uFEFF\uFFFE\uFFFF]" ,
2016-10-19 13:50:18 +02:00
RegexOptions . Compiled ) ) ;
2014-08-12 14:18:49 +02:00
2014-08-13 09:38:40 +02:00
/// <summary>
2016-05-26 17:12:04 +02:00
/// An extension method that returns a new string in which all occurrences of an
/// unicode characters that are invalid in XML files are replaced with an empty string.
2014-08-13 09:38:40 +02:00
/// </summary>
/// <param name="text">Current instance of the string</param>
/// <returns>Updated string</returns>
2016-05-26 17:12:04 +02:00
///
2014-08-12 14:18:49 +02:00
/// <summary>
/// removes any unusual unicode characters that can't be encoded into XML
/// </summary>
2014-08-13 09:38:40 +02:00
internal static string ToValidXmlString ( this string text )
2014-08-12 14:18:49 +02:00
{
2016-10-19 13:50:18 +02:00
return string . IsNullOrEmpty ( text ) ? text : InvalidXmlChars . Value . Replace ( text , "" ) ;
2014-08-12 14:18:49 +02:00
}
2015-08-11 15:56:48 +02:00
/// <summary>
/// Converts a string to a Guid - WARNING, depending on the string, this may not be unique
/// </summary>
/// <param name="text"></param>
/// <returns></returns>
internal static Guid ToGuid ( this string text )
{
2017-08-24 21:24:14 +02:00
return CreateGuidFromHash ( UrlNamespace ,
text ,
CryptoConfig . AllowOnlyFipsAlgorithms
? 5 // SHA1
: 3 ) ; // MD5
}
/// <summary>
/// The namespace for URLs (from RFC 4122, Appendix C).
2017-09-23 10:08:18 +02:00
///
2017-08-24 21:24:14 +02:00
/// See <a href="http://www.ietf.org/rfc/rfc4122.txt">RFC 4122</a>
/// </summary>
internal static readonly Guid UrlNamespace = new Guid ( "6ba7b811-9dad-11d1-80b4-00c04fd430c8" ) ;
/// <summary>
/// Creates a name-based UUID using the algorithm from RFC 4122 §4.3.
2017-09-23 10:08:18 +02:00
///
2017-08-24 21:24:14 +02:00
/// See <a href="https://github.com/LogosBible/Logos.Utility/blob/master/src/Logos.Utility/GuidUtility.cs#L34">GuidUtility.cs</a> for original implementation.
/// </summary>
/// <param name="namespaceId">The ID of the namespace.</param>
/// <param name="name">The name (within that namespace).</param>
/// <param name="version">The version number of the UUID to create; this value must be either
/// 3 (for MD5 hashing) or 5 (for SHA-1 hashing).</param>
/// <returns>A UUID derived from the namespace and name.</returns>
/// <remarks>See <a href="http://code.logos.com/blog/2011/04/generating_a_deterministic_guid.html">Generating a deterministic GUID</a>.</remarks>
internal static Guid CreateGuidFromHash ( Guid namespaceId , string name , int version )
{
if ( name = = null )
throw new ArgumentNullException ( "name" ) ;
if ( version ! = 3 & & version ! = 5 )
throw new ArgumentOutOfRangeException ( "version" , "version must be either 3 or 5." ) ;
// convert the name to a sequence of octets (as defined by the standard or conventions of its namespace) (step 3)
// ASSUME: UTF-8 encoding is always appropriate
byte [ ] nameBytes = Encoding . UTF8 . GetBytes ( name ) ;
// convert the namespace UUID to network order (step 3)
byte [ ] namespaceBytes = namespaceId . ToByteArray ( ) ;
SwapByteOrder ( namespaceBytes ) ;
// comput the hash of the name space ID concatenated with the name (step 4)
byte [ ] hash ;
using ( HashAlgorithm algorithm = version = = 3 ? ( HashAlgorithm ) MD5 . Create ( ) : SHA1 . Create ( ) )
{
algorithm . TransformBlock ( namespaceBytes , 0 , namespaceBytes . Length , null , 0 ) ;
algorithm . TransformFinalBlock ( nameBytes , 0 , nameBytes . Length ) ;
hash = algorithm . Hash ;
}
// most bytes from the hash are copied straight to the bytes of the new GUID (steps 5-7, 9, 11-12)
byte [ ] newGuid = new byte [ 16 ] ;
Array . Copy ( hash , 0 , newGuid , 0 , 16 ) ;
// set the four most significant bits (bits 12 through 15) of the time_hi_and_version field to the appropriate 4-bit version number from Section 4.1.3 (step 8)
newGuid [ 6 ] = ( byte ) ( ( newGuid [ 6 ] & 0x0F ) | ( version < < 4 ) ) ;
// set the two most significant bits (bits 6 and 7) of the clock_seq_hi_and_reserved to zero and one, respectively (step 10)
newGuid [ 8 ] = ( byte ) ( ( newGuid [ 8 ] & 0x3F ) | 0x80 ) ;
// convert the resulting UUID to local byte order (step 13)
SwapByteOrder ( newGuid ) ;
return new Guid ( newGuid ) ;
}
// Converts a GUID (expressed as a byte array) to/from network order (MSB-first).
internal static void SwapByteOrder ( byte [ ] guid )
{
SwapBytes ( guid , 0 , 3 ) ;
SwapBytes ( guid , 1 , 2 ) ;
SwapBytes ( guid , 4 , 5 ) ;
SwapBytes ( guid , 6 , 7 ) ;
}
private static void SwapBytes ( byte [ ] guid , int left , int right )
{
byte temp = guid [ left ] ;
guid [ left ] = guid [ right ] ;
guid [ right ] = temp ;
2015-08-11 15:56:48 +02:00
}
2018-04-26 16:03:08 +02:00
/// <summary>
/// Turns an null-or-whitespace string into a null string.
/// </summary>
public static string NullEmpty ( this string text )
= > string . IsNullOrWhiteSpace ( text ) ? null : text ;
2012-07-17 03:51:34 +06:00
}
}