diff --git a/README.md b/README.md index 595a616..b647479 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,7 @@ # Property List [![Build status](https://img.shields.io/appveyor/ci/UMCO/umbraco-property-list.svg)](https://ci.appveyor.com/project/UMCO/umbraco-property-list) +[![NuGet release](https://img.shields.io/nuget/v/Our.Umbraco.PropertyList.svg)](https://www.nuget.org/packages/Our.Umbraco.PropertyList) Property List is a property editor for making repeatable lists of a datatype for Umbraco 7.6+ @@ -10,7 +11,16 @@ Property List is a property editor for making repeatable lists of a datatype for > *Note:* Property List has been developed against **Umbraco v7.6.0** and will support that version and above. -Property List can be built manually from the source-code: +Property List can be installed from either NuGet package repositories, or build manually from the source-code: + + +#### NuGet package repository + +To [install from NuGet](https://www.nuget.org/packages/Our.Umbraco.PropertyList), you can run the following command from within Visual Studio: + + PM> Install-Package Our.Umbraco.PropertyList + +We also have a [MyGet package repository](https://www.myget.org/gallery/umbraco-packages) - for bleeding-edge / development releases. #### Manual build @@ -44,60 +54,11 @@ Anyone and everyone is welcome to contribute. Please take a moment to review the What's left to do? -- [x] Pre Value Editor - - [x] Prevalues - - [x] DataType Picker - - [x] Only save the ID - - [x] Minimum items - - [x] Maximum items - - [x] Hide label - - **Ideas** - - [ ] Disable sorting? - -- [ ] Value Editor - - [x] ContentType preview - - [x] HTML view - - [x] Check if we can we reuse any Umbraco directives? - - [x] Check if any UMCO projects are useful for reuse? - - [x] CSS - - [x] Angular / JS - - [x] Prepare the value-editor - - [x] Get the DataType by ID; then get... - - [x] config/prevalues - - [x] view-path - - [x] property alias - - [x] Initialize the list values - - [x] Set the list values - - [x] Set the IsDirty flag - - [x] Render the DataType/property-editor - - [x] Repeatable - - [x] Addable - - [x] Removeable - - [x] Sortable - - [x] Saving the values - - [x] PropertyValueEditor - - [x] ConvertDbToString - - [x] ConvertDbToEditor - - [x] ConvertEditorToDb - - [x] Browser testing (Chrome, Firefox, IE/Edge) - -- [x] PropertyValueConverter - - [x] Get target DataType definition - - [x] Create dummy PropertyType (in order to run the target property-editor's value-converter) - - [x] Return as IEnumerable of that type - - [x] Investigate ModelsBuilder support - think it's to provide the return type - -- [x] Packaging - - [x] MSBuild script - - [x] Umbraco package - - [x] NuGet package - - [x] AppVeyor - -- [ ] Courier data-resolver - - [ ] Pre Value Editor - - [ ] Add DataType dependency - - [ ] Value Editor - - [ ] Processing all list item DataTypes +- [ ] Deploy ValueConnector + - [ ] Pre Value Editor + - [ ] Add DataType dependency + - [ ] Value Editor + - [ ] Processing all list item DataTypes --- diff --git a/appveyor.yml b/appveyor.yml index 61eb05e..af9a8c4 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -6,7 +6,7 @@ version: 1.0.0.{build} # UMBRACO_PACKAGE_PRERELEASE_SUFFIX if a rtm release build this should be blank, otherwise if empty will default to alpha # example UMBRACO_PACKAGE_PRERELEASE_SUFFIX=beta init: - - set UMBRACO_PACKAGE_PRERELEASE_SUFFIX=beta + - set UMBRACO_PACKAGE_PRERELEASE_SUFFIX=beta2 cache: - src\packages -> **\packages.config # preserve "packages" directory in the root of build folder but will reset it if packages.config is modified diff --git a/src/Our.Umbraco.PropertyList/Bootstrap.cs b/src/Our.Umbraco.PropertyList/Bootstrap.cs index dc7600e..d33a973 100644 --- a/src/Our.Umbraco.PropertyList/Bootstrap.cs +++ b/src/Our.Umbraco.PropertyList/Bootstrap.cs @@ -1,5 +1,5 @@ using Newtonsoft.Json; -using Our.Umbraco.PropertyList.Converters; +using Our.Umbraco.PropertyList.ValueConverters; using Umbraco.Core; using Umbraco.Core.Sync; using Umbraco.Web.Cache; diff --git a/src/Our.Umbraco.PropertyList/Our.Umbraco.PropertyList.csproj b/src/Our.Umbraco.PropertyList/Our.Umbraco.PropertyList.csproj index 12fd292..0a7ea38 100644 --- a/src/Our.Umbraco.PropertyList/Our.Umbraco.PropertyList.csproj +++ b/src/Our.Umbraco.PropertyList/Our.Umbraco.PropertyList.csproj @@ -35,38 +35,10 @@ ..\packages\AutoMapper.3.3.1\lib\net40\AutoMapper.dll False - - ..\packages\UmbracoCms.Core.7.6.0\lib\net45\businesslogic.dll - False - ..\packages\ClientDependency.1.9.2\lib\net45\ClientDependency.Core.dll False - - ..\packages\UmbracoCms.Core.7.6.0\lib\net45\cms.dll - False - - - ..\packages\UmbracoCms.Core.7.6.0\lib\net45\controls.dll - False - - - ..\packages\Examine.0.1.82\lib\net45\Examine.dll - False - - - ..\packages\HtmlAgilityPack.1.4.9.5\lib\Net45\HtmlAgilityPack.dll - False - - - ..\packages\ImageProcessor.2.5.3\lib\net45\ImageProcessor.dll - False - - - ..\packages\ImageProcessor.Web.4.8.3\lib\net45\ImageProcessor.Web.dll - False - ..\packages\UmbracoCms.Core.7.6.0\lib\net45\interfaces.dll False @@ -75,18 +47,6 @@ ..\packages\UmbracoCms.Core.7.6.0\lib\net45\log4net.dll False - - ..\packages\Log4Net.Async.2.0.4\lib\net40\Log4Net.Async.dll - False - - - ..\packages\UmbracoCms.Core.7.6.0\lib\net45\Microsoft.ApplicationBlocks.Data.dll - False - - - ..\packages\Microsoft.AspNet.SignalR.Core.2.2.1\lib\net45\Microsoft.AspNet.SignalR.Core.dll - False - ..\packages\MiniProfiler.2.1.0\lib\net40\MiniProfiler.dll False @@ -95,59 +55,22 @@ ..\packages\Newtonsoft.Json.10.0.2\lib\net45\Newtonsoft.Json.dll False - - ..\packages\Owin.1.0\lib\net40\Owin.dll - False - - - ..\packages\UmbracoCms.Core.7.6.0\lib\net45\SQLCE4Umbraco.dll - False - - - ..\packages\UmbracoCms.Core.7.6.0\lib\net45\System.Data.SqlServerCe.dll - False - - - ..\packages\UmbracoCms.Core.7.6.0\lib\net45\System.Data.SqlServerCe.Entity.dll - False - - - ..\packages\Microsoft.AspNet.WebApi.Client.5.2.3\lib\net45\System.Net.Http.Formatting.dll - False - - - ..\packages\System.Threading.Tasks.Dataflow.4.7.0\lib\portable-net45+win8+wpa81\System.Threading.Tasks.Dataflow.dll - False - - - ..\packages\Microsoft.AspNet.WebPages.3.2.3\lib\net45\System.Web.Helpers.dll - False - ..\packages\Microsoft.AspNet.WebApi.Core.5.2.3\lib\net45\System.Web.Http.dll False - - ..\packages\Microsoft.AspNet.WebApi.WebHost.5.2.3\lib\net45\System.Web.Http.WebHost.dll - False - ..\packages\Microsoft.AspNet.Mvc.5.2.3\lib\net45\System.Web.Mvc.dll False - - - ..\packages\UmbracoCms.Core.7.6.0\lib\net45\TidyNet.dll - False - ..\packages\UmbracoCms.Core.7.6.0\lib\net45\umbraco.dll False @@ -156,34 +79,10 @@ ..\packages\UmbracoCms.Core.7.6.0\lib\net45\Umbraco.Core.dll False - - ..\packages\UmbracoCms.Core.7.6.0\lib\net45\umbraco.DataLayer.dll - False - - - ..\packages\UmbracoCms.Core.7.6.0\lib\net45\umbraco.editorControls.dll - False - - - ..\packages\UmbracoCms.Core.7.6.0\lib\net45\umbraco.MacroEngines.dll - False - - - ..\packages\UmbracoCms.Core.7.6.0\lib\net45\umbraco.providers.dll - False - - - ..\packages\UmbracoCms.Core.7.6.0\lib\net45\Umbraco.Web.UI.dll - False - - - ..\packages\UmbracoCms.Core.7.6.0\lib\net45\UmbracoExamine.dll - False - - + @@ -198,9 +97,7 @@ - - - + diff --git a/src/Our.Umbraco.PropertyList/PropertyEditors/PropertyListPreValueEditor.cs b/src/Our.Umbraco.PropertyList/PropertyEditors/PropertyListPreValueEditor.cs index c1e7fa6..b017b2b 100644 --- a/src/Our.Umbraco.PropertyList/PropertyEditors/PropertyListPreValueEditor.cs +++ b/src/Our.Umbraco.PropertyList/PropertyEditors/PropertyListPreValueEditor.cs @@ -1,19 +1,47 @@ -using Umbraco.Core.PropertyEditors; +using Umbraco.Core.IO; +using Umbraco.Core.PropertyEditors; namespace Our.Umbraco.PropertyList.PropertyEditors { internal class PropertyListPreValueEditor : PreValueEditor { - [PreValueField("dataType", "Data Type", "~/App_Plugins/PropertyList/views/propertylist.datatypepicker.html", Description = "Select a data type.")] - public string DataType { get; set; } + public PropertyListPreValueEditor() + : base() + { + // In terms of inheritance, we'd like the "dataType" field to always be at the top. + Fields.Insert(0, new PreValueField + { + Key = "dataType", + Name = "Data Type", + View = IOHelper.ResolveUrl("~/App_Plugins/PropertyList/views/propertylist.datatypepicker.html"), + Description = "Select a data type." + }); - [PreValueField("minItems", "Min Items", "number", Description = "Set the minimum number of items allowed.")] - public string MinItems { get; set; } - - [PreValueField("maxItems", "Max Items", "number", Description = "Set the maximum number of items allowed.")] - public string MaxItems { get; set; } - - [PreValueField("hideLabel", "Hide Label", "boolean", Description = "Set whether to hide the editor label and have the list take up the full width of the editor window.")] - public string HideLabel { get; set; } + // The rest of the fields can be added at the bottom. + Fields.AddRange(new[] + { + new PreValueField + { + Key = "minItems", + Name = "Min Items", + View = "number", + Description = "Set the minimum number of items allowed." + }, + new PreValueField + { + Key = "maxItems", + Name = "Max Items", + View = "number", + Description = "Set the maximum number of items allowed." + }, + new PreValueField + { + Key = "hideLabel", + Name = "Hide Label", + View = "boolean", + Description = "Set whether to hide the editor label and have the list take up the full width of the editor window." + } + }); + } } } \ No newline at end of file diff --git a/src/Our.Umbraco.PropertyList/PropertyEditors/PropertyListPropertyEditor.cs b/src/Our.Umbraco.PropertyList/PropertyEditors/PropertyListPropertyEditor.cs index 81300a1..c919767 100644 --- a/src/Our.Umbraco.PropertyList/PropertyEditors/PropertyListPropertyEditor.cs +++ b/src/Our.Umbraco.PropertyList/PropertyEditors/PropertyListPropertyEditor.cs @@ -1,36 +1,17 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel.DataAnnotations; -using System.Linq; -using System.Xml.Linq; +using System.Collections.Generic; using ClientDependency.Core; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; -using Our.Umbraco.PropertyList.Models; -using Umbraco.Core; -using Umbraco.Core.Logging; -using Umbraco.Core.Models; -using Umbraco.Core.Models.Editors; using Umbraco.Core.PropertyEditors; -using Umbraco.Core.Services; using Umbraco.Web.PropertyEditors; namespace Our.Umbraco.PropertyList.PropertyEditors { - [PropertyEditor( - PropertyEditorAlias, - "Property List", - "JSON", - "~/App_Plugins/PropertyList/Views/propertylist.html", - Group = "Lists", - Icon = "icon-bulleted-list", - IsParameterEditor = false)] - [PropertyEditorAsset(ClientDependencyType.Css, "~/App_Plugins/PropertyList/css/propertylist.css")] - [PropertyEditorAsset(ClientDependencyType.Javascript, "~/App_Plugins/PropertyList/js/propertylist.controllers.js")] - [PropertyEditorAsset(ClientDependencyType.Javascript, "~/App_Plugins/PropertyList/js/propertylist.resources.js")] + [PropertyEditor(PropertyEditorAlias, PropertyEditorName, "JSON", PropertyEditorViewPath, Group = "Lists", Icon = "icon-bulleted-list", IsParameterEditor = false)] + [PropertyEditorAsset(ClientDependencyType.Javascript, "~/App_Plugins/PropertyList/js/propertylist.js")] public class PropertyListPropertyEditor : PropertyEditor { public const string PropertyEditorAlias = "Our.Umbraco.PropertyList"; + public const string PropertyEditorName = "Property List"; + public const string PropertyEditorViewPath = "~/App_Plugins/PropertyList/Views/propertylist.html"; internal const string DefaultTextstringPropertyEditorGuid = "0cc0eba1-9960-42c9-bf9b-60e150b429ae"; // Guid for default Textstring diff --git a/src/Our.Umbraco.PropertyList/Converters/PropertyListValueConverter.cs b/src/Our.Umbraco.PropertyList/ValueConverters/PropertyListValueConverter.cs similarity index 84% rename from src/Our.Umbraco.PropertyList/Converters/PropertyListValueConverter.cs rename to src/Our.Umbraco.PropertyList/ValueConverters/PropertyListValueConverter.cs index 915b410..0ef3750 100644 --- a/src/Our.Umbraco.PropertyList/Converters/PropertyListValueConverter.cs +++ b/src/Our.Umbraco.PropertyList/ValueConverters/PropertyListValueConverter.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.IO; using System.Linq; using System.Xml.Linq; using System.Xml.XPath; @@ -12,7 +11,7 @@ using Umbraco.Core.Models.PublishedContent; using Umbraco.Core.PropertyEditors; -namespace Our.Umbraco.PropertyList.Converters +namespace Our.Umbraco.PropertyList.ValueConverters { public class PropertyListValueConverter : PropertyValueConverterBase, IPropertyValueConverterMeta { @@ -23,8 +22,8 @@ public override bool IsConverter(PublishedPropertyType propertyType) public override object ConvertDataToSource(PublishedPropertyType propertyType, object source, bool preview) { - var sourceString = source?.ToString(); - if (string.IsNullOrWhiteSpace(sourceString)) + var data = source?.ToString(); + if (string.IsNullOrWhiteSpace(data)) return null; var innerPropertyType = this.GetInnerPublishedPropertyType(propertyType); @@ -36,16 +35,16 @@ public override object ConvertDataToSource(PublishedPropertyType propertyType, o // NOTE: We can't be sure which format the data is in. // With "nested property-editors", (e.g. Nested Content, Stacked Content), // they don't convert the call `ConvertDbToXml`. - if (sourceString.DetectIsJson()) + if (data.DetectIsJson()) { - var model = JsonConvert.DeserializeObject(sourceString); + var model = JsonConvert.DeserializeObject(data); if (model != null) items.AddRange(model.Values); } else { // otherwise we assume it's XML - var elements = XElement.Parse(sourceString); + var elements = XElement.Parse(data); if (elements != null && elements.HasElements) items.AddRange(elements.XPathSelectElements("value").Select(x => x.Value)); } @@ -96,12 +95,29 @@ public override object ConvertSourceToObject(PublishedPropertyType propertyType, public override object ConvertSourceToXPath(PublishedPropertyType propertyType, object source, bool preview) { - // TODO: Review if we need to call `ConvertSourceToXPath` for each of the values? + if (source is List items) + { + var innerPropertyType = this.GetInnerPublishedPropertyType(propertyType); + + var elements = new List(); + + foreach (var item in items) + { + if (item != null) + { + var xpathValue = innerPropertyType.ConvertSourceToXPath(item, preview); + var element = new XElement("value", xpathValue); + elements.Add(element); + } + } + + return new XElement("values", elements).CreateNavigator(); + } // This method must return either a `string` or `XPathNavigator` object-type, see Umbraco core for details: // https://github.com/umbraco/Umbraco-CMS/blob/release-7.6.0/src/Umbraco.Core/Models/PublishedContent/PublishedPropertyType.cs#L312 - - return new XPathDocument(new StringReader(source.ToString())).CreateNavigator(); + //return new XPathDocument(new StringReader(source.ToString())).CreateNavigator(); + return base.ConvertSourceToXPath(propertyType, source, preview); } private PublishedPropertyType GetInnerPublishedPropertyType(PublishedPropertyType propertyType) diff --git a/src/Our.Umbraco.PropertyList/Web/Controllers/PropertyListApiController.cs b/src/Our.Umbraco.PropertyList/Web/Controllers/PropertyListApiController.cs index d2c374b..5fb9d68 100644 --- a/src/Our.Umbraco.PropertyList/Web/Controllers/PropertyListApiController.cs +++ b/src/Our.Umbraco.PropertyList/Web/Controllers/PropertyListApiController.cs @@ -18,10 +18,12 @@ public class PropertyListApiController : UmbracoAuthorizedJsonController public DataTypeDisplay GetDataTypeByKey(Guid key) { var dataType = Services.DataTypeService.GetDataTypeDefinitionById(key); + if (dataType == null) { throw new HttpResponseException(HttpStatusCode.NotFound); } + return Mapper.Map(dataType); } diff --git a/src/Our.Umbraco.PropertyList/Web/UI/App_Plugins/PropertyList/css/propertylist.css b/src/Our.Umbraco.PropertyList/Web/UI/App_Plugins/PropertyList/css/propertylist.css deleted file mode 100644 index ef8ed4f..0000000 --- a/src/Our.Umbraco.PropertyList/Web/UI/App_Plugins/PropertyList/css/propertylist.css +++ /dev/null @@ -1,196 +0,0 @@ -/*=================== - PROPERTY LIST -===================*/ - -.pl__editor { - position: relative; -} - -.pl__wrapper { - margin-right: 12px; -} - -.pl__property-item { - position: relative; - display: block; - margin: 0; - /*z-index: 10; Turns out that the modal overlay doesn't like this rule */ -} - -.pl__property-wrapper { - position: relative; - background-color: #f8f8f8; - border: 1px solid #b3afbd; - padding: 10px 7px; - cursor: move; -} - -.pl__property-wrapper > .icon { - float: left; - margin-top: 5px; - font-size: 18px; - color: #b3afbd; -} - -.pl__property-wrapper > .umb-property-editor { - margin: auto 25px; - cursor: auto; -} - -.pl__buttons { - position: absolute; - right: -12px; - top: -5px; - background-color: #675e7a; - color: white; - opacity: 0; - transition: opacity .25s ease-in-out; - -moz-transition: opacity .25s ease-in-out; - -webkit-transition: opacity .25s ease-in-out; - -webkit-box-shadow: 0px 0px 10px 0px rgba(0,0,0,0.25); - -moz-box-shadow: 0px 0px 10px 0px rgba(0,0,0,0.25); - box-shadow: 0px 0px 10px 0px rgba(0,0,0,0.25); -} - -.pl__buttons:after { - position: absolute; - bottom: -12px; - right: 0; - content: ""; - width: 0; - height: 0; - border-style: solid; - border-width: 12px 12px 0 0; - border-color: #2e2246 transparent transparent transparent; -} - -.pl__buttons .umb_confirm-action__overlay { - position: relative; - display: inline-block; - left: 0; - z-index: 5; -} - -.pl__buttons .umb_confirm-action__overlay-action { - margin: 0 !important; - background-color: #675e7a; - -webkit-border-radius: 0; - -moz-border-radius: 0; - border-radius: 0; - -webkit-box-shadow: none; - -moz-box-shadow: none; - box-shadow: none; - z-index: 5; -} - -.pl__property-wrapper:hover .pl__buttons, -.pl__buttons:hover { - opacity: 1; -} - -.pl__button { - position: relative; - z-index: 10; -} - -.pl__button, -.pl__add-button, -.pl__buttons .umb_confirm-action__overlay-action { - display: inline-block; - background-color: #675e7a; - color: white !important; - font-size: 20px; - text-decoration: none !important; - width: 30px; - height: 30px; - line-height: 30px; - text-align: center; - cursor: pointer; -} - -.pl__button:hover, -.pl__add-button:hover, -.pl__buttons .umb_confirm-action__overlay-action.-cancel:hover, -.pl__buttons .umb_confirm-action__overlay-action.-confirm:hover { - color: white !important; - text-decoration: none !important; -} - -.pl__buttons .umb_confirm-action__overlay-action.-confirm:hover { - background-color: #35c786; -} - -.pl__buttons .umb_confirm-action__overlay-action.-cancel:hover { - background-color: #fe6561; -} - -.pl__add-button { - -webkit-border-radius: 2px; - -moz-border-radius: 2px; - border-radius: 2px; - z-index: 20; -} - -.pl__add-bar { - position: relative; - height: 10px; - z-index: 15; -} - -.pl__add-bar .pl__add-button { - position: absolute; - left: 50%; - top: 50%; - margin: -15px 0 0 -15px; - opacity: 0; - transition: opacity .0s ease-in-out; - -moz-transition: opacity .0s ease-in-out; - -webkit-transition: opacity .0s ease-in-out; - -webkit-box-shadow: 0px 0px 10px 0px rgba(0,0,0,0.25); - -moz-box-shadow: 0px 0px 10px 0px rgba(0,0,0,0.25); - box-shadow: 0px 0px 10px 0px rgba(0,0,0,0.25); -} - -.pl__add-bar:hover { - margin: -10px 0; - height: 30px; -} - -.pl__item:first-child .pl__add-bar--top:hover { - margin: 0 0 -10px; - height: 20px; -} - -.pl__add-bar:hover .pl__add-button { - opacity: 1; - transition-duration: .25s; - -moz-transition-duration: .25s; - -webkit-transition-duration: .25s; - -webkit-transition-delay: .05s; - -moz-transition-delay: .05s; - transition-delay: .05s; -} - -.pl__item.ui-sortable-placeholder { - top: 5px; - height: 2px !important; - visibility: visible !important; - background-color: #675e7a; -} - -.pl__sortable-helper { - overflow: visible; - background-color: rgba(103, 94, 122, 0.1); -} - -.pl__sortable-helper > div { - font-size: 18px; - margin-top: 15px; - margin-left: 7px; - color: #675e7a; -} - -.pl__buttons .no-overflow { - max-width: 100%; - overflow: hidden; -} diff --git a/src/Our.Umbraco.PropertyList/Web/UI/App_Plugins/PropertyList/js/propertylist.controllers.js b/src/Our.Umbraco.PropertyList/Web/UI/App_Plugins/PropertyList/js/propertylist.controllers.js deleted file mode 100644 index d7f4877..0000000 --- a/src/Our.Umbraco.PropertyList/Web/UI/App_Plugins/PropertyList/js/propertylist.controllers.js +++ /dev/null @@ -1,244 +0,0 @@ -angular.module("umbraco").controller("Our.Umbraco.PropertyList.Controllers.PropertyListController", [ - "$scope", - "Our.Umbraco.PropertyList.Resources.PropertyListResources", - "umbPropEditorHelper", - function ($scope, propertyListResource, umbPropEditorHelper) { - - $scope.inited = false; - - var dataTypeGuid = $scope.model.config.dataType; - var minItems = $scope.model.config.minItems || 0; - var maxItems = $scope.model.config.maxItems || 0; - - $scope.isConfigured = dataTypeGuid != null; - - if (!$scope.isConfigured) { - - // Model is ready so set inited - $scope.inited = true; - - } else { - - if (!angular.isObject($scope.model.value)) - $scope.model.value = undefined; - - $scope.model.value = $scope.model.value || { - dtd: dataTypeGuid, - values: [] - }; - - $scope.prompts = {}; - - propertyListResource.getPropertyTypeScaffoldByKey(dataTypeGuid).then(function (propertyType) { - - $scope.propertyType = propertyType; - - var propertyTypeViewPath = umbPropEditorHelper.getViewPath(propertyType.view); - - if (!$scope.model.controls) { - $scope.model.controls = []; - } - - if (!$scope.model.value.values) { - $scope.model.value.values = []; - } - - // Enforce min items - if ($scope.model.value.values.length < minItems) { - for (var i = $scope.model.value.values.length; i < minItems; i++) { - $scope.addContent(null, i); - } - } - - _.each($scope.model.value.values, function (value, idx) { - - // NOTE: Must be a copy of the config, not the same object reference. - // Otherwise any config modifications made by the editor will apply to following editors. - var propertyTypeConfig = JSON.parse(JSON.stringify(propertyType.config)); - - $scope.model.controls.push({ - alias: $scope.model.alias + "_" + idx, - config: propertyTypeConfig, - view: propertyTypeViewPath, - value: value - }); - }); - - // Model is ready so set inited - $scope.inited = true; - - }); - - } - - $scope.canAdd = function () { - return !maxItems || maxItems == 0 || $scope.model.controls.length < maxItems; - } - - $scope.canDelete = function () { - return !minItems || minItems == 0 || $scope.model.controls.length > minItems; - } - - $scope.addContent = function (evt, idx) { - - var control = { - alias: $scope.model.alias + "_" + idx, - config: JSON.parse(JSON.stringify($scope.propertyType.config)), - view: umbPropEditorHelper.getViewPath($scope.propertyType.view), - value: "" - }; - - $scope.model.controls.splice(idx, 0, control); - $scope.setDirty(); - } - - $scope.deleteContent = function (evt, idx) { - $scope.model.controls.splice(idx, 1); - $scope.setDirty(); - } - - $scope.sortableOptions = { - axis: 'y', - cursor: "move", - handle: ".pl__property-wrapper", - helper: function () { - return $('
'); - }, - cursorAt: { - top: 0 - }, - stop: function (e, ui) { - $scope.setDirty(); - } - }; - - $scope.setDirty = function () { - if ($scope.propertyForm) { - $scope.propertyForm.$setDirty(); - } - }; - - var unsubscribe = $scope.$on("formSubmitting", function (ev, args) { - var tmpValues = []; - - _.each($scope.model.controls, function (control, idx) { - tmpValues[idx] = control.value; - }); - - $scope.model.value = { - dtd: dataTypeGuid, - values: !_.isEmpty(tmpValues) ? tmpValues : [] - }; - }); - - $scope.$on('$destroy', function () { - unsubscribe(); - }); - - }]); - -angular.module("umbraco").controller("Our.Umbraco.PropertyList.Controllers.DataTypePickerController", [ - "$scope", - "contentTypeResource", - "dataTypeHelper", - "dataTypeResource", - "entityResource", - "Our.Umbraco.PropertyList.Resources.PropertyListResources", - function ($scope, contentTypeResource, dataTypeHelper, dataTypeResource, entityResource, propertyListResource) { - - if (!$scope.model.property) { - - $scope.model.property = {}; - - if ($scope.model.value) { - propertyListResource.getDataTypeByKey($scope.model.value).then(function (dataType) { - // update editor - $scope.model.property.editor = dataType.selectedEditor; - $scope.model.property.dataTypeId = dataType.id; - $scope.model.property.dataTypeIcon = dataType.icon; - $scope.model.property.dataTypeName = dataType.name; - }); - } - } - - var vm = this; - - vm.openEditorPickerOverlay = openEditorPickerOverlay; - vm.openEditorSettingsOverlay = openEditorSettingsOverlay; - - function setModelValue(dataTypeId) { - entityResource.getById(dataTypeId, "DataType").then(function (entity) { - $scope.model.value = entity.key; - }); - }; - - function openEditorPickerOverlay(property) { - - vm.editorPickerOverlay = {}; - vm.editorPickerOverlay.property = $scope.model.property; - vm.editorPickerOverlay.view = "views/common/overlays/contenttypeeditor/editorpicker/editorpicker.html"; - vm.editorPickerOverlay.show = true; - - vm.editorPickerOverlay.submit = function (model) { - - setModelValue(model.property.dataTypeId); - - vm.editorPickerOverlay.show = false; - vm.editorPickerOverlay = null; - }; - - vm.editorPickerOverlay.close = function (model) { - vm.editorPickerOverlay.show = false; - vm.editorPickerOverlay = null; - }; - - } - - function openEditorSettingsOverlay(property) { - - // get data type - dataTypeResource.getById(property.dataTypeId).then(function (dataType) { - - vm.editorSettingsOverlay = {}; - vm.editorSettingsOverlay.title = "Editor settings"; - vm.editorSettingsOverlay.view = "views/common/overlays/contenttypeeditor/editorsettings/editorsettings.html"; - vm.editorSettingsOverlay.dataType = dataType; - vm.editorSettingsOverlay.show = true; - - vm.editorSettingsOverlay.submit = function (model) { - - var preValues = dataTypeHelper.createPreValueProps(model.dataType.preValues); - - dataTypeResource.save(model.dataType, preValues, false).then(function (newDataType) { - - contentTypeResource.getPropertyTypeScaffold(newDataType.id).then(function (propertyType) { - - setModelValue(newDataType.id); - - // update editor - property.config = propertyType.config; - property.editor = propertyType.editor; - property.view = propertyType.view; - property.dataTypeId = newDataType.id; - property.dataTypeIcon = newDataType.icon; - property.dataTypeName = newDataType.name; - - vm.editorSettingsOverlay.show = false; - vm.editorSettingsOverlay = null; - - }); - - }); - - }; - - vm.editorSettingsOverlay.close = function (oldModel) { - vm.editorSettingsOverlay.show = false; - vm.editorSettingsOverlay = null; - }; - - }); - - } - - }]); diff --git a/src/Our.Umbraco.PropertyList/Web/UI/App_Plugins/PropertyList/js/propertylist.js b/src/Our.Umbraco.PropertyList/Web/UI/App_Plugins/PropertyList/js/propertylist.js new file mode 100644 index 0000000..484b3e2 --- /dev/null +++ b/src/Our.Umbraco.PropertyList/Web/UI/App_Plugins/PropertyList/js/propertylist.js @@ -0,0 +1,302 @@ +angular.module("umbraco").controller("Our.Umbraco.PropertyList.Controllers.PropertyListController", [ + "$scope", + "Our.Umbraco.PropertyList.Resources.PropertyListResources", + "umbPropEditorHelper", + function ($scope, propertyListResource, umbPropEditorHelper) { + + var dataTypeGuid = $scope.model.config.dataType; + var minItems = $scope.model.config.minItems || 0; + var maxItems = $scope.model.config.maxItems || 0; + var propertyType = null; // this will be set in the `getPropertyTypeScaffoldByKey` callback + + var vm = this; + + vm.sortableOptions = { + axis: "y", + containment: "parent", + cursor: "move", + handle: ".list-view-layout__sort-handle", + opacity: 0.7, + scroll: true, + tolerance: "pointer", + stop: function (e, ui) { + setDirty(); + } + }; + + vm.canAdd = canAdd; + vm.canDelete = canDelete; + vm.showPrompt = showPrompt; + vm.hidePrompt = hidePrompt; + vm.addContent = addContent; + vm.deleteContent = deleteContent; + + + if (!angular.isObject($scope.model.value)) + $scope.model.value = undefined; + + $scope.model.value = $scope.model.value || { + dtd: dataTypeGuid, + values: [] + }; + + propertyListResource.getPropertyTypeScaffoldByKey(dataTypeGuid).then(function (scaffold) { + + propertyType = scaffold; + + var propertyTypeViewPath = umbPropEditorHelper.getViewPath(propertyType.view); + + if (!vm.controls) { + vm.controls = []; + } + + if (!$scope.model.value.values) { + $scope.model.value.values = []; + } + + // Enforce min items + if ($scope.model.value.values.length < minItems) { + for (var i = $scope.model.value.values.length; i < minItems; i++) { + $scope.addContent(null, i); + } + } + + _.each($scope.model.value.values, function (value, idx) { + + // NOTE: Must be a copy of the config, not the same object reference. + // Otherwise any config modifications made by the editor will apply to following editors. + var propertyTypeConfig = JSON.parse(JSON.stringify(propertyType.config)); + + vm.controls.push({ + alias: $scope.model.alias + "_" + idx, + config: propertyTypeConfig, + view: propertyTypeViewPath, + value: value + }); + }); + + }); + + function canAdd() { + return !maxItems || maxItems === "0" || vm.length < maxItems; + } + + function canDelete() { + return !minItems || minItems === "0" || vm.length > minItems; + } + + function showPrompt(control) { + control.deletePrompt = true; + } + + function hidePrompt(control) { + control.deletePrompt = false; + } + + function addContent() { + + var control = { + alias: $scope.model.alias + "_" + (vm.controls.length + 1), + config: JSON.parse(JSON.stringify(propertyType.config)), + view: umbPropEditorHelper.getViewPath(propertyType.view), + value: "" + }; + + vm.controls.push(control); + setDirty(); + } + + function deleteContent(idx) { + vm.controls.splice(idx, 1); + setDirty(); + } + + function setDirty() { + if ($scope.propertyForm) { + $scope.propertyForm.$setDirty(); + } + }; + + var unsubscribe = $scope.$on("formSubmitting", function (ev, args) { + + $scope.$broadcast("propertyListFormSubmitting"); + + var tmpValue = { + dtd: dataTypeGuid, + values: [] + }; + + _.each(vm.controls, function (control, idx) { + tmpValue.values.push(control.value); + }); + + $scope.model.value = tmpValue; + }); + + $scope.$on("$destroy", function () { + unsubscribe(); + }); + + }]); + +angular.module("umbraco").controller("Our.Umbraco.PropertyList.Controllers.DataTypePickerController", [ + "$scope", + "dataTypeHelper", + "dataTypeResource", + "entityResource", + "Our.Umbraco.PropertyList.Resources.PropertyListResources", + function ($scope, dataTypeHelper, dataTypeResource, entityResource, propertyListResource) { + + var vm = this; + + vm.selectedDataType = null; + vm.allowAdd = true; + vm.allowRemove = true; + vm.allowEdit = true; + vm.sortable = false; + + vm.add = add; + vm.edit = edit; + vm.remove = remove; + + if (!!$scope.model.value) { + propertyListResource.getDataTypeByKey($scope.model.value).then(function (dataType) { + vm.selectedDataType = dataType; + vm.allowAdd = false; + }); + } + + function add() { + vm.editorPickerOverlay = { + property: {}, + view: "views/common/overlays/contenttypeeditor/editorpicker/editorpicker.html", + show: true + }; + + vm.editorPickerOverlay.submit = function (model) { + + entityResource.getById(model.property.dataTypeId, "DataType").then(function (entity) { + + $scope.model.value = entity.key; + + vm.selectedDataType = { + id: entity.id, + key: entity.key, + name: entity.name, + icon: model.property.dataTypeIcon, + selectedEditor: model.property.editor + }; + + vm.allowAdd = false; + + setDirty(); + }); + + vm.editorPickerOverlay.show = false; + vm.editorPickerOverlay = null; + }; + + vm.editorPickerOverlay.close = function (model) { + vm.editorPickerOverlay.show = false; + vm.editorPickerOverlay = null; + }; + }; + + function edit() { + dataTypeResource.getById(vm.selectedDataType.id).then(function (dataType) { + + vm.editorSettingsOverlay = { + title: "Editor settings", + view: "views/common/overlays/contenttypeeditor/editorsettings/editorsettings.html", + dataType: dataType, + show: true + }; + + vm.editorSettingsOverlay.submit = function (model) { + var preValues = dataTypeHelper.createPreValueProps(model.dataType.preValues); + dataTypeResource.save(model.dataType, preValues, false).then(function (newDataType) { + vm.selectedDataType.name = newDataType.name; + vm.editorSettingsOverlay.show = false; + vm.editorSettingsOverlay = null; + setDirty(); + }); + }; + + vm.editorSettingsOverlay.close = function (oldModel) { + vm.editorSettingsOverlay.show = false; + vm.editorSettingsOverlay = null; + }; + + }); + }; + + function remove() { + $scope.model.value = null; + vm.selectedDataType = null; + vm.allowAdd = true; + setDirty(); + }; + + function setDirty() { + if ($scope.propertyForm) { + $scope.propertyForm.$setDirty(); + } + }; + + }]); + +angular.module("umbraco.directives").directive("propertyListPropertyEditor", [ + function () { + + var link = function ($scope, $element, $attrs, $ctrl) { + + var unsubscribe = $scope.$on("propertyListFormSubmitting", function (ev, args) { + $scope.$broadcast("formSubmitting", { scope: $scope }); + }); + + $scope.$on("$destroy", function () { + unsubscribe(); + }); + }; + + return { + require: "^form", + restrict: "E", + rep1ace: true, + link: link, + template: '', + scope: { + control: "=model" + } + }; + } +]); + +angular.module("umbraco.resources").factory("Our.Umbraco.PropertyList.Resources.PropertyListResources", [ + "$http", + "umbRequestHelper", + function ($http, umbRequestHelper) { + return { + getDataTypeByKey: function (key) { + return umbRequestHelper.resourcePromise( + $http({ + url: "/umbraco/backoffice/PropertyList/PropertyListApi/GetDataTypeByKey", + method: "GET", + params: { key: key } + }), + "Failed to retrieve datatype by key" + ); + }, + getPropertyTypeScaffoldByKey: function (key) { + return umbRequestHelper.resourcePromise( + $http({ + url: "/umbraco/backoffice/PropertyList/PropertyListApi/GetPropertyTypeScaffoldByKey", + method: "GET", + params: { key: key } + }), + "Failed to retrieve property type scaffold by key" + ); + } + }; + } +]); diff --git a/src/Our.Umbraco.PropertyList/Web/UI/App_Plugins/PropertyList/js/propertylist.resources.js b/src/Our.Umbraco.PropertyList/Web/UI/App_Plugins/PropertyList/js/propertylist.resources.js deleted file mode 100644 index a26adfd..0000000 --- a/src/Our.Umbraco.PropertyList/Web/UI/App_Plugins/PropertyList/js/propertylist.resources.js +++ /dev/null @@ -1,25 +0,0 @@ -angular.module("umbraco.resources").factory("Our.Umbraco.PropertyList.Resources.PropertyListResources", - function ($q, $http, umbRequestHelper) { - return { - getDataTypeByKey: function (key) { - return umbRequestHelper.resourcePromise( - $http({ - url: "/umbraco/backoffice/PropertyList/PropertyListApi/GetDataTypeByKey", - method: "GET", - params: { key: key } - }), - "Failed to retrieve datatype by key" - ); - }, - getPropertyTypeScaffoldByKey: function (key) { - return umbRequestHelper.resourcePromise( - $http({ - url: "/umbraco/backoffice/PropertyList/PropertyListApi/GetPropertyTypeScaffoldByKey", - method: "GET", - params: { key: key } - }), - "Failed to retrieve property type scaffold by key" - ); - } - }; - }); \ No newline at end of file diff --git a/src/Our.Umbraco.PropertyList/Web/UI/App_Plugins/PropertyList/views/propertylist.datatypepicker.html b/src/Our.Umbraco.PropertyList/Web/UI/App_Plugins/PropertyList/views/propertylist.datatypepicker.html index 9e84d6a..02280ff 100644 --- a/src/Our.Umbraco.PropertyList/Web/UI/App_Plugins/PropertyList/views/propertylist.datatypepicker.html +++ b/src/Our.Umbraco.PropertyList/Web/UI/App_Plugins/PropertyList/views/propertylist.datatypepicker.html @@ -1,33 +1,21 @@ -
- -
- - - - - - - +
+ +
+ +
+ + Select editor + + + - +
-
- -

This data type has not been configured, click here to select a repeatable data type.

-
- -
- - -
- -
-
- -
- -
- -
+
-
- -
- -
- - - - - -
-
- - - -
-
- -
- -
+ +
+
-
- +
+ + +
-
\ No newline at end of file + Add item + + \ No newline at end of file