Fixing: U4-3686 Umbraco 7 - Rich Text Editor and Macro Issues + fixed up the per-controller webapi configuration and more fixes to loading propertyeditors/param editors, this saves a assembly scan.
This commit is contained in:
@@ -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
|
||||
/// </summary>
|
||||
internal class MacroTagParser
|
||||
{
|
||||
private static readonly Regex MacroRteContent = new Regex(@"(<div.*?>.*?<!--\s*?)(<\?UMBRACO_MACRO.*?/>)(.*?</div>)", RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.Singleline);
|
||||
private static readonly Regex MacroRteContent = new Regex(@"(<!--\s*?)(<\?UMBRACO_MACRO.*?/>)(\s*?-->)", RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.Singleline);
|
||||
private static readonly Regex MacroPersistedFormat = new Regex(@"(<\?UMBRACO_MACRO macroAlias=[""'](\w+?)[""'].+?)(?:/>|>.*?</\?UMBRACO_MACRO>)", RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.Singleline);
|
||||
|
||||
/// <summary>
|
||||
@@ -88,7 +89,33 @@ namespace Umbraco.Core.Macros
|
||||
/// </remarks>
|
||||
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(., '<?UMBRACO_MACRO')]");
|
||||
if (commentNodes == null)
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
//replace each containing parent <div> 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 <!-- and --> 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;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -459,24 +459,24 @@ namespace Umbraco.Core
|
||||
private readonly HashSet<TypeList> _types = new HashSet<TypeList>();
|
||||
private IEnumerable<Assembly> _assemblies;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Returns all found property editors
|
||||
/// Returns all found property editors (based on the resolved Iparameter editors - this saves a scan)
|
||||
/// </summary>
|
||||
internal IEnumerable<Type> ResolvePropertyEditors()
|
||||
{
|
||||
//return all proeprty editor types found except for the base property editor type
|
||||
return ResolveTypes<PropertyEditor>().ToArray()
|
||||
.Except(new[] {typeof (PropertyEditor)});
|
||||
return ResolveTypes<IParameterEditor>()
|
||||
.Where(x => x.IsType<PropertyEditor>())
|
||||
.Except(new[] { typeof(PropertyEditor) });
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns all found parameter editors
|
||||
/// Returns all found parameter editors (which includes property editors)
|
||||
/// </summary>
|
||||
internal IEnumerable<Type> ResolveParameterEditors()
|
||||
{
|
||||
//return all paramter editor types found except for the base property editor type
|
||||
return ResolveTypes<IParameterEditor>().ToArray()
|
||||
return ResolveTypes<IParameterEditor>()
|
||||
.Except(new[] { typeof(ParameterEditor), typeof(PropertyEditor) });
|
||||
}
|
||||
|
||||
|
||||
@@ -44,6 +44,9 @@
|
||||
<Reference Include="AutoMapper.Net4">
|
||||
<HintPath>..\packages\AutoMapper.3.0.0\lib\net40\AutoMapper.Net4.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="HtmlAgilityPack">
|
||||
<HintPath>..\packages\HtmlAgilityPack.1.4.6\lib\Net45\HtmlAgilityPack.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="log4net, Version=1.2.11.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\packages\log4net-mediumtrust.2.0.0\lib\log4net.dll</HintPath>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="AutoMapper" version="3.0.0" targetFramework="net45" />
|
||||
<package id="HtmlAgilityPack" version="1.4.6" targetFramework="net45" />
|
||||
<package id="log4net-mediumtrust" version="2.0.0" targetFramework="net40" />
|
||||
<package id="Microsoft.AspNet.Mvc" version="4.0.30506.0" targetFramework="net40" />
|
||||
<package id="Microsoft.AspNet.Razor" version="2.0.30506.0" targetFramework="net40" />
|
||||
|
||||
@@ -158,5 +158,22 @@ asdfsdf
|
||||
</body>
|
||||
</html>".Replace(Environment.NewLine, string.Empty), result.Replace(Environment.NewLine, string.Empty));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Format_RTE_Data_For_Persistence_Custom_Single_Entry()
|
||||
{
|
||||
var content = @"<div class=""umb-macro-holder Test mceNonEditable umb-macro-mce_1""><!-- <?UMBRACO_MACRO macroAlias=""Test"" content=""1089"" textArea=""asdfasdf"" title="""" bool=""0"" number="""" contentType="""" multiContentType="""" multiProperties="""" properties="""" tabs="""" multiTabs="""" /> --><ins>
|
||||
<div class=""facts-box"">
|
||||
<div class=""fatcs-box-header"">
|
||||
<h3>null</h3>
|
||||
</div>
|
||||
<div class=""fatcs-box-body"">1089</div>
|
||||
</div>
|
||||
</ins></div>";
|
||||
var result = MacroTagParser.FormatRichTextContentForPersistence(content);
|
||||
|
||||
Assert.AreEqual(@"<?UMBRACO_MACRO macroAlias=""Test"" content=""1089"" textArea=""asdfasdf"" title="""" bool=""0"" number="""" contentType="""" multiContentType="""" multiProperties="""" properties="""" tabs="""" multiTabs="""" />", result);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -28,18 +28,9 @@ namespace Umbraco.Web.Editors
|
||||
/// </summary>
|
||||
[PluginController("UmbracoApi")]
|
||||
[ValidationFilter]
|
||||
[AngularJsonOnlyConfiguration]
|
||||
public class AuthenticationController : UmbracoApiController
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Remove the xml formatter... only support JSON!
|
||||
/// </summary>
|
||||
/// <param name="controllerContext"></param>
|
||||
protected override void Initialize(HttpControllerContext controllerContext)
|
||||
{
|
||||
base.Initialize(controllerContext);
|
||||
controllerContext.EnsureJsonOutputOnly();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This is a special method that will return the current users' remaining session seconds, the reason
|
||||
|
||||
@@ -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.
|
||||
/// </remarks>
|
||||
[ValidateAngularAntiForgeryToken]
|
||||
[AngularJsonOnlyConfiguration]
|
||||
public abstract class UmbracoAuthorizedJsonController : UmbracoAuthorizedApiController
|
||||
{
|
||||
protected UmbracoAuthorizedJsonController()
|
||||
@@ -22,17 +23,5 @@ namespace Umbraco.Web.Editors
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remove the xml formatter... only support JSON!
|
||||
/// </summary>
|
||||
/// <param name="controllerContext"></param>
|
||||
protected override void Initialize(HttpControllerContext controllerContext)
|
||||
{
|
||||
base.Initialize(controllerContext);
|
||||
controllerContext.EnsureJsonOutputOnly();
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -20,21 +20,10 @@ using umbraco;
|
||||
|
||||
namespace Umbraco.Web.Trees
|
||||
{
|
||||
|
||||
[AngularJsonOnlyConfiguration]
|
||||
[PluginController("UmbracoTrees")]
|
||||
public class ApplicationTreeController : UmbracoAuthorizedApiController
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Remove the xml formatter... only support JSON!
|
||||
/// </summary>
|
||||
/// <param name="controllerContext"></param>
|
||||
protected override void Initialize(global::System.Web.Http.Controllers.HttpControllerContext controllerContext)
|
||||
{
|
||||
base.Initialize(controllerContext);
|
||||
controllerContext.EnsureJsonOutputOnly();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the tree nodes for an application
|
||||
/// </summary>
|
||||
|
||||
@@ -15,18 +15,9 @@ namespace Umbraco.Web.Trees
|
||||
/// A base controller reference for non-attributed trees (un-registered). Developers should inherit from
|
||||
/// TreeController.
|
||||
/// </summary>
|
||||
[AngularJsonOnlyConfiguration]
|
||||
public abstract class TreeControllerBase : UmbracoAuthorizedApiController
|
||||
{
|
||||
/// <summary>
|
||||
/// Remove the xml formatter... only support JSON!
|
||||
/// </summary>
|
||||
/// <param name="controllerContext"></param>
|
||||
protected override void Initialize(global::System.Web.Http.Controllers.HttpControllerContext controllerContext)
|
||||
{
|
||||
base.Initialize(controllerContext);
|
||||
controllerContext.EnsureJsonOutputOnly();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The method called to render the contents of the tree structure
|
||||
/// </summary>
|
||||
|
||||
@@ -976,35 +976,32 @@ namespace Umbraco.Web
|
||||
{
|
||||
var doc = new HtmlDocument();
|
||||
doc.LoadHtml("<p>" + html + "</p>");
|
||||
using (var ms = new MemoryStream())
|
||||
{
|
||||
var targets = new List<HtmlNode>();
|
||||
var targets = new List<HtmlNode>();
|
||||
|
||||
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)
|
||||
|
||||
@@ -96,22 +96,38 @@ namespace Umbraco.Web.WebApi
|
||||
jsonFormatter.SerializerSettings.Converters.Add(new CustomDateTimeConvertor("yyyy-MM-dd HH:mm:ss"));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes the xml formatter so it only outputs angularized json (with the json vulnerability prefix added)
|
||||
/// </summary>
|
||||
/// <param name="controllerContext"></param>
|
||||
internal static void EnsureJsonOutputOnly(this HttpControllerContext controllerContext)
|
||||
{
|
||||
///// <summary>
|
||||
///// Removes the xml formatter so it only outputs angularized json (with the json vulnerability prefix added)
|
||||
///// </summary>
|
||||
///// <param name="controllerContext"></param>
|
||||
//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());
|
||||
//}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Applying this attribute to any webapi controller will ensure that it only contains one json formatter compatible with the angular json vulnerability prevention.
|
||||
/// </summary>
|
||||
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());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user