diff --git a/src/Umbraco.Core/Dynamics/DynamicBackingItem.cs b/src/Umbraco.Core/Dynamics/DynamicBackingItem.cs
index f80b204bd4..de7a024858 100644
--- a/src/Umbraco.Core/Dynamics/DynamicBackingItem.cs
+++ b/src/Umbraco.Core/Dynamics/DynamicBackingItem.cs
@@ -106,10 +106,7 @@ namespace Umbraco.Core.Dynamics
//if we're looking for a user defined property
if (checkUserProperty)
{
- var prop = content.GetProperty(alias)
- ?? (alias[0].IsUpperCase() //if it's null, try to get it with a different casing format (pascal vs camel)
- ? content.GetProperty(alias.ConvertCase(StringAliasCaseType.CamelCase))
- : content.GetProperty(alias.ConvertCase(StringAliasCaseType.PascalCase)));
+ var prop = content.GetProperty(alias);
return prop == null
? null
diff --git a/src/Umbraco.Core/Models/DocumentExtensions.cs b/src/Umbraco.Core/Models/DocumentExtensions.cs
new file mode 100644
index 0000000000..84be18b9ae
--- /dev/null
+++ b/src/Umbraco.Core/Models/DocumentExtensions.cs
@@ -0,0 +1,21 @@
+using System.Linq;
+
+namespace Umbraco.Core.Models
+{
+ ///
+ /// Extension methods for IDocument
+ ///
+ public static class DocumentExtensions
+ {
+ ///
+ /// 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 c162157eab..fae2866ff2 100644
--- a/src/Umbraco.Core/Models/IDocument.cs
+++ b/src/Umbraco.Core/Models/IDocument.cs
@@ -31,7 +31,6 @@ namespace Umbraco.Core.Models
Guid Version { get; }
int Level { get; }
Collection Properties { get; }
- IEnumerable Children { get; }
- IDocumentProperty GetProperty(string alias);
+ IEnumerable Children { get; }
}
}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj
index 221f573c1f..5fdeed9fec 100644
--- a/src/Umbraco.Core/Umbraco.Core.csproj
+++ b/src/Umbraco.Core/Umbraco.Core.csproj
@@ -90,6 +90,7 @@
+
diff --git a/src/Umbraco.Tests/DynamicDocument/DynamicDocumentTests.cs b/src/Umbraco.Tests/DynamicDocument/DynamicDocumentTests.cs
index 40aec8da96..b25a87ff65 100644
--- a/src/Umbraco.Tests/DynamicDocument/DynamicDocumentTests.cs
+++ b/src/Umbraco.Tests/DynamicDocument/DynamicDocumentTests.cs
@@ -54,7 +54,7 @@ namespace Umbraco.Tests.DynamicDocument
{
var template = Template.MakeNew("test", new User(0));
var ctx = GetUmbracoContext("/test", template.Id);
- var contentStore = new XmlPublishedContentStore();
+ var contentStore = new DefaultPublishedContentStore();
var doc = contentStore.GetDocumentById(ctx, id);
Assert.IsNotNull(doc);
var dynamicNode = new Core.Dynamics.DynamicDocument(doc);
diff --git a/src/Umbraco.Tests/DynamicDocument/DynamicNodeTests.cs b/src/Umbraco.Tests/DynamicDocument/DynamicNodeTests.cs
index 020451936a..61a510001a 100644
--- a/src/Umbraco.Tests/DynamicDocument/DynamicNodeTests.cs
+++ b/src/Umbraco.Tests/DynamicDocument/DynamicNodeTests.cs
@@ -35,7 +35,7 @@ namespace Umbraco.Tests.DynamicDocument
{
var template = Template.MakeNew("test", new User(0));
var ctx = GetUmbracoContext("/test", template.Id);
- var contentStore = new XmlPublishedContentStore();
+ var contentStore = new DefaultPublishedContentStore();
var node = new DynamicNode(
new DynamicBackingItem(
new Node(ctx.GetXml().SelectSingleNode("//*[@id='" + id + "' and @isDoc]"))));
diff --git a/src/Umbraco.Tests/PublishContentStoreTests.cs b/src/Umbraco.Tests/PublishContentStoreTests.cs
index d4decac6e5..e686582e9a 100644
--- a/src/Umbraco.Tests/PublishContentStoreTests.cs
+++ b/src/Umbraco.Tests/PublishContentStoreTests.cs
@@ -13,7 +13,7 @@ namespace Umbraco.Tests
{
private FakeHttpContextFactory _httpContextFactory;
private UmbracoContext _umbracoContext;
- private XmlPublishedContentStore _publishedContentStore;
+ private DefaultPublishedContentStore _publishedContentStore;
[SetUp]
public void SetUp()
@@ -56,7 +56,7 @@ namespace Umbraco.Tests
return xDoc;
};
- _publishedContentStore = new XmlPublishedContentStore();
+ _publishedContentStore = new DefaultPublishedContentStore();
}
diff --git a/src/Umbraco.Tests/Routing/BaseRoutingTest.cs b/src/Umbraco.Tests/Routing/BaseRoutingTest.cs
index eb489b01d0..2dc58d9937 100644
--- a/src/Umbraco.Tests/Routing/BaseRoutingTest.cs
+++ b/src/Umbraco.Tests/Routing/BaseRoutingTest.cs
@@ -38,7 +38,7 @@ namespace Umbraco.Tests.Routing
protected RoutingContext GetRoutingContext(string url, int templateId, RouteData routeData = null)
{
var umbracoContext = GetUmbracoContext(url, templateId, routeData);
- var contentStore = new XmlPublishedContentStore();
+ var contentStore = new DefaultPublishedContentStore();
var niceUrls = new NiceUrlProvider(contentStore, umbracoContext);
var routingRequest = new RoutingContext(
umbracoContext,
diff --git a/src/Umbraco.Web/XmlPublishedContentStore.cs b/src/Umbraco.Web/DefaultPublishedContentStore.cs
similarity index 73%
rename from src/Umbraco.Web/XmlPublishedContentStore.cs
rename to src/Umbraco.Web/DefaultPublishedContentStore.cs
index 8eec803f05..2e41db2819 100644
--- a/src/Umbraco.Web/XmlPublishedContentStore.cs
+++ b/src/Umbraco.Web/DefaultPublishedContentStore.cs
@@ -1,7 +1,7 @@
using System;
using System.Text;
using System.Xml;
-using Umbraco.Core;
+using System.Xml.Linq;
using Umbraco.Core.Models;
using Umbraco.Web.Routing;
using umbraco;
@@ -11,9 +11,9 @@ using umbraco.interfaces;
namespace Umbraco.Web
{
///
- /// An IContentStore which uses the Xml cache system to return data
+ /// An IPublishedContentStore which uses the Xml cache system to return data
///
- internal class XmlPublishedContentStore : IPublishedContentStore
+ internal class DefaultPublishedContentStore : DefaultPublishedMediaStore, IPublishedContentStore
{
private IDocument ConvertToDocument(XmlNode xmlNode)
@@ -24,7 +24,7 @@ namespace Umbraco.Web
return new Models.XmlDocument(xmlNode);
}
- public IDocument GetDocumentById(UmbracoContext umbracoContext, int nodeId)
+ public override IDocument GetDocumentById(UmbracoContext umbracoContext, int nodeId)
{
if (umbracoContext == null) throw new ArgumentNullException("umbracoContext");
@@ -84,37 +84,6 @@ namespace Umbraco.Web
return ConvertToDocument(GetXml(umbracoContext).SelectSingleNode(xpath));
}
- //public IDocument GetNodeParent(IDocument node)
- //{
- // return node.Parent;
- //}
-
- public string GetDocumentProperty(UmbracoContext umbracoContext, IDocument node, string propertyAlias)
- {
- if (umbracoContext == null) throw new ArgumentNullException("umbracoContext");
- if (node == null) throw new ArgumentNullException("node");
- if (propertyAlias == null) throw new ArgumentNullException("propertyAlias");
-
- if (propertyAlias.StartsWith("@"))
- {
- //if it starts with an @ then its a property of the object, not a user defined property
- var propName = propertyAlias.TrimStart('@');
- var prop = TypeHelper.GetProperty(typeof(IDocument), propName, true, false, false, false);
- if (prop == null)
- throw new ArgumentException("The property name " + propertyAlias + " was not found on type " + typeof(IDocument));
- var val = prop.GetValue(node, null);
- var valAsString = val == null ? "" : val.ToString();
- return valAsString;
- }
- else
- {
- var prop = node.GetProperty(propertyAlias);
- return prop == null ? null : Convert.ToString(prop.Value);
- //var propertyNode = node.SelectSingleNode("./" + propertyAlias);
- //return propertyNode == null ? null : propertyNode.InnerText;
- }
- }
-
XmlDocument GetXml(UmbracoContext umbracoContext)
{
if (umbracoContext == null) throw new ArgumentNullException("umbracoContext");
diff --git a/src/Umbraco.Web/DefaultPublishedMediaStore.cs b/src/Umbraco.Web/DefaultPublishedMediaStore.cs
new file mode 100644
index 0000000000..ec8ad95dc5
--- /dev/null
+++ b/src/Umbraco.Web/DefaultPublishedMediaStore.cs
@@ -0,0 +1,230 @@
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.IO;
+using System.Linq;
+using System.Xml.XPath;
+using Examine;
+using Umbraco.Core;
+using Umbraco.Core.Dynamics;
+using Umbraco.Core.Models;
+
+namespace Umbraco.Web
+{
+ ///
+ /// An IPublishedMediaStore that first checks for the media in Examine, and then reverts to the database
+ ///
+ internal class DefaultPublishedMediaStore : IPublishedMediaStore
+ {
+ public virtual IDocument GetDocumentById(UmbracoContext umbracoContext, int nodeId)
+ {
+ return GetUmbracoMedia(nodeId);
+ }
+
+ public virtual string GetDocumentProperty(UmbracoContext umbracoContext, IDocument node, string propertyAlias)
+ {
+ if (umbracoContext == null) throw new ArgumentNullException("umbracoContext");
+ if (node == null) throw new ArgumentNullException("node");
+ if (propertyAlias == null) throw new ArgumentNullException("propertyAlias");
+
+ if (propertyAlias.StartsWith("@"))
+ {
+ //if it starts with an @ then its a property of the object, not a user defined property
+ var propName = propertyAlias.TrimStart('@');
+ var prop = TypeHelper.GetProperty(typeof(IDocument), propName, true, false, false, false);
+ if (prop == null)
+ throw new ArgumentException("The property name " + propertyAlias + " was not found on type " + typeof(IDocument));
+ var val = prop.GetValue(node, null);
+ var valAsString = val == null ? "" : val.ToString();
+ return valAsString;
+ }
+ else
+ {
+ var prop = node.GetProperty(propertyAlias);
+ return prop == null ? null : Convert.ToString(prop.Value);
+ //var propertyNode = node.SelectSingleNode("./" + propertyAlias);
+ //return propertyNode == null ? null : propertyNode.InnerText;
+ }
+ }
+
+ private IDocument GetUmbracoMedia(int id)
+ {
+
+ try
+ {
+ //first check in Examine as this is WAY faster
+ var criteria = ExamineManager.Instance
+ .SearchProviderCollection["InternalSearcher"]
+ .CreateSearchCriteria("media");
+ var filter = criteria.Id(id);
+ var results = ExamineManager
+ .Instance.SearchProviderCollection["InternalSearcher"]
+ .Search(filter.Compile());
+ if (results.Any())
+ {
+ return ConvertFromSearchResult(results.First());
+ }
+ }
+ catch (FileNotFoundException)
+ {
+ //Currently examine is throwing FileNotFound exceptions when we have a loadbalanced filestore and a node is published in umbraco
+ //See this thread: http://examine.cdodeplex.com/discussions/264341
+ //Catch the exception here for the time being, and just fallback to GetMedia
+ //TODO: Need to fix examine in LB scenarios!
+ }
+
+ var media = global::umbraco.library.GetMedia(id, true);
+ if (media != null && media.Current != null)
+ {
+ media.MoveNext();
+ return ConvertFromXPathNavigator(media.Current);
+ }
+
+ return null;
+ }
+
+ internal IDocument ConvertFromSearchResult(SearchResult searchResult)
+ {
+ //TODO: Unit test this
+ throw new NotImplementedException();
+ }
+
+ internal IDocument ConvertFromXPathNavigator(XPathNavigator xpath)
+ {
+ //TODO: Unit test this
+
+ if (xpath == null) throw new ArgumentNullException("xpath");
+
+ var values = new Dictionary {{"nodeName", xpath.GetAttribute("nodeName", "")}};
+
+ var result = xpath.SelectChildren(XPathNodeType.Element);
+ //add the attributes e.g. id, parentId etc
+ if (result.Current != null && result.Current.HasAttributes)
+ {
+ if (result.Current.MoveToFirstAttribute())
+ {
+ values.Add(result.Current.Name, result.Current.Value);
+ while (result.Current.MoveToNextAttribute())
+ {
+ values.Add(result.Current.Name, result.Current.Value);
+ }
+ result.Current.MoveToParent();
+ }
+ }
+ //add the user props
+ while (result.MoveNext())
+ {
+ if (result.Current != null && !result.Current.HasAttributes)
+ {
+ string value = result.Current.Value;
+ if (string.IsNullOrEmpty(value))
+ {
+ if (result.Current.HasAttributes || result.Current.SelectChildren(XPathNodeType.Element).Count > 0)
+ {
+ value = result.Current.OuterXml;
+ }
+ }
+ values.Add(result.Current.Name, value);
+ }
+ }
+
+ return new DictionaryDocument(values, d => d.ParentId.HasValue ? GetUmbracoMedia(d.ParentId.Value) : null);
+ }
+
+ ///
+ /// An IDocument that is represented all by a dictionary.
+ ///
+ ///
+ /// This is a helper class and definitely not intended for public use, it expects that all of the values required
+ /// to create an IDocument exist in the dictionary by specific aliases.
+ ///
+ internal class DictionaryDocument : IDocument
+ {
+
+ //TODO: Unit test this!
+
+ public DictionaryDocument(IDictionary valueDictionary, Func getParent)
+ {
+ if (valueDictionary == null) throw new ArgumentNullException("valueDictionary");
+ if (getParent == null) throw new ArgumentNullException("getParent");
+
+ _getParent = getParent;
+
+ ValidateAndSetProperty(valueDictionary, val => Id = int.Parse(val), "id", "nodeId", "__NodeId"); //should validate the int!
+ ValidateAndSetProperty(valueDictionary, val => TemplateId = int.Parse(val), "template", "templateId");
+ ValidateAndSetProperty(valueDictionary, val => SortOrder = int.Parse(val), "sortOrder");
+ ValidateAndSetProperty(valueDictionary, val => Name = val, "nodeName", "__nodeName");
+ ValidateAndSetProperty(valueDictionary, val => UrlName = val, "urlName");
+ ValidateAndSetProperty(valueDictionary, val => DocumentTypeAlias = val, "nodeTypeAlias", "__NodeTypeAlias");
+ ValidateAndSetProperty(valueDictionary, val => DocumentTypeId = int.Parse(val), "nodeType");
+ ValidateAndSetProperty(valueDictionary, val => WriterName = val, "writerName");
+ ValidateAndSetProperty(valueDictionary, val => CreatorName = val, "creatorName");
+ ValidateAndSetProperty(valueDictionary, val => WriterId = int.Parse(val), "writerID");
+ ValidateAndSetProperty(valueDictionary, val => CreatorId = int.Parse(val), "creatorID");
+ ValidateAndSetProperty(valueDictionary, val => Path = val, "path", "__Path");
+ ValidateAndSetProperty(valueDictionary, val => CreateDate = DateTime.Parse(val), "createDate");
+ ValidateAndSetProperty(valueDictionary, val => Level = int.Parse(val), "level");
+ ValidateAndSetProperty(valueDictionary, val =>
+ {
+ int pId;
+ ParentId = null;
+ if (int.TryParse(val, out pId))
+ {
+ ParentId = pId;
+ }
+ }, "parentID");
+
+ Properties = new Collection();
+
+ //loop through remaining values that haven't been applied
+ foreach (var i in valueDictionary.Where(x => !_keysAdded.Contains(x.Key)))
+ {
+ //this is taken from examine
+ Properties.Add(i.Key.InvariantStartsWith("__")
+ ? new PropertyResult(i.Key, i.Value, Guid.Empty, PropertyResultType.CustomProperty)
+ : new PropertyResult(i.Key, i.Value, Guid.Empty, PropertyResultType.UserProperty));
+ }
+ }
+
+ private readonly Func _getParent;
+ public IDocument Parent
+ {
+ get { return _getParent(this); }
+ }
+
+ public int? ParentId { get; private set; }
+ public int Id { get; private set; }
+ public int TemplateId { get; private set; }
+ public int SortOrder { get; private set; }
+ public string Name { get; private set; }
+ public string UrlName { get; private set; }
+ public string DocumentTypeAlias { get; private set; }
+ public int DocumentTypeId { 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 int Level { get; private set; }
+ public Collection Properties { get; private set; }
+ public IEnumerable Children { get; private set; }
+
+ private readonly List _keysAdded = new List();
+ private void ValidateAndSetProperty(IDictionary valueDictionary, Action setProperty, params string[] potentialKeys)
+ {
+ foreach (var s in potentialKeys)
+ {
+ if (valueDictionary[s] == null)
+ throw new FormatException("The valueDictionary is not formatted correctly and is missing the '" + s + "' element");
+ setProperty(valueDictionary[s]);
+ _keysAdded.Add(s);
+ break;
+ }
+
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Web/IPublishedContentStore.cs b/src/Umbraco.Web/IPublishedContentStore.cs
index 3b89537340..22ee23ba7a 100644
--- a/src/Umbraco.Web/IPublishedContentStore.cs
+++ b/src/Umbraco.Web/IPublishedContentStore.cs
@@ -2,11 +2,12 @@ using Umbraco.Core.Models;
namespace Umbraco.Web
{
- internal interface IPublishedContentStore
- {
- IDocument GetDocumentById(UmbracoContext umbracoContext, int nodeId);
+ ///
+ /// Defines the methods to access published content
+ ///
+ internal interface IPublishedContentStore : IPublishedMediaStore
+ {
IDocument GetDocumentByRoute(UmbracoContext umbracoContext, string route, bool? hideTopLevelNode = null);
IDocument GetDocumentByUrlAlias(UmbracoContext umbracoContext, int rootNodeId, string alias);
- string GetDocumentProperty(UmbracoContext umbracoContext, IDocument node, string propertyAlias);
}
}
\ No newline at end of file
diff --git a/src/Umbraco.Web/IPublishedMediaStore.cs b/src/Umbraco.Web/IPublishedMediaStore.cs
new file mode 100644
index 0000000000..78efe3488f
--- /dev/null
+++ b/src/Umbraco.Web/IPublishedMediaStore.cs
@@ -0,0 +1,13 @@
+using Umbraco.Core.Models;
+
+namespace Umbraco.Web
+{
+ ///
+ /// Defines the methods to access published media
+ ///
+ internal interface IPublishedMediaStore
+ {
+ IDocument GetDocumentById(UmbracoContext umbracoContext, int nodeId);
+ string GetDocumentProperty(UmbracoContext umbracoContext, IDocument node, string propertyAlias);
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Web/Models/XmlDocument.cs b/src/Umbraco.Web/Models/XmlDocument.cs
index cf782d642a..f51e8dc973 100644
--- a/src/Umbraco.Web/Models/XmlDocument.cs
+++ b/src/Umbraco.Web/Models/XmlDocument.cs
@@ -236,11 +236,6 @@ namespace Umbraco.Web.Models
}
}
- //public string NiceUrl
- //{
- // get { return _niceUrlProvider.GetNiceUrl(Id); }
- //}
-
public int Level
{
get
@@ -260,12 +255,7 @@ namespace Umbraco.Web.Models
return _properties;
}
}
-
-
- public IDocumentProperty GetProperty(string alias)
- {
- return Properties.FirstOrDefault(p => p.Alias.InvariantEquals(alias));
- }
+
private void InitializeStructure()
{
diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj
index 5f723910fe..9a23411e87 100644
--- a/src/Umbraco.Web/Umbraco.Web.csproj
+++ b/src/Umbraco.Web/Umbraco.Web.csproj
@@ -241,6 +241,7 @@
+
@@ -249,6 +250,7 @@
+
@@ -271,7 +273,7 @@
-
+
diff --git a/src/Umbraco.Web/WebBootManager.cs b/src/Umbraco.Web/WebBootManager.cs
index f045af6a89..1887d0151c 100644
--- a/src/Umbraco.Web/WebBootManager.cs
+++ b/src/Umbraco.Web/WebBootManager.cs
@@ -133,7 +133,7 @@ namespace Umbraco.Web
PropertyEditorValueConvertersResolver.Current.RemoveType();
PropertyEditorValueConvertersResolver.Current.AddType();
- PublishedContentStoreResolver.Current = new PublishedContentStoreResolver(new XmlPublishedContentStore());
+ PublishedContentStoreResolver.Current = new PublishedContentStoreResolver(new DefaultPublishedContentStore());
FilteredControllerFactoriesResolver.Current = new FilteredControllerFactoriesResolver(
//add all known factories, devs can then modify this list on application startup either by binding to events