From 2ae5acbb656ca87920620010c1a544befcaef758 Mon Sep 17 00:00:00 2001 From: Rasmus John Pedersen Date: Mon, 28 May 2018 19:47:45 +0200 Subject: [PATCH] Update to Umbraco v8 preview --- README.md | 2 +- build/version.txt | 3 +- .../MultiUrlPicker.html | 44 ++- .../MultiUrlPicker.js | 5 +- src/RJP.MultiUrlPicker/Information.cs | 23 -- src/RJP.MultiUrlPicker/Models/Link.cs | 227 +------------- src/RJP.MultiUrlPicker/Models/LinkDisplay.cs | 14 +- src/RJP.MultiUrlPicker/Models/LinkDto.cs | 19 +- src/RJP.MultiUrlPicker/Models/MultiUrls.cs | 81 ----- .../MultiUrlPickerPropertyEditor.cs | 281 ------------------ .../MultiUrlPickerValueConverter.cs | 127 -------- .../Properties/AssemblyInfo.cs | 4 +- .../MultiUrlPickerConfiguration.cs | 13 + .../MultiUrlPickerConfigurationEditor.cs | 8 + .../MultiUrlPickerDataEditor.cs | 31 ++ .../MultiUrlPickerDataValueEditor.cs | 166 +++++++++++ .../MultiUrlPickerValueConverter.cs | 92 ++++++ .../RJP.MultiUrlPicker.csproj | 249 ++-------------- src/RJP.MultiUrlPicker/packages.config | 46 --- 19 files changed, 392 insertions(+), 1043 deletions(-) delete mode 100644 src/RJP.MultiUrlPicker/Information.cs delete mode 100644 src/RJP.MultiUrlPicker/Models/MultiUrls.cs delete mode 100644 src/RJP.MultiUrlPicker/MultiUrlPickerPropertyEditor.cs delete mode 100644 src/RJP.MultiUrlPicker/MultiUrlPickerValueConverter.cs create mode 100644 src/RJP.MultiUrlPicker/PropertyEditors/MultiUrlPickerConfiguration.cs create mode 100644 src/RJP.MultiUrlPicker/PropertyEditors/MultiUrlPickerConfigurationEditor.cs create mode 100644 src/RJP.MultiUrlPicker/PropertyEditors/MultiUrlPickerDataEditor.cs create mode 100644 src/RJP.MultiUrlPicker/PropertyEditors/MultiUrlPickerDataValueEditor.cs create mode 100644 src/RJP.MultiUrlPicker/PropertyEditors/ValueConverters/MultiUrlPickerValueConverter.cs delete mode 100644 src/RJP.MultiUrlPicker/packages.config diff --git a/README.md b/README.md index 5bea8f2..ab47310 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Multi Url Picker for Umbraco 7 +# Multi Url Picker for Umbraco 8 [![NuGet release](https://img.shields.io/nuget/v/RJP.UmbracoMultiUrlPicker.svg)](https://www.nuget.org/packages/RJP.UmbracoMultiUrlPicker) [![Our Umbraco project page](https://img.shields.io/badge/our-umbraco-orange.svg)](https://our.umbraco.org/projects/backoffice-extensions/multi-url-picker) diff --git a/build/version.txt b/build/version.txt index 7ec1d6d..81cf610 100644 --- a/build/version.txt +++ b/build/version.txt @@ -1 +1,2 @@ -2.1.0 +3.0.0 +alpha \ No newline at end of file diff --git a/src/RJP.MultiUrlPicker.Web.UI/MultiUrlPicker.html b/src/RJP.MultiUrlPicker.Web.UI/MultiUrlPicker.html index 54dd09d..443fe0f 100644 --- a/src/RJP.MultiUrlPicker.Web.UI/MultiUrlPicker.html +++ b/src/RJP.MultiUrlPicker.Web.UI/MultiUrlPicker.html @@ -1,4 +1,7 @@ -
+
+

+

+
@@ -14,7 +17,6 @@
- Add +
+ + + + Add between {{model.config.minNumberOfItems}} and {{model.config.maxNumberOfItems}} items + + You can only have {{model.config.maxNumberOfItems}} items selected + + + + + + Add {{model.config.minNumberOfItems - renderModel.length}} item(s) + + You can only have {{model.config.maxNumberOfItems}} items selected + + + + + + Add up to {{model.config.maxNumberOfItems}} items + + You can only have {{model.config.maxNumberOfItems}} items selected + + + + + + Add at least {{model.config.minNumberOfItems}} item(s) + + +
+ +
- You need to add at least {{model.config.minNumberOfItems}} items + You need to add at least {{model.config.minNumberOfItems}} items
- You can only have {{model.config.maxNumberOfItems}} items selected + You can only have {{model.config.maxNumberOfItems}} items selected
diff --git a/src/RJP.MultiUrlPicker.Web.UI/MultiUrlPicker.js b/src/RJP.MultiUrlPicker.Web.UI/MultiUrlPicker.js index b7d2b44..fea861a 100644 --- a/src/RJP.MultiUrlPicker.Web.UI/MultiUrlPicker.js +++ b/src/RJP.MultiUrlPicker.Web.UI/MultiUrlPicker.js @@ -2,6 +2,7 @@ 'use strict' var MultiUrlPickerController = function ($scope, angularHelper, entityResource, iconHelper) { + this.renderModel = [] if ($scope.preview) { @@ -126,7 +127,7 @@ // Inject the querystring field var $markup = $(response.data) var $urlField = $markup.find('[label="@defaultdialogs_urlLinkPicker"]') - $urlField.after('') + $urlField.after('') response.data = $markup[0] } return response @@ -136,5 +137,5 @@ } angular.module('umbraco').controller('RJP.MultiUrlPickerController', MultiUrlPickerController) - angular.module("umbraco.services").config(['$httpProvider', mupHttpProvider]); + angular.module('umbraco.services').config(['$httpProvider', mupHttpProvider]) })() diff --git a/src/RJP.MultiUrlPicker/Information.cs b/src/RJP.MultiUrlPicker/Information.cs deleted file mode 100644 index c8b98ba..0000000 --- a/src/RJP.MultiUrlPicker/Information.cs +++ /dev/null @@ -1,23 +0,0 @@ -namespace RJP.MultiUrlPicker -{ - using System; - using System.Reflection; - - internal static class Information - { - private static readonly Lazy _version; - - static Information() - { - _version = new Lazy(() => Assembly.GetExecutingAssembly().GetName().Version); - } - - public static Version Version - { - get - { - return _version.Value; - } - } - } -} diff --git a/src/RJP.MultiUrlPicker/Models/Link.cs b/src/RJP.MultiUrlPicker/Models/Link.cs index dbcf02f..216fe81 100644 --- a/src/RJP.MultiUrlPicker/Models/Link.cs +++ b/src/RJP.MultiUrlPicker/Models/Link.cs @@ -1,228 +1,13 @@ -namespace RJP.MultiUrlPicker.Models +namespace RJP.MultiUrlPicker.Models { - using System; - - using Newtonsoft.Json.Linq; - - using Umbraco.Core.Models; using Umbraco.Core; - using Umbraco.Web; - using Umbraco.Web.Extensions; - using Umbraco.Core.Models.PublishedContent; - public class Link { - private readonly JToken _linkItem; - private bool _publishedContentInitialized = false; - private string _name; - private string _url; - private string _target; - private bool? _deleted; - private LinkType? _linkType; - private IPublishedContent _content; - private Udi _udi; - private int? _id; - - public Link(JToken linkItem) - { - _linkItem = linkItem; - } - - private IPublishedContent PublishedContent - { - get - { - InitPublishedContent(); - return _content; - } - } - - [Obsolete("Use Udi instead")] - public int? Id - { - get - { - if (_id == null) - { - _id = _linkItem.Value("id"); - if (!_id.HasValue) - { - InitPublishedContent(); - } - } - return _id; - } - - } - - public Udi Udi - { - get - { - if (_udi == null) - { - if (!Udi.TryParse(_linkItem.Value("udi"), out _udi)) - { - InitPublishedContent(); - } - } - return _udi; - } - } - - public string Name - { - get - { - if (string.IsNullOrEmpty(_name)) - { - _name = _linkItem.Value("name"); - } - return _name; - } - } - - internal bool Deleted - { - get - { - if (_deleted == null) - { - if (Id.HasValue || Udi != null) - { - _deleted = PublishedContent == null; - } - else - { - _deleted = false; - } - } - return (bool)_deleted; - } - } - - public string Url - { - get - { - if (string.IsNullOrEmpty(_url)) - { - _url = PublishedContent?.Url ?? _linkItem.Value("url"); - - var qs = _linkItem.Value("querystring"); - if (!string.IsNullOrWhiteSpace(qs)) - { - _url += qs; - } - } - return _url; - } - } - - public string Target - { - get - { - if (string.IsNullOrEmpty(_target)) - { - _target = _linkItem.Value("target"); - } - return _target == string.Empty ? null : _target; - } - } - - public LinkType Type - { - get - { - if (_linkType == null) - { - if (Udi != null) - { - if (Udi.EntityType == Constants.UdiEntityType.Media) - { - _linkType = LinkType.Media; - } - else - { - _linkType = LinkType.Content; - } - } - else - { - _linkType = LinkType.External; - } - } - return _linkType.Value; - } - } - - - private void InitPublishedContent() - { - if (!_publishedContentInitialized) - { - _publishedContentInitialized = true; - - if (UmbracoContext.Current == null) - { - return; - } - - if (Udi.TryParse(_linkItem.Value("udi"), out _udi)) - { - _content = _udi.ToPublishedContent(); - _id = _content?.Id; - } - else - { - var helper = new UmbracoHelper(UmbracoContext.Current); - - // there were no Udi so let's try the legacy way - _id = _linkItem.Value("id"); - - if (_id.HasValue) - { - bool isMedia = _linkItem.Value("isMedia"); - - if (_linkItem.Value("isMedia")) - { - _content = helper.TypedMedia(_id.Value); - } - else - { - _content = helper.TypedContent(_id.Value); - } - SetUdi(); - } - } - } - } - - private void SetUdi() - { - if (_content != null && _udi == null) - { - Guid? key = _content.GetKey(); - if (key == Guid.Empty) - { - // if the key is Guid.Empty the model might be created by the ModelsBuilder, - // if so it, by default, derives from PublishedContentModel. - // By calling UnWrap() we get the original content, which probably implements - // IPublishedContentWithKey, so we can get the key - key = (_content as PublishedContentWrapped)?.Unwrap().GetKey(); - } - - if (key.HasValue && key != Guid.Empty) - { - string udiType = _content.ItemType == PublishedItemType.Media ? - Constants.UdiEntityType.Media : - Constants.UdiEntityType.Document; - - _udi = Udi.Create(udiType, key.Value); - } - } - } + public string Name { get; set; } + public string Target { get; set; } + public LinkType Type { get; set; } + public Udi Udi { get; set; } + public string Url { get; set; } } } diff --git a/src/RJP.MultiUrlPicker/Models/LinkDisplay.cs b/src/RJP.MultiUrlPicker/Models/LinkDisplay.cs index 25533e2..9381dbb 100644 --- a/src/RJP.MultiUrlPicker/Models/LinkDisplay.cs +++ b/src/RJP.MultiUrlPicker/Models/LinkDisplay.cs @@ -1,4 +1,4 @@ -namespace RJP.MultiUrlPicker.Models +namespace RJP.MultiUrlPicker.Models { using System.Runtime.Serialization; @@ -10,9 +10,6 @@ internal class LinkDisplay [DataMember(Name = "icon")] public string Icon { get; set; } - [DataMember(Name = "id")] - public int? Id { get; set; } - [DataMember(Name = "isMedia")] public bool IsMedia { get; set; } @@ -22,16 +19,19 @@ internal class LinkDisplay [DataMember(Name = "published")] public bool Published { get; set; } + [DataMember(Name = "querystring")] + public string Querystring { get; set; } + [DataMember(Name = "target")] public string Target { get; set; } + [DataMember(Name = "trashed")] + public bool Trashed { get; set; } + [DataMember(Name = "udi")] public GuidUdi Udi { get; set; } [DataMember(Name = "url")] public string Url { get; set; } - - [DataMember(Name = "querystring")] - public string Querystring { get; set; } } } diff --git a/src/RJP.MultiUrlPicker/Models/LinkDto.cs b/src/RJP.MultiUrlPicker/Models/LinkDto.cs index 9eaef7c..836dd46 100644 --- a/src/RJP.MultiUrlPicker/Models/LinkDto.cs +++ b/src/RJP.MultiUrlPicker/Models/LinkDto.cs @@ -1,4 +1,4 @@ -namespace RJP.MultiUrlPicker.Models +namespace RJP.MultiUrlPicker.Models { using System.Runtime.Serialization; @@ -7,30 +7,19 @@ [DataContract] internal class LinkDto { - [DataMember(Name = "icon")] - public string Icon { get; set; } - - [DataMember(Name = "id")] - public int? Id { get; set; } - - [DataMember(Name = "isMedia")] - public bool? IsMedia { get; set; } - [DataMember(Name = "name")] public string Name { get; set; } [DataMember(Name = "target")] public string Target { get; set; } + [DataMember(Name = "querystring")] + public string Querystring { get; set; } + [DataMember(Name = "udi")] public GuidUdi Udi { get; set; } [DataMember(Name = "url")] public string Url { get; set; } - - [DataMember(Name = "querystring")] - public string Querystring { get; set; } - - internal bool Published { get; set; } } } diff --git a/src/RJP.MultiUrlPicker/Models/MultiUrls.cs b/src/RJP.MultiUrlPicker/Models/MultiUrls.cs deleted file mode 100644 index 976d577..0000000 --- a/src/RJP.MultiUrlPicker/Models/MultiUrls.cs +++ /dev/null @@ -1,81 +0,0 @@ -namespace RJP.MultiUrlPicker.Models -{ - using System; - using System.Collections.Generic; - using System.Linq; - - using Newtonsoft.Json; - using Newtonsoft.Json.Linq; - - using Umbraco.Core.Logging; - - [Obsolete("Use IEnumerable instead")] - public class MultiUrls : IEnumerable - { - private readonly string _propertyData; - private readonly List _multiUrls = new List(); - - internal MultiUrls() - { - } - - internal MultiUrls(JArray propertyData) - { - _propertyData = propertyData.ToString(); - - Initialize(propertyData); - } - - public MultiUrls(string propertyData) - { - _propertyData = propertyData; - - if (!string.IsNullOrEmpty(propertyData)) - { - var relatedLinks = JsonConvert.DeserializeObject(propertyData); - Initialize(relatedLinks); - } - } - - private void Initialize(JArray data) - { - foreach (var item in data) - { - var newLink = new Link(item); - if (!newLink.Deleted) - { - _multiUrls.Add(new Link(item)); - } - else - { - LogHelper.Warn( - string.Format("MultiUrlPicker value converter skipped a link as the node has been upublished/deleted (Id: {0}), ", newLink.Id)); - } - } - } - - public string PropertyData - { - get - { - return _propertyData; - } - } - - // Although this method seems unnecessary it makes .Any() available in Dynamics - public bool Any() - { - return Enumerable.Any(this); - } - - public IEnumerator GetEnumerator() - { - return _multiUrls.GetEnumerator(); - } - - System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } - } -} diff --git a/src/RJP.MultiUrlPicker/MultiUrlPickerPropertyEditor.cs b/src/RJP.MultiUrlPicker/MultiUrlPickerPropertyEditor.cs deleted file mode 100644 index ee55b28..0000000 --- a/src/RJP.MultiUrlPicker/MultiUrlPickerPropertyEditor.cs +++ /dev/null @@ -1,281 +0,0 @@ -namespace RJP.MultiUrlPicker -{ - using System; - using System.Collections.Generic; - using System.Linq; - - using ClientDependency.Core; - - using Newtonsoft.Json; - - using Umbraco.Core; - using Umbraco.Core.Logging; - using Umbraco.Core.Models; - using Umbraco.Core.Models.Editors; - using Umbraco.Core.Models.EntityBase; - using Umbraco.Core.PropertyEditors; - using Umbraco.Core.Services; - using Umbraco.Web; - using Umbraco.Web.PropertyEditors; - - using Models; - - using Constants = Umbraco.Core.Constants; - - [PropertyEditorAsset(ClientDependencyType.Javascript, "~/App_Plugins/RJP.MultiUrlPicker/MultiUrlPicker.js")] - [PropertyEditor("RJP.MultiUrlPicker", "Multi Url Picker", "JSON", - "~/App_Plugins/RJP.MultiUrlPicker/MultiUrlPicker.html", - Group ="pickers", Icon = "icon-link", IsParameterEditor = true)] - public class MultiUrlPickerPropertyEditor : PropertyEditor - { - private IDictionary _defaultPreValues; - - public MultiUrlPickerPropertyEditor() - { - _defaultPreValues = new Dictionary - { - {"minNumberOfItems", null}, - {"maxNumberOfItems", null}, - {"version", Information.Version.ToString(3)}, - }; - } - - public override IDictionary DefaultPreValues - { - get { return _defaultPreValues; } - set { _defaultPreValues = value; } - } - - protected override PreValueEditor CreatePreValueEditor() - { - return new MultiUrlPickerPreValueEditor(); - } - - protected override PropertyValueEditor CreateValueEditor() - { - return new MultiUrlPickerPropertyValueEditor(base.CreateValueEditor()); - } - - private class MultiUrlPickerPreValueEditor : PreValueEditor - { - [PreValueField("minNumberOfItems", "Min number of items", "number")] - public int? MinNumberOfItems { get; set; } - - [PreValueField("maxNumberOfItems", "Max number of items", "number")] - public int? MaxNumberOfItems { get; set; } - - [PreValueField("version", "Multi Url Picker version", "hidden", HideLabel = true)] - public string Version { get; set; } - - public override IDictionary ConvertDbToEditor(IDictionary defaultPreVals, PreValueCollection persistedPreVals) - { - // if there isn't a version stored set it to 0 for backwards compatibility - if(!persistedPreVals.PreValuesAsDictionary.ContainsKey("version")) - { - persistedPreVals.PreValuesAsDictionary["version"] = new PreValue("0"); - } - - return base.ConvertDbToEditor(defaultPreVals, persistedPreVals); - } - } - - private class MultiUrlPickerPropertyValueEditor : PropertyValueEditorWrapper - { - public MultiUrlPickerPropertyValueEditor(PropertyValueEditor wrapped) : base(wrapped) - { - } - - public override object ConvertDbToEditor(Property property, PropertyType propertyType, IDataTypeService dataTypeService) - { - string value = property.Value?.ToString(); - - if(string.IsNullOrEmpty(value)) - { - return Enumerable.Empty(); - } - - try - { - ServiceContext services = ApplicationContext.Current.Services; - IEntityService entityService = services.EntityService; - IContentTypeService contentTypeService = services.ContentTypeService; - - var links = JsonConvert.DeserializeObject>(value); - - var documentLinks = links.FindAll(link => - link.Id.HasValue && false == link.IsMedia.GetValueOrDefault() || - link.Udi != null && link.Udi.EntityType == Constants.UdiEntityType.Document - ); - - var mediaLinks = links.FindAll(link => - link.Id.HasValue && true == link.IsMedia.GetValueOrDefault() || - link.Udi != null && link.Udi.EntityType == Constants.UdiEntityType.Media - ); - - List entities = new List(); - if (documentLinks.Count > 0) - { - if(documentLinks[0].Id.HasValue) - { - entities.AddRange( - entityService.GetAll(UmbracoObjectTypes.Document, documentLinks.Select(link => link.Id.Value).ToArray()) - ); - } - else - { - entities.AddRange( - entityService.GetAll(UmbracoObjectTypes.Document, documentLinks.Select(link => link.Udi.Guid).ToArray()) - ); - } - } - - if(mediaLinks.Count > 0) - { - if (mediaLinks[0].Id.HasValue) - { - entities.AddRange( - entityService.GetAll(UmbracoObjectTypes.Media, mediaLinks.Select(link => link.Id.Value).ToArray()) - ); - } - else - { - entities.AddRange( - entityService.GetAll(UmbracoObjectTypes.Media, mediaLinks.Select(link => link.Udi.Guid).ToArray()) - ); - } - } - - var result = new List(); - foreach(LinkDto dto in links) - { - if (dto.Id.HasValue || dto.Udi != null) - { - IUmbracoEntity entity = entities.Find(e => e.Key == dto.Udi?.Guid || e.Id == dto.Id); - if (entity == null) - { - continue; - } - - string entityType = Equals(entity.AdditionalData["NodeObjectTypeId"], Constants.ObjectTypes.MediaGuid) ? - Constants.UdiEntityType.Media : - Constants.UdiEntityType.Document; - - var udi = new GuidUdi(entityType, entity.Key); - - string contentTypeAlias = entity.AdditionalData["ContentTypeAlias"] as string; - string icon; - bool isMedia = false; - bool published = Equals(entity.AdditionalData["IsPublished"], true); - string url = dto.Url; - - if(string.IsNullOrEmpty(contentTypeAlias)) - { - continue; - } - - if (udi.EntityType == Constants.UdiEntityType.Document) - { - IContentType contentType = contentTypeService.GetContentType(contentTypeAlias); - - if (contentType == null) - { - continue; - } - - icon = contentType.Icon; - - if (UmbracoContext.Current != null) - { - url = UmbracoContext.Current.UrlProvider.GetUrl(entity.Id); - } - } - else - { - IMediaType mediaType = contentTypeService.GetMediaType(contentTypeAlias); - - if (mediaType == null) - { - continue; - } - - icon = mediaType.Icon; - isMedia = true; - published = true; - - if (UmbracoContext.Current != null) - { - url = UmbracoContext.Current.MediaCache.GetById(entity.Id)?.Url; - } - } - - result.Add(new LinkDisplay - { - Icon = icon, - Id = entity.Id, - IsMedia = isMedia, - Name = dto.Name, - Target = dto.Target, - Published = published, - Udi = udi, - Url = url, - Querystring = dto.Querystring - }); - } - else - { - result.Add(new LinkDisplay - { - Icon = "icon-link", - Name = dto.Name, - Published = true, - Target = dto.Target, - Url = dto.Url, - Querystring = dto.Querystring - }); - } - } - return result; - } - catch(Exception ex) - { - ApplicationContext.Current.ProfilingLogger.Logger.Error("Error getting links", ex); - } - - return base.ConvertDbToEditor(property, propertyType, dataTypeService); - } - - public override object ConvertEditorToDb(ContentPropertyData editorValue, object currentValue) - { - string value = editorValue.Value?.ToString(); - - if (string.IsNullOrEmpty(value)) - { - return string.Empty; - } - - try - { - return JsonConvert.SerializeObject( - from link in JsonConvert.DeserializeObject>(value) - select new LinkDto - { - Name = link.Name, - Target = link.Target, - Udi = link.Udi, - Url = link.Udi == null ? link.Url : null, // only save the url for external links - Querystring = link.Querystring - }, - new JsonSerializerSettings - { - NullValueHandling = NullValueHandling.Ignore - }); - } - catch (Exception ex) - { - ApplicationContext.Current.ProfilingLogger.Logger.Error("Error saving links", ex); - } - return base.ConvertEditorToDb(editorValue, currentValue); - } - } - } -} diff --git a/src/RJP.MultiUrlPicker/MultiUrlPickerValueConverter.cs b/src/RJP.MultiUrlPicker/MultiUrlPickerValueConverter.cs deleted file mode 100644 index afe7724..0000000 --- a/src/RJP.MultiUrlPicker/MultiUrlPickerValueConverter.cs +++ /dev/null @@ -1,127 +0,0 @@ -using Umbraco.Core.Logging; -using Umbraco.Core.PropertyEditors.ValueConverters; - -namespace RJP.MultiUrlPicker -{ - using System; - using System.Linq; - using System.Collections.Generic; - - using Newtonsoft.Json.Linq; - - using Umbraco.Core; - using Umbraco.Core.Models; - using Umbraco.Core.Models.PublishedContent; - using Umbraco.Core.PropertyEditors; - using Umbraco.Core.Services; - - using Models; - - public class MultiUrlPickerValueConverter : PropertyValueConverterBase, IPropertyValueConverterMeta - { - private readonly IDataTypeService _dataTypeService; - - public MultiUrlPickerValueConverter(IDataTypeService dataTypeService) - { - _dataTypeService = dataTypeService; - } - - public MultiUrlPickerValueConverter() : this(ApplicationContext.Current.Services.DataTypeService) - { - } - - public override bool IsConverter(PublishedPropertyType propertyType) - { - return propertyType.PropertyEditorAlias.Equals("RJP.MultiUrlPicker"); - } - - public override object ConvertDataToSource(PublishedPropertyType propertyType, object source, bool preview) - { - if (string.IsNullOrWhiteSpace(source?.ToString())) - { - return null; - } - - if (source.ToString().Trim().StartsWith("[")) - { - try - { - return JArray.Parse(source.ToString()); - } - catch (Exception ex) - { - LogHelper.Error("Error parsing JSON", ex); - } - } - return null; - } - - public override object ConvertSourceToObject(PublishedPropertyType propertyType, object source, bool preview) - { - bool isMultiple = IsMultipleDataType(propertyType.DataTypeId, out int maxNumberOfItems); - if (source == null) - { - return isMultiple ? new MultiUrls() : null; - } - - var urls = new MultiUrls((JArray)source); - if(isMultiple) - { - if(maxNumberOfItems > 0) - { - return urls.Take(maxNumberOfItems); - } - return urls; - } - return urls.FirstOrDefault(); - } - - public Type GetPropertyValueType(PublishedPropertyType propertyType) - { - if (IsMultipleDataType(propertyType.DataTypeId, out int maxNumberOfItems)) - { - return typeof(IEnumerable); - } - return typeof(Link); - } - - public PropertyCacheLevel GetPropertyCacheLevel(PublishedPropertyType propertyType, PropertyCacheValue cacheValue) - { - switch (cacheValue) - { - case PropertyCacheValue.Source: - return PropertyCacheLevel.Content; - case PropertyCacheValue.Object: - case PropertyCacheValue.XPath: - return PropertyCacheLevel.ContentCache; - } - - return PropertyCacheLevel.None; - } - - private bool IsMultipleDataType(int dataTypeId, out int maxNumberOfItems) - { - IDictionary preValues = _dataTypeService - .GetPreValuesCollectionByDataTypeId(dataTypeId) - .PreValuesAsDictionary; - - if (preValues.TryGetValue("maxNumberOfItems", out PreValue maxNumberOfItemsPreValue) && - int.TryParse(maxNumberOfItemsPreValue.Value, out maxNumberOfItems)) - { - PreValue versionPreValue; - Version version; - // for backwards compatibility, always return true if version - // is less than 2.0.0 - if (preValues.TryGetValue("version", out versionPreValue) && - Version.TryParse(versionPreValue.Value, out version) - && version >= new Version(2, 0, 0)) - { - return maxNumberOfItems != 1; - } - } - - maxNumberOfItems = 0; - return true; - } - } -} diff --git a/src/RJP.MultiUrlPicker/Properties/AssemblyInfo.cs b/src/RJP.MultiUrlPicker/Properties/AssemblyInfo.cs index 4d25dfc..fd8489f 100644 --- a/src/RJP.MultiUrlPicker/Properties/AssemblyInfo.cs +++ b/src/RJP.MultiUrlPicker/Properties/AssemblyInfo.cs @@ -31,5 +31,5 @@ // // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: -[assembly: AssemblyVersion("2.1.0.*")] -[assembly: AssemblyInformationalVersion("2.1.0")] +[assembly: AssemblyVersion("3.0.0.*")] +[assembly: AssemblyInformationalVersion("3.0.0-alpha")] diff --git a/src/RJP.MultiUrlPicker/PropertyEditors/MultiUrlPickerConfiguration.cs b/src/RJP.MultiUrlPicker/PropertyEditors/MultiUrlPickerConfiguration.cs new file mode 100644 index 0000000..c828b80 --- /dev/null +++ b/src/RJP.MultiUrlPicker/PropertyEditors/MultiUrlPickerConfiguration.cs @@ -0,0 +1,13 @@ +namespace RJP.MultiUrlPicker.PropertyEditors +{ + using Umbraco.Core.PropertyEditors; + + public class MultiUrlPickerConfiguration + { + [ConfigurationField("minNumberOfItems", "Min number of items", "number")] + public int? MinNumberOfItems { get; set; } + + [ConfigurationField("maxNumberOfItems", "Max number of items", "number")] + public int? MaxNumberOfItems { get; set; } + } +} diff --git a/src/RJP.MultiUrlPicker/PropertyEditors/MultiUrlPickerConfigurationEditor.cs b/src/RJP.MultiUrlPicker/PropertyEditors/MultiUrlPickerConfigurationEditor.cs new file mode 100644 index 0000000..9852eba --- /dev/null +++ b/src/RJP.MultiUrlPicker/PropertyEditors/MultiUrlPickerConfigurationEditor.cs @@ -0,0 +1,8 @@ +namespace RJP.MultiUrlPicker.PropertyEditors +{ + using Umbraco.Core.PropertyEditors; + + public class MultiUrlPickerConfigurationEditor : ConfigurationEditor + { + } +} diff --git a/src/RJP.MultiUrlPicker/PropertyEditors/MultiUrlPickerDataEditor.cs b/src/RJP.MultiUrlPicker/PropertyEditors/MultiUrlPickerDataEditor.cs new file mode 100644 index 0000000..fd3f474 --- /dev/null +++ b/src/RJP.MultiUrlPicker/PropertyEditors/MultiUrlPickerDataEditor.cs @@ -0,0 +1,31 @@ +namespace RJP.MultiUrlPicker.PropertyEditors +{ + using System; + + using ClientDependency.Core; + + using Umbraco.Core.Logging; + using Umbraco.Core.PropertyEditors; + using Umbraco.Core.Services; + using Umbraco.Web.PropertyEditors; + using Umbraco.Web.PublishedCache; + + [PropertyEditorAsset(ClientDependencyType.Javascript, "~/App_Plugins/RJP.MultiUrlPicker/MultiUrlPicker.js")] + [DataEditor("RJP.MultiUrlPicker", "Multi Url Picker", "~/App_Plugins/RJP.MultiUrlPicker/MultiUrlPicker.html", + ValueType = "JSON", Group ="pickers", Icon = "icon-link")] + public class MultiUrlPickerPropertyEditor : DataEditor + { + private readonly IEntityService _entityService; + private readonly IPublishedSnapshotAccessor _publishedSnapshotAccessor; + + public MultiUrlPickerPropertyEditor(ILogger logger, IEntityService entityService, IPublishedSnapshotAccessor publishedSnapshotAccessor) : base(logger, EditorType.PropertyValue|EditorType.MacroParameter) + { + _entityService = entityService ?? throw new ArgumentNullException(nameof(entityService)); + _publishedSnapshotAccessor = publishedSnapshotAccessor ?? throw new ArgumentNullException(nameof(publishedSnapshotAccessor)); + } + + protected override IConfigurationEditor CreateConfigurationEditor() => new MultiUrlPickerConfigurationEditor(); + + protected override IDataValueEditor CreateValueEditor() => new MultiUrlPickerDataValueEditor(_entityService, _publishedSnapshotAccessor, Logger, Attribute); + } +} diff --git a/src/RJP.MultiUrlPicker/PropertyEditors/MultiUrlPickerDataValueEditor.cs b/src/RJP.MultiUrlPicker/PropertyEditors/MultiUrlPickerDataValueEditor.cs new file mode 100644 index 0000000..0cec759 --- /dev/null +++ b/src/RJP.MultiUrlPicker/PropertyEditors/MultiUrlPickerDataValueEditor.cs @@ -0,0 +1,166 @@ +namespace RJP.MultiUrlPicker.PropertyEditors +{ + using System; + using System.Collections.Generic; + using System.Linq; + + using Newtonsoft.Json; + + using Umbraco.Core; + using Umbraco.Core.Logging; + using Umbraco.Core.Models; + using Umbraco.Core.Models.Editors; + using Umbraco.Core.Models.Entities; + using Umbraco.Core.Services; + using Umbraco.Core.PropertyEditors; + using Umbraco.Web.PublishedCache; + + using Models; + + using Constants = Umbraco.Core.Constants; + + public class MultiUrlPickerDataValueEditor : DataValueEditor + { + private readonly IEntityService _entityService; + private readonly ILogger _logger; + private readonly IPublishedSnapshotAccessor _publishedSnapshotAccessor; + + public MultiUrlPickerDataValueEditor(IEntityService entityService, IPublishedSnapshotAccessor publishedSnapshotAccessor, ILogger logger, DataEditorAttribute attribute) : base(attribute) + { + _entityService = entityService ?? throw new ArgumentNullException(nameof(entityService)); + _publishedSnapshotAccessor = publishedSnapshotAccessor ?? throw new ArgumentNullException(nameof(publishedSnapshotAccessor)); + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); + } + + public override object ToEditor(Property property, IDataTypeService dataTypeService, string culture = null, string segment = null) + { + string value = property.GetValue(culture, segment)?.ToString(); + + if (string.IsNullOrEmpty(value)) + { + return Enumerable.Empty(); + } + + try + { + var links = JsonConvert.DeserializeObject>(value); + + List documentLinks = links.FindAll(link => link.Udi != null && link.Udi.EntityType == Constants.UdiEntityType.Document); + List mediaLinks = links.FindAll(link => link.Udi != null && link.Udi.EntityType == Constants.UdiEntityType.Media); + + List entities = new List(); + if (documentLinks.Count > 0) + { + entities.AddRange( + _entityService.GetAll(UmbracoObjectTypes.Document, documentLinks.Select(link => link.Udi.Guid).ToArray()) + ); + } + + if (mediaLinks.Count > 0) + { + entities.AddRange( + _entityService.GetAll(UmbracoObjectTypes.Media, mediaLinks.Select(link => link.Udi.Guid).ToArray()) + ); + } + + var result = new List(); + foreach (LinkDto dto in links) + { + GuidUdi udi = null; + string icon = "icon-link"; + bool isMedia = false; + bool published = true; + bool trashed = false; + string url = dto.Url; + + if (dto.Udi != null) + { + IUmbracoEntity entity = entities.Find(e => e.Key == dto.Udi.Guid); + if (entity == null) + { + continue; + } + + if (entity is IDocumentEntitySlim documentEntity) + { + icon = documentEntity.ContentTypeIcon; + published = documentEntity.Published; + udi = new GuidUdi(Constants.UdiEntityType.Document, documentEntity.Key); + url = _publishedSnapshotAccessor.PublishedSnapshot.Content.GetById(entity.Key)?.Url ?? "#"; + trashed = documentEntity.Trashed; + } + else if(entity is IContentEntitySlim contentEntity) + { + icon = contentEntity.ContentTypeIcon; + isMedia = true; + published = false == contentEntity.Trashed; + udi = new GuidUdi(Constants.UdiEntityType.Media, contentEntity.Key); + url = _publishedSnapshotAccessor.PublishedSnapshot.Media.GetById(entity.Key)?.Url ?? "#"; + trashed = contentEntity.Trashed; + } + else + { + // Not supported + continue; + } + } + + result.Add(new LinkDisplay + { + Icon = icon, + IsMedia = isMedia, + Name = dto.Name, + Target = dto.Target, + Trashed = trashed, + Published = published, + Udi = udi, + Url = url, + Querystring = dto.Querystring + }); + } + return result; + } + catch (Exception ex) + { + _logger.Error("Error getting links", ex); + } + + return base.ToEditor(property, dataTypeService, culture, segment); + } + + + public override object FromEditor(ContentPropertyData editorValue, object currentValue) + { + string value = editorValue.Value?.ToString(); + + if (string.IsNullOrEmpty(value)) + { + return string.Empty; + } + + try + { + return JsonConvert.SerializeObject( + from link in JsonConvert.DeserializeObject>(value) + select new LinkDto + { + Name = link.Name, + Target = link.Target, + Udi = link.Udi, + Url = link.Udi == null ? link.Url : null, // only save the url for external links + Querystring = string.IsNullOrWhiteSpace(link.Querystring?.Trim()) ? null : link.Querystring.EnsureStartsWith('?') + }, + new JsonSerializerSettings + { + NullValueHandling = NullValueHandling.Ignore + }); + } + catch (Exception ex) + { + _logger.Error("Error saving links", ex); + } + + return base.FromEditor(editorValue, currentValue); + } + } +} diff --git a/src/RJP.MultiUrlPicker/PropertyEditors/ValueConverters/MultiUrlPickerValueConverter.cs b/src/RJP.MultiUrlPicker/PropertyEditors/ValueConverters/MultiUrlPickerValueConverter.cs new file mode 100644 index 0000000..b52c656 --- /dev/null +++ b/src/RJP.MultiUrlPicker/PropertyEditors/ValueConverters/MultiUrlPickerValueConverter.cs @@ -0,0 +1,92 @@ +namespace RJP.MultiUrlPicker.PropertyEditors.ValueConverters +{ + using System; + using System.Linq; + using System.Collections.Generic; + + using Newtonsoft.Json; + + using Umbraco.Core; + using Umbraco.Core.Logging; + using Umbraco.Core.Models.PublishedContent; + using Umbraco.Core.PropertyEditors; + using Umbraco.Web.PublishedCache; + + using Models; + + public class MultiUrlPickerValueConverter : PropertyValueConverterBase + { + private readonly IPublishedSnapshotAccessor _publishedSnapshotAccessor; + private readonly ProfilingLogger _proflog; + + public MultiUrlPickerValueConverter(IPublishedSnapshotAccessor publishedSnapshotAccessor, ProfilingLogger proflog) + { + _publishedSnapshotAccessor = publishedSnapshotAccessor ?? throw new ArgumentNullException(nameof(publishedSnapshotAccessor)); + _proflog = proflog ?? throw new ArgumentNullException(nameof(proflog)); + } + public override bool IsConverter(PublishedPropertyType propertyType) => propertyType.EditorAlias.Equals("RJP.MultiUrlPicker"); + + public override Type GetPropertyValueType(PublishedPropertyType propertyType) => + propertyType.DataType.ConfigurationAs().MaxNumberOfItems == 1 ? + typeof(Link) : + typeof(IEnumerable); + + public override PropertyCacheLevel GetPropertyCacheLevel(PublishedPropertyType propertyType) => PropertyCacheLevel.Snapshot; + + public override object ConvertSourceToIntermediate(IPublishedElement owner, PublishedPropertyType propertyType, object source, bool preview) => source?.ToString(); + + public override object ConvertIntermediateToObject(IPublishedElement owner, PublishedPropertyType propertyType, PropertyCacheLevel referenceCacheLevel, object inter, bool preview) + { + using (_proflog.DebugDuration($"ConvertPropertyToLinks ({propertyType.DataType.Id})")) + { + int? maxNumber = propertyType.DataType.ConfigurationAs().MaxNumberOfItems; + + if (inter == null) + { + return maxNumber == 1 ? null : Enumerable.Empty(); + } + + var links = new List(); + var dtos = JsonConvert.DeserializeObject>((string)inter); + + foreach (var dto in dtos) + { + LinkType type = LinkType.External; + string url = dto.Url; + + if (dto.Udi != null) + { + type = dto.Udi.EntityType == Constants.UdiEntityType.Media + ? LinkType.Media + : LinkType.Content; + + var content = type == LinkType.Media ? + _publishedSnapshotAccessor.PublishedSnapshot.Media.GetById(preview, dto.Udi.Guid) : + _publishedSnapshotAccessor.PublishedSnapshot.Content.GetById(preview, dto.Udi.Guid); + + if (content == null) + { + continue; + } + url = content.Url; + } + + links.Add( + new Link + { + Name = dto.Name, + Target = dto.Target, + Type = type, + Udi = dto.Udi, + Url = url + dto.Querystring, + } + ); + } + + if (maxNumber == 1) return links.FirstOrDefault(); + if (maxNumber > 0) return links.Take(maxNumber.Value); + return links; + } + } + } +} diff --git a/src/RJP.MultiUrlPicker/RJP.MultiUrlPicker.csproj b/src/RJP.MultiUrlPicker/RJP.MultiUrlPicker.csproj index 4fcdef5..19674e9 100644 --- a/src/RJP.MultiUrlPicker/RJP.MultiUrlPicker.csproj +++ b/src/RJP.MultiUrlPicker/RJP.MultiUrlPicker.csproj @@ -9,7 +9,7 @@ Properties RJP.MultiUrlPicker RJP.MultiUrlPicker - v4.5 + v4.7.2 512 ..\ @@ -34,265 +34,50 @@ true - - ..\..\packages\AutoMapper.3.3.1\lib\net40\AutoMapper.dll - True - - - ..\..\packages\AutoMapper.3.3.1\lib\net40\AutoMapper.Net4.dll - True - - - ..\..\packages\UmbracoCms.Core.7.6.0\lib\net45\businesslogic.dll - True - - ..\..\packages\ClientDependency.1.9.2\lib\net45\ClientDependency.Core.dll - True - - - ..\..\packages\ClientDependency-Mvc5.1.8.0.0\lib\net45\ClientDependency.Core.Mvc.dll - True - - - ..\..\packages\UmbracoCms.Core.7.6.0\lib\net45\cms.dll - True - - - ..\..\packages\UmbracoCms.Core.7.6.0\lib\net45\controls.dll - True - - - ..\..\packages\xmlrpcnet.2.5.0\lib\net20\CookComputing.XmlRpcV2.dll - False - - - ..\..\packages\Examine.0.1.82\lib\net45\Examine.dll - True - - - ..\..\packages\HtmlAgilityPack.1.4.9.5\lib\Net45\HtmlAgilityPack.dll - True - - - ..\..\packages\SharpZipLib.0.86.0\lib\20\ICSharpCode.SharpZipLib.dll - False - - - ..\..\packages\ImageProcessor.2.5.3\lib\net45\ImageProcessor.dll - True - - - ..\..\packages\ImageProcessor.Web.4.8.3\lib\net45\ImageProcessor.Web.dll - True - - - ..\..\packages\UmbracoCms.Core.7.6.0\lib\net45\interfaces.dll - True - - - ..\..\packages\UmbracoCms.Core.7.6.0\lib\net45\log4net.dll - True - - - ..\..\packages\Log4Net.Async.2.0.4\lib\net40\Log4Net.Async.dll - True - - False - ..\..\packages\Lucene.Net.2.9.4.1\lib\net40\Lucene.Net.dll - False - - - ..\..\packages\Markdown.1.14.7\lib\net45\MarkdownSharp.dll - True - - - ..\..\packages\UmbracoCms.Core.7.6.0\lib\net45\Microsoft.ApplicationBlocks.Data.dll - True - - - ..\..\packages\Microsoft.AspNet.Identity.Core.2.2.1\lib\net45\Microsoft.AspNet.Identity.Core.dll - True - - - ..\..\packages\Microsoft.AspNet.Identity.Owin.2.2.1\lib\net45\Microsoft.AspNet.Identity.Owin.dll - True - - - ..\..\packages\Microsoft.AspNet.SignalR.Core.2.2.1\lib\net45\Microsoft.AspNet.SignalR.Core.dll - True - - - ..\..\packages\Microsoft.IO.RecyclableMemoryStream.1.2.1\lib\net45\Microsoft.IO.RecyclableMemoryStream.dll - True - - - ..\..\packages\Microsoft.Owin.3.0.1\lib\net45\Microsoft.Owin.dll - True - - - ..\..\packages\Microsoft.Owin.Host.SystemWeb.3.0.1\lib\net45\Microsoft.Owin.Host.SystemWeb.dll - True - - - ..\..\packages\Microsoft.Owin.Security.3.0.1\lib\net45\Microsoft.Owin.Security.dll - True - - - ..\..\packages\Microsoft.Owin.Security.Cookies.3.0.1\lib\net45\Microsoft.Owin.Security.Cookies.dll - True - - - ..\..\packages\Microsoft.Owin.Security.OAuth.3.0.1\lib\net45\Microsoft.Owin.Security.OAuth.dll - True - - - False - ..\..\packages\Microsoft.Web.Infrastructure.1.0.0.0\lib\net40\Microsoft.Web.Infrastructure.dll + ..\..\lib\ClientDependency.Core.dll - - ..\..\packages\MiniProfiler.2.1.0\lib\net40\MiniProfiler.dll - False - - - ..\..\packages\MySql.Data.6.9.9\lib\net45\MySql.Data.dll - True - - - ..\..\packages\Newtonsoft.Json.10.0.2\lib\net45\Newtonsoft.Json.dll - True - - - ..\..\packages\Owin.1.0\lib\net40\Owin.dll - True - - - ..\..\packages\Semver.2.0.4\lib\netstandard1.1\Semver.dll - True - - - ..\..\packages\UmbracoCms.Core.7.6.0\lib\net45\SQLCE4Umbraco.dll - True + + False + ..\..\lib\Newtonsoft.Json.dll - + - - ..\..\packages\UmbracoCms.Core.7.6.0\lib\net45\System.Data.SqlServerCe.dll - True - - - ..\..\packages\UmbracoCms.Core.7.6.0\lib\net45\System.Data.SqlServerCe.Entity.dll - True - - - - ..\..\packages\Microsoft.AspNet.WebApi.Client.5.2.3\lib\net45\System.Net.Http.Formatting.dll - True - - - - ..\..\packages\System.Threading.Tasks.Dataflow.4.7.0\lib\portable-net45+win8+wpa81\System.Threading.Tasks.Dataflow.dll - True - - - - ..\..\packages\Microsoft.AspNet.WebPages.3.2.3\lib\net45\System.Web.Helpers.dll - True - - - ..\..\packages\Microsoft.AspNet.WebApi.Core.5.2.3\lib\net45\System.Web.Http.dll - True - - - ..\..\packages\Microsoft.AspNet.WebApi.WebHost.5.2.3\lib\net45\System.Web.Http.WebHost.dll - True - - - ..\..\packages\Microsoft.AspNet.Mvc.5.2.3\lib\net45\System.Web.Mvc.dll - True - - - ..\..\packages\Microsoft.AspNet.Razor.3.2.3\lib\net45\System.Web.Razor.dll - True - - - ..\..\packages\Microsoft.AspNet.WebPages.3.2.3\lib\net45\System.Web.WebPages.dll - True - - - ..\..\packages\Microsoft.AspNet.WebPages.3.2.3\lib\net45\System.Web.WebPages.Deployment.dll - True - - - ..\..\packages\Microsoft.AspNet.WebPages.3.2.3\lib\net45\System.Web.WebPages.Razor.dll - True - - - False - + - - ..\..\packages\UmbracoCms.Core.7.6.0\lib\net45\TidyNet.dll - True - - - ..\..\packages\UmbracoCms.Core.7.6.0\lib\net45\umbraco.dll - True - + - ..\..\packages\UmbracoCms.Core.7.6.0\lib\net45\Umbraco.Core.dll - True - - - ..\..\packages\UmbracoCms.Core.7.6.0\lib\net45\umbraco.DataLayer.dll - True - - - ..\..\packages\UmbracoCms.Core.7.6.0\lib\net45\umbraco.editorControls.dll - True - - - ..\..\packages\UmbracoCms.Core.7.6.0\lib\net45\umbraco.MacroEngines.dll - True - - - ..\..\packages\UmbracoCms.Core.7.6.0\lib\net45\umbraco.providers.dll - True - - - ..\..\packages\UmbracoCms.Core.7.6.0\lib\net45\Umbraco.Web.UI.dll - True + False + ..\..\lib\Umbraco.Core.dll - - ..\..\packages\UmbracoCms.Core.7.6.0\lib\net45\UmbracoExamine.dll - True + + ..\..\lib\Umbraco.Web.dll - - - - + + + + + - -