INode to IPublishedContent transition for Razor macros

This commit is contained in:
Stephan
2013-09-28 17:23:34 +02:00
parent ea82e9c67e
commit e7754213f1
9 changed files with 229 additions and 127 deletions

View File

@@ -47,9 +47,9 @@ namespace Umbraco.Web.Media
}
else
{
var itemPage = new page(content.Instance.XmlContent.GetElementById(nodeId.GetValueOrDefault().ToString(CultureInfo.InvariantCulture)));
var value = itemPage.Elements[field];
fieldValue = value != null ? value.ToString() : string.Empty;
var p = UmbracoContext.Current.ContentCache.GetById(nodeId.GetValueOrDefault());
var v = p.GetPropertyValue(field);
fieldValue = v == null ? string.Empty : v.ToString();
}
}
else

View File

@@ -95,13 +95,20 @@ namespace Umbraco.Web.Routing
// can't go beyond that point without a PublishedContent to render
// it's ok not to have a template, in order to give MVC a chance to hijack routes
// assign the legacy page back to the docrequest
// handlers like default.aspx will want it and most macros currently need it
_pcr.UmbracoPage = new page(_pcr);
// note - the page() ctor below will cause the "page" to get the value of all its
// "elements" ie of all the IPublishedContent property. If we use the object value,
// that will trigger macro execution - which can't happen because macro execution
// requires that _pcr.UmbracoPage is already initialized = catch-22. The "legacy"
// pipeline did _not_ evaluate the macros, ie it is using the data value, and we
// have to keep doing it because of that catch-22.
// these two are used by many legacy objects
_routingContext.UmbracoContext.HttpContext.Items["pageID"] = _pcr.PublishedContent.Id;
_routingContext.UmbracoContext.HttpContext.Items["pageElements"] = _pcr.UmbracoPage.Elements;
// assign the legacy page back to the docrequest
// handlers like default.aspx will want it and most macros currently need it
_pcr.UmbracoPage = new page(_pcr);
// used by many legacy objects
_routingContext.UmbracoContext.HttpContext.Items["pageID"] = _pcr.PublishedContent.Id;
_routingContext.UmbracoContext.HttpContext.Items["pageElements"] = _pcr.UmbracoPage.Elements;
}
/// <summary>
@@ -138,6 +145,8 @@ namespace Umbraco.Web.Routing
return;
}
// see note in PrepareRequest()
// assign the legacy page back to the docrequest
// handlers like default.aspx will want it and most macros currently need it
_pcr.UmbracoPage = new page(_pcr);

View File

@@ -383,6 +383,7 @@
<Compile Include="Security\ValidateRequestAttempt.cs" />
<Compile Include="Security\WebSecurity.cs" />
<Compile Include="UI\CdfLogger.cs" />
<Compile Include="umbraco.presentation\CompatibilityHelper.cs" />
<Compile Include="umbraco.presentation\LegacyClasses.cs" />
<Compile Include="umbraco.presentation\umbraco\ActionHandlers\SimilarNodeNameComparer.cs" />
<Compile Include="umbraco.presentation\umbraco\controls\ContentTypeControlNew.ascx.cs">

View File

