diff --git a/src/Umbraco.Core/StringExtensions.cs b/src/Umbraco.Core/StringExtensions.cs index 03b21cfc0c..8d4883a6fc 100644 --- a/src/Umbraco.Core/StringExtensions.cs +++ b/src/Umbraco.Core/StringExtensions.cs @@ -23,7 +23,9 @@ namespace Umbraco.Core /// public static class StringExtensions { + [UmbracoWillObsolete("Do not use this constants. See IShortStringHelper.CleanStringForSafeAliasJavaScriptCode.")] public const string UmbracoValidAliasCharacters = "_-abcdefghijklmnopqrstuvwxyz1234567890"; + [UmbracoWillObsolete("Do not use this constants. See IShortStringHelper.CleanStringForSafeAliasJavaScriptCode.")] public const string UmbracoInvalidFirstCharacters = "01234567890"; /// diff --git a/src/Umbraco.Core/Strings/DefaultShortStringHelper.cs b/src/Umbraco.Core/Strings/DefaultShortStringHelper.cs index a3fd8f7ec1..16aa05d1a0 100644 --- a/src/Umbraco.Core/Strings/DefaultShortStringHelper.cs +++ b/src/Umbraco.Core/Strings/DefaultShortStringHelper.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Linq; using System.Globalization; +using Umbraco.Core.Configuration; namespace Umbraco.Core.Strings { @@ -133,6 +134,65 @@ namespace Umbraco.Core.Strings #endregion + #region JavaScript + + const string CsfaJsValidCharacters = "_abcdefghijklmnopqrstuvwxyz1234567890"; + const string CsfaJsInvalidFirstCharacters = "01234567890_"; + + private const string CsfsaJsFormat = @" +var UMBRACO_FORCE_SAFE_ALIAS = {0}; +var UMBRACO_FORCE_SAFE_ALIAS_VALIDCHARS = '{1}'; +var UMBRACO_FORCE_SAFE_ALIAS_INVALID_FIRST_CHARS = '{2}'; + +function safeAlias(alias) {{ + if (UMBRACO_FORCE_SAFE_ALIAS) {{ + var safeAlias = ''; + var aliasLength = alias.length; + for (var i = 0; i < aliasLength; i++) {{ + currentChar = alias.substring(i, i + 1); + if (UMBRACO_FORCE_SAFE_ALIAS_VALIDCHARS.indexOf(currentChar.toLowerCase()) > -1) {{ + // check for camel (if previous character is a space, we'll upper case the current one + if (safeAlias == '' && UMBRACO_FORCE_SAFE_ALIAS_INVALID_FIRST_CHARS.indexOf(currentChar.toLowerCase()) > 0) {{ + currentChar = ''; + }} else {{ + // first char should always be lowercase (camel style) + if (safeAlias.length == 0) + currentChar = currentChar.toLowerCase(); + + if (i < aliasLength - 1 && safeAlias != '' && alias.substring(i - 1, i) == ' ') + currentChar = currentChar.toUpperCase(); + + safeAlias += currentChar; + }} + }} + }} + + return safeAlias; + + }} else {{ + return alias; + }} +}} + +function isSafeAlias(alias) {{ + return alias == safeAlias(alias); +}} +"; + + /// + /// Gets the JavaScript code defining functions safeAlias(alias) and isSafeAlias(alias). + /// + public string CleanStringForSafeAliasJavaScriptCode + { + get + { + return string.Format(CsfsaJsFormat, + UmbracoSettings.ForceSafeAliases ? "true" : "false", CsfaJsValidCharacters, CsfaJsInvalidFirstCharacters); + } + } + + #endregion + #region IShortStringHelper CleanFor... /// diff --git a/src/Umbraco.Core/Strings/IShortStringHelper.cs b/src/Umbraco.Core/Strings/IShortStringHelper.cs index 4ce03888e3..d98f14ac96 100644 --- a/src/Umbraco.Core/Strings/IShortStringHelper.cs +++ b/src/Umbraco.Core/Strings/IShortStringHelper.cs @@ -15,12 +15,20 @@ namespace Umbraco.Core.Strings /// Will be called by ShortStringHelperResolver when resolution freezes. void Freeze(); + /// + /// Gets the JavaScript code defining functions safeAlias(alias) and isSafeAlias(alias). + /// + string CleanStringForSafeAliasJavaScriptCode { get; } + /// /// Cleans a string to produce a string that can safely be used in an alias. /// /// The text to filter. /// The safe alias. - /// The string will be cleaned in the context of the IShortStringHelper default culture. + /// + /// The string will be cleaned in the context of the IShortStringHelper default culture. + /// A safe alias is [a-z][a-zA-Z0-9_]* although legacy will also accept '-', and '_' at the beginning. + /// string CleanStringForSafeAlias(string text); /// diff --git a/src/Umbraco.Core/Strings/LegacyShortStringHelper.cs b/src/Umbraco.Core/Strings/LegacyShortStringHelper.cs index d278cf32e8..30367c176c 100644 --- a/src/Umbraco.Core/Strings/LegacyShortStringHelper.cs +++ b/src/Umbraco.Core/Strings/LegacyShortStringHelper.cs @@ -35,6 +35,65 @@ namespace Umbraco.Core.Strings #endregion + #region JavaScript + + const string CsfaJsValidCharacters = "_-abcdefghijklmnopqrstuvwxyz1234567890"; + const string CsfaJsInvalidFirstCharacters = "01234567890"; + + private const string CsfsaJsFormat = @" +var UMBRACO_FORCE_SAFE_ALIAS = {0}; +var UMBRACO_FORCE_SAFE_ALIAS_VALIDCHARS = '{1}'; +var UMBRACO_FORCE_SAFE_ALIAS_INVALID_FIRST_CHARS = '{2}'; + +function safeAlias(alias) {{ + if (UMBRACO_FORCE_SAFE_ALIAS) {{ + var safeAlias = ''; + var aliasLength = alias.length; + for (var i = 0; i < aliasLength; i++) {{ + currentChar = alias.substring(i, i + 1); + if (UMBRACO_FORCE_SAFE_ALIAS_VALIDCHARS.indexOf(currentChar.toLowerCase()) > -1) {{ + // check for camel (if previous character is a space, we'll upper case the current one + if (safeAlias == '' && UMBRACO_FORCE_SAFE_ALIAS_INVALID_FIRST_CHARS.indexOf(currentChar.toLowerCase()) > 0) {{ + currentChar = ''; + }} else {{ + // first char should always be lowercase (camel style) + if (safeAlias.length == 0) + currentChar = currentChar.toLowerCase(); + + if (i < aliasLength - 1 && safeAlias != '' && alias.substring(i - 1, i) == ' ') + currentChar = currentChar.toUpperCase(); + + safeAlias += currentChar; + }} + }} + }} + + return safeAlias; + + }} else {{ + return alias; + }} +}} + +function isSafeAlias(alias) {{ + return alias == safeAlias(alias); +}} +"; + + /// + /// Gets the JavaScript code defining functions safeAlias(alias) and isSafeAlias(alias). + /// + public string CleanStringForSafeAliasJavaScriptCode + { + get + { + return string.Format(CsfsaJsFormat, + UmbracoSettings.ForceSafeAliases ? "true" : "false", CsfaJsValidCharacters, CsfaJsInvalidFirstCharacters); + } + } + + #endregion + #region IShortStringHelper CleanFor... /// diff --git a/src/Umbraco.Core/Strings/Notes.txt b/src/Umbraco.Core/Strings/Notes.txt new file mode 100644 index 0000000000..b1f82636cd --- /dev/null +++ b/src/Umbraco.Core/Strings/Notes.txt @@ -0,0 +1,40 @@ +Two constants are defined in StringExtensions: +UmbracoValidAliasCharacters +UmbracoInvalidFirstCharacters + +They were used exclusively by umbraco/js/UmbracoCasingRules.aspx.cs to define some JavaScript variables +which in turn were used by genericProperty.js to provide validation for aliases. + +UmbracoCasingRules.aspx and genericProperty.js are used in: + Umbraco.Web\umbraco.presentation\umbraco\controls\ContentTypeControlNew.ascx + Umbraco.Web\umbraco.presentation\umbraco\controls\GenericProperties\GenericProperty.ascx + +genericProperty.js defined the following functions: + safeAlias(alias) : provides a safe alias version of alias + isValidAlias(alias) : whether an alias is safe + checkAlias(id) : plugs a safe alias validator / corrector on an element + duplicatePropertyNameAsSafeAlias(propertyId, aliasId) : creates the alias from the prop. name + +These are used to validate the alias of the content type, and of properties. No other alias in +Umbraco back-end seem to be validated in UI at that point. And, there seem to be no server-side +validation so the server just seems trust the UI. + +Whereas in theory, we should only accept that the casing required by the user is different +from what our "toSafeAlias" method would provide, anything else is a validation error. + +Legacy safe aliases accept leading and trailing dashes and underscores. + +REFACTOR + +Abstract string methods into IShortStringHelper, and create a new DefaultShortStringHelper which +re-implements methods in a clean nice way. The new aliases do not accepts dashes, nor leading +underscores. + +Entirely refactor StringExtensions to rely on IShortStringHelper (via a resolver). + +The constants in StringExtensions should go, but we keep them for backward compatility. +The legacy helper is 100% backward compatible even at JavaScript level, but the new helper +is NOT, it does server-side validation of aliases, all sorts of things, using an api service. + +UmbracoCasingRules.aspx.cs is _gone_ in all cases, replaced by the JavaScript served by +the api service. diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index 675ee57f4d..1bde2f8857 100644 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -751,7 +751,9 @@ umbraco.interfaces - + + +