From 54967e0150fa84a06b080da88af80b66415b501c Mon Sep 17 00:00:00 2001 From: Shannon Date: Fri, 19 May 2017 11:42:58 +1000 Subject: [PATCH] U4-9935 UmbracoHelper doesn't have querying methods for handling UDI --- src/Umbraco.Web/Extensions/UdiExtensions.cs | 21 ++-- .../ITypedPublishedContentQuery.cs | 16 ++- .../ContentPickerPropertyConverter.cs | 35 +++--- .../LegacyRelatedLinksEditorValueConvertor.cs | 51 ++++---- .../MediaPickerPropertyConverter.cs | 4 +- .../MemberPickerPropertyConverter.cs | 31 +++-- .../MultiNodeTreePickerPropertyConverter.cs | 87 +++++++------ .../RelatedLinksEditorValueConvertor.cs | 6 +- .../ContextualPublishedCache.cs | 6 +- .../PublishedContentQueryExtensions.cs | 23 ++++ src/Umbraco.Web/Umbraco.Web.csproj | 1 + src/Umbraco.Web/UmbracoHelper.cs | 115 ++++++++++++++---- 12 files changed, 256 insertions(+), 140 deletions(-) create mode 100644 src/Umbraco.Web/PublishedContentQueryExtensions.cs diff --git a/src/Umbraco.Web/Extensions/UdiExtensions.cs b/src/Umbraco.Web/Extensions/UdiExtensions.cs index c814d63b8c..1129a30b02 100644 --- a/src/Umbraco.Web/Extensions/UdiExtensions.cs +++ b/src/Umbraco.Web/Extensions/UdiExtensions.cs @@ -1,23 +1,22 @@ -using Umbraco.Core; +using System; +using System.ComponentModel; +using Umbraco.Core; using Umbraco.Core.Models; namespace Umbraco.Web.Extensions { + [Obsolete("Use methods on UmbracoHelper instead")] + [EditorBrowsable(EditorBrowsableState.Never)] public static class UdiExtensions { - /// - /// An extension method to easily acquire a typed version of content, media or member item for a given Udi - /// - /// - /// An item if the item is a Document, Media or Member + [Obsolete("Use methods on UmbracoHelper instead")] + [EditorBrowsable(EditorBrowsableState.Never)] public static IPublishedContent ToPublishedContent(this Udi udi) { - Udi identifier; - if (Udi.TryParse(udi.ToString(), out identifier) == false) - return null; + var guidUdi = udi as GuidUdi; + if (guidUdi == null) return null; - var guidUdi = GuidUdi.Parse(udi.ToString()); - var umbracoType = Constants.UdiEntityType.ToUmbracoObjectType(identifier.EntityType); + var umbracoType = Constants.UdiEntityType.ToUmbracoObjectType(guidUdi.EntityType); var umbracoHelper = new UmbracoHelper(UmbracoContext.Current); var entityService = ApplicationContext.Current.Services.EntityService; diff --git a/src/Umbraco.Web/ITypedPublishedContentQuery.cs b/src/Umbraco.Web/ITypedPublishedContentQuery.cs index 7a2be964f7..893c036958 100644 --- a/src/Umbraco.Web/ITypedPublishedContentQuery.cs +++ b/src/Umbraco.Web/ITypedPublishedContentQuery.cs @@ -11,8 +11,20 @@ namespace Umbraco.Web /// public interface ITypedPublishedContentQuery { + /// + /// Gets a content item from the cache + /// + /// + /// IPublishedContent TypedContent(int id); + + /// + /// Gets a content item from the cache + /// + /// + /// IPublishedContent TypedContent(Guid id); + IPublishedContent TypedContentSingleAtXPath(string xpath, params XPathVariable[] vars); IEnumerable TypedContent(IEnumerable ids); IEnumerable TypedContent(IEnumerable ids); @@ -20,8 +32,8 @@ namespace Umbraco.Web IEnumerable TypedContentAtXPath(XPathExpression xpath, params XPathVariable[] vars); IEnumerable TypedContentAtRoot(); - // note: we CANNOT implement TypedMedia by Guid in v7 without break-changing IPublishedCache, - // since we don't support XPath navigation of the media tree. + // TODO: we CANNOT implement TypedMedia by Guid in v7 without break-changing IPublishedCache, since we don't support XPath navigation of the media tree. + // surely there is a way we can support this without XPath, it's needed so we can query properly by UDI IPublishedContent TypedMedia(int id); //IPublishedContent TypedMedia(Guid id); diff --git a/src/Umbraco.Web/PropertyEditors/ValueConverters/ContentPickerPropertyConverter.cs b/src/Umbraco.Web/PropertyEditors/ValueConverters/ContentPickerPropertyConverter.cs index ca147698ec..d71faf0b1e 100644 --- a/src/Umbraco.Web/PropertyEditors/ValueConverters/ContentPickerPropertyConverter.cs +++ b/src/Umbraco.Web/PropertyEditors/ValueConverters/ContentPickerPropertyConverter.cs @@ -106,25 +106,26 @@ namespace Umbraco.Web.PropertyEditors.ValueConverters return null; } - if (UmbracoContext.Current != null) + if (UmbracoContext.Current == null) return source; + + if ((propertyType.PropertyTypeAlias != null && PropertiesToExclude.Contains(propertyType.PropertyTypeAlias.ToLower(CultureInfo.InvariantCulture))) == false) { - if ((propertyType.PropertyTypeAlias != null && PropertiesToExclude.Contains(propertyType.PropertyTypeAlias.ToLower(CultureInfo.InvariantCulture))) == false) + IPublishedContent content; + if (source is int) { - IPublishedContent content; - if (source is int) - { - var sourceInt = (int)source; - content = UmbracoContext.Current.ContentCache.GetById(sourceInt); - if(content != null) - return content; - } - else - { - var sourceUdi = source as Udi; - content = sourceUdi.ToPublishedContent(); - if (content != null) - return content; - } + var sourceInt = (int)source; + content = UmbracoContext.Current.ContentCache.GetById(sourceInt); + if(content != null) + return content; + } + else + { + var sourceUdi = source as Udi; + if (sourceUdi == null) return null; + var umbHelper = new UmbracoHelper(UmbracoContext.Current); + content = umbHelper.TypedContent(sourceUdi); + if (content != null) + return content; } } return source; diff --git a/src/Umbraco.Web/PropertyEditors/ValueConverters/LegacyRelatedLinksEditorValueConvertor.cs b/src/Umbraco.Web/PropertyEditors/ValueConverters/LegacyRelatedLinksEditorValueConvertor.cs index ee64bf6c15..d89a9ea4c5 100644 --- a/src/Umbraco.Web/PropertyEditors/ValueConverters/LegacyRelatedLinksEditorValueConvertor.cs +++ b/src/Umbraco.Web/PropertyEditors/ValueConverters/LegacyRelatedLinksEditorValueConvertor.cs @@ -42,37 +42,38 @@ namespace Umbraco.Web.PropertyEditors.ValueConverters try { var obj = JsonConvert.DeserializeObject(sourceString); + //update the internal links if we have a context - if (UmbracoContext.Current != null) + if (UmbracoContext.Current == null) return obj; + + var helper = new UmbracoHelper(UmbracoContext.Current); + foreach (var a in obj) { - var helper = new UmbracoHelper(UmbracoContext.Current); - foreach (var a in obj) + var type = a.Value("type"); + if (type.IsNullOrWhiteSpace() == false) { - var type = a.Value("type"); - if (type.IsNullOrWhiteSpace() == false) + if (type == "internal") { - if (type == "internal") + switch (propertyType.PropertyEditorAlias) { - switch (propertyType.PropertyEditorAlias) - { - case Constants.PropertyEditors.RelatedLinksAlias: - var intLinkId = a.Value("link"); - var intLink = helper.NiceUrl(intLinkId); - a["link"] = intLink; - break; - case Constants.PropertyEditors.RelatedLinks2Alias: - var strLinkId = a.Value("link"); - var udiAttempt = strLinkId.TryConvertTo(); - if (udiAttempt) - { - var content = udiAttempt.Result.ToPublishedContent(); - a["link"] = helper.NiceUrl(content.Id); - } - break; - } - } + case Constants.PropertyEditors.RelatedLinksAlias: + var intLinkId = a.Value("link"); + var intLink = helper.NiceUrl(intLinkId); + a["link"] = intLink; + break; + case Constants.PropertyEditors.RelatedLinks2Alias: + var strLinkId = a.Value("link"); + var udiAttempt = strLinkId.TryConvertTo(); + if (udiAttempt) + { + var content = helper.TypedContent(udiAttempt.Result); + if (content == null) break; + a["link"] = helper.NiceUrl(content.Id); + } + break; + } } - } + } } return obj; } diff --git a/src/Umbraco.Web/PropertyEditors/ValueConverters/MediaPickerPropertyConverter.cs b/src/Umbraco.Web/PropertyEditors/ValueConverters/MediaPickerPropertyConverter.cs index 4b3db67025..c876a069ef 100644 --- a/src/Umbraco.Web/PropertyEditors/ValueConverters/MediaPickerPropertyConverter.cs +++ b/src/Umbraco.Web/PropertyEditors/ValueConverters/MediaPickerPropertyConverter.cs @@ -173,11 +173,13 @@ namespace Umbraco.Web.PropertyEditors.ValueConverters var udis = (Udi[])source; var mediaItems = new List(); + if (UmbracoContext.Current == null) return source; + var helper = new UmbracoHelper(UmbracoContext.Current); if (udis.Any()) { foreach (var udi in udis) { - var item = udi.ToPublishedContent(); + var item = helper.TypedMedia(udi); if (item != null) mediaItems.Add(item); } diff --git a/src/Umbraco.Web/PropertyEditors/ValueConverters/MemberPickerPropertyConverter.cs b/src/Umbraco.Web/PropertyEditors/ValueConverters/MemberPickerPropertyConverter.cs index 4467c464f5..a80e10529a 100644 --- a/src/Umbraco.Web/PropertyEditors/ValueConverters/MemberPickerPropertyConverter.cs +++ b/src/Umbraco.Web/PropertyEditors/ValueConverters/MemberPickerPropertyConverter.cs @@ -41,23 +41,22 @@ namespace Umbraco.Web.PropertyEditors.ValueConverters if (source == null) return null; - if (UmbracoContext.Current != null) + if (UmbracoContext.Current == null) return source; + + var umbracoHelper = new UmbracoHelper(UmbracoContext.Current); + IPublishedContent member; + if (source is int) + { + member = umbracoHelper.TypedMember((int)source); + if (member != null) + return member; + } + else { - IPublishedContent member; - if (source is int) - { - var membershipHelper = new MembershipHelper(UmbracoContext.Current); - member = membershipHelper.GetById((int)source); - if (member != null) - return member; - } - else - { - var sourceUdi = source as Udi; - member = sourceUdi.ToPublishedContent(); - if (member != null) - return member; - } + var sourceUdi = source as Udi; + member = umbracoHelper.TypedMember(sourceUdi); + if (member != null) + return member; } return source; diff --git a/src/Umbraco.Web/PropertyEditors/ValueConverters/MultiNodeTreePickerPropertyConverter.cs b/src/Umbraco.Web/PropertyEditors/ValueConverters/MultiNodeTreePickerPropertyConverter.cs index c0c0321e91..a4483c0521 100644 --- a/src/Umbraco.Web/PropertyEditors/ValueConverters/MultiNodeTreePickerPropertyConverter.cs +++ b/src/Umbraco.Web/PropertyEditors/ValueConverters/MultiNodeTreePickerPropertyConverter.cs @@ -119,68 +119,67 @@ namespace Umbraco.Web.PropertyEditors.ValueConverters } //TODO: Inject an UmbracoHelper and create a GetUmbracoHelper method based on either injected or singleton - if (UmbracoContext.Current != null) + if (UmbracoContext.Current == null) return source; + + var umbHelper = new UmbracoHelper(UmbracoContext.Current); + + if (propertyType.PropertyEditorAlias.Equals(Constants.PropertyEditors.MultiNodeTreePickerAlias)) { - if (propertyType.PropertyEditorAlias.Equals(Constants.PropertyEditors.MultiNodeTreePickerAlias)) + var nodeIds = (int[])source; + + if ((propertyType.PropertyTypeAlias != null && PropertiesToExclude.InvariantContains(propertyType.PropertyTypeAlias)) == false) { - var nodeIds = (int[])source; + var multiNodeTreePicker = new List(); - if ((propertyType.PropertyTypeAlias != null && PropertiesToExclude.InvariantContains(propertyType.PropertyTypeAlias)) == false) + var objectType = UmbracoObjectTypes.Unknown; + + foreach (var nodeId in nodeIds) { - var multiNodeTreePicker = new List(); + var multiNodeTreePickerItem = + GetPublishedContent(nodeId, ref objectType, UmbracoObjectTypes.Document, umbHelper.TypedContent) + ?? GetPublishedContent(nodeId, ref objectType, UmbracoObjectTypes.Media, umbHelper.TypedMedia) + ?? GetPublishedContent(nodeId, ref objectType, UmbracoObjectTypes.Member, umbHelper.TypedMember); - if (nodeIds.Length > 0) + if (multiNodeTreePickerItem != null) { - var umbHelper = new UmbracoHelper(UmbracoContext.Current); - var objectType = UmbracoObjectTypes.Unknown; - - foreach (var nodeId in nodeIds) - { - var multiNodeTreePickerItem = - GetPublishedContent(nodeId, ref objectType, UmbracoObjectTypes.Document, umbHelper.TypedContent) - ?? GetPublishedContent(nodeId, ref objectType, UmbracoObjectTypes.Media, umbHelper.TypedMedia) - ?? GetPublishedContent(nodeId, ref objectType, UmbracoObjectTypes.Member, umbHelper.TypedMember); - - if (multiNodeTreePickerItem != null) - { - multiNodeTreePicker.Add(multiNodeTreePickerItem); - } - } + multiNodeTreePicker.Add(multiNodeTreePickerItem); } - - return multiNodeTreePicker; } - // return the first nodeId as this is one of the excluded properties that expects a single id - return nodeIds.FirstOrDefault(); + return multiNodeTreePicker; } - if (propertyType.PropertyEditorAlias.Equals(Constants.PropertyEditors.MultiNodeTreePicker2Alias)) + // return the first nodeId as this is one of the excluded properties that expects a single id + return nodeIds.FirstOrDefault(); + } + + if (propertyType.PropertyEditorAlias.Equals(Constants.PropertyEditors.MultiNodeTreePicker2Alias)) + { + var udis = (Udi[])source; + + if ((propertyType.PropertyTypeAlias != null && PropertiesToExclude.InvariantContains(propertyType.PropertyTypeAlias)) == false) { - var udis = (Udi[])source; + var multiNodeTreePicker = new List(); - if ((propertyType.PropertyTypeAlias != null && PropertiesToExclude.InvariantContains(propertyType.PropertyTypeAlias)) == false) + var objectType = UmbracoObjectTypes.Unknown; + + foreach (var udi in udis) { - var multiNodeTreePicker = new List(); - - if (udis.Length > 0) + var multiNodeTreePickerItem = + GetPublishedContent(udi, ref objectType, UmbracoObjectTypes.Document, umbHelper.TypedContent) + ?? GetPublishedContent(udi, ref objectType, UmbracoObjectTypes.Media, umbHelper.TypedMedia) + ?? GetPublishedContent(udi, ref objectType, UmbracoObjectTypes.Member, umbHelper.TypedMember); + if (multiNodeTreePickerItem != null) { - foreach (var udi in udis) - { - var item = udi.ToPublishedContent(); - if (item != null) - { - multiNodeTreePicker.Add(item); - } - } + multiNodeTreePicker.Add(multiNodeTreePickerItem); } - - return multiNodeTreePicker; } - // return the first nodeId as this is one of the excluded properties that expects a single id - return udis.FirstOrDefault(); + return multiNodeTreePicker; } + + // return the first nodeId as this is one of the excluded properties that expects a single id + return udis.FirstOrDefault(); } return source; } @@ -193,7 +192,7 @@ namespace Umbraco.Web.PropertyEditors.ValueConverters /// The type of content expected/supported by /// A function to fetch content of type /// The requested content, or null if either it does not exist or does not match - private IPublishedContent GetPublishedContent(int nodeId, ref UmbracoObjectTypes actualType, UmbracoObjectTypes expectedType, Func contentFetcher) + private IPublishedContent GetPublishedContent(T nodeId, ref UmbracoObjectTypes actualType, UmbracoObjectTypes expectedType, Func contentFetcher) { // is the actual type supported by the content fetcher? if (actualType != UmbracoObjectTypes.Unknown && actualType != expectedType) diff --git a/src/Umbraco.Web/PropertyEditors/ValueConverters/RelatedLinksEditorValueConvertor.cs b/src/Umbraco.Web/PropertyEditors/ValueConverters/RelatedLinksEditorValueConvertor.cs index b95644d9ca..d6ec4dcaf5 100644 --- a/src/Umbraco.Web/PropertyEditors/ValueConverters/RelatedLinksEditorValueConvertor.cs +++ b/src/Umbraco.Web/PropertyEditors/ValueConverters/RelatedLinksEditorValueConvertor.cs @@ -88,6 +88,10 @@ namespace Umbraco.Web.PropertyEditors.ValueConverters var relatedLinksData = JsonConvert.DeserializeObject>(sourceString); var relatedLinks = new List(); + if (UmbracoContext.Current == null) return source; + + var helper = new UmbracoHelper(UmbracoContext.Current); + foreach (var linkData in relatedLinksData) { var relatedLink = new RelatedLink @@ -111,7 +115,7 @@ namespace Umbraco.Web.PropertyEditors.ValueConverters var udiAttempt = strLinkId.TryConvertTo(); if (udiAttempt.Success) { - var content = udiAttempt.Result.ToPublishedContent(); + var content = helper.TypedContent(udiAttempt.Result); if (content != null) { relatedLink.Id = content.Id; diff --git a/src/Umbraco.Web/PublishedCache/ContextualPublishedCache.cs b/src/Umbraco.Web/PublishedCache/ContextualPublishedCache.cs index b5c3d850d2..c340beaa78 100644 --- a/src/Umbraco.Web/PublishedCache/ContextualPublishedCache.cs +++ b/src/Umbraco.Web/PublishedCache/ContextualPublishedCache.cs @@ -12,7 +12,11 @@ namespace Umbraco.Web.PublishedCache /// Provides access to cached contents in a specified context. /// public abstract class ContextualPublishedCache - { + { + //TODO: We need to add: + //* GetById(Guid contentId) + //* GetById(UDI contentId) + protected readonly UmbracoContext UmbracoContext; /// diff --git a/src/Umbraco.Web/PublishedContentQueryExtensions.cs b/src/Umbraco.Web/PublishedContentQueryExtensions.cs new file mode 100644 index 0000000000..c40daf5e82 --- /dev/null +++ b/src/Umbraco.Web/PublishedContentQueryExtensions.cs @@ -0,0 +1,23 @@ +using System; +using Umbraco.Core; +using Umbraco.Core.Models; + +namespace Umbraco.Web +{ + public static class PublishedContentQueryExtensions + { + /// + /// Gets a content item from the cache + /// + /// + /// + /// + public static IPublishedContent TypedContent(this ITypedPublishedContentQuery contentQuery, Udi id) + { + var guidUdi = id as GuidUdi; + if (guidUdi == null) + throw new InvalidOperationException("UDIs for content items must be " + typeof(GuidUdi)); + return contentQuery.TypedContent(guidUdi.Guid); + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index d3e31fa366..0b5652e509 100644 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -412,6 +412,7 @@ + diff --git a/src/Umbraco.Web/UmbracoHelper.cs b/src/Umbraco.Web/UmbracoHelper.cs index 5befbda7cf..1e8291e91f 100644 --- a/src/Umbraco.Web/UmbracoHelper.cs +++ b/src/Umbraco.Web/UmbracoHelper.cs @@ -527,11 +527,23 @@ namespace Umbraco.Web public string UrlAbsolute(int contentId) { return UrlProvider.GetUrl(contentId, true); + } + + #endregion + + #region Members + + public IPublishedContent TypedMember(Udi id) + { + var guidUdi = id as GuidUdi; + if (guidUdi == null) return null; + return TypedMember(guidUdi.Guid); } - #endregion - - #region Members + public IPublishedContent TypedMember(Guid id) + { + return MembershipHelper.GetByProviderKey(id); + } public IPublishedContent TypedMember(object id) { @@ -593,6 +605,9 @@ namespace Umbraco.Web Guid guidId; if (ConvertIdObjectToGuid(id, out guidId)) return ContentQuery.TypedContent(guidId); + Udi udiId; + if (ConvertIdObjectToUdi(id, out udiId)) + return ContentQuery.TypedContent(udiId); return null; } @@ -615,6 +630,16 @@ namespace Umbraco.Web { return ContentQuery.TypedContent(id); } + + /// + /// Gets a content item from the cache + /// + /// + /// + public IPublishedContent TypedContent(Udi id) + { + return ContentQuery.TypedContent(id); + } /// /// Gets a content item from the cache. @@ -907,6 +932,22 @@ namespace Umbraco.Web return false; } + private static bool ConvertIdObjectToUdi(object id, out Udi guidId) + { + var s = id as string; + if (s != null) + { + return Udi.TryParse(s, out guidId); + } + if (id is Udi) + { + guidId = (Udi)id; + return true; + } + guidId = null; + return false; + } + private static bool ConvertIdsObjectToInts(IEnumerable ids, out IEnumerable intIds) { var list = new List(); @@ -937,29 +978,59 @@ namespace Umbraco.Web } guidIds = list; return true; + } + + #endregion + + #region Media + + public IPublishedContent TypedMedia(Udi id) + { + var guidUdi = id as GuidUdi; + if (guidUdi == null) return null; + return TypedMedia(guidUdi.Guid); } - #endregion + public IPublishedContent TypedMedia(Guid id) + { + //TODO: This is horrible but until the media cache properly supports GUIDs we have no choice here and + // currently there won't be any way to add this method correctly to `ITypedPublishedContentQuery` without breaking an interface and adding GUID support for media + + var entityService = UmbracoContext.Application.Services.EntityService; + var mediaAttempt = entityService.GetIdForKey(id, UmbracoObjectTypes.Media); + return mediaAttempt.Success ? ContentQuery.TypedMedia(mediaAttempt.Result) : null; + } + + /// + /// Overloaded method accepting an 'object' type + /// + /// + /// + /// + /// 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. + /// + public IPublishedContent TypedMedia(object id) + { + return TypedMediaForObject(id); + } - #region Media - - /// - /// Overloaded method accepting an 'object' type - /// - /// - /// - /// - /// 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. - /// - public IPublishedContent TypedMedia(object id) - { + private IPublishedContent TypedMediaForObject(object id) + { int intId; - return ConvertIdObjectToInt(id, out intId) ? ContentQuery.TypedMedia(intId) : null; - } - - public IPublishedContent TypedMedia(int id) + if (ConvertIdObjectToInt(id, out intId)) + return ContentQuery.TypedMedia(intId); + Guid guidId; + if (ConvertIdObjectToGuid(id, out guidId)) + return TypedMedia(guidId); + Udi udiId; + if (ConvertIdObjectToUdi(id, out udiId)) + return TypedMedia(udiId); + return null; + } + + public IPublishedContent TypedMedia(int id) { return ContentQuery.TypedMedia(id); }