@@ -0,0 +1,172 @@
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Text;
using System.Web;
using Umbraco.Core.Models;
using umbraco.interfaces;
namespace Umbraco.Web.umbraco.presentation
{
static class CompatibilityHelper
{
// NOTE - this is all already in umbraco.MacroEngines
// which references Umbraco.Web - so we can't reference it without
// creating circular references
// fixme - there has to be a better way?
public static INode ConvertToNode(IPublishedContent doc)
{
var node = new ConvertedNode(doc);
return node;
}
public static IProperty ConvertToNodeProperty(IPublishedProperty prop)
{
return new ConvertedProperty(prop.PropertyTypeAlias, prop.Value.ToString());
}
private class ConvertedNode : INode
{
private readonly IPublishedContent _doc;
public ConvertedNode(IPublishedContent doc)
{
_doc = doc;
if (doc == null)
{
Id = 0;
return;
}
template = doc.TemplateId;
Id = doc.Id;
Path = doc.Path;
CreatorName = doc.CreatorName;
SortOrder = doc.SortOrder;
UpdateDate = doc.UpdateDate;
Name = doc.Name;
NodeTypeAlias = doc.DocumentTypeAlias;
CreateDate = doc.CreateDate;
CreatorID = doc.CreatorId;
Level = doc.Level;
UrlName = doc.UrlName;
Version = doc.Version;
WriterID = doc.WriterId;
WriterName = doc.WriterName;
}
public INode Parent
{
get { return ConvertToNode(_doc.Parent); }
}
public int Id { get; private set; }
public int template { get; private set; }
public int SortOrder { get; private set; }
public string Name { get; private set; }
public string UrlName { get; private set; }
public string NodeTypeAlias { get; private set; }
public string WriterName { get; private set; }
public string CreatorName { get; private set; }
public int WriterID { get; private set; }
public int CreatorID { get; private set; }
public string Path { get; private set; }
public DateTime CreateDate { get; private set; }
public DateTime UpdateDate { get; private set; }
public Guid Version { get; private set; }
public string NiceUrl
{
get { return _doc.Url; }
}
public string Url
{
get { return _doc.Url; }
}
public int Level { get; private set; }
public List<IProperty> PropertiesAsList
{
get { return _doc.Properties.Select(ConvertToNodeProperty).ToList(); }
}
public List<INode> ChildrenAsList
{
get { return _doc.Children.Select(ConvertToNode).ToList(); }
}
public IProperty GetProperty(string Alias)
{
return PropertiesAsList.Cast<global::umbraco.NodeFactory.Property>().FirstOrDefault(p => p.Alias == Alias);
}
public IProperty GetProperty(string Alias, out bool propertyExists)
{
foreach (var p in from global::umbraco.NodeFactory.Property p in PropertiesAsList where p.Alias == Alias select p)
{
propertyExists = true;
return p;
}
propertyExists = false;
return null;
}
public DataTable ChildrenAsTable()
{
return _doc.ChildrenAsTable();
}
public DataTable ChildrenAsTable(string nodeTypeAliasFilter)
{
return _doc.ChildrenAsTable(nodeTypeAliasFilter);
}
}
private class ConvertedProperty : IProperty, IHtmlString
{
private readonly string _alias;
private readonly string _value;
public ConvertedProperty(string alias, string value)
{
_alias = alias;
_value = value;
}
public string Alias
{
get { return _alias; }
}
public string Value
{
get { return _value; }
}
public Guid Version
{
get { return Guid.Empty; }
}
public bool IsNull()
{
return Value == null;
}
public bool HasValue()
{
return !string.IsNullOrWhiteSpace(Value);
}
public int ContextId { get; set; }
public string ContextAlias { get; set; }
// implements IHtmlString.ToHtmlString
public string ToHtmlString()
{
return Value;
}
}
}
}

View File

@@ -1,9 +1,11 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Security;
using System.Reflection;
@@ -20,11 +22,14 @@ using System.Xml.Xsl;
using StackExchange.Profiling;
using Umbraco.Core;
using Umbraco.Core.Cache;
using Umbraco.Core.Dynamics;
using Umbraco.Core.Events;
using Umbraco.Core.IO;
using Umbraco.Core.Logging;
using Umbraco.Core.Models;
using Umbraco.Core.Xml.XPath;
using Umbraco.Core.Profiling;
using umbraco.interfaces;
using Umbraco.Web;
using Umbraco.Web.Cache;
using Umbraco.Web.Macros;
@@ -32,13 +37,16 @@ using Umbraco.Web.Templates;
using umbraco.BusinessLogic;
using umbraco.cms.businesslogic;
using umbraco.cms.businesslogic.macro;
using umbraco.cms.businesslogic.member;
using umbraco.DataLayer;
using umbraco.NodeFactory;
using umbraco.presentation.templateControls;
using Umbraco.Web.umbraco.presentation;
using Content = umbraco.cms.businesslogic.Content;
using File = System.IO.File;
using Macro = umbraco.cms.businesslogic.macro.Macro;
using MacroErrorEventArgs = Umbraco.Core.Events.MacroErrorEventArgs;
using Member = umbraco.cms.businesslogic.member.Member;
using Property = umbraco.NodeFactory.Property;
namespace umbraco
{
@@ -1455,7 +1463,7 @@ namespace umbraco
IMacroEngine engine = null;
engine = MacroEngineFactory.GetEngine(PartialViewMacroEngine.EngineName);
var ret = engine.Execute(macro, Node.GetCurrent());
var ret = engine.Execute(macro, CompatibilityHelper.ConvertToNode(UmbracoContext.Current.PublishedContentRequest.PublishedContent));
// if the macro engine supports success reporting and executing failed, then return an empty control so it's not cached
if (engine is IMacroEngineResultStatus)
@@ -1480,13 +1488,13 @@ namespace umbraco
engine = MacroEngineFactory.GetByExtension(macro.ScriptLanguage);
ret = engine.Execute(
macro,
Node.GetCurrent());
CompatibilityHelper.ConvertToNode(UmbracoContext.Current.PublishedContentRequest.PublishedContent));
}
else
{
string path = IOHelper.MapPath(SystemDirectories.MacroScripts + "/" + macro.ScriptName);
engine = MacroEngineFactory.GetByFilename(path);
ret = engine.Execute(macro, Node.GetCurrent());
ret = engine.Execute(macro, CompatibilityHelper.ConvertToNode(UmbracoContext.Current.PublishedContentRequest.PublishedContent));
}
// if the macro engine supports success reporting and executing failed, then return an empty control so it's not cached
@@ -1513,13 +1521,13 @@ namespace umbraco
engine = MacroEngineFactory.GetByExtension(macro.ScriptLanguage);
ret.Text = engine.Execute(
macro,
Node.GetCurrent());
CompatibilityHelper.ConvertToNode(UmbracoContext.Current.PublishedContentRequest.PublishedContent));
}
else
{
string path = IOHelper.MapPath(SystemDirectories.MacroScripts + "/" + macro.ScriptName);
engine = MacroEngineFactory.GetByFilename(path);
ret.Text = engine.Execute(macro, Node.GetCurrent());
ret.Text = engine.Execute(macro, CompatibilityHelper.ConvertToNode(UmbracoContext.Current.PublishedContentRequest.PublishedContent));
}
// if the macro engine supports success reporting and executing failed, then return an empty control so it's not cached

View File

@@ -273,9 +273,17 @@ namespace umbraco
{
foreach(var p in node.Properties)
{
if (!_elements.ContainsKey(p.PropertyTypeAlias))
if (_elements.ContainsKey(p.PropertyTypeAlias) == false)
{
_elements[p.PropertyTypeAlias] = p.Value;
// note: legacy used the raw value (see populating from an Xml node below)
// so we're doing the same here, using DataValue. If we use Value then every
// value will be converted NOW - including RTEs that may contain macros that
// require that the 'page' is already initialized = catch-22.
// to properly fix this, we'd need to turn the elements collection into some
// sort of collection of lazy values.
_elements[p.PropertyTypeAlias] = p.DataValue;
}
}
}

View File

@@ -10,6 +10,7 @@ using umbraco.cms.businesslogic;
using umbraco.cms.businesslogic.propertytype;
using umbraco.interfaces;
using Umbraco.Core;
using Umbraco.Web;
namespace umbraco.NodeFactory
{
@@ -541,11 +542,10 @@ namespace umbraco.NodeFactory
public static int getCurrentNodeId()
{
XmlNode n = ((IHasXmlNode)library.GetXmlNodeCurrent().Current).GetNode();
if (n.Attributes == null || n.Attributes.GetNamedItem("id") == null)
throw new ArgumentException("Current node is null. This might be due to previewing an unpublished node. As the NodeFactory works with published data, macros using the node factory won't work in preview mode.", "Current node is " + System.Web.HttpContext.Current.Items["pageID"].ToString());
return int.Parse(n.Attributes.GetNamedItem("id").Value);
if (UmbracoContext.Current == null) throw new InvalidOperationException("Cannot get current node id without an UmbracoContext.");
if (UmbracoContext.Current.PublishedContentRequest == null) throw new InvalidOperationException("Cannot get current node id without a PublishedContentRequest.");
if (UmbracoContext.Current.PublishedContentRequest.HasPublishedContent == false) throw new InvalidOperationException("Cannot get current node id because the current PublishedContentRequest has no content.");
return UmbracoContext.Current.PublishedContentRequest.PublishedContent.Id;
}
}
}

View File

@@ -6,9 +6,11 @@ using umbraco.interfaces;
using umbraco.cms.businesslogic.media;
using umbraco.cms.businesslogic;
using umbraco.cms.businesslogic.property;
using umbraco.presentation.nodeFactory;
using System.Data;
using Umbraco.Core;
using umbraco.MacroEngines.Library;
using Umbraco.Web;
using Umbraco.Web.umbraco.presentation;
namespace umbraco.MacroEngines
{
@@ -30,11 +32,11 @@ namespace umbraco.MacroEngines
}
public DynamicBackingItem(int Id)
{
NodeFactory.Node baseNode = new NodeFactory.Node(Id);
var n = UmbracoContext.Current.ContentCache.GetById(Id).ConvertToNode();
this.content = baseNode;
this.content = n;
this.Type = DynamicBackingItemType.Content;
if (baseNode.Id == 0 && Id != 0)
if (n.Id == 0 && Id != 0)
{
this.media = ExamineBackedMedia.GetUmbracoMedia(Id);
this.Type = DynamicBackingItemType.Media;
@@ -48,7 +50,6 @@ namespace umbraco.MacroEngines
}
public DynamicBackingItem(int Id, DynamicBackingItemType Type)
{
NodeFactory.Node baseNode = new NodeFactory.Node(Id);
if (Type == DynamicBackingItemType.Media)
{
this.media = ExamineBackedMedia.GetUmbracoMedia(Id);
@@ -56,7 +57,7 @@ namespace umbraco.MacroEngines
}
else
{
this.content = baseNode;
this.content = UmbracoContext.Current.ContentCache.GetById(Id).ConvertToNode();
this.Type = Type;
}
}

View File

@@ -8,6 +8,7 @@ using Umbraco.Core.Models;
using Umbraco.Web;
using umbraco.NodeFactory;
using umbraco.interfaces;
using Umbraco.Web.umbraco.presentation;
using Property = umbraco.NodeFactory.Property;
namespace umbraco.MacroEngines.Library
@@ -18,107 +19,9 @@ namespace umbraco.MacroEngines.Library
/// <remarks>These are dedicated to converting DynamicPublishedContent to INode.</remarks>
internal static class PublishedContentExtensions
{
internal static IProperty ConvertToNodeProperty(this IPublishedProperty prop)
{
return new PropertyResult(prop.PropertyTypeAlias, prop.Value.ToString());
}
internal static INode ConvertToNode(this IPublishedContent doc)
{
var node = new ConvertedNode(doc);
return node;
}
/// <summary>
/// Internal custom INode class used for conversions from DynamicPublishedContent.
/// </summary>
private class ConvertedNode : INode
{
private readonly IPublishedContent _doc;
public ConvertedNode(IPublishedContent doc)
{
_doc = doc;
template = doc.TemplateId;
Id = doc.Id;
Path = doc.Path;
CreatorName = doc.CreatorName;
SortOrder = doc.SortOrder;
UpdateDate = doc.UpdateDate;
Name = doc.Name;
NodeTypeAlias = doc.DocumentTypeAlias;
CreateDate = doc.CreateDate;
CreatorID = doc.CreatorId;
Level = doc.Level;
UrlName = doc.UrlName;
Version = doc.Version;
WriterID = doc.WriterId;
WriterName = doc.WriterName;
}
public INode Parent
{
get { return _doc.Parent.ConvertToNode(); }
}
public int Id { get; private set; }
public int template { get; private set; }
public int SortOrder { get; private set; }
public string Name { get; private set; }
public string UrlName { get; private set; }
public string NodeTypeAlias { get; private set; }
public string WriterName { get; private set; }
public string CreatorName { get; private set; }
public int WriterID { get; private set; }
public int CreatorID { get; private set; }
public string Path { get; private set; }
public DateTime CreateDate { get; private set; }
public DateTime UpdateDate { get; private set; }
public Guid Version { get; private set; }
public string NiceUrl
{
get { return library.NiceUrl(Id); }
}
public string Url
{
get { return library.NiceUrl(Id); }
}
public int Level { get; private set; }
public List<IProperty> PropertiesAsList
{
get { return _doc.Properties.Select(ConvertToNodeProperty).ToList(); }
}
public List<INode> ChildrenAsList
{
get { return _doc.Children.Select(x => x.ConvertToNode()).ToList(); }
}
public IProperty GetProperty(string Alias)
{
return PropertiesAsList.Cast<Property>().FirstOrDefault(p => p.Alias == Alias);
}
public IProperty GetProperty(string Alias, out bool propertyExists)
{
foreach (var p in from Property p in PropertiesAsList where p.Alias == Alias select p)
{
propertyExists = true;
return p;
}
propertyExists = false;
return null;
}
public DataTable ChildrenAsTable()
{
return _doc.ChildrenAsTable();
}
public DataTable ChildrenAsTable(string nodeTypeAliasFilter)
{
return _doc.ChildrenAsTable(nodeTypeAliasFilter);
}
return CompatibilityHelper.ConvertToNode(doc);
}
}
}