diff --git a/src/Umbraco.Core/PublishedContentExtensions.cs b/src/Umbraco.Core/PublishedContentExtensions.cs
index 9d54198982..c039cf881c 100644
--- a/src/Umbraco.Core/PublishedContentExtensions.cs
+++ b/src/Umbraco.Core/PublishedContentExtensions.cs
@@ -75,6 +75,36 @@ namespace Umbraco.Core
}
#endregion
+ ///
+ /// Returns the recursive value of a field by iterating up the parent chain but starting at the publishedContent passed in
+ ///
+ ///
+ ///
+ ///
+ public static string GetRecursiveValue(this IPublishedContent publishedContent, string fieldname)
+ {
+ var contentValue = "";
+ var currentContent = publishedContent;
+
+ while (contentValue.IsNullOrWhiteSpace())
+ {
+ var val = currentContent[fieldname];
+ if (val == null || val.ToString().IsNullOrWhiteSpace())
+ {
+ if (currentContent.Parent == null)
+ {
+ break; //we've reached the top
+ }
+ currentContent = currentContent.Parent;
+ }
+ else
+ {
+ contentValue = val.ToString(); //we've found a recursive val
+ }
+ }
+ return contentValue;
+ }
+
public static bool IsVisible(this IPublishedContent doc)
{
var umbracoNaviHide = doc.GetProperty("umbracoNaviHide");
diff --git a/src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs b/src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs
index bf3a410e51..d2570dacfd 100644
--- a/src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs
+++ b/src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs
@@ -35,13 +35,16 @@ namespace Umbraco.Tests.PublishedContent
1
+
This is some content]]>
+
+
@@ -105,6 +108,16 @@ namespace Umbraco.Tests.PublishedContent
return doc;
}
+ [Test]
+ public void Test_Get_Recursive_Val()
+ {
+ var doc = GetNode(1174);
+ var rVal = doc.GetRecursiveValue("testRecursive");
+ var nullVal = doc.GetRecursiveValue("DoNotFindThis");
+ Assert.AreEqual("This is the recursive val", rVal);
+ Assert.AreEqual("", nullVal);
+ }
+
[Test]
public void Get_Property_Value_Uses_Converter()
{
diff --git a/src/Umbraco.Web/UmbracoHelper.cs b/src/Umbraco.Web/UmbracoHelper.cs
index db5323eede..7a9ba39cf3 100644
--- a/src/Umbraco.Web/UmbracoHelper.cs
+++ b/src/Umbraco.Web/UmbracoHelper.cs
@@ -300,8 +300,7 @@ namespace Umbraco.Web
var item = new Item()
- {
- //NodeId = currentPage.Id.ToString();
+ {
Field = fieldAlias,
TextIfEmpty = altText,
LegacyAttributes = attributesForItem
@@ -470,17 +469,17 @@ namespace Umbraco.Web
public dynamic Content(object id)
{
- return DocumentById(id, PublishedContentStoreResolver.Current.PublishedContentStore);
+ return DocumentById(id, PublishedContentStoreResolver.Current.PublishedContentStore, new DynamicNull());
}
public dynamic Content(int id)
{
- return DocumentById(id, PublishedContentStoreResolver.Current.PublishedContentStore);
+ return DocumentById(id, PublishedContentStoreResolver.Current.PublishedContentStore, new DynamicNull());
}
public dynamic Content(string id)
{
- return DocumentById(id, PublishedContentStoreResolver.Current.PublishedContentStore);
+ return DocumentById(id, PublishedContentStoreResolver.Current.PublishedContentStore, new DynamicNull());
}
public dynamic Content(params object[] ids)
@@ -574,17 +573,17 @@ namespace Umbraco.Web
public dynamic Media(object id)
{
- return DocumentById(id, PublishedMediaStoreResolver.Current.PublishedMediaStore);
+ return DocumentById(id, PublishedMediaStoreResolver.Current.PublishedMediaStore, new DynamicNull());
}
public dynamic Media(int id)
{
- return DocumentById(id, PublishedMediaStoreResolver.Current.PublishedMediaStore);
+ return DocumentById(id, PublishedMediaStoreResolver.Current.PublishedMediaStore, new DynamicNull());
}
public dynamic Media(string id)
{
- return DocumentById(id, PublishedMediaStoreResolver.Current.PublishedMediaStore);
+ return DocumentById(id, PublishedMediaStoreResolver.Current.PublishedMediaStore, new DynamicNull());
}
public dynamic Media(params object[] ids)
@@ -651,7 +650,7 @@ namespace Umbraco.Web
{
int docId;
return int.TryParse(id, out docId)
- ? DocumentById(docId, store)
+ ? DocumentById(docId, store, null)
: null;
}
@@ -686,35 +685,36 @@ namespace Umbraco.Web
///
///
///
+ ///
///
///
/// We accept an object type because GetPropertyValue now returns an 'object', we still want to allow people to pass
/// this result in to this method.
/// This method will throw an exception if the value is not of type int or string.
///
- private dynamic DocumentById(object id, IPublishedStore store)
+ private dynamic DocumentById(object id, IPublishedStore store, object ifNotFound)
{
if (id is string)
- return DocumentById((string)id, store);
+ return DocumentById((string)id, store, ifNotFound);
if (id is int)
- return DocumentById((int)id, store);
+ return DocumentById((int)id, store, ifNotFound);
throw new InvalidOperationException("The value of parameter 'id' must be either a string or an integer");
}
- private dynamic DocumentById(int id, IPublishedStore store)
+ private dynamic DocumentById(int id, IPublishedStore store, object ifNotFound)
{
var doc = store.GetDocumentById(UmbracoContext.Current, id);
return doc == null
- ? new DynamicNull()
+ ? ifNotFound
: new DynamicPublishedContent(doc).AsDynamic();
}
- private dynamic DocumentById(string id, IPublishedStore store)
+ private dynamic DocumentById(string id, IPublishedStore store, object ifNotFound)
{
int docId;
return int.TryParse(id, out docId)
- ? DocumentById(docId, store)
- : new DynamicNull();
+ ? DocumentById(docId, store, ifNotFound)
+ : ifNotFound;
}
///
@@ -730,7 +730,8 @@ namespace Umbraco.Web
///
private dynamic DocumentByIds(IPublishedStore store, params object[] ids)
{
- var nodes = ids.Select(eachId => DocumentById(eachId, store))
+ var dNull = new DynamicNull();
+ var nodes = ids.Select(eachId => DocumentById(eachId, store, dNull))
.Where(x => !TypeHelper.IsTypeAssignableFrom(x))
.Cast();
return new DynamicPublishedContentList(nodes);
@@ -738,7 +739,8 @@ namespace Umbraco.Web
private dynamic DocumentByIds(IPublishedStore store, params int[] ids)
{
- var nodes = ids.Select(eachId => DocumentById(eachId, store))
+ var dNull = new DynamicNull();
+ var nodes = ids.Select(eachId => DocumentById(eachId, store, dNull))
.Where(x => !TypeHelper.IsTypeAssignableFrom(x))
.Cast();
return new DynamicPublishedContentList(nodes);
@@ -746,7 +748,8 @@ namespace Umbraco.Web
private dynamic DocumentByIds(IPublishedStore store, params string[] ids)
{
- var nodes = ids.Select(eachId => DocumentById(eachId, store))
+ var dNull = new DynamicNull();
+ var nodes = ids.Select(eachId => DocumentById(eachId, store, dNull))
.Where(x => !TypeHelper.IsTypeAssignableFrom(x))
.Cast();
return new DynamicPublishedContentList(nodes);
diff --git a/src/Umbraco.Web/umbraco.presentation/item.cs b/src/Umbraco.Web/umbraco.presentation/item.cs
index 30c5a2c217..fe6c8faea7 100644
--- a/src/Umbraco.Web/umbraco.presentation/item.cs
+++ b/src/Umbraco.Web/umbraco.presentation/item.cs
@@ -3,6 +3,7 @@ using System.Collections;
using System.Web;
using System.Xml;
using Umbraco.Core;
+using Umbraco.Core.Models;
namespace umbraco
{
@@ -12,7 +13,7 @@ namespace umbraco
public class item
{
private String _fieldContent = "";
- private String _fieldName;
+ private readonly String _fieldName;
public String FieldContent
{
@@ -22,64 +23,108 @@ namespace umbraco
public item(string itemValue, IDictionary attributes)
{
_fieldContent = itemValue;
- parseItem(attributes);
+ ParseItem(attributes);
}
///
- ///
+ /// Creates a new Legacy item
///
- ///
+ ///
///
- ///
public item(IDictionary elements, IDictionary attributes)
+ : this(null, elements, attributes)
+ {
+ }
+
+ ///
+ /// Creates an Item with a publishedContent item in order to properly recurse and return the value.
+ ///
+ ///
+ ///
+ ///
+ ///
+ /// THIS ENTIRE CLASS WILL BECOME LEGACY, THE FIELD RENDERING NEEDS TO BE REPLACES SO THAT IS WHY THIS
+ /// CTOR IS INTERNAL.
+ ///
+ internal item(IPublishedContent publishedContent, IDictionary elements, IDictionary attributes)
{
_fieldName = helper.FindAttribute(attributes, "field");
- if(_fieldName.StartsWith("#"))
+ if (_fieldName.StartsWith("#"))
{
_fieldContent = library.GetDictionaryItem(_fieldName.Substring(1, _fieldName.Length - 1));
}
else
{
// Loop through XML children we need to find the fields recursive
- if(helper.FindAttribute(attributes, "recursive") == "true")
+ if (helper.FindAttribute(attributes, "recursive") == "true")
{
-
- XmlDocument umbracoXML = presentation.UmbracoContext.Current.GetXml();
-
- String[] splitpath = (String[]) elements["splitpath"];
- for(int i = 0; i < splitpath.Length - 1; i++)
+ if (publishedContent == null)
{
- XmlNode element = umbracoXML.GetElementById(splitpath[splitpath.Length - i - 1].ToString());
- if (element == null)
- continue;
- string xpath = UmbracoSettings.UseLegacyXmlSchema ? "./data [@alias = '{0}']" : "./{0}";
- XmlNode currentNode = element.SelectSingleNode(string.Format(xpath,
- _fieldName));
- if(currentNode != null && currentNode.FirstChild != null &&
- !string.IsNullOrEmpty(currentNode.FirstChild.Value) &&
- !string.IsNullOrEmpty(currentNode.FirstChild.Value.Trim()))
- {
- HttpContext.Current.Trace.Write("item.recursive", "Item loaded from " + splitpath[splitpath.Length - i - 1]);
- _fieldContent = currentNode.FirstChild.Value;
- break;
- }
+ var recursiveVal = GetRecursiveValueLegacy(elements);
+ _fieldContent = recursiveVal.IsNullOrWhiteSpace() ? _fieldContent : recursiveVal;
+ }
+ else
+ {
+ var recursiveVal = publishedContent.GetRecursiveValue(_fieldName);
+ _fieldContent = recursiveVal.IsNullOrWhiteSpace() ? _fieldContent : recursiveVal;
}
}
else
{
- if (elements[_fieldName] != null && !string.IsNullOrEmpty(elements[_fieldName].ToString()))
- _fieldContent = elements[_fieldName].ToString().Trim();
- else if(!string.IsNullOrEmpty(helper.FindAttribute(attributes, "useIfEmpty")))
- if (elements[helper.FindAttribute(attributes, "useIfEmpty")] != null && !string.IsNullOrEmpty(elements[helper.FindAttribute(attributes, "useIfEmpty")].ToString()))
- _fieldContent = elements[helper.FindAttribute(attributes, "useIfEmpty")].ToString().Trim();
+ if (elements[_fieldName] != null && !string.IsNullOrEmpty(elements[_fieldName].ToString()))
+ {
+ _fieldContent = elements[_fieldName].ToString().Trim();
+ }
+ else if (!string.IsNullOrEmpty(helper.FindAttribute(attributes, "useIfEmpty")))
+ {
+ if (elements[helper.FindAttribute(attributes, "useIfEmpty")] != null && !string.IsNullOrEmpty(elements[helper.FindAttribute(attributes, "useIfEmpty")].ToString()))
+ {
+ _fieldContent = elements[helper.FindAttribute(attributes, "useIfEmpty")].ToString().Trim();
+ }
+ }
+
}
}
- parseItem(attributes);
+ ParseItem(attributes);
+ }
+
+ ///
+ /// Returns the recursive value using a legacy strategy of looking at the xml cache and the splitPath in the elements collection
+ ///
+ ///
+ ///
+ private string GetRecursiveValueLegacy(IDictionary elements)
+ {
+ var content = "";
+
+ var umbracoXml = presentation.UmbracoContext.Current.GetXml();
+
+ var splitpath = (String[])elements["splitpath"];
+ for (int i = 0; i < splitpath.Length - 1; i++)
+ {
+ XmlNode element = umbracoXml.GetElementById(splitpath[splitpath.Length - i - 1]);
+
+ if (element == null)
+ continue;
+
+ var xpath = UmbracoSettings.UseLegacyXmlSchema ? "./data [@alias = '{0}']" : "./{0}";
+ var currentNode = element.SelectSingleNode(string.Format(xpath, _fieldName));
+
+ //continue if all is null
+ if (currentNode == null || currentNode.FirstChild == null || string.IsNullOrEmpty(currentNode.FirstChild.Value) || string.IsNullOrEmpty(currentNode.FirstChild.Value.Trim()))
+ continue;
+
+ HttpContext.Current.Trace.Write("item.recursive", "Item loaded from " + splitpath[splitpath.Length - i - 1]);
+ content = currentNode.FirstChild.Value;
+ break;
+ }
+
+ return content;
}
- private void parseItem(IDictionary attributes)
+ private void ParseItem(IDictionary attributes)
{
HttpContext.Current.Trace.Write("item", "Start parsing '" + _fieldName + "'");
if(helper.FindAttribute(attributes, "textIfEmpty") != "" && _fieldContent == "")
diff --git a/src/Umbraco.Web/umbraco.presentation/page.cs b/src/Umbraco.Web/umbraco.presentation/page.cs
index efba296b44..e98efd8e5e 100644
--- a/src/Umbraco.Web/umbraco.presentation/page.cs
+++ b/src/Umbraco.Web/umbraco.presentation/page.cs
@@ -87,9 +87,14 @@ namespace umbraco
}
///
- /// Initializes a new instance of the class for a published document.
+ /// Initializes a new instance of the class for a published document request.
///
/// The pointing to the document.
+ ///
+ /// The difference between creating the page with PublishedContentRequest vs an IPublishedContent item is
+ /// that the PublishedContentRequest takes into account how a template is assigned during the routing process whereas
+ /// with an IPublishedContent item, the template id is asssigned purely based on the default.
+ ///
internal page(PublishedContentRequest docreq)
{
@@ -112,6 +117,29 @@ namespace umbraco
}
+ ///
+ /// Initializes a new instance of the page for a published document
+ ///
+ ///
+ internal page(IPublishedContent doc)
+ {
+ if (doc == null) throw new ArgumentNullException("doc");
+
+ populatePageData(doc.Id,
+ doc.Name, doc.DocumentTypeId, doc.DocumentTypeAlias,
+ doc.WriterName, doc.CreatorName, doc.CreateDate, doc.UpdateDate,
+ doc.Path, doc.Version, doc.Parent == null ? -1 : doc.Parent.Id);
+
+ if (doc.TemplateId > 0)
+ {
+ //set the template to whatever is assigned to the doc
+ _template = doc.TemplateId;
+ _elements["template"] = _template.ToString();
+ }
+
+ PopulateElementData(doc);
+ }
+
///
/// Initializes a new instance of the class for a published document.
///
diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/templateControls/ItemRenderer.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/templateControls/ItemRenderer.cs
index de2149b1c4..a1d423bfa3 100644
--- a/src/Umbraco.Web/umbraco.presentation/umbraco/templateControls/ItemRenderer.cs
+++ b/src/Umbraco.Web/umbraco.presentation/umbraco/templateControls/ItemRenderer.cs
@@ -8,6 +8,8 @@ using System.Web;
using System.Web.UI;
using System.Xml;
using Umbraco.Core.Macros;
+using Umbraco.Web;
+using Umbraco.Web.Routing;
using Umbraco.Web.Templates;
using umbraco.cms.businesslogic;
using umbraco.cms.businesslogic.property;
@@ -99,8 +101,13 @@ namespace umbraco.presentation.templateControls
//moved the following from the catch block up as this will allow fallback options alt text etc to work
- page itemPage = new page(content.Instance.XmlContent.GetElementById(tempNodeId.ToString()));
- tempElementContent = new item(itemPage.Elements, item.LegacyAttributes).FieldContent;
+ //get the publishedcontent item
+ var publishedContent = PublishedContentStoreResolver.Current.PublishedContentStore.GetDocumentById(
+ Umbraco.Web.UmbracoContext.Current,
+ tempNodeId.Value);
+
+ var itemPage = new page(publishedContent);
+ tempElementContent = new item(publishedContent, itemPage.Elements, item.LegacyAttributes).FieldContent;
/*removed as would fail as there is a incorrect cast in the method called.
Also the following code does not respect any of Umbraco Items fallback and formatting options */
diff --git a/src/umbraco.MacroEngines/RazorDynamicNode/DynamicNode.cs b/src/umbraco.MacroEngines/RazorDynamicNode/DynamicNode.cs
index 3c92cfa223..4fc8b76db6 100644
--- a/src/umbraco.MacroEngines/RazorDynamicNode/DynamicNode.cs
+++ b/src/umbraco.MacroEngines/RazorDynamicNode/DynamicNode.cs
@@ -406,44 +406,6 @@ namespace umbraco.MacroEngines
}
return result;
}
- private List GetAncestorOrSelfNodeTypeAlias(DynamicBackingItem node)
- {
- List list = new List();
- if (node != null)
- {
- if (node.Type == DynamicBackingItemType.Content)
- {
- //find the doctype node, so we can walk it's parent's tree- not the working.parent content tree
- CMSNode working = ContentType.GetByAlias(node.NodeTypeAlias);
- while (working != null)
- {
- //NOTE: I'm not sure if anyone has ever tested this but if you get working.Parent it will return a CMSNode and
- // it will never be castable to a 'ContentType' object
- // pretty sure the only reason why this method works for the one place that it is used is that it returns
- // the current node's alias which is all that is actually requried, this is just added overhead for no
- // reason
-
- if ((working as ContentType) != null)
- {
- list.Add((working as ContentType).Alias);
- }
- try
- {
- working = working.Parent;
- }
- catch (ArgumentException)
- {
- break;
- }
- }
- }
- else
- {
- return null;
- }
- }
- return list;
- }
private static Dictionary, Type> _razorDataTypeModelTypes = null;
private static readonly ReaderWriterLockSlim _locker = new ReaderWriterLockSlim();
@@ -485,14 +447,6 @@ namespace umbraco.MacroEngines
}
});
- //NOTE: We really dont need to log this?
- //var i = 1;
- //foreach (var item in foundTypes)
- //{
- // HttpContext.Current.Trace.Write(string.Format("{0}/{1}: {2}@{4} => {3}", i, foundTypes.Count, item.Key.Item1, item.Value.FullName, item.Key.Item2));
- // i++;
- //}
-
//there is no error, so set the collection
_razorDataTypeModelTypes = foundTypes;
@@ -605,21 +559,8 @@ namespace umbraco.MacroEngines
LogHelper.Warn(string.Format("Could not get the dataTypeType for the RazorDataTypeModel"));
}
}
- else
- {
- //NOTE: Do we really want to log this? I'm not sure.
- //if (RazorDataTypeModelTypes == null)
- //{
- // HttpContext.Current.Trace.Write(string.Format("RazorDataTypeModelTypes is null, probably an exception while building the cache, falling back to ConvertPropertyValueByDataType", dataType));
- //}
- //else
- //{
- // HttpContext.Current.Trace.Write(string.Format("GUID {0} does not have a DataTypeModel, falling back to ConvertPropertyValueByDataType", dataType));
- //}
-
- }
-
- //convert the string value to a known type
+
+ //convert the string value to a known type
return ConvertPropertyValueByDataType(ref result, name, dataType);
}
@@ -629,15 +570,10 @@ namespace umbraco.MacroEngines
var typeChildren = n.ChildrenAsList;
if (typeChildren != null)
{
- var filteredTypeChildren = typeChildren.Where(x =>
- {
- List ancestorAliases = GetAncestorOrSelfNodeTypeAlias(x);
- if (ancestorAliases == null)
- {
- return false;
- }
- return ancestorAliases.Any(alias => alias == name || MakePluralName(alias) == name);
- });
+
+ var filteredTypeChildren = typeChildren
+ .Where(x => x.NodeTypeAlias.InvariantEquals(name) || x.NodeTypeAlias.MakePluralName().InvariantEquals(binder.Name))
+ .ToArray();
if (filteredTypeChildren.Any())
{
result = new DynamicNodeList(filteredTypeChildren);