diff --git a/build/NuSpecs/UmbracoCms.Core.nuspec b/build/NuSpecs/UmbracoCms.Core.nuspec index 3a972cd9b6..22fb9ff366 100644 --- a/build/NuSpecs/UmbracoCms.Core.nuspec +++ b/build/NuSpecs/UmbracoCms.Core.nuspec @@ -27,7 +27,7 @@ - + diff --git a/src/Umbraco.Core/IO/IOHelper.cs b/src/Umbraco.Core/IO/IOHelper.cs index 161296baff..2d87f634f4 100644 --- a/src/Umbraco.Core/IO/IOHelper.cs +++ b/src/Umbraco.Core/IO/IOHelper.cs @@ -52,7 +52,23 @@ namespace Umbraco.Core.IO return VirtualPathUtility.ToAbsolute(virtualPath, SystemDirectories.Root); } - [Obsolete("Use Umbraco.Web.Templates.TemplateUtilities.ResolveUrlsFromTextString instead, this method on this class will be removed in future versions")] + public static Attempt TryResolveUrl(string virtualPath) + { + try + { + if (virtualPath.StartsWith("~")) + return Attempt.Succeed(virtualPath.Replace("~", SystemDirectories.Root).Replace("//", "/")); + if (Uri.IsWellFormedUriString(virtualPath, UriKind.Absolute)) + return Attempt.Succeed(virtualPath); + return Attempt.Succeed(VirtualPathUtility.ToAbsolute(virtualPath, SystemDirectories.Root)); + } + catch (Exception ex) + { + return Attempt.Fail(virtualPath, ex); + } + } + + [Obsolete("Use Umbraco.Web.Templates.TemplateUtilities.ResolveUrlsFromTextString instead, this method on this class will be removed in future versions")] internal static string ResolveUrlsFromTextString(string text) { if (UmbracoConfig.For.UmbracoSettings().Content.ResolveUrlsFromTextString) diff --git a/src/Umbraco.Core/Macros/MacroTagParser.cs b/src/Umbraco.Core/Macros/MacroTagParser.cs index 7da2e1d3fc..850e3845af 100644 --- a/src/Umbraco.Core/Macros/MacroTagParser.cs +++ b/src/Umbraco.Core/Macros/MacroTagParser.cs @@ -12,7 +12,7 @@ namespace Umbraco.Core.Macros internal class MacroTagParser { private static readonly Regex MacroRteContent = new Regex(@"()", RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.Singleline); - private static readonly Regex MacroPersistedFormat = new Regex(@"(<\?UMBRACO_MACRO(?:.+?)?macroAlias=[""']([^""\'\n\r]+?)[""'].+?)(?:/>|>.*?)", RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.Singleline); + private static readonly Regex MacroPersistedFormat = new Regex(@"(<\?UMBRACO_MACRO (?:.+?)?macroAlias=[""']([^""\'\n\r]+?)[""'].+?)(?:/>|>.*?)", RegexOptions.Compiled | RegexOptions.IgnoreCase); /// /// This formats the persisted string to something useful for the rte so that the macro renders properly since we diff --git a/src/Umbraco.Core/StringExtensions.cs b/src/Umbraco.Core/StringExtensions.cs index 0f2796f8da..e54164bd39 100644 --- a/src/Umbraco.Core/StringExtensions.cs +++ b/src/Umbraco.Core/StringExtensions.cs @@ -15,6 +15,7 @@ using Umbraco.Core.Configuration; using System.Web.Security; using Umbraco.Core.Strings; using Umbraco.Core.CodeAnnotations; +using Umbraco.Core.IO; namespace Umbraco.Core { @@ -59,6 +60,50 @@ namespace Umbraco.Core } + /// + /// 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 + /// + /// + /// + /// + /// This is only used for legacy purposes for the Action.JsSource stuff and shouldn't be needed in v8 + /// + internal static Attempt 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. + return resolvedUrlResult.Success + ? resolvedUrlResult + : Attempt.Succeed(input); + } + } + + return Attempt.Fail(input); + } + /// /// This tries to detect a json string, this is not a fail safe way but it is quicker than doing /// a try/catch when deserializing when it is not json. diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index 438ad349a5..9266d16f36 100644 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -86,7 +86,7 @@ False - ..\packages\MySql.Data.6.9.6\lib\net45\MySql.Data.dll + ..\packages\MySql.Data.6.9.7\lib\net45\MySql.Data.dll False diff --git a/src/Umbraco.Core/packages.config b/src/Umbraco.Core/packages.config index 9edfbb097e..5f9cadcda8 100644 --- a/src/Umbraco.Core/packages.config +++ b/src/Umbraco.Core/packages.config @@ -14,7 +14,7 @@ - + diff --git a/src/Umbraco.Tests/Controllers/BackOfficeControllerUnitTests.cs b/src/Umbraco.Tests/Controllers/BackOfficeControllerUnitTests.cs index 22c0d92979..59626a1b9a 100644 --- a/src/Umbraco.Tests/Controllers/BackOfficeControllerUnitTests.cs +++ b/src/Umbraco.Tests/Controllers/BackOfficeControllerUnitTests.cs @@ -35,7 +35,7 @@ namespace Umbraco.Tests.Controllers Assert.That(jsBlocks.Count() == 4); Assert.That(jsUrls.Count() == 3); - Assert.That(!jsUrls.Last().StartsWith("~/")); + Assert.That(jsUrls.Last().StartsWith("~/") == false); } } } \ No newline at end of file diff --git a/src/Umbraco.Tests/IO/IOHelperTest.cs b/src/Umbraco.Tests/IO/IOHelperTest.cs index 9f8822fbff..559ba1a2f3 100644 --- a/src/Umbraco.Tests/IO/IOHelperTest.cs +++ b/src/Umbraco.Tests/IO/IOHelperTest.cs @@ -1,4 +1,5 @@ -using NUnit.Framework; +using System; +using NUnit.Framework; using Umbraco.Core.IO; namespace Umbraco.Tests.IO @@ -13,11 +14,20 @@ namespace Umbraco.Tests.IO public class IOHelperTest { - [Test] - public void IOHelper_ResolveUrl() + [TestCase("~/Scripts", "/Scripts", null)] + [TestCase("/Scripts", "/Scripts", null)] + [TestCase("../Scripts", "/Scripts", typeof(ArgumentException))] + public void IOHelper_ResolveUrl(string input, string expected, Type expectedExceptionType) { - var result = IOHelper.ResolveUrl("~/Scripts"); - Assert.AreEqual("/Scripts", result); + if (expectedExceptionType != null) + { + Assert.Throws(expectedExceptionType, () => IOHelper.ResolveUrl(input)); + } + else + { + var result = IOHelper.ResolveUrl(input); + Assert.AreEqual(expected, result); + } } /// diff --git a/src/Umbraco.Tests/Macros/MacroParserTests.cs b/src/Umbraco.Tests/Macros/MacroParserTests.cs index 6a6a6d6ffc..7fb5130d3c 100644 --- a/src/Umbraco.Tests/Macros/MacroParserTests.cs +++ b/src/Umbraco.Tests/Macros/MacroParserTests.cs @@ -200,6 +200,29 @@ namespace Umbraco.Tests.Macros

asdfasdf

".Replace(Environment.NewLine, string.Empty), result.Replace(Environment.NewLine, string.Empty)); } + [Test] + public void Format_RTE_Data_For_Editor_With_Multiple_Macros() + { + var content = @"

asdfasdf

+ +

asdfsadf

+

 

+ +

 

"; + var result = MacroTagParser.FormatRichTextPersistedDataForEditor(content, new Dictionary()); + + Assert.AreEqual(@"

asdfasdf

+
+ +Macro alias: Breadcrumb
+

asdfsadf

+

 

+
+ +Macro alias: login
+

 

".Replace(Environment.NewLine, string.Empty), result.Replace(Environment.NewLine, string.Empty)); + } + [Test] public void Format_RTE_Data_For_Editor_With_Params_Closing_Tag() { diff --git a/src/Umbraco.Tests/Strings/StringExtensionsTests.cs b/src/Umbraco.Tests/Strings/StringExtensionsTests.cs index 1bc4661de7..d47e7a21b2 100644 --- a/src/Umbraco.Tests/Strings/StringExtensionsTests.cs +++ b/src/Umbraco.Tests/Strings/StringExtensionsTests.cs @@ -24,6 +24,19 @@ namespace Umbraco.Tests.Strings ShortStringHelperResolver.Reset(); } + [TestCase("alert('hello');", false)] + [TestCase("~/Test.js", true)] + [TestCase("../Test.js", true)] + [TestCase("/Test.js", true)] + [TestCase("Test.js", true)] + [TestCase("Test.js==", false)] + [TestCase("/Test.js function(){return true;}", false)] + public void Detect_Is_JavaScript_Path(string input, bool result) + { + var output = input.DetectIsJavaScriptPath(); + Assert.AreEqual(result, output.Success); + } + [TestCase("hello.txt", "hello")] [TestCase("this.is.a.Txt", "this.is.a")] [TestCase("this.is.not.a. Txt", "this.is.not.a. Txt")] diff --git a/src/Umbraco.Web.UI.Client/src/common/services/macro.service.js b/src/Umbraco.Web.UI.Client/src/common/services/macro.service.js index f36b932d5f..f0f1649bbe 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/macro.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/macro.service.js @@ -15,7 +15,7 @@ function macroService() { //This regex will match an alias of anything except characters that are quotes or new lines (for legacy reasons, when new macros are created // their aliases are cleaned an invalid chars are stripped) - var expression = /(<\?UMBRACO_MACRO(?:.+?)?macroAlias=["']([^\"\'\n\r]+?)["'][\s\S]+?)(\/>|>.*?<\/\?UMBRACO_MACRO>)/i; + var expression = /(<\?UMBRACO_MACRO (?:.+?)?macroAlias=["']([^\"\'\n\r]+?)["'][\s\S]+?)(\/>|>.*?<\/\?UMBRACO_MACRO>)/i; var match = expression.exec(syntax); if (!match || match.length < 3) { return null; diff --git a/src/Umbraco.Web.UI.Client/src/installer/installer.service.js b/src/Umbraco.Web.UI.Client/src/installer/installer.service.js index f2df5dea2b..970cf8da31 100644 --- a/src/Umbraco.Web.UI.Client/src/installer/installer.service.js +++ b/src/Umbraco.Web.UI.Client/src/installer/installer.service.js @@ -28,7 +28,7 @@ angular.module("umbraco.install").factory('installerService', function($rootScop 'Umbraco is the best of both worlds: 100% free and open source, and backed by a professional and profitable company', "There's a pretty big chance, you've visited a website powered by Umbraco today", "'Umbraco-spotting' is the game of spotting big brands running Umbraco", - "At least 3 people have the Umbraco logo tattooed on them", + "At least 4 people have the Umbraco logo tattooed on them", "'Umbraco' is the danish name for an allen key", "Umbraco has been around since 2005, that's a looong time in IT", "More than 400 people from all over the world meet each year in Denmark in June for our annual conference CodeGarden", diff --git a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj index b54954007d..d704139e04 100644 --- a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj +++ b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj @@ -194,7 +194,7 @@ False - ..\packages\MySql.Data.6.9.6\lib\net45\MySql.Data.dll + ..\packages\MySql.Data.6.9.7\lib\net45\MySql.Data.dll ..\packages\Newtonsoft.Json.6.0.4\lib\net45\Newtonsoft.Json.dll diff --git a/src/Umbraco.Web.UI/packages.config b/src/Umbraco.Web.UI/packages.config index 6a76f411c0..b7c4c3f55b 100644 --- a/src/Umbraco.Web.UI/packages.config +++ b/src/Umbraco.Web.UI/packages.config @@ -28,7 +28,7 @@ - + diff --git a/src/Umbraco.Web/Editors/BackOfficeController.cs b/src/Umbraco.Web/Editors/BackOfficeController.cs index dd44a9fd17..23a7d768a0 100644 --- a/src/Umbraco.Web/Editors/BackOfficeController.cs +++ b/src/Umbraco.Web/Editors/BackOfficeController.cs @@ -691,39 +691,15 @@ namespace Umbraco.Web.Editors var urlList = new List(); foreach (var jsFile in values) { - //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; + var isJsPath = jsFile.DetectIsJavaScriptPath(); + if (isJsPath.Success) - if (Uri.IsWellFormedUriString(jsFile, 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(jsFile, @"[^a-zA-Z0-9-._~:/?#\[\]@!$&'\(\)*\+,%;=]")) - isValid = false; - - //we'll have to be smarter and just check for certain js patterns now too! - var jsPatterns = new string[] {@"\+\s*\=", @"\);", @"function\s*\(", @"!=", @"=="}; - if (jsPatterns.Any(p => Regex.IsMatch(jsFile, p))) - { - isValid = false; - } - if (isValid) - { - //it is a valid URL add to Url list - urlList.Add(jsFile.StartsWith("~/") ? IOHelper.ResolveUrl(jsFile) : jsFile); - } + urlList.Add(isJsPath.Result); } else { - isValid = false; - } - - if (isValid == false) - { - //it isn't a valid URL, must be a js block - blockList.Add(jsFile); + blockList.Add(isJsPath.Result); } } diff --git a/src/umbraco.datalayer/packages.config b/src/umbraco.datalayer/packages.config index 6c3f4c3945..caf5b0d195 100644 --- a/src/umbraco.datalayer/packages.config +++ b/src/umbraco.datalayer/packages.config @@ -1,6 +1,6 @@  - - - + + + \ No newline at end of file diff --git a/src/umbraco.datalayer/umbraco.datalayer.csproj b/src/umbraco.datalayer/umbraco.datalayer.csproj index 1002075a03..0bd6950356 100644 --- a/src/umbraco.datalayer/umbraco.datalayer.csproj +++ b/src/umbraco.datalayer/umbraco.datalayer.csproj @@ -78,7 +78,7 @@ False - ..\packages\MySql.Data.6.9.6\lib\net45\MySql.Data.dll + ..\packages\MySql.Data.6.9.7\lib\net45\MySql.Data.dll