Started implementing new IPublishedMediaStore which will replace the notion of the legacy ExamineBackedMedia and

moving forward will open up many nice opportunities. This will be used for the media accessor in the new DynamicDocument.
Renamed XmlPublishedContentStore to just DefaultPublishedContentStore.
Made GetProperty an extension method for IDocument and removed from the interface as it is not needed there.
This commit is contained in:
Shannon Deminick
2012-09-08 13:22:45 +07:00
parent ce9be79ec6
commit 27bcea78d2
15 changed files with 286 additions and 63 deletions

View File

@@ -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

View File

@@ -0,0 +1,21 @@
using System.Linq;
namespace Umbraco.Core.Models
{
/// <summary>
/// Extension methods for IDocument
/// </summary>
public static class DocumentExtensions
{
/// <summary>
/// Returns the property based on the case insensitive match of the alias
/// </summary>
/// <param name="d"></param>
/// <param name="alias"></param>
/// <returns></returns>
public static IDocumentProperty GetProperty(this IDocument d, string alias)
{
return d.Properties.FirstOrDefault(p => p.Alias.InvariantEquals(alias));
}
}
}

View File

@@ -31,7 +31,6 @@ namespace Umbraco.Core.Models
Guid Version { get; }
int Level { get; }
Collection<IDocumentProperty> Properties { get; }
IEnumerable<IDocument> Children { get; }
IDocumentProperty GetProperty(string alias);
IEnumerable<IDocument> Children { get; }
}
}

View File

@@ -90,6 +90,7 @@
<Compile Include="IO\IFileSystemExtensions.cs" />
<Compile Include="IO\IMediaFileSystem.cs" />
<Compile Include="Macros\MacroTagParser.cs" />
<Compile Include="Models\DocumentExtensions.cs" />
<Compile Include="PropertyEditors\DatePickerPropertyEditorValueConverter.cs" />
<Compile Include="PropertyEditors\IPropertyEditorValueConverter.cs" />
<Compile Include="HtmlTagWrapper.cs" />

View File

@@ -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);

View File

@@ -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]"))));

View File

@@ -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();
}

View File

@@ -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,

View File

@@ -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
{
/// <summary>
/// An IContentStore which uses the Xml cache system to return data
/// An IPublishedContentStore which uses the Xml cache system to return data
/// </summary>
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");

View File

@@ -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
{
/// <summary>
/// An IPublishedMediaStore that first checks for the media in Examine, and then reverts to the database
/// </summary>
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<string, string> {{"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);
}
/// <summary>
/// An IDocument that is represented all by a dictionary.
/// </summary>
/// <remarks>
/// 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.
/// </remarks>
internal class DictionaryDocument : IDocument
{
//TODO: Unit test this!
public DictionaryDocument(IDictionary<string, string> valueDictionary, Func<DictionaryDocument, IDocument> 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<IDocumentProperty>();
//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<DictionaryDocument, IDocument> _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<IDocumentProperty> Properties { get; private set; }
public IEnumerable<IDocument> Children { get; private set; }
private readonly List<string> _keysAdded = new List<string>();
private void ValidateAndSetProperty(IDictionary<string, string> valueDictionary, Action<string> 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;
}
}
}
}
}

View File

@@ -2,11 +2,12 @@ using Umbraco.Core.Models;
namespace Umbraco.Web
{
internal interface IPublishedContentStore
{
IDocument GetDocumentById(UmbracoContext umbracoContext, int nodeId);
/// <summary>
/// Defines the methods to access published content
/// </summary>
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);
}
}

View File

@@ -0,0 +1,13 @@
using Umbraco.Core.Models;
namespace Umbraco.Web
{
/// <summary>
/// Defines the methods to access published media
/// </summary>
internal interface IPublishedMediaStore
{
IDocument GetDocumentById(UmbracoContext umbracoContext, int nodeId);
string GetDocumentProperty(UmbracoContext umbracoContext, IDocument node, string propertyAlias);
}
}

View File

@@ -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()
{

View File

@@ -241,6 +241,7 @@
</Compile>
<Compile Include="ApplicationEventsResolver.cs" />
<Compile Include="DefaultDynamicDocumentDataSource.cs" />
<Compile Include="DefaultPublishedMediaStore.cs" />
<Compile Include="Dictionary\UmbracoCultureDictionary.cs" />
<Compile Include="Dictionary\UmbracoCultureDictionaryFactory.cs" />
<Compile Include="DynamicDocumentSearchExtensions.cs" />
@@ -249,6 +250,7 @@
</Compile>
<Compile Include="HtmlHelperRenderExtensions.cs" />
<Compile Include="IApplicationEventHandler.cs" />
<Compile Include="IPublishedMediaStore.cs" />
<Compile Include="Media\EmbedProviders\AbstractOEmbedProvider.cs" />
<Compile Include="Media\EmbedProviders\AbstractProvider.cs" />
<Compile Include="Media\EmbedProviders\Flickr.cs" />
@@ -271,7 +273,7 @@
<Compile Include="UmbracoHelper.cs" />
<Compile Include="ViewContextExtensions.cs" />
<Compile Include="ViewDataContainerExtensions.cs" />
<Compile Include="XmlPublishedContentStore.cs" />
<Compile Include="DefaultPublishedContentStore.cs" />
<Compile Include="PublishedContentStoreResolver.cs" />
<Compile Include="Routing\DocumentNotFoundHandler.cs" />
<Compile Include="IPublishedContentStore.cs" />

View File

@@ -133,7 +133,7 @@ namespace Umbraco.Web
PropertyEditorValueConvertersResolver.Current.RemoveType<TinyMcePropertyEditorValueConverter>();
PropertyEditorValueConvertersResolver.Current.AddType<RteMacroRenderingPropertyEditorValueConverter>();
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