diff --git a/src/Umbraco.Core/DocumentExtensions.cs b/src/Umbraco.Core/DocumentExtensions.cs
index af670fe03e..628acc7ff2 100644
--- a/src/Umbraco.Core/DocumentExtensions.cs
+++ b/src/Umbraco.Core/DocumentExtensions.cs
@@ -32,17 +32,6 @@ namespace Umbraco.Core
return converted.Result;
return ifCannotConvert;
}
-
- ///
- /// Returns the property based on the case insensitive match of the alias
- ///
- ///
- ///
- ///
- public static IDocumentProperty GetProperty(this IDocument d, string alias)
- {
- return d.Properties.FirstOrDefault(p => p.Alias.InvariantEquals(alias));
- }
}
}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Models/IDocument.cs b/src/Umbraco.Core/Models/IDocument.cs
index fae2866ff2..07d9b55317 100644
--- a/src/Umbraco.Core/Models/IDocument.cs
+++ b/src/Umbraco.Core/Models/IDocument.cs
@@ -31,6 +31,21 @@ namespace Umbraco.Core.Models
Guid Version { get; }
int Level { get; }
Collection Properties { get; }
- IEnumerable Children { get; }
+ IEnumerable Children { get; }
+
+ ///
+ /// Returns a property on the object based on an alias
+ ///
+ ///
+ ///
+ ///
+ /// Although we do have a a property to return Properties of the object, in some cases a custom implementation may not know
+ /// about all properties until specifically asked for one by alias.
+ ///
+ /// This method is mostly used in places such as DynamicDocument when trying to resolve a property based on an alias.
+ /// In some cases Pulish Stores, a property value may exist in multiple places and we need to fallback to different cached locations
+ /// therefore sometimes the 'Properties' collection may not be sufficient.
+ ///
+ IDocumentProperty GetProperty(string alias);
}
}
\ No newline at end of file
diff --git a/src/Umbraco.Tests/ContentStores/PublishMediaStoreTests.cs b/src/Umbraco.Tests/ContentStores/PublishMediaStoreTests.cs
index 8a988a7a35..960fde1891 100644
--- a/src/Umbraco.Tests/ContentStores/PublishMediaStoreTests.cs
+++ b/src/Umbraco.Tests/ContentStores/PublishMediaStoreTests.cs
@@ -4,6 +4,7 @@ using System.Linq;
using System.Xml;
using Examine;
using NUnit.Framework;
+using Umbraco.Core;
using Umbraco.Core.Models;
using Umbraco.Web;
@@ -189,9 +190,11 @@ namespace Umbraco.Tests
//there is no parent
a => null,
//we're not going to test this so ignore
- a => new List()),
+ a => new List(),
+ (dd, a) => dd.Properties.FirstOrDefault(x => x.Alias.InvariantEquals(a))),
//callback to get the children
- d => children);
+ d => children,
+ (dd, a) => dd.Properties.FirstOrDefault(x => x.Alias.InvariantEquals(a)));
return dicDoc;
}
diff --git a/src/Umbraco.Tests/DynamicDocument/DocumentTests.cs b/src/Umbraco.Tests/DynamicDocument/DocumentTests.cs
new file mode 100644
index 0000000000..a5c081f7e3
--- /dev/null
+++ b/src/Umbraco.Tests/DynamicDocument/DocumentTests.cs
@@ -0,0 +1,198 @@
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Linq;
+using NUnit.Framework;
+using Umbraco.Core;
+using Umbraco.Core.Dynamics;
+using Umbraco.Core.Models;
+using Umbraco.Tests.TestHelpers;
+using Umbraco.Web;
+
+namespace Umbraco.Tests.DynamicDocument
+{
+ ///
+ /// Unit tests for IDocument and extensions
+ ///
+ [TestFixture]
+ public class DocumentTests : BaseRoutingTest
+ {
+ public override void Initialize()
+ {
+ base.Initialize();
+ //need to specify a different callback for testing
+ Umbraco.Web.DocumentExtensions.GetPropertyAliasesAndNames = s =>
+ {
+ var userFields = new Dictionary()
+ {
+ {"property1", "Property 1"},
+ {"property2", "Property 2"}
+ };
+ if (s == "Child")
+ {
+ userFields.Add("property4", "Property 4");
+ }
+ else
+ {
+ userFields.Add("property3", "Property 3");
+ }
+
+ //ensure the standard fields are there
+ var allFields = new Dictionary()
+ {
+ {"Id", "Id"},
+ {"NodeName", "NodeName"},
+ {"NodeTypeAlias", "NodeTypeAlias"},
+ {"CreateDate", "CreateDate"},
+ {"UpdateDate", "UpdateDate"},
+ {"CreatorName", "CreatorName"},
+ {"WriterName", "WriterName"},
+ {"Url", "Url"}
+ };
+ foreach (var f in userFields.Where(f => !allFields.ContainsKey(f.Key)))
+ {
+ allFields.Add(f.Key, f.Value);
+ }
+ return allFields;
+ };
+ var routingContext = GetRoutingContext("/test");
+
+ //set the UmbracoContext.Current since the extension methods rely on it
+ UmbracoContext.Current = routingContext.UmbracoContext;
+ }
+
+ public override void TearDown()
+ {
+ base.TearDown();
+ Umbraco.Web.DocumentExtensions.GetPropertyAliasesAndNames = null;
+ UmbracoContext.Current = null;
+ }
+
+ [Test]
+ public void To_DataTable()
+ {
+ var doc = GetDocument(true, 1);
+ var dt = doc.ChildrenAsTable();
+
+ Assert.AreEqual(11, dt.Columns.Count);
+ Assert.AreEqual(3, dt.Rows.Count);
+ Assert.AreEqual("value4", dt.Rows[0]["Property 1"]);
+ Assert.AreEqual("value5", dt.Rows[0]["Property 2"]);
+ Assert.AreEqual("value6", dt.Rows[0]["Property 4"]);
+ Assert.AreEqual("value7", dt.Rows[1]["Property 1"]);
+ Assert.AreEqual("value8", dt.Rows[1]["Property 2"]);
+ Assert.AreEqual("value9", dt.Rows[1]["Property 4"]);
+ Assert.AreEqual("value10", dt.Rows[2]["Property 1"]);
+ Assert.AreEqual("value11", dt.Rows[2]["Property 2"]);
+ Assert.AreEqual("value12", dt.Rows[2]["Property 4"]);
+ }
+
+ [Test]
+ public void To_DataTable_With_Filter()
+ {
+ var doc = GetDocument(true, 1);
+ //change a doc type alias
+ ((TestDocument) doc.Children.ElementAt(0)).DocumentTypeAlias = "DontMatch";
+
+ var dt = doc.ChildrenAsTable("Child");
+
+ Assert.AreEqual(11, dt.Columns.Count);
+ Assert.AreEqual(2, dt.Rows.Count);
+ Assert.AreEqual("value7", dt.Rows[0]["Property 1"]);
+ Assert.AreEqual("value8", dt.Rows[0]["Property 2"]);
+ Assert.AreEqual("value9", dt.Rows[0]["Property 4"]);
+ Assert.AreEqual("value10", dt.Rows[1]["Property 1"]);
+ Assert.AreEqual("value11", dt.Rows[1]["Property 2"]);
+ Assert.AreEqual("value12", dt.Rows[1]["Property 4"]);
+ }
+
+ [Test]
+ public void To_DataTable_No_Rows()
+ {
+ var doc = GetDocument(false, 1);
+ var dt = doc.ChildrenAsTable();
+ //will return an empty data table
+ Assert.AreEqual(0, dt.Columns.Count);
+ Assert.AreEqual(0, dt.Rows.Count);
+ }
+
+ private IDocument GetDocument(bool createChildren, int indexVals)
+ {
+ var d = new TestDocument
+ {
+ CreateDate = DateTime.Now,
+ CreatorId = 1,
+ CreatorName = "Shannon",
+ DocumentTypeAlias = createChildren? "Parent" : "Child",
+ DocumentTypeId = 2,
+ Id = 3,
+ SortOrder = 4,
+ TemplateId = 5,
+ UpdateDate = DateTime.Now,
+ Path = "-1,3",
+ UrlName = "home-page",
+ Name = "Page" + Guid.NewGuid().ToString(),
+ Version = Guid.NewGuid(),
+ WriterId = 1,
+ WriterName = "Shannon",
+ Parent = null,
+ Level = 1,
+ Properties = new Collection(
+ new List()
+ {
+ new PropertyResult("property1", "value" + indexVals, Guid.NewGuid(), PropertyResultType.UserProperty),
+ new PropertyResult("property2", "value" + (indexVals + 1), Guid.NewGuid(), PropertyResultType.UserProperty)
+ }),
+ Children = new List()
+ };
+ if (createChildren)
+ {
+ d.Children = new List()
+ {
+ GetDocument(false, indexVals + 3),
+ GetDocument(false, indexVals + 6),
+ GetDocument(false, indexVals + 9)
+ };
+ }
+ if (!createChildren)
+ {
+ //create additional columns, used to test the different columns for child nodes
+ d.Properties.Add(new PropertyResult("property4", "value" + (indexVals + 2), Guid.NewGuid(), PropertyResultType.UserProperty));
+ }
+ else
+ {
+ d.Properties.Add(new PropertyResult("property3", "value" + (indexVals + 2), Guid.NewGuid(), PropertyResultType.UserProperty));
+ }
+ return d;
+ }
+
+
+ private class TestDocument : IDocument
+ {
+ public IDocument Parent { get; set; }
+ public int Id { get; set; }
+ public int TemplateId { get; set; }
+ public int SortOrder { get; set; }
+ public string Name { get; set; }
+ public string UrlName { get; set; }
+ public string DocumentTypeAlias { get; set; }
+ public int DocumentTypeId { get; set; }
+ public string WriterName { get; set; }
+ public string CreatorName { get; set; }
+ public int WriterId { get; set; }
+ public int CreatorId { get; set; }
+ public string Path { get; set; }
+ public DateTime CreateDate { get; set; }
+ public DateTime UpdateDate { get; set; }
+ public Guid Version { get; set; }
+ public int Level { get; set; }
+ public Collection Properties { get; set; }
+ public IEnumerable Children { get; set; }
+ public IDocumentProperty GetProperty(string alias)
+ {
+ return Properties.FirstOrDefault(x => x.Alias.InvariantEquals(alias));
+ }
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Tests/DynamicDocument/DynamicDocumentTests.cs b/src/Umbraco.Tests/DynamicDocument/DynamicDocumentTests.cs
index 98c59dc1d3..b81372557e 100644
--- a/src/Umbraco.Tests/DynamicDocument/DynamicDocumentTests.cs
+++ b/src/Umbraco.Tests/DynamicDocument/DynamicDocumentTests.cs
@@ -1,13 +1,7 @@
-using System;
-using System.Collections.Generic;
-using System.Collections.ObjectModel;
-using System.Linq;
using NUnit.Framework;
using Umbraco.Core.Dynamics;
-using Umbraco.Core.Models;
using Umbraco.Core.PropertyEditors;
using Umbraco.Tests.Routing;
-using Umbraco.Tests.TestHelpers;
using Umbraco.Web;
using Umbraco.Web.Routing;
using umbraco.BusinessLogic;
@@ -17,188 +11,6 @@ using umbraco.cms.businesslogic.web;
namespace Umbraco.Tests.DynamicDocument
{
-
- ///
- /// Unit tests for IDocument and extensions
- ///
- [TestFixture]
- public class DocumentTests : BaseRoutingTest
- {
- public override void Initialize()
- {
- base.Initialize();
- //need to specify a different callback for testing
- DocumentExtensions.GetPropertyAliasesAndNames = s =>
- {
- var userFields = new Dictionary()
- {
- {"property1", "Property 1"},
- {"property2", "Property 2"}
- };
- if (s == "Child")
- {
- userFields.Add("property4", "Property 4");
- }
- else
- {
- userFields.Add("property3", "Property 3");
- }
-
- //ensure the standard fields are there
- var allFields = new Dictionary()
- {
- {"Id", "Id"},
- {"NodeName", "NodeName"},
- {"NodeTypeAlias", "NodeTypeAlias"},
- {"CreateDate", "CreateDate"},
- {"UpdateDate", "UpdateDate"},
- {"CreatorName", "CreatorName"},
- {"WriterName", "WriterName"},
- {"Url", "Url"}
- };
- foreach (var f in userFields.Where(f => !allFields.ContainsKey(f.Key)))
- {
- allFields.Add(f.Key, f.Value);
- }
- return allFields;
- };
- var routingContext = GetRoutingContext("/test");
-
- //set the UmbracoContext.Current since the extension methods rely on it
- UmbracoContext.Current = routingContext.UmbracoContext;
- }
-
- public override void TearDown()
- {
- base.TearDown();
- DocumentExtensions.GetPropertyAliasesAndNames = null;
- UmbracoContext.Current = null;
- }
-
- [Test]
- public void To_DataTable()
- {
- var doc = GetDocument(true, 1);
- var dt = doc.ChildrenAsTable();
-
- Assert.AreEqual(11, dt.Columns.Count);
- Assert.AreEqual(3, dt.Rows.Count);
- Assert.AreEqual("value4", dt.Rows[0]["Property 1"]);
- Assert.AreEqual("value5", dt.Rows[0]["Property 2"]);
- Assert.AreEqual("value6", dt.Rows[0]["Property 4"]);
- Assert.AreEqual("value7", dt.Rows[1]["Property 1"]);
- Assert.AreEqual("value8", dt.Rows[1]["Property 2"]);
- Assert.AreEqual("value9", dt.Rows[1]["Property 4"]);
- Assert.AreEqual("value10", dt.Rows[2]["Property 1"]);
- Assert.AreEqual("value11", dt.Rows[2]["Property 2"]);
- Assert.AreEqual("value12", dt.Rows[2]["Property 4"]);
- }
-
- [Test]
- public void To_DataTable_With_Filter()
- {
- var doc = GetDocument(true, 1);
- //change a doc type alias
- ((TestDocument) doc.Children.ElementAt(0)).DocumentTypeAlias = "DontMatch";
-
- var dt = doc.ChildrenAsTable("Child");
-
- Assert.AreEqual(11, dt.Columns.Count);
- Assert.AreEqual(2, dt.Rows.Count);
- Assert.AreEqual("value7", dt.Rows[0]["Property 1"]);
- Assert.AreEqual("value8", dt.Rows[0]["Property 2"]);
- Assert.AreEqual("value9", dt.Rows[0]["Property 4"]);
- Assert.AreEqual("value10", dt.Rows[1]["Property 1"]);
- Assert.AreEqual("value11", dt.Rows[1]["Property 2"]);
- Assert.AreEqual("value12", dt.Rows[1]["Property 4"]);
- }
-
- [Test]
- public void To_DataTable_No_Rows()
- {
- var doc = GetDocument(false, 1);
- var dt = doc.ChildrenAsTable();
- //will return an empty data table
- Assert.AreEqual(0, dt.Columns.Count);
- Assert.AreEqual(0, dt.Rows.Count);
- }
-
- private IDocument GetDocument(bool createChildren, int indexVals)
- {
- var d = new TestDocument
- {
- CreateDate = DateTime.Now,
- CreatorId = 1,
- CreatorName = "Shannon",
- DocumentTypeAlias = createChildren? "Parent" : "Child",
- DocumentTypeId = 2,
- Id = 3,
- SortOrder = 4,
- TemplateId = 5,
- UpdateDate = DateTime.Now,
- Path = "-1,3",
- UrlName = "home-page",
- Name = "Page" + Guid.NewGuid().ToString(),
- Version = Guid.NewGuid(),
- WriterId = 1,
- WriterName = "Shannon",
- Parent = null,
- Level = 1,
- Properties = new Collection(
- new List()
- {
- new PropertyResult("property1", "value" + indexVals, Guid.NewGuid(), PropertyResultType.UserProperty),
- new PropertyResult("property2", "value" + (indexVals + 1), Guid.NewGuid(), PropertyResultType.UserProperty)
- }),
- Children = new List()
- };
- if (createChildren)
- {
- d.Children = new List()
- {
- GetDocument(false, indexVals + 3),
- GetDocument(false, indexVals + 6),
- GetDocument(false, indexVals + 9)
- };
- }
- if (!createChildren)
- {
- //create additional columns, used to test the different columns for child nodes
- d.Properties.Add(new PropertyResult("property4", "value" + (indexVals + 2), Guid.NewGuid(), PropertyResultType.UserProperty));
- }
- else
- {
- d.Properties.Add(new PropertyResult("property3", "value" + (indexVals + 2), Guid.NewGuid(), PropertyResultType.UserProperty));
- }
- return d;
- }
-
-
- private class TestDocument : IDocument
- {
- public IDocument Parent { get; set; }
- public int Id { get; set; }
- public int TemplateId { get; set; }
- public int SortOrder { get; set; }
- public string Name { get; set; }
- public string UrlName { get; set; }
- public string DocumentTypeAlias { get; set; }
- public int DocumentTypeId { get; set; }
- public string WriterName { get; set; }
- public string CreatorName { get; set; }
- public int WriterId { get; set; }
- public int CreatorId { get; set; }
- public string Path { get; set; }
- public DateTime CreateDate { get; set; }
- public DateTime UpdateDate { get; set; }
- public Guid Version { get; set; }
- public int Level { get; set; }
- public Collection Properties { get; set; }
- public IEnumerable Children { get; set; }
- }
-
- }
-
[TestFixture]
public class DynamicDocumentTests : DynamicDocumentTestsBase
{
diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj
index eda0dcba03..f35cdee1e5 100644
--- a/src/Umbraco.Tests/Umbraco.Tests.csproj
+++ b/src/Umbraco.Tests/Umbraco.Tests.csproj
@@ -54,6 +54,7 @@
+
diff --git a/src/Umbraco.Web/DefaultPublishedMediaStore.cs b/src/Umbraco.Web/DefaultPublishedMediaStore.cs
index fd4ce31389..05d797a6b7 100644
--- a/src/Umbraco.Web/DefaultPublishedMediaStore.cs
+++ b/src/Umbraco.Web/DefaultPublishedMediaStore.cs
@@ -9,6 +9,7 @@ using Umbraco.Core;
using Umbraco.Core.Dynamics;
using Umbraco.Core.Models;
using umbraco;
+using umbraco.cms.businesslogic;
namespace Umbraco.Web
{
@@ -88,14 +89,18 @@ namespace Umbraco.Web
{
values.Add("level", values["__Path"].Split(',').Length.ToString());
}
-
+
return new DictionaryDocument(values,
- d => d.ParentId != -1 //parent should be null if -1
- ? GetUmbracoMedia(d.ParentId)
- : null,
- //callback to return the children of the current node
- d => GetChildrenMedia(d.ParentId));
+ d => d.ParentId != -1 //parent should be null if -1
+ ? GetUmbracoMedia(d.ParentId)
+ : null,
+ //callback to return the children of the current node
+ d => GetChildrenMedia(d.ParentId),
+ GetProperty)
+ {
+ LoadedFromExamine = true
+ };
}
internal IDocument ConvertFromXPathNavigator(XPathNavigator xpath)
@@ -151,7 +156,52 @@ namespace Umbraco.Web
? GetUmbracoMedia(d.ParentId)
: null,
//callback to return the children of the current node based on the xml structure already found
- d => GetChildrenMedia(d.ParentId, xpath));
+ d => GetChildrenMedia(d.ParentId, xpath),
+ GetProperty);
+ }
+
+ ///
+ /// We will need to first check if the document was loaded by Examine, if so we'll need to check if this property exists
+ /// in the results, if it does not, then we'll have to revert to looking up in the db.
+ ///
+ ///
+ ///
+ ///
+ private IDocumentProperty GetProperty(DictionaryDocument dd, string alias)
+ {
+ if (dd.LoadedFromExamine)
+ {
+ //if this is from Examine, lets check if the alias does not exist on the document
+ if (dd.Properties.All(x => x.Alias != alias))
+ {
+ //ok it doesn't exist, we might assume now that Examine didn't index this property because the index is not set up correctly
+ //so before we go loading this from the database, we can check if the alias exists on the content type at all, this information
+ //is cached so will be quicker to look up.
+ if (dd.Properties.Any(x => x.Alias == "__NodeTypeAlias"))
+ {
+ var aliasesAndNames = ContentType.GetAliasesAndNames(dd.Properties.First(x => x.Alias.InvariantEquals("__NodeTypeAlias")).Value.ToString());
+ if (aliasesAndNames != null)
+ {
+ if (!aliasesAndNames.ContainsKey(alias))
+ {
+ //Ok, now we know it doesn't exist on this content type anyways
+ return null;
+ }
+ }
+ }
+
+ //if we've made it here, that means it does exist on the content type but not in examine, we'll need to query the db :(
+ var media = global::umbraco.library.GetMedia(dd.Id, true);
+ if (media != null && media.Current != null)
+ {
+ media.MoveNext();
+ var mediaDoc = ConvertFromXPathNavigator(media.Current);
+ return mediaDoc.Properties.FirstOrDefault(x => x.Alias.InvariantEquals(alias));
+ }
+ }
+ }
+
+ return dd.Properties.FirstOrDefault(x => x.Alias.InvariantEquals(alias));
}
///
@@ -234,13 +284,18 @@ namespace Umbraco.Web
public DictionaryDocument(
IDictionary valueDictionary,
Func getParent,
- Func> getChildren)
+ Func> getChildren,
+ Func getProperty)
{
if (valueDictionary == null) throw new ArgumentNullException("valueDictionary");
if (getParent == null) throw new ArgumentNullException("getParent");
-
+ if (getProperty == null) throw new ArgumentNullException("getProperty");
+
_getParent = getParent;
_getChildren = getChildren;
+ _getProperty = getProperty;
+
+ LoadedFromExamine = false; //default to false
ValidateAndSetProperty(valueDictionary, val => Id = int.Parse(val), "id", "nodeId", "__NodeId"); //should validate the int!
ValidateAndSetProperty(valueDictionary, val => TemplateId = int.Parse(val), "template", "templateId");
@@ -279,8 +334,14 @@ namespace Umbraco.Web
}
}
+ ///
+ /// Flag to get/set if this was laoded from examine cache
+ ///
+ internal bool LoadedFromExamine { get; set; }
+
private readonly Func _getParent;
private readonly Func> _getChildren;
+ private readonly Func _getProperty;
public IDocument Parent
{
@@ -310,6 +371,11 @@ namespace Umbraco.Web
get { return _getChildren(this); }
}
+ public IDocumentProperty GetProperty(string alias)
+ {
+ return _getProperty(this, alias);
+ }
+
private readonly List _keysAdded = new List();
private void ValidateAndSetProperty(IDictionary valueDictionary, Action setProperty, params string[] potentialKeys)
{
diff --git a/src/Umbraco.Web/Models/XmlDocument.cs b/src/Umbraco.Web/Models/XmlDocument.cs
index f51e8dc973..cf8aaaf32e 100644
--- a/src/Umbraco.Web/Models/XmlDocument.cs
+++ b/src/Umbraco.Web/Models/XmlDocument.cs
@@ -75,6 +75,11 @@ namespace Umbraco.Web.Models
}
}
+ public IDocumentProperty GetProperty(string alias)
+ {
+ return Properties.FirstOrDefault(x => x.Alias.InvariantEquals(alias));
+ }
+
public IDocument Parent
{
get