diff --git a/LICENSE.md b/LICENSE.md index 6271e30..6cef29c 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -1,3 +1,5 @@ +The MIT License (MIT) + Copyright © 2014 Umbrella Inc, Our Umbraco and other contributors Permission is hereby granted, free of charge, to any person obtaining a copy of diff --git a/README.md b/README.md index b2a3050..5cadb88 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Nested Content -[![Build status](https://img.shields.io/appveyor/ci/leekelleher/umbraco-nested-content.svg)](https://ci.appveyor.com/project/leekelleher/umbraco-nested-content) +[![Build status](https://img.shields.io/appveyor/ci/UMCO/umbraco-nested-content.svg)](https://ci.appveyor.com/project/UMCO/umbraco-nested-content) [![NuGet release](https://img.shields.io/nuget/v/Our.Umbraco.NestedContent.svg)](https://www.nuget.org/packages/Our.Umbraco.NestedContent) [![Our Umbraco project page](https://img.shields.io/badge/our-umbraco-orange.svg)](https://our.umbraco.org/projects/backoffice-extensions/nested-content) [![Chat on Gitter](https://img.shields.io/badge/gitter-join_chat-green.svg)](https://gitter.im/leekelleher/umbraco-nested-content) @@ -21,7 +21,7 @@ Nested Content can be installed from either Our Umbraco or NuGet package reposit To install from Our Umbraco, please download the package from: -> [https://our.umbraco.org/projects/backoffice-extensions/nested-content](https://our.umbraco.org/projects/backoffice-extensions/nested-content) +> #### NuGet package repository @@ -39,7 +39,7 @@ If you prefer, you can compile Nested Content yourself, you'll need: To clone it locally click the "Clone in Windows" button above or run the following git commands. - git clone https://github.com/leekelleher/umbraco-nested-content.git umbraco-nested-content + git clone https://github.com/umco/umbraco-nested-content.git umbraco-nested-content cd umbraco-nested-content .\build.cmd @@ -55,14 +55,16 @@ A PDF download is also available: [Nested Content - Developers Guide v1.0.pdf](d ## Known Issues -Please be aware that not all property-editors will work within Nested Content. The following property-editors are known to have compatibility issues: +Please be aware that not all property-editors will work within Nested Content. The following Umbraco core property-editors are known to have compatibility issues: -* Checkbox List (default Umbraco core) -* Image Cropper (default Umbraco core) -* Macro Container (default Umbraco core) -* Radiobutton List (default Umbraco core) -* Repeatable Textstring (default Umbraco core) - this works in the back-office, but due to a bug in the value-converter it will produce additional blank entries -* Upload (default Umbraco core) +* Checkbox List +* Dropdown List Multiple +* Image Cropper +* Macro Container +* Radiobutton List +* Repeatable Textstring - _this works in the back-office, but due to a bug in the value-converter it will produce additional blank entries_ +* Tags - _this appears to work, but by design it is intended to work once per page_ +* Upload --- @@ -80,7 +82,7 @@ Anyone and everyone is welcome to contribute. Please take a moment to review the Have a question? * [Nested Content Forum](https://our.umbraco.org/projects/backoffice-extensions/nested-content/nested-content-feedback) on Our Umbraco -* [Raise an issue](https://github.com/leekelleher/umbraco-nested-content/issues) on GitHub +* [Raise an issue](https://github.com/umco/umbraco-nested-content/issues) on GitHub ## Dev Team @@ -95,6 +97,6 @@ Have a question? ## License -Copyright © 2015 Umbrella Inc, Our Umbraco and [other contributors](https://github.com/leekelleher/umbraco-nested-content/graphs/contributors) +Copyright © 2015 Umbrella Inc, Our Umbraco and [other contributors](https://github.com/umco/umbraco-nested-content/graphs/contributors) Licensed under the [MIT License](LICENSE.md) diff --git a/appveyor.yml b/appveyor.yml index 6cfa984..57d07e6 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,5 +1,5 @@ # version format -version: 0.3.0.{build} +version: 0.4.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 @@ -17,22 +17,12 @@ artifacts: - path: artifacts\*.zip deploy: - # MyGet (Nested Content feed) Deployment for builds & releases - - provider: NuGet - server: https://www.myget.org/F/umbraco-nested-content/ - symbol_server: https://nuget.symbolsource.org/MyGet/umbraco-nested-content - api_key: - secure: Q1/4K8VSwr7BjwmKDTef8y5lOc7S+jK9ELuWy67y6OVRpjxmnF9M3Gfs1kT+ir8x - artifact: /.*\.nupkg/ - on: - branch: develop - - # MyGet (Umbraco Community feed) Deployment for builds & releases + # MyGet Deployment for builds & releases - provider: NuGet server: https://www.myget.org/F/umbraco-packages/ symbol_server: https://nuget.symbolsource.org/MyGet/umbraco-packages api_key: - secure: Q1/4K8VSwr7BjwmKDTef8y5lOc7S+jK9ELuWy67y6OVRpjxmnF9M3Gfs1kT+ir8x + secure: 36/Ax5O+e6wENlhoTwgvoEBZV3FG4XjF429SNTej2qsGTAL+cdfA1kT/tm1St8vx artifact: /.*\.nupkg/ on: branch: develop @@ -40,7 +30,7 @@ deploy: # GitHub Deployment for releases - provider: GitHub auth_token: - secure: pEozEGTqJutQwOidJU6BTB+Ix0NV4vrUnomhfeqheVz4RNwfxjEYLoqR4XabhlPz + secure: yDxrRTveSScJA35MQTOaLYVjoPKFKl2bHBkG+JMZjiN0r7AfuUCxVU3CgW8Imu4h artifact: /.*\.zip/ # upload all Zip packages to release assets draft: false prerelease: false @@ -52,7 +42,7 @@ deploy: - provider: NuGet server: api_key: - secure: CGzDKxw4QI/z2VSe9ceiYlIabqGXHolgBgVNWWZjVAJ2V5WLF11IFdlp9r5Qp+Sw + secure: eSLiOXbGVrxSG+X7PV6qTTUZ5VzS9EFj5+EufaWPfd+QXkF6gc8rZ4mGoHIVp/fL artifact: /.*\.nupkg/ on: branch: master diff --git a/build/package.proj b/build/package.proj index e30d6d4..c2de2cc 100644 --- a/build/package.proj +++ b/build/package.proj @@ -21,17 +21,17 @@ 7.1.4 Nested Content is a list editing property editor for Umbraco 7.1+ Matt Brailsford, Lee Kelleher - https://github.com/leekelleher/umbraco-nested-content/graphs/contributors + https://github.com/umco/umbraco-nested-content/graphs/contributors MIT license http://opensource.org/licenses/MIT - https://github.com/leekelleher/umbraco-nested-content + https://github.com/umco/umbraco-nested-content Our.Umbraco.NestedContent - Umbraco Nested Content - Copyright © 2015 Matt Brailsford, Lee Kelleher, Our Umbraco and other contributors + Nested Content for Umbraco + Copyright © 2015 Umbrella Inc, Our Umbraco and other contributors Matt Brailsford, Lee Kelleher https://our.umbraco.org/media/wiki/145710/635623752021571595_ncpng.png umbraco list editor diff --git a/docs/developers-guide.md b/docs/developers-guide.md index 9b4ca94..1b6d4f8 100644 --- a/docs/developers-guide.md +++ b/docs/developers-guide.md @@ -46,10 +46,10 @@ The prevalue editor allows you to configure the following properties. | Member | Type | Description | |-----------------|---------|-------------| -| Doc Types | List | Defines a list of doc types to use as data blue prints for this **Nested Content** instance. For each doc type you can provide the alias of the tab you wish to render (first tab is used by default if not set) as well as a template for generating list item labels using the syntax `{{propertyAlias}}`. | -| Min Items | Int | Sets the minimum number of items that should be allowed in the list. If greater than 0, **Nested Content** will pre-populate your list with the minimum amount of allowed items and prevent deleting items below this level. Defaults to 0. -| Max Itemd | Int | Sets the maximum number of items that should be allowed in the list. If greater than 0, **Nested Content** will prevent new items being added to the list above this threshold. Defaults to 0. | -| Confirm Deletes | Boolean | Enabling this will require item deletions to require a confirmation before being deleted. Defaults to TRUE | +| Doc Types | List | Defines a list of doc types to use as data blue prints for this **Nested Content** instance. For each doc type you can provide the alias of the tab you wish to render (first tab is used by default if not set) as well as a template for generating list item labels using the syntax `{{propertyAlias}}`. If you would like to include the index position in the label, you can use `{{$index}}`. | +| Min Items | Integer | Sets the minimum number of items that should be allowed in the list. If greater than `0`, **Nested Content** will pre-populate your list with the minimum amount of allowed items and prevent deleting items below this level. Defaults to `0`. +| Max Items | Integer | Sets the maximum number of items that should be allowed in the list. If greater than `0`, **Nested Content** will prevent new items being added to the list above this threshold. Defaults to `0`. | +| Confirm Deletes | Boolean | Enabling this will require item deletions to require a confirmation before being deleted. Defaults to `true`. | | Show Icons | Boolean | Enabling this will display the items doc type icon next to the name in the **Nested Content** list. | | Hide Label | Boolean | Enabling this will hide the property editors label and expand the **Nested Content** property editor to the full with of the editor window. | diff --git a/src/Our.Umbraco.NestedContent.sln b/src/Our.Umbraco.NestedContent.sln index b00a091..013aa2f 100644 --- a/src/Our.Umbraco.NestedContent.sln +++ b/src/Our.Umbraco.NestedContent.sln @@ -1,7 +1,6 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 2013 -VisualStudioVersion = 12.0.31101.0 +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 14 +VisualStudioVersion = 14.0.25420.1 MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nuget", ".nuget", "{CEE9961C-D747-40CD-B0B2-868D6B46833E}" ProjectSection(SolutionItems) = preProject diff --git a/src/Our.Umbraco.NestedContent/Converters/NestedContentValueConverter.cs b/src/Our.Umbraco.NestedContent/Converters/NestedContentValueConverter.cs index 853781c..da536da 100644 --- a/src/Our.Umbraco.NestedContent/Converters/NestedContentValueConverter.cs +++ b/src/Our.Umbraco.NestedContent/Converters/NestedContentValueConverter.cs @@ -21,7 +21,7 @@ public override object ConvertDataToSource(PublishedPropertyType propertyType, o { try { - return propertyType.ConvertPropertyToNestedContent(source); + return propertyType.ConvertPropertyToNestedContent(source, preview); } catch (Exception e) { diff --git a/src/Our.Umbraco.NestedContent/Converters/SingleNestedContentValueConverter.cs b/src/Our.Umbraco.NestedContent/Converters/SingleNestedContentValueConverter.cs index 7dd66ee..9da1bd8 100644 --- a/src/Our.Umbraco.NestedContent/Converters/SingleNestedContentValueConverter.cs +++ b/src/Our.Umbraco.NestedContent/Converters/SingleNestedContentValueConverter.cs @@ -20,7 +20,7 @@ public override object ConvertDataToSource(PublishedPropertyType propertyType, o { try { - return propertyType.ConvertPropertyToNestedContent(source); + return propertyType.ConvertPropertyToNestedContent(source, preview); } catch (Exception e) { diff --git a/src/Our.Umbraco.NestedContent/Extensions/PublishedPropertyTypeExtensions.cs b/src/Our.Umbraco.NestedContent/Extensions/PublishedPropertyTypeExtensions.cs index 88694dd..3315eb0 100644 --- a/src/Our.Umbraco.NestedContent/Extensions/PublishedPropertyTypeExtensions.cs +++ b/src/Our.Umbraco.NestedContent/Extensions/PublishedPropertyTypeExtensions.cs @@ -36,7 +36,7 @@ public static bool IsSingleNestedContentProperty(this PublishedPropertyType publ int.TryParse(preValueDictionary["maxItems"], out maxItems) && maxItems == 1; } - public static object ConvertPropertyToNestedContent(this PublishedPropertyType propertyType, object source) + public static object ConvertPropertyToNestedContent(this PublishedPropertyType propertyType, object source, bool preview) { using (DisposableTimer.DebugDuration(string.Format("ConvertPropertyToNestedContent ({0})", propertyType.DataTypeId))) { @@ -78,7 +78,7 @@ public static object ConvertPropertyToNestedContent(this PublishedPropertyType p var propType = publishedContentType.GetPropertyType(jProp.Key); if (propType != null) { - properties.Add(new DetachedPublishedProperty(propType, jProp.Value)); + properties.Add(new DetachedPublishedProperty(propType, jProp.Value, preview)); } } @@ -98,7 +98,8 @@ public static object ConvertPropertyToNestedContent(this PublishedPropertyType p publishedContentType, properties.ToArray(), containerNode, - i)); + i, + preview)); } if (propertyType.IsSingleNestedContentProperty()) diff --git a/src/Our.Umbraco.NestedContent/Our.Umbraco.NestedContent.csproj b/src/Our.Umbraco.NestedContent/Our.Umbraco.NestedContent.csproj index 1f75b5e..3006c60 100644 --- a/src/Our.Umbraco.NestedContent/Our.Umbraco.NestedContent.csproj +++ b/src/Our.Umbraco.NestedContent/Our.Umbraco.NestedContent.csproj @@ -243,6 +243,7 @@ + diff --git a/src/Our.Umbraco.NestedContent/PropertyEditors/NestedContentPropertyEditor.cs b/src/Our.Umbraco.NestedContent/PropertyEditors/NestedContentPropertyEditor.cs index e1d33fa..160a380 100644 --- a/src/Our.Umbraco.NestedContent/PropertyEditors/NestedContentPropertyEditor.cs +++ b/src/Our.Umbraco.NestedContent/PropertyEditors/NestedContentPropertyEditor.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.Linq; using System.Text.RegularExpressions; @@ -46,7 +47,7 @@ public NestedContentPropertyEditor() protected override PreValueEditor CreatePreValueEditor() { - return new NestedContentPreValueEditor(); + return new NestedContentPreValueEditor(); } internal class NestedContentPreValueEditor : PreValueEditor @@ -160,15 +161,26 @@ public override string ConvertDbToString(Property property, PropertyType propert } else { - // Create a fake property using the property abd stored value - var prop = new Property(propType, propValues[propKey] == null ? null : propValues[propKey].ToString()); + try + { + // Create a fake property using the property abd stored value + var prop = new Property(propType, propValues[propKey] == null ? null : propValues[propKey].ToString()); - // Lookup the property editor - var propEditor = PropertyEditorResolver.Current.GetByAlias(propType.PropertyEditorAlias); + // Lookup the property editor + var propEditor = PropertyEditorResolver.Current.GetByAlias(propType.PropertyEditorAlias); - // Get the editor to do it's conversion, and store it back - propValues[propKey] = propEditor.ValueEditor.ConvertDbToString(prop, propType, - ApplicationContext.Current.Services.DataTypeService); + // Get the editor to do it's conversion, and store it back + propValues[propKey] = propEditor.ValueEditor.ConvertDbToString(prop, propType, + ApplicationContext.Current.Services.DataTypeService); + } + catch (InvalidOperationException) + { + // https://github.com/umco/umbraco-nested-content/issues/111 + // Catch any invalid cast operations as likely means courier failed due to missing + // or trashed item so couldn't convert a guid back to an int + + propValues[propKey] = null; + } } } @@ -225,18 +237,29 @@ public override object ConvertDbToEditor(Property property, PropertyType propert } else { - // Create a fake property using the property abd stored value - var prop = new Property(propType, propValues[propKey] == null ? null : propValues[propKey].ToString()); + try + { + // Create a fake property using the property abd stored value + var prop = new Property(propType, propValues[propKey] == null ? null : propValues[propKey].ToString()); - // Lookup the property editor - var propEditor = PropertyEditorResolver.Current.GetByAlias(propType.PropertyEditorAlias); + // Lookup the property editor + var propEditor = PropertyEditorResolver.Current.GetByAlias(propType.PropertyEditorAlias); - // Get the editor to do it's conversion - var newValue = propEditor.ValueEditor.ConvertDbToEditor(prop, propType, - ApplicationContext.Current.Services.DataTypeService); + // Get the editor to do it's conversion + var newValue = propEditor.ValueEditor.ConvertDbToEditor(prop, propType, + ApplicationContext.Current.Services.DataTypeService); - // Store the value back - propValues[propKey] = (newValue == null) ? null : JToken.FromObject(newValue); + // Store the value back + propValues[propKey] = (newValue == null) ? null : JToken.FromObject(newValue); + } + catch (InvalidOperationException) + { + // https://github.com/umco/umbraco-nested-content/issues/111 + // Catch any invalid cast operations as likely means courier failed due to missing + // or trashed item so couldn't convert a guid back to an int + + propValues[propKey] = null; + } } } diff --git a/src/Our.Umbraco.NestedContent/Web/Controllers/NestedContentApiController.cs b/src/Our.Umbraco.NestedContent/Web/Controllers/NestedContentApiController.cs index dc7f109..3c234c6 100644 --- a/src/Our.Umbraco.NestedContent/Web/Controllers/NestedContentApiController.cs +++ b/src/Our.Umbraco.NestedContent/Web/Controllers/NestedContentApiController.cs @@ -1,10 +1,7 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using System.Linq; -using Our.Umbraco.NestedContent.Extensions; using Umbraco.Web.Editors; using Umbraco.Web.Mvc; -using System.Web.Http.ModelBinding; namespace Our.Umbraco.NestedContent.Web.Controllers { diff --git a/src/Our.Umbraco.NestedContent/Web/UI/App_Plugins/NestedContent/Js/nestedcontent.controllers.js b/src/Our.Umbraco.NestedContent/Web/UI/App_Plugins/NestedContent/Js/nestedcontent.controllers.js index c5dad75..05a07b1 100644 --- a/src/Our.Umbraco.NestedContent/Web/UI/App_Plugins/NestedContent/Js/nestedcontent.controllers.js +++ b/src/Our.Umbraco.NestedContent/Web/UI/App_Plugins/NestedContent/Js/nestedcontent.controllers.js @@ -5,19 +5,19 @@ function ($scope, ncResources) { - $scope.add = function() { + $scope.add = function () { $scope.model.value.push({ - // As per PR #4, all stored content type aliases must be prefixed "nc" for easier recognition. - // For good measure we'll also prefix the tab alias "nc" - ncAlias: "", - ncTabAlias: "", - nameTemplate: "" - } + // As per PR #4, all stored content type aliases must be prefixed "nc" for easier recognition. + // For good measure we'll also prefix the tab alias "nc" + ncAlias: "", + ncTabAlias: "", + nameTemplate: "" + } ); } - $scope.selectedDocTypeTabs = function(cfg) { - var dt = _.find($scope.model.docTypes, function(itm) { + $scope.selectedDocTypeTabs = function (cfg) { + var dt = _.find($scope.model.docTypes, function (itm) { return itm.alias.toLowerCase() == cfg.ncAlias.toLowerCase(); }); var tabs = dt ? dt.tabs : []; @@ -53,11 +53,12 @@ angular.module("umbraco").controller("Our.Umbraco.NestedContent.Controllers.Nest "$scope", "$interpolate", "$filter", + "$timeout", "contentResource", "localizationService", "Our.Umbraco.NestedContent.Resources.NestedContentResources", - function ($scope, $interpolate, $filter, contentResource, localizationService, ncResources) { + function ($scope, $interpolate, $filter, $timeout, contentResource, localizationService, ncResources) { //$scope.model.config.contentTypes; //$scope.model.config.minItems; @@ -152,17 +153,24 @@ angular.module("umbraco").controller("Our.Umbraco.NestedContent.Controllers.Nest return; } - // calculate overlay position - // - yeah... it's jQuery (ungh!) but that's how the Grid does it. - var offset = $(event.target).offset(); - var scrollTop = $(event.target).closest(".umb-panel-body").scrollTop(); - if (offset.top < 400) { - $scope.overlayMenu.style.top = 300 + scrollTop; - } - else { - $scope.overlayMenu.style.top = offset.top - 150 + scrollTop; - } + // Position off screen till we are visible and can calculate offset + $scope.overlayMenu.style.top = -1000; + $scope.overlayMenu.style.left = -1000; + $scope.overlayMenu.show = true; + + $timeout(function () { + + var wrapper = $("#contentwrapper"); + var el = $("#nested-content--" + $scope.model.id + " .nested-content__node-type-picker .cell-tools-menu"); + + var offset = el.offsetRelative("#contentwrapper"); + + $scope.overlayMenu.style.top = (Math.round(wrapper.height() / 2) + offset.top) - Math.round(el.height() / 2); + $scope.overlayMenu.style.left = (Math.round(wrapper.width() / 2) + offset.left) - Math.round(el.width() / 2); + + }); + }; $scope.closeNodeTypePicker = function () { @@ -200,10 +208,19 @@ angular.module("umbraco").controller("Our.Umbraco.NestedContent.Controllers.Nest var contentType = $scope.getContentTypeConfig($scope.model.value[idx].ncContentTypeAlias); if (contentType != null && contentType.nameExp) { - var newName = contentType.nameExp($scope.model.value[idx]); // Run it against the stored dictionary value, NOT the node object + // Run the expression against the stored dictionary value, NOT the node object + var item = $scope.model.value[idx]; + + // Add a temporary index property + item['$index'] = (idx + 1); + + var newName = contentType.nameExp(item); if (newName && (newName = $.trim(newName))) { name = newName; } + + // Delete the index property as we don't want to persist it + delete item['$index']; } } @@ -264,9 +281,9 @@ angular.module("umbraco").controller("Our.Umbraco.NestedContent.Controllers.Nest var scaffoldsLoaded = 0; $scope.scaffolds = []; _.each($scope.model.config.contentTypes, function (contentType) { - contentResource.getScaffold(-20, contentType.ncAlias).then(function(scaffold) { + contentResource.getScaffold(-20, contentType.ncAlias).then(function (scaffold) { // remove all tabs except the specified tab - var tab = _.find(scaffold.tabs, function(tab) { + var tab = _.find(scaffold.tabs, function (tab) { return tab.id != 0 && (tab.alias.toLowerCase() == contentType.ncTabAlias.toLowerCase() || contentType.ncTabAlias == ""); }); scaffold.tabs = []; @@ -279,19 +296,19 @@ angular.module("umbraco").controller("Our.Umbraco.NestedContent.Controllers.Nest scaffoldsLoaded++; initIfAllScaffoldsHaveLoaded(); - }, function(error) { + }, function (error) { scaffoldsLoaded++; initIfAllScaffoldsHaveLoaded(); }); }); - var initIfAllScaffoldsHaveLoaded = function() { + var initIfAllScaffoldsHaveLoaded = function () { // Initialize when all scaffolds have loaded if ($scope.model.config.contentTypes.length == scaffoldsLoaded) { // Because we're loading the scaffolds async one at a time, we need to // sort them explicitly according to the sort order defined by the data type. var contentTypeAliases = []; - _.each($scope.model.config.contentTypes, function(contentType) { + _.each($scope.model.config.contentTypes, function (contentType) { contentTypeAliases.push(contentType.ncAlias); }); $scope.scaffolds = $filter('orderBy')($scope.scaffolds, function (s) { @@ -319,7 +336,7 @@ angular.module("umbraco").controller("Our.Umbraco.NestedContent.Controllers.Nest } // If there is only one item, set it as current node - if ($scope.singleMode) { + if ($scope.singleMode || ($scope.nodes.length == 1 && $scope.maxItems == 1)) { $scope.currentNode = $scope.nodes[0]; } @@ -408,4 +425,48 @@ angular.module("umbraco").controller("Our.Umbraco.NestedContent.Controllers.Nest }; } -]); \ No newline at end of file +]); + +// offsetRelative (or, if you prefer, positionRelative) +(function ($) { + + $.fn.offsetRelative = function (ancestor) { + var positionedAncestor = $(ancestor); + var object = $(this); + + var relativeOffset = { left: 0, top: 0 }; + + var leftSpacing = parseInt(object.css("margin-left")); + leftSpacing += parseInt(object.css("border-left-width")); + + var topSpacing = parseInt(object.css("margin-top")); + topSpacing += parseInt(object.css("border-top-width")); + + relativeOffset.left -= leftSpacing; + relativeOffset.top -= topSpacing; + + var offsetParent = object.offsetParent(); + + while (offsetParent[0] !== positionedAncestor[0] && !offsetParent.is('html')) { + var offsetParentPosition = offsetParent.position(); + + var offsetParentPositionLeft = offsetParentPosition.left; + var offsetParentPositionTop = offsetParentPosition.top; + + relativeOffset.top -= offsetParentPositionTop; + relativeOffset.left -= offsetParentPositionLeft; + + leftSpacing = parseInt(offsetParent.css("margin-left")); + leftSpacing += parseInt(offsetParent.css("border-left-width")); + topSpacing = parseInt(offsetParent.css("margin-top")); + topSpacing += parseInt(offsetParent.css("border-top-width")); + + relativeOffset.left -= leftSpacing; + relativeOffset.top -= topSpacing; + + offsetParent = offsetParent.offsetParent(); + } + return relativeOffset; + }; + +}(jQuery)); \ No newline at end of file diff --git a/src/Our.Umbraco.NestedContent/Web/UI/App_Plugins/NestedContent/Js/nestedcontent.directives.js b/src/Our.Umbraco.NestedContent/Web/UI/App_Plugins/NestedContent/Js/nestedcontent.directives.js index e70d238..01e3794 100644 --- a/src/Our.Umbraco.NestedContent/Web/UI/App_Plugins/NestedContent/Js/nestedcontent.directives.js +++ b/src/Our.Umbraco.NestedContent/Web/UI/App_Plugins/NestedContent/Js/nestedcontent.directives.js @@ -2,25 +2,53 @@ function () { - var link = function ($scope, element, attrs, ctrl) { - $scope.nodeContext = $scope.model = $scope.ngModel; + var link = function ($scope) { - var tab = $scope.ngModel.tabs[0]; + // Clone the model because some property editors + // do weird things like updating and config values + // so we want to ensure we start from a fresh every + // time, we'll just sync the value back when we need to + $scope.model = angular.copy($scope.ngModel); + $scope.nodeContext = $scope.model; + + // Find the selected tab + var selectedTab = $scope.model.tabs[0]; if ($scope.tabAlias) { - angular.forEach($scope.ngModel.tabs, function (value, key) { - if (value.alias.toLowerCase() == $scope.tabAlias.toLowerCase()) { - tab = value; + angular.forEach($scope.model.tabs, function (tab) { + if (tab.alias.toLowerCase() == $scope.tabAlias.toLowerCase()) { + selectedTab = tab; return; } }); } - $scope.tab = tab; + $scope.tab = selectedTab; + // Listen for sync request var unsubscribe = $scope.$on("ncSyncVal", function (ev, args) { if (args.id === $scope.model.id) { + + // Tell inner controls we are submitting $scope.$broadcast("formSubmitting", { scope: $scope }); + + // Sync the values back + angular.forEach($scope.ngModel.tabs, function (tab) { + if (tab.alias.toLowerCase() == selectedTab.alias.toLowerCase()) { + + var localPropsMap = selectedTab.properties.reduce(function (map, obj) { + map[obj.alias] = obj; + return map; + }, {}); + + angular.forEach(tab.properties, function (prop) { + if (localPropsMap.hasOwnProperty(prop.alias)) { + prop.value = localPropsMap[prop.alias].value; + } + }); + + } + }); } }); @@ -32,7 +60,7 @@ return { restrict: "E", replace: true, - templateUrl: "/App_Plugins/NestedContent/Views/nestedcontent.editor.html", + templateUrl: Umbraco.Sys.ServerVariables.umbracoSettings.appPluginsPath + "/NestedContent/Views/nestedcontent.editor.html", scope: { ngModel: '=', tabAlias: '=' @@ -54,7 +82,7 @@ // } // }); // } - + // return { // restrict: "E", // replace: true, diff --git a/src/Our.Umbraco.NestedContent/Web/UI/App_Plugins/NestedContent/Js/nestedcontent.filters.js b/src/Our.Umbraco.NestedContent/Web/UI/App_Plugins/NestedContent/Js/nestedcontent.filters.js new file mode 100644 index 0000000..cecd1fa --- /dev/null +++ b/src/Our.Umbraco.NestedContent/Web/UI/App_Plugins/NestedContent/Js/nestedcontent.filters.js @@ -0,0 +1,46 @@ +// Filter to take a node id and grab it's name instead +// Usage: {{ pickerAlias | ncNodeName }} + +// Cache for node names so we don't make a ton of requests +var ncNodeNameCache = { + id: "", + keys: {} +} + +angular.module("umbraco.filters").filter("ncNodeName", function (editorState, entityResource) { + + return function (input) { + + // Check we have a value at all + if (input == "" || input.toString() == "0") + return ""; + + var currentNode = editorState.getCurrent(); + + // Ensure a unique cache per editor instance + var key = "ncNodeName_" + currentNode.key; + if (ncNodeNameCache.id != key) { + ncNodeNameCache.id = key; + ncNodeNameCache.keys = {}; + } + + // See if there is a value in the cache and use that + if (ncNodeNameCache.keys[input]) { + return ncNodeNameCache.keys[input]; + } + + // No value, so go fetch one + // We'll put a temp value in the cache though so we don't + // make a load of requests while we wait for a response + ncNodeNameCache.keys[input] = "Loading..."; + + entityResource.getById(input, "Document") + .then(function (ent) { + ncNodeNameCache.keys[input] = ent.name; + }); + + // Return the current value for now + return ncNodeNameCache.keys[input]; + } + +}); \ No newline at end of file diff --git a/src/Our.Umbraco.NestedContent/Web/UI/App_Plugins/NestedContent/Js/nestedcontent.resources.js b/src/Our.Umbraco.NestedContent/Web/UI/App_Plugins/NestedContent/Js/nestedcontent.resources.js index 192a501..d9a4edb 100644 --- a/src/Our.Umbraco.NestedContent/Web/UI/App_Plugins/NestedContent/Js/nestedcontent.resources.js +++ b/src/Our.Umbraco.NestedContent/Web/UI/App_Plugins/NestedContent/Js/nestedcontent.resources.js @@ -2,7 +2,7 @@ function ($q, $http, umbRequestHelper) { return { getContentTypes: function () { - var url = "/umbraco/backoffice/NestedContent/NestedContentApi/GetContentTypes"; + var url = Umbraco.Sys.ServerVariables.umbracoSettings.umbracoPath + "/backoffice/NestedContent/NestedContentApi/GetContentTypes"; return umbRequestHelper.resourcePromise( $http.get(url), 'Failed to retrieve content types' diff --git a/src/Our.Umbraco.NestedContent/Web/UI/App_Plugins/NestedContent/Views/nestedcontent.doctypepicker.html b/src/Our.Umbraco.NestedContent/Web/UI/App_Plugins/NestedContent/Views/nestedcontent.doctypepicker.html index 5a42748..3572a5e 100644 --- a/src/Our.Umbraco.NestedContent/Web/UI/App_Plugins/NestedContent/Views/nestedcontent.doctypepicker.html +++ b/src/Our.Umbraco.NestedContent/Web/UI/App_Plugins/NestedContent/Views/nestedcontent.doctypepicker.html @@ -52,7 +52,7 @@

Name template:
- Enter an angular expression to evaluate against each item for its name. + Enter an angular expression to evaluate against each item for its name. Use {{$index}} to display the item index

- \ No newline at end of file + diff --git a/src/Our.Umbraco.NestedContent/Web/UI/App_Plugins/NestedContent/Views/nestedcontent.html b/src/Our.Umbraco.NestedContent/Web/UI/App_Plugins/NestedContent/Views/nestedcontent.html index 0943780..0a98742 100644 --- a/src/Our.Umbraco.NestedContent/Web/UI/App_Plugins/NestedContent/Views/nestedcontent.html +++ b/src/Our.Umbraco.NestedContent/Web/UI/App_Plugins/NestedContent/Views/nestedcontent.html @@ -12,10 +12,10 @@
- +
@@ -42,8 +42,8 @@
-
-
+
+
diff --git a/src/Our.Umbraco.NestedContent/Web/UI/App_Plugins/NestedContent/package.manifest b/src/Our.Umbraco.NestedContent/Web/UI/App_Plugins/NestedContent/package.manifest index 9e967b3..62fa763 100644 --- a/src/Our.Umbraco.NestedContent/Web/UI/App_Plugins/NestedContent/package.manifest +++ b/src/Our.Umbraco.NestedContent/Web/UI/App_Plugins/NestedContent/package.manifest @@ -1,10 +1,11 @@ { "javascript" : [ + '~/App_Plugins/NestedContent/Js/nestedcontent.filters.js', '~/App_Plugins/NestedContent/Js/nestedcontent.resources.js', '~/App_Plugins/NestedContent/Js/nestedcontent.directives.js', '~/App_Plugins/NestedContent/Js/nestedcontent.controllers.js' ], - "css" : [ - "~/App_Plugins/NestedContent/Css/nestedcontent.css" - ] + "css" : [ + "~/App_Plugins/NestedContent/Css/nestedcontent.css" + ] } \ No newline at end of file