Macros - support navigable caches
This commit is contained in:
@@ -805,6 +805,7 @@
|
||||
<Compile Include="WriteLock.cs" />
|
||||
<Compile Include="XmlExtensions.cs" />
|
||||
<Compile Include="XmlHelper.cs" />
|
||||
<Compile Include="Xml\XPath\MacroNavigator.cs" />
|
||||
<Compile Include="Xml\XPath\INavigableContentType.cs" />
|
||||
<Compile Include="Xml\XPath\NavigableNavigator.cs" />
|
||||
<Compile Include="Xml\XPath\INavigableContent.cs" />
|
||||
|
||||
1042
src/Umbraco.Core/Xml/XPath/MacroNavigator.cs
Normal file
1042
src/Umbraco.Core/Xml/XPath/MacroNavigator.cs
Normal file
File diff suppressed because it is too large
Load Diff
@@ -594,6 +594,143 @@ namespace Umbraco.Tests.CoreXml
|
||||
Assert.IsFalse(nav.MoveToId("2"));
|
||||
}
|
||||
|
||||
[TestCase(true, true)]
|
||||
[TestCase(true, false)]
|
||||
[TestCase(false, true)]
|
||||
[TestCase(false, false)]
|
||||
public void XsltDebugModeAndSortOrder(bool native, bool debug)
|
||||
{
|
||||
const string xml = @"<?xml version=""1.0"" encoding=""UTF-8""?>
|
||||
<root>
|
||||
<node id=""1"" isDoc=""1"">
|
||||
<title>title-1</title>
|
||||
<node id=""3"" isDoc=""1"">
|
||||
<title>title-3</title>
|
||||
<node id=""7"" isDoc=""1"">
|
||||
<title>title-7</title>
|
||||
</node>
|
||||
<node id=""8"" isDoc=""1"">
|
||||
<title>title-8</title>
|
||||
</node>
|
||||
</node>
|
||||
<node id=""5"" isDoc=""1"">
|
||||
<title>title-5</title>
|
||||
</node>
|
||||
</node>
|
||||
<node id=""2"" isDoc=""1"">
|
||||
<title>title-2</title>
|
||||
<node id=""4"" isDoc=""1"">
|
||||
<title>title-4</title>
|
||||
</node>
|
||||
<node id=""6"" isDoc=""1"">
|
||||
<title>title-6</title>
|
||||
</node>
|
||||
</node>
|
||||
</root>
|
||||
";
|
||||
|
||||
const string xslt = @"<?xml version=""1.0"" encoding=""UTF-8""?>
|
||||
<!DOCTYPE xsl:stylesheet [
|
||||
<!ENTITY nbsp "" "">
|
||||
]>
|
||||
<xsl:stylesheet
|
||||
version=""1.0""
|
||||
xmlns:xsl=""http://www.w3.org/1999/XSL/Transform""
|
||||
xmlns:msxml=""urn:schemas-microsoft-com:xslt""
|
||||
xmlns:umbraco.library=""urn:umbraco.library"" xmlns:Exslt.ExsltCommon=""urn:Exslt.ExsltCommon"" xmlns:Exslt.ExsltDatesAndTimes=""urn:Exslt.ExsltDatesAndTimes"" xmlns:Exslt.ExsltMath=""urn:Exslt.ExsltMath"" xmlns:Exslt.ExsltRegularExpressions=""urn:Exslt.ExsltRegularExpressions"" xmlns:Exslt.ExsltStrings=""urn:Exslt.ExsltStrings"" xmlns:Exslt.ExsltSets=""urn:Exslt.ExsltSets"" xmlns:Examine=""urn:Examine""
|
||||
exclude-result-prefixes=""msxml umbraco.library Exslt.ExsltCommon Exslt.ExsltDatesAndTimes Exslt.ExsltMath Exslt.ExsltRegularExpressions Exslt.ExsltStrings Exslt.ExsltSets Examine "">
|
||||
|
||||
<xsl:output method=""xml"" omit-xml-declaration=""yes"" />
|
||||
<xsl:param name=""currentPage""/>
|
||||
|
||||
<xsl:template match=""/"">
|
||||
<!-- <xsl:for-each select=""/root/* [@isDoc]""> -->
|
||||
<!-- <xsl:for-each select=""$currentPage/root/* [@isDoc]""> -->
|
||||
<xsl:for-each select=""/macro/nav/root/* [@isDoc]"">
|
||||
<xsl:text>! </xsl:text><xsl:value-of select=""title"" /><xsl:text>
|
||||
</xsl:text>
|
||||
<xsl:for-each select=""./* [@isDoc]"">
|
||||
<xsl:text>!! </xsl:text><xsl:value-of select=""title"" /><xsl:text>
|
||||
</xsl:text>
|
||||
<xsl:for-each select=""./* [@isDoc]"">
|
||||
<xsl:text>!!! </xsl:text><xsl:value-of select=""title"" /><xsl:text>
|
||||
</xsl:text>
|
||||
</xsl:for-each>
|
||||
</xsl:for-each>
|
||||
</xsl:for-each>
|
||||
</xsl:template>
|
||||
|
||||
</xsl:stylesheet>
|
||||
";
|
||||
const string expected = @"! title-1
|
||||
!! title-3
|
||||
!!! title-7
|
||||
!!! title-8
|
||||
!! title-5
|
||||
! title-2
|
||||
!! title-4
|
||||
!! title-6
|
||||
";
|
||||
|
||||
// see http://www.onenaught.com/posts/352/xslt-performance-tip-dont-indent-output
|
||||
// why aren't we using an XmlWriter here?
|
||||
|
||||
var transform = new XslCompiledTransform(debug);
|
||||
var xmlReader = new XmlTextReader(new StringReader(xslt))
|
||||
{
|
||||
EntityHandling = EntityHandling.ExpandEntities
|
||||
};
|
||||
var xslResolver = new XmlUrlResolver
|
||||
{
|
||||
Credentials = CredentialCache.DefaultCredentials
|
||||
};
|
||||
var args = new XsltArgumentList();
|
||||
|
||||
// .Default is more restrictive than .TrustedXslt
|
||||
transform.Load(xmlReader, XsltSettings.Default, xslResolver);
|
||||
|
||||
XPathNavigator macro;
|
||||
if (!native)
|
||||
{
|
||||
var source = new TestSource7();
|
||||
var nav = new NavigableNavigator(source);
|
||||
//args.AddParam("currentPage", string.Empty, nav.Clone());
|
||||
|
||||
var x = new XmlDocument();
|
||||
x.LoadXml(xml);
|
||||
|
||||
macro = new MacroNavigator(new[]
|
||||
{
|
||||
// it even fails like that => macro nav. issue?
|
||||
new MacroNavigator.MacroParameter("nav", x.CreateNavigator()) // nav.Clone())
|
||||
}
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
var doc = new XmlDocument();
|
||||
doc.LoadXml("<macro />");
|
||||
var nav = doc.CreateElement("nav");
|
||||
doc.DocumentElement.AppendChild(nav);
|
||||
var x = new XmlDocument();
|
||||
x.LoadXml(xml);
|
||||
nav.AppendChild(doc.ImportNode(x.DocumentElement, true));
|
||||
macro = doc.CreateNavigator();
|
||||
}
|
||||
|
||||
var writer = new StringWriter();
|
||||
transform.Transform(macro, args, writer);
|
||||
|
||||
// this was working with native, debug and non-debug
|
||||
// this was working with macro nav, non-debug
|
||||
// but was NOT working (changing the order of nodes) with macro nav, debug
|
||||
// was due to an issue with macro nav IsSamePosition, fixed
|
||||
|
||||
//Console.WriteLine("--------");
|
||||
//Console.WriteLine(writer.ToString());
|
||||
Assert.AreEqual(expected, writer.ToString());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void WhiteSpacesAndEmptyValues()
|
||||
{
|
||||
|
||||
@@ -2085,4 +2085,4 @@
|
||||
<!--<PostBuildEvent>xcopy "$(ProjectDir)..\..\lib\*.dll" "$(TargetDir)*.dll" /Y</PostBuildEvent>-->
|
||||
</PropertyGroup>
|
||||
<Import Project="$(SolutionDir)\.nuget\nuget.targets" />
|
||||
</Project>
|
||||
</Project>
|
||||
|
||||
@@ -23,6 +23,7 @@ using Umbraco.Core.Cache;
|
||||
using Umbraco.Core.Events;
|
||||
using Umbraco.Core.IO;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Xml.XPath;
|
||||
using Umbraco.Core.Profiling;
|
||||
using Umbraco.Web;
|
||||
using Umbraco.Web.Cache;
|
||||
@@ -859,21 +860,41 @@ namespace umbraco
|
||||
using (DisposableTimer.DebugDuration<macro>("Executing XSLT: " + XsltFile))
|
||||
{
|
||||
XmlDocument macroXml = null;
|
||||
MacroNavigator macroNavigator = null;
|
||||
NavigableNavigator contentNavigator = null;
|
||||
|
||||
// get master xml document
|
||||
var cache = UmbracoContext.Current.ContentCache.InnerCache as Umbraco.Web.PublishedCache.XmlPublishedCache.PublishedContentCache;
|
||||
if (cache == null) throw new Exception("Unsupported IPublishedContentCache, only the Xml one is supported.");
|
||||
XmlDocument umbracoXml = cache.GetXml(UmbracoContext.Current, UmbracoContext.Current.InPreviewMode);
|
||||
macroXml = new XmlDocument();
|
||||
macroXml.LoadXml("<macro/>");
|
||||
foreach (var prop in macro.Model.Properties)
|
||||
var canNavigate =
|
||||
UmbracoContext.Current.ContentCache.XPathNavigatorIsNavigable &&
|
||||
UmbracoContext.Current.MediaCache.XPathNavigatorIsNavigable;
|
||||
|
||||
if (!canNavigate)
|
||||
{
|
||||
AddMacroXmlNode(umbracoXml, macroXml, prop.Key, prop.Type, prop.Value);
|
||||
// get master xml document
|
||||
var cache = UmbracoContext.Current.ContentCache.InnerCache as Umbraco.Web.PublishedCache.XmlPublishedCache.PublishedContentCache;
|
||||
if (cache == null) throw new Exception("Unsupported IPublishedContentCache, only the Xml one is supported.");
|
||||
XmlDocument umbracoXml = cache.GetXml(UmbracoContext.Current, UmbracoContext.Current.InPreviewMode);
|
||||
macroXml = new XmlDocument();
|
||||
macroXml.LoadXml("<macro/>");
|
||||
foreach (var prop in macro.Model.Properties)
|
||||
{
|
||||
AddMacroXmlNode(umbracoXml, macroXml, prop.Key, prop.Type, prop.Value);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var parameters = new List<MacroNavigator.MacroParameter>();
|
||||
contentNavigator = UmbracoContext.Current.ContentCache.GetXPathNavigator() as NavigableNavigator;
|
||||
var mediaNavigator = UmbracoContext.Current.MediaCache.GetXPathNavigator() as NavigableNavigator;
|
||||
foreach (var prop in macro.Model.Properties)
|
||||
{
|
||||
AddMacroParameter(parameters, contentNavigator, mediaNavigator, prop.Key, prop.Type, prop.Value);
|
||||
}
|
||||
macroNavigator = new MacroNavigator(parameters);
|
||||
}
|
||||
|
||||
if (HttpContext.Current.Request.QueryString["umbDebug"] != null && GlobalSettings.DebugMode)
|
||||
{
|
||||
var outerXml = macroXml.OuterXml;
|
||||
var outerXml = macroXml == null ? macroNavigator.OuterXml : macroXml.OuterXml;
|
||||
return
|
||||
new LiteralControl("<div style=\"border: 2px solid green; padding: 5px;\"><b>Debug from " +
|
||||
macro.Name +
|
||||
@@ -889,7 +910,9 @@ namespace umbraco
|
||||
{
|
||||
try
|
||||
{
|
||||
var transformed = GetXsltTransformResult(macroXml, xsltFile);
|
||||
var transformed = canNavigate
|
||||
? GetXsltTransformResult(macroNavigator, contentNavigator, xsltFile) // better?
|
||||
: GetXsltTransformResult(macroXml, xsltFile); // document
|
||||
var result = CreateControlsFromText(transformed);
|
||||
|
||||
return result;
|
||||
@@ -1323,6 +1346,101 @@ namespace umbraco
|
||||
macroXml.FirstChild.AppendChild(macroXmlNode);
|
||||
}
|
||||
|
||||
// add parameters to the macro parameters collection
|
||||
private void AddMacroParameter(ICollection<MacroNavigator.MacroParameter> parameters,
|
||||
NavigableNavigator contentNavigator, NavigableNavigator mediaNavigator,
|
||||
string macroPropertyAlias,string macroPropertyType, string macroPropertyValue)
|
||||
{
|
||||
// if no value is passed, then use the current "pageID" as value
|
||||
var contentId = macroPropertyValue == string.Empty ? UmbracoContext.Current.PageId.ToString() : macroPropertyValue;
|
||||
|
||||
TraceInfo("umbracoMacro",
|
||||
"Xslt node adding search start (" + macroPropertyAlias + ",'" +
|
||||
macroPropertyValue + "')");
|
||||
|
||||
// beware! do not use the raw content- or media- navigators, but clones !!
|
||||
|
||||
switch (macroPropertyType)
|
||||
{
|
||||
case "contentTree":
|
||||
parameters.Add(new MacroNavigator.MacroParameter(
|
||||
macroPropertyAlias,
|
||||
contentNavigator.CloneWithNewRoot(contentId), // null if not found - will be reported as empty
|
||||
attributes: new Dictionary<string, string> { { "nodeID", contentId } }));
|
||||
|
||||
break;
|
||||
|
||||
case "contentPicker":
|
||||
parameters.Add(new MacroNavigator.MacroParameter(
|
||||
macroPropertyAlias,
|
||||
contentNavigator.CloneWithNewRoot(contentId), // null if not found - will be reported as empty
|
||||
0));
|
||||
break;
|
||||
|
||||
case "contentSubs":
|
||||
parameters.Add(new MacroNavigator.MacroParameter(
|
||||
macroPropertyAlias,
|
||||
contentNavigator.CloneWithNewRoot(contentId), // null if not found - will be reported as empty
|
||||
1));
|
||||
break;
|
||||
|
||||
case "contentAll":
|
||||
parameters.Add(new MacroNavigator.MacroParameter(macroPropertyAlias, contentNavigator.Clone()));
|
||||
break;
|
||||
|
||||
case "contentRandom":
|
||||
var nav = contentNavigator.Clone();
|
||||
if (nav.MoveToId(contentId))
|
||||
{
|
||||
var descendantIterator = nav.Select("./* [@isDoc]");
|
||||
if (descendantIterator.MoveNext())
|
||||
{
|
||||
// not empty - and won't change
|
||||
var descendantCount = descendantIterator.Count;
|
||||
|
||||
int index;
|
||||
var r = library.GetRandom();
|
||||
lock (r)
|
||||
{
|
||||
index = r.Next(descendantCount);
|
||||
}
|
||||
|
||||
while (index > 0 && descendantIterator.MoveNext())
|
||||
index--;
|
||||
|
||||
var node = descendantIterator.Current.UnderlyingObject as INavigableContent;
|
||||
if (node != null)
|
||||
{
|
||||
nav = contentNavigator.CloneWithNewRoot(node.Id.ToString(CultureInfo.InvariantCulture));
|
||||
parameters.Add(new MacroNavigator.MacroParameter(macroPropertyAlias, nav, 0));
|
||||
}
|
||||
else
|
||||
throw new InvalidOperationException("Iterator contains non-INavigableContent elements.");
|
||||
}
|
||||
else
|
||||
TraceWarn("umbracoMacro",
|
||||
"Error adding random node - parent (" + macroPropertyValue +
|
||||
") doesn't have children!");
|
||||
}
|
||||
else
|
||||
TraceWarn("umbracoMacro",
|
||||
"Error adding random node - parent (" + macroPropertyValue +
|
||||
") doesn't exists!");
|
||||
break;
|
||||
|
||||
case "mediaCurrent":
|
||||
parameters.Add(new MacroNavigator.MacroParameter(
|
||||
macroPropertyAlias,
|
||||
mediaNavigator.CloneWithNewRoot(contentId), // null if not found - will be reported as empty
|
||||
0));
|
||||
break;
|
||||
|
||||
default:
|
||||
parameters.Add(new MacroNavigator.MacroParameter(macroPropertyAlias, HttpContext.Current.Server.HtmlDecode(macroPropertyValue)));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
|
||||
Reference in New Issue
Block a user