diff --git a/src/Umbraco.Core/Macros/MacroTagParser.cs b/src/Umbraco.Core/Macros/MacroTagParser.cs index 236a184cc0..94eb6a69bc 100644 --- a/src/Umbraco.Core/Macros/MacroTagParser.cs +++ b/src/Umbraco.Core/Macros/MacroTagParser.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; using System.Text; using System.Text.RegularExpressions; +using HtmlAgilityPack; namespace Umbraco.Core.Macros { @@ -10,7 +11,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=[""'](\w+?)[""'].+?)(?:/>|>.*?)", RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.Singleline); /// @@ -88,7 +89,33 @@ namespace Umbraco.Core.Macros /// internal static string FormatRichTextContentForPersistence(string rteContent) { - return MacroRteContent.Replace(rteContent, match => + if (string.IsNullOrEmpty(rteContent)) + { + return string.Empty; + } + + var html = new HtmlDocument(); + html.LoadHtml(rteContent); + + //get all the comment nodes we want + var commentNodes = html.DocumentNode.SelectNodes("//comment()[contains(., ' with the comment node itself. + foreach (var c in commentNodes) + { + var div = c.ParentNode; + var divContainer = div.ParentNode; + divContainer.ReplaceChild(c, div); + } + + var parsed = html.DocumentNode.OuterHtml; + + //now replace all the with nothing + return MacroRteContent.Replace(parsed, match => { if (match.Groups.Count >= 3) { @@ -96,7 +123,7 @@ namespace Umbraco.Core.Macros return match.Groups[2].Value; } //replace with nothing if we couldn't find the syntax for whatever reason - return ""; + return string.Empty; }); } diff --git a/src/Umbraco.Core/PluginManager.cs b/src/Umbraco.Core/PluginManager.cs index 22d8f5bb5c..d3305af031 100644 --- a/src/Umbraco.Core/PluginManager.cs +++ b/src/Umbraco.Core/PluginManager.cs @@ -459,24 +459,24 @@ namespace Umbraco.Core private readonly HashSet _types = new HashSet(); private IEnumerable _assemblies; - /// - /// Returns all found property editors + /// Returns all found property editors (based on the resolved Iparameter editors - this saves a scan) /// internal IEnumerable ResolvePropertyEditors() { //return all proeprty editor types found except for the base property editor type - return ResolveTypes().ToArray() - .Except(new[] {typeof (PropertyEditor)}); + return ResolveTypes() + .Where(x => x.IsType()) + .Except(new[] { typeof(PropertyEditor) }); } /// - /// Returns all found parameter editors + /// Returns all found parameter editors (which includes property editors) /// internal IEnumerable ResolveParameterEditors() { //return all paramter editor types found except for the base property editor type - return ResolveTypes().ToArray() + return ResolveTypes() .Except(new[] { typeof(ParameterEditor), typeof(PropertyEditor) }); } diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index 3aa3a3d5bc..880673ef6e 100644 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -44,6 +44,9 @@ ..\packages\AutoMapper.3.0.0\lib\net40\AutoMapper.Net4.dll + + ..\packages\HtmlAgilityPack.1.4.6\lib\Net45\HtmlAgilityPack.dll + False ..\packages\log4net-mediumtrust.2.0.0\lib\log4net.dll diff --git a/src/Umbraco.Core/packages.config b/src/Umbraco.Core/packages.config index 7ad8fc5ac1..cc673f88a1 100644 --- a/src/Umbraco.Core/packages.config +++ b/src/Umbraco.Core/packages.config @@ -1,6 +1,7 @@  + diff --git a/src/Umbraco.Tests/Macros/MacroParserTests.cs b/src/Umbraco.Tests/Macros/MacroParserTests.cs index 56fc4f2062..78dd51e855 100644 --- a/src/Umbraco.Tests/Macros/MacroParserTests.cs +++ b/src/Umbraco.Tests/Macros/MacroParserTests.cs @@ -158,5 +158,22 @@ asdfsdf ".Replace(Environment.NewLine, string.Empty), result.Replace(Environment.NewLine, string.Empty)); } + + [Test] + public void Format_RTE_Data_For_Persistence_Custom_Single_Entry() + { + var content = @"
+
+
+

null

+
+
1089
+
+
"; + var result = MacroTagParser.FormatRichTextContentForPersistence(content); + + Assert.AreEqual(@"", result); + } + } } \ No newline at end of file diff --git a/src/Umbraco.Web/Editors/AuthenticationController.cs b/src/Umbraco.Web/Editors/AuthenticationController.cs index 780302f959..d9452e23fe 100644 --- a/src/Umbraco.Web/Editors/AuthenticationController.cs +++ b/src/Umbraco.Web/Editors/AuthenticationController.cs @@ -28,18 +28,9 @@ namespace Umbraco.Web.Editors ///
[PluginController("UmbracoApi")] [ValidationFilter] + [AngularJsonOnlyConfiguration] public class AuthenticationController : UmbracoApiController { - - /// - /// Remove the xml formatter... only support JSON! - /// - /// - protected override void Initialize(HttpControllerContext controllerContext) - { - base.Initialize(controllerContext); - controllerContext.EnsureJsonOutputOnly(); - } /// /// This is a special method that will return the current users' remaining session seconds, the reason diff --git a/src/Umbraco.Web/Editors/UmbracoAuthorizedJsonController.cs b/src/Umbraco.Web/Editors/UmbracoAuthorizedJsonController.cs index cd8b9f6084..e60abe0859 100644 --- a/src/Umbraco.Web/Editors/UmbracoAuthorizedJsonController.cs +++ b/src/Umbraco.Web/Editors/UmbracoAuthorizedJsonController.cs @@ -12,6 +12,7 @@ namespace Umbraco.Web.Editors /// methods that are not called by Angular or don't contain a valid csrf header will NOT work. /// [ValidateAngularAntiForgeryToken] + [AngularJsonOnlyConfiguration] public abstract class UmbracoAuthorizedJsonController : UmbracoAuthorizedApiController { protected UmbracoAuthorizedJsonController() @@ -22,17 +23,5 @@ namespace Umbraco.Web.Editors { } - /// - /// Remove the xml formatter... only support JSON! - /// - /// - protected override void Initialize(HttpControllerContext controllerContext) - { - base.Initialize(controllerContext); - controllerContext.EnsureJsonOutputOnly(); - } - - - } } \ No newline at end of file diff --git a/src/Umbraco.Web/Trees/ApplicationTreeController.cs b/src/Umbraco.Web/Trees/ApplicationTreeController.cs index 2355b0c443..1b90ae0dd8 100644 --- a/src/Umbraco.Web/Trees/ApplicationTreeController.cs +++ b/src/Umbraco.Web/Trees/ApplicationTreeController.cs @@ -20,21 +20,10 @@ using umbraco; namespace Umbraco.Web.Trees { - + [AngularJsonOnlyConfiguration] [PluginController("UmbracoTrees")] public class ApplicationTreeController : UmbracoAuthorizedApiController { - - /// - /// Remove the xml formatter... only support JSON! - /// - /// - protected override void Initialize(global::System.Web.Http.Controllers.HttpControllerContext controllerContext) - { - base.Initialize(controllerContext); - controllerContext.EnsureJsonOutputOnly(); - } - /// /// Returns the tree nodes for an application /// diff --git a/src/Umbraco.Web/Trees/TreeControllerBase.cs b/src/Umbraco.Web/Trees/TreeControllerBase.cs index 3271b6124e..ebf4275c1f 100644 --- a/src/Umbraco.Web/Trees/TreeControllerBase.cs +++ b/src/Umbraco.Web/Trees/TreeControllerBase.cs @@ -15,18 +15,9 @@ namespace Umbraco.Web.Trees /// A base controller reference for non-attributed trees (un-registered). Developers should inherit from /// TreeController. /// + [AngularJsonOnlyConfiguration] public abstract class TreeControllerBase : UmbracoAuthorizedApiController { - /// - /// Remove the xml formatter... only support JSON! - /// - /// - protected override void Initialize(global::System.Web.Http.Controllers.HttpControllerContext controllerContext) - { - base.Initialize(controllerContext); - controllerContext.EnsureJsonOutputOnly(); - } - /// /// The method called to render the contents of the tree structure /// diff --git a/src/Umbraco.Web/UmbracoHelper.cs b/src/Umbraco.Web/UmbracoHelper.cs index 0275ba2ce5..44fc11feb8 100644 --- a/src/Umbraco.Web/UmbracoHelper.cs +++ b/src/Umbraco.Web/UmbracoHelper.cs @@ -976,35 +976,32 @@ namespace Umbraco.Web { var doc = new HtmlDocument(); doc.LoadHtml("

" + html + "

"); - using (var ms = new MemoryStream()) - { - var targets = new List(); + var targets = new List(); - var nodes = doc.DocumentNode.FirstChild.SelectNodes(".//*"); - if (nodes != null) - { - foreach (var node in nodes) - { - //is element - if (node.NodeType != HtmlNodeType.Element) continue; - var filterAllTags = (tags == null || !tags.Any()); - if (filterAllTags || tags.Any(tag => string.Equals(tag, node.Name, StringComparison.CurrentCultureIgnoreCase))) - { - targets.Add(node); - } - } - foreach (var target in targets) - { - HtmlNode content = doc.CreateTextNode(target.InnerText); - target.ParentNode.ReplaceChild(content, target); - } - } - else - { - return new HtmlString(html); - } - return new HtmlString(doc.DocumentNode.FirstChild.InnerHtml); - } + var nodes = doc.DocumentNode.FirstChild.SelectNodes(".//*"); + if (nodes != null) + { + foreach (var node in nodes) + { + //is element + if (node.NodeType != HtmlNodeType.Element) continue; + var filterAllTags = (tags == null || !tags.Any()); + if (filterAllTags || tags.Any(tag => string.Equals(tag, node.Name, StringComparison.CurrentCultureIgnoreCase))) + { + targets.Add(node); + } + } + foreach (var target in targets) + { + HtmlNode content = doc.CreateTextNode(target.InnerText); + target.ParentNode.ReplaceChild(content, target); + } + } + else + { + return new HtmlString(html); + } + return new HtmlString(doc.DocumentNode.FirstChild.InnerHtml); } public string Coalesce(params object[] args) diff --git a/src/Umbraco.Web/WebApi/HttpControllerContextExtensions.cs b/src/Umbraco.Web/WebApi/HttpControllerContextExtensions.cs index 710e795226..f8f8203f8d 100644 --- a/src/Umbraco.Web/WebApi/HttpControllerContextExtensions.cs +++ b/src/Umbraco.Web/WebApi/HttpControllerContextExtensions.cs @@ -96,22 +96,38 @@ namespace Umbraco.Web.WebApi jsonFormatter.SerializerSettings.Converters.Add(new CustomDateTimeConvertor("yyyy-MM-dd HH:mm:ss")); } - /// - /// Removes the xml formatter so it only outputs angularized json (with the json vulnerability prefix added) - /// - /// - internal static void EnsureJsonOutputOnly(this HttpControllerContext controllerContext) - { + ///// + ///// Removes the xml formatter so it only outputs angularized json (with the json vulnerability prefix added) + ///// + ///// + //internal static void EnsureJsonOutputOnly(this HttpControllerContext controllerContext) + //{ + // controllerContext.Configuration.Formatters = new MediaTypeFormatterCollection(); + + // //remove all json/xml formatters then add our custom one + // var toRemove = controllerContext.Configuration.Formatters.Where(t => (t is JsonMediaTypeFormatter) || (t is XmlMediaTypeFormatter)).ToList(); + // foreach (var r in toRemove) + // { + // controllerContext.Configuration.Formatters.Remove(r); + // } + // controllerContext.Configuration.Formatters.Add(new AngularJsonMediaTypeFormatter()); + //} + } + + /// + /// Applying this attribute to any webapi controller will ensure that it only contains one json formatter compatible with the angular json vulnerability prevention. + /// + public class AngularJsonOnlyConfigurationAttribute : Attribute, IControllerConfiguration + { + public void Initialize(HttpControllerSettings controllerSettings, HttpControllerDescriptor controllerDescriptor) + { //remove all json/xml formatters then add our custom one - for (var i = 0; i < controllerContext.Configuration.Formatters.Count;i++) + var toRemove = controllerSettings.Formatters.Where(t => (t is JsonMediaTypeFormatter) || (t is XmlMediaTypeFormatter)).ToList(); + foreach (var r in toRemove) { - if ((controllerContext.Configuration.Formatters[i] is JsonMediaTypeFormatter) - || (controllerContext.Configuration.Formatters[i] is XmlMediaTypeFormatter)) - { - controllerContext.Configuration.Formatters.RemoveAt(i); - } + controllerSettings.Formatters.Remove(r); } - controllerContext.Configuration.Formatters.Add(new AngularJsonMediaTypeFormatter()); + controllerSettings.Formatters.Add(new AngularJsonMediaTypeFormatter()); } } } \ No newline at end of file diff --git a/src/Umbraco.Web/umbraco.presentation/macro.cs b/src/Umbraco.Web/umbraco.presentation/macro.cs index d9b3e83747..5934b6e7e7 100644 --- a/src/Umbraco.Web/umbraco.presentation/macro.cs +++ b/src/Umbraco.Web/umbraco.presentation/macro.cs @@ -796,7 +796,9 @@ namespace umbraco { if (attributes.ContainsKey(mp.Key.ToLower())) { - mp.Value = attributes[mp.Key.ToLower()].ToString(); + var item = attributes[mp.Key.ToLower()]; + + mp.Value = item == null ? string.Empty : item.ToString(); } else {