diff --git a/.gitignore b/.gitignore index b91f0cc..c9b8ca7 100644 --- a/.gitignore +++ b/.gitignore @@ -1,43 +1,17 @@ -######################################### -# # -# TheOutfiled's Ignore file # - -##### File globs to match #### -syntax: glob -# Resharper _ReSharper.* -# Visual Studio Stuff -*.obj -*.pdb -*.user -*.aps -*.pch -*.vspscc -*.vssscc -*_i.c -*_p.c -*.ncb +[Bb]in/ +[Oo]bj/ *.suo -*.tlb -*.tlh -*.bak +*.user +*.userprefs *.cache -*.ilk -*.log -*.lib -*.sbr -*.scc -*.csproj.user -obj/ -bin/ -[Dd]ebug*/ -[Rr]elease*/ -*.[Pp]ublish.xml -# General *.orig -*/Thumbs.db -*/log.txt -/Assets/ -/Build/ -*.DS_Store -[Pp]ackages/*/** +Thumbs.db +.DS_Store +*.log + +artifacts/ +src/packages/*/** +build/App_plugins/ +build/bin/ +assets/ \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..2bfb43c --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,142 @@ +# Contributing to this project + +Please take a moment to review this document in order to make the contribution +process easy and effective for everyone involved. + +Following these guidelines helps to communicate that you respect the time of +the developers managing and developing this open source project. In return, +they should reciprocate that respect in addressing your issue or assessing +patches and features. + + +## Using the issue tracker + +The issue tracker is the preferred channel for [bug reports](#bugs), +[features requests](#features) and [submitting pull +requests](#pull-requests), but please respect the following restrictions: + +* Please **do not** use the issue tracker for personal support requests (use + [Our Umbraco](https://our.umbraco.org/projects/backoffice-extensions/nested-content/nested-content-feedback) or Twitter). + +* Please **do not** derail or troll issues. Keep the discussion on topic and + respect the opinions of others. + + + +## Bug reports + +A bug is a _demonstrable problem_ that is caused by the code in the repository. +Good bug reports are extremely helpful - thank you! + +Guidelines for bug reports: + +1. **Use the GitHub issue search** — check if the issue has already been + reported. + +2. **Check if the issue has been fixed** — try to reproduce it using the + latest `master` or development branch in the repository. + +3. **Isolate the problem** — create a [reduced test + case](http://css-tricks.com/6263-reduced-test-cases/) and a live example. + +A good bug report shouldn't leave others needing to chase you up for more +information. Please try to be as detailed as possible in your report. What is +your environment? What steps will reproduce the issue? What browser(s) and OS +experience the problem? What would you expect to be the outcome? All these +details will help people to fix any potential bugs. + +Example: + +> Short and descriptive example bug report title +> +> A summary of the issue and the browser/OS environment in which it occurs. If +> suitable, include the steps required to reproduce the bug. +> +> 1. This is the first step +> 2. This is the second step +> 3. Further steps, etc. +> +> `` - a link to the reduced test case +> +> Any other information you want to share that is relevant to the issue being +> reported. This might include the lines of code that you have identified as +> causing the bug, and potential solutions (and your opinions on their +> merits). + + + +## Feature requests + +Feature requests are welcome. But take a moment to find out whether your idea +fits with the scope and aims of the project. It's up to *you* to make a strong +case to convince the project's developers of the merits of this feature. Please +provide as much detail and context as possible. + + + +## Pull requests + +Good pull requests - patches, improvements, new features - are a fantastic +help. They should remain focused in scope and avoid containing unrelated +commits. + +**Please ask first** before embarking on any significant pull request (e.g. +implementing features, refactoring code, porting to a different language), +otherwise you risk spending a lot of time working on something that the +project's developers might not want to merge into the project. + +Please adhere to the coding conventions used throughout a project (indentation, +accurate comments, etc.) and any other requirements (such as test coverage). + +Follow this process if you'd like your work considered for inclusion in the +project: + +1. [Fork](http://help.github.com/fork-a-repo/) the project, clone your fork, + and configure the remotes: + + ```bash + # Clone your fork of the repo into the current directory + git clone https://github.com// + # Navigate to the newly cloned directory + cd + # Assign the original repo to a remote called "upstream" + git remote add upstream https://github.com// + ``` + +2. If you cloned a while ago, get the latest changes from upstream: + + ```bash + git checkout develop + git pull upstream develop + ``` + +3. Create a new topic branch (off the main project `develop` branch) to + contain your feature, change, or fix: + + ```bash + git checkout -b + ``` + +4. Commit your changes in logical chunks. Please adhere to these [git commit + message guidelines](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html) + or your code is unlikely be merged into the main project. Use Git's + [interactive rebase](https://help.github.com/articles/interactive-rebase) + feature to tidy up your commits before making them public. + +5. Locally merge (or rebase) the upstream development branch into your topic branch: + + ```bash + git pull [--rebase] upstream develop + ``` + +6. Push your topic branch up to your fork: + + ```bash + git push origin + ``` + +7. [Open a Pull Request](https://help.github.com/articles/using-pull-requests/) + with a clear title and description. + +**IMPORTANT**: By submitting a patch, you agree to allow the project owner to +license your work under the same license as that used by the project. \ No newline at end of file diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..6271e30 --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,19 @@ +Copyright © 2014 Umbrella Inc, Our Umbraco and other contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/MIT-LICENSE.txt b/MIT-LICENSE.txt deleted file mode 100644 index 3b853b4..0000000 --- a/MIT-LICENSE.txt +++ /dev/null @@ -1,25 +0,0 @@ -Copyright (c) 2011 Umbraco Community - -This software consists of voluntary contributions made by Umbraco -Community (https://bitbucket.org/mattbrailsford/ugolive) For exact -contribution history, see the revision history and logs, available -at https://bitbucket.org/mattbrailsford/ugolive/changesets - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/Package.build.cmd b/Package.build.cmd deleted file mode 100644 index 72e7747..0000000 --- a/Package.build.cmd +++ /dev/null @@ -1,2 +0,0 @@ -C:\WINDOWS\Microsoft.NET\Framework\v4.0.30319\msbuild.exe Package.build.xml -pause \ No newline at end of file diff --git a/Package.build.xml b/Package.build.xml deleted file mode 100644 index 53d2a80..0000000 --- a/Package.build.xml +++ /dev/null @@ -1,102 +0,0 @@ - - - - - - $(MSBuildProjectDirectory)\Tools\MSBuildCommunityTasks - $(MSBuildProjectDirectory)\Tools\MSBuildUmbracoTasks - - - - - - - - 0.1.1 - Our.Umbraco.NestedContent - Debug - - - - $(MSBuildProjectDirectory) - $(RootDir)\Build - $(RootDir)\Package - $(RootDir)\Src\$(ProjectName) - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Package.xml b/Package.xml deleted file mode 100644 index 23eb591..0000000 --- a/Package.xml +++ /dev/null @@ -1,34 +0,0 @@ - - - - - Nested Content - 0.0 - MIT license - http://our.umbraco.org/projects/backoffice-extensions/nested-content - - 7 - 1 - 4 - - - - Matt Brailsford, Lee Kelleher - http://our.umbraco.org - - - - - - - - - - - - - - - - - diff --git a/Package/Nested_Content_0.1.1.zip b/Package/Nested_Content_0.1.1.zip deleted file mode 100644 index 15525ae..0000000 Binary files a/Package/Nested_Content_0.1.1.zip and /dev/null differ diff --git a/Packages/repositories.config b/Packages/repositories.config deleted file mode 100644 index e855250..0000000 --- a/Packages/repositories.config +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file diff --git a/README.markdown b/README.markdown deleted file mode 100644 index f15f901..0000000 --- a/README.markdown +++ /dev/null @@ -1,3 +0,0 @@ -# Nested Content - -A nested content property editor for Umbraco 7 that allows you to use Doc Types as a blue print for list items diff --git a/README.md b/README.md new file mode 100644 index 0000000..75b81b5 --- /dev/null +++ b/README.md @@ -0,0 +1,96 @@ +# Nested Content + +[![Build status](https://img.shields.io/appveyor/ci/leekelleher/umbraco-nested-content.svg)](https://ci.appveyor.com/project/leekelleher/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) + + +A nested content property editor for Umbraco 7 that allows you to use Doc Types as a blue print for list items. + + +## Getting Started + +### Installation + +> *Note:* Nested Content has been developed against **Umbraco v7.1.4** and will support that version and above. + +Nested Content can be installed from either Our Umbraco or NuGet package repositories, or build manually from the source-code: + +#### Our Umbraco package repository + +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 + +To [install from NuGet](https://www.nuget.org/packages/Our.Umbraco.NestedContent), you can run the following command from within Visual Studio: + + PM> Install-Package Our.Umbraco.NestedContent + +We also have a [MyGet package repository](https://www.myget.org/gallery/umbraco-nested-content) - for bleeding-edge / development releases. + +#### Manual build + +If you prefer, you can compile Nested Content yourself, you'll need: + +* Visual Studio 2012 (or above) + +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 + cd umbraco-nested-content + .\build.cmd + +--- + +## Developers Guide + +For details on how to use the Nested Content package, please refer to our [Developers Guide](docs/developers-guide.md) documentation. + +A PDF download is also available: [Nested Content - Developers Guide v1.0.pdf](docs/assets/pdf/Nested-Content--Developers-Guide-v1.0.pdf) + +--- + +## 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: + +* Upload (default Umbraco core) +* Macro Container (default Umbraco core) + +--- + +## Contributing to this project + +Anyone and everyone is welcome to contribute. Please take a moment to review the [guidelines for contributing](CONTRIBUTING.md). + +* [Bug reports](CONTRIBUTING.md#bugs) +* [Feature requests](CONTRIBUTING.md#features) +* [Pull requests](CONTRIBUTING.md#pull-requests) + + +## Contact + +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 + + +## Dev Team + +* [Matt Brailsford](https://github.com/mattbrailsford) +* [Lee Kelleher](https://github.com/leekelleher) + +### Special thanks + +* Thanks to [Jeavon Leopold](https://github.com/Jeavon) for being a rockstar and adding AppVeyor support. + + +## License + +Copyright © 2015 Umbrella Inc, Our Umbraco and [other contributors](https://github.com/leekelleher/umbraco-nested-content/graphs/contributors) + +Licensed under the [MIT License](LICENSE.md) diff --git a/Src/Our.Umbraco.NestedContent/Extensions/ContentTypeServiceExtensions.cs b/Src/Our.Umbraco.NestedContent/Extensions/ContentTypeServiceExtensions.cs deleted file mode 100644 index 7fe9e59..0000000 --- a/Src/Our.Umbraco.NestedContent/Extensions/ContentTypeServiceExtensions.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System; -using Umbraco.Core; -using Umbraco.Core.Services; - -namespace Our.Umbraco.NestedContent.Extensions -{ - internal static class ContentTypeServiceExtensions - { - public static string GetAliasByGuid(this IContentTypeService contentTypeService, Guid id) - { - return (string)ApplicationContext.Current.ApplicationCache.RuntimeCache.GetCacheItem( - string.Concat("Our.Umbraco.NestedContent.GetContentTypeAliasByGuid_", id), - () => ApplicationContext.Current.DatabaseContext.Database - .ExecuteScalar("SELECT [cmsContentType].[alias] FROM [cmsContentType] INNER JOIN [umbracoNode] ON [cmsContentType].[nodeId] = [umbracoNode].[id] WHERE [umbracoNode].[uniqueID] = @0", id)); - } - } -} diff --git a/Src/Our.Umbraco.NestedContent/Helpers/NestedContentHelper.cs b/Src/Our.Umbraco.NestedContent/Helpers/NestedContentHelper.cs deleted file mode 100644 index c36bdbf..0000000 --- a/Src/Our.Umbraco.NestedContent/Helpers/NestedContentHelper.cs +++ /dev/null @@ -1,51 +0,0 @@ -using System; -using System.Collections; -using System.Linq; -using Our.Umbraco.NestedContent.Extensions; -using Umbraco.Core; -using Umbraco.Core.Models; -using System.Collections.Generic; - -namespace Our.Umbraco.NestedContent.Helpers -{ - internal static class NestedContentHelper - { - public static PreValueCollection GetPreValuesCollectionByDataTypeId(int dtdId) - { - var preValueCollection = (PreValueCollection)ApplicationContext.Current.ApplicationCache.RuntimeCache.GetCacheItem( - string.Concat("Our.Umbraco.NestedContent.GetPreValuesCollectionByDataTypeId_", dtdId), - () => ApplicationContext.Current.Services.DataTypeService.GetPreValuesCollectionByDataTypeId(dtdId)); - - return preValueCollection; - } - - public static IDictionary GetPreValuesDictionaryByDataTypeId(int dtdId) - { - return GetPreValuesCollectionByDataTypeId(dtdId).AsPreValueDictionary(); - } - - public static IContentType GetContentTypeFromPreValue(int dtdId) - { - var preValueCollection = GetPreValuesCollectionByDataTypeId(dtdId); - - return GetContentTypeFromPreValue(preValueCollection); - } - - public static IContentType GetContentTypeFromPreValue(PreValueCollection preValues) - { - var preValuesDict = preValues.AsPreValueDictionary(); - - Guid contentTypeGuid; - if (!preValuesDict.ContainsKey("docTypeGuid") || !Guid.TryParse(preValuesDict["docTypeGuid"], out contentTypeGuid)) - return null; - - var contentTypeAlias = ApplicationContext.Current.Services.ContentTypeService.GetAliasByGuid(Guid.Parse(preValuesDict["docTypeGuid"])); - var contentType = ApplicationContext.Current.Services.ContentTypeService.GetContentType(contentTypeAlias); - - if (contentType == null || contentType.PropertyTypes == null) - return null; - - return contentType; - } - } -} diff --git a/Src/Our.Umbraco.NestedContent/Properties/AssemblyInfo.cs b/Src/Our.Umbraco.NestedContent/Properties/AssemblyInfo.cs deleted file mode 100644 index b5913af..0000000 --- a/Src/Our.Umbraco.NestedContent/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("Our.Umbraco.NestedContent")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("The Umbraco Community")] -[assembly: AssemblyProduct("Our.Umbraco.NestedContent")] -[assembly: AssemblyCopyright("Copyright ? The Umbraco Community 2015")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("8b4fa1d2-118f-497f-b7fb-636f366e245e")] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the values or you can default the Build and Revision Numbers -// by using the '*' as shown below: -// [assembly: AssemblyVersion("0.1.1")] -[assembly: AssemblyVersion("0.1.1")] -[assembly: AssemblyFileVersion("0.1.1")] 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 deleted file mode 100644 index c70ebcb..0000000 --- a/Src/Our.Umbraco.NestedContent/Web/UI/App_Plugins/NestedContent/Js/nestedcontent.controllers.js +++ /dev/null @@ -1,215 +0,0 @@ -angular.module("umbraco").controller("Our.Umbraco.NestedContent.Controllers.DocTypePickerController", [ - - "$scope", - "Our.Umbraco.NestedContent.Resources.NestedContentResources", - - function ($scope, ncResources) { - ncResources.getContentTypes().then(function (docTypes) { - $scope.model.docTypes = docTypes; - }); - } - -]); - -angular.module("umbraco").controller("Our.Umbraco.NestedContent.Controllers.NestedContentPropertyEditorController", [ - - "$scope", - "$interpolate", - "contentResource", - "Our.Umbraco.NestedContent.Resources.NestedContentResources", - - function ($scope, $interpolate, contentResource, ncResources) { - - //$scope.model.config.docTypeGuid; - //$scope.model.config.tabAlias; - //$scope.model.config.nameTemplate; - //$scope.model.config.minItems; - //$scope.model.config.maxItems; - //console.log($scope); - - var inited = false; - var nameExp = !!$scope.model.config.nameTemplate - ? $interpolate($scope.model.config.nameTemplate) - : undefined; - - $scope.nodes = []; - $scope.currentNode = undefined; - $scope.scaffold = undefined; - $scope.sorting = false; - - $scope.tabAlias = $scope.model.config.tabAlias; - $scope.minItems = $scope.model.config.minItems || 0; - $scope.maxItems = $scope.model.config.maxItems || 0; - - if ($scope.maxItems == 0) - $scope.maxItems = 1000; - - $scope.singleMode = $scope.minItems == 1 && $scope.maxItems == 1; - - $scope.addNode = function () { - if ($scope.nodes.length < $scope.maxItems) { - var newNode = angular.copy($scope.scaffold); - newNode.id = guid(); - - $scope.nodes.push(newNode); - $scope.currentNode = newNode; - } - }; - - $scope.editNode = function (idx) { - if ($scope.currentNode && $scope.currentNode.id == $scope.nodes[idx].id) { - $scope.currentNode = undefined; - } else { - $scope.currentNode = $scope.nodes[idx]; - } - }; - - $scope.deleteNode = function (idx) { - if ($scope.nodes.length > $scope.model.config.minItems) { - if ($scope.model.config.confirmDeletes && $scope.model.config.confirmDeletes == 1) { - if (confirm("Are you sure you want to delete this item?")) { - $scope.nodes.splice(idx, 1); - } - } else { - $scope.nodes.splice(idx, 1); - } - } - }; - - $scope.getName = function (idx) { - - var name = "Item " + (idx + 1); - - if (nameExp) - { - var newName = nameExp($scope.model.value[idx]); // Run it against the stored dictionary value, NOT the node object - if (newName && (newName = $.trim(newName))) { - name = newName; - } - } - - // Update the nodes actual name value - if ($scope.nodes[idx].name != newName) { - $scope.nodes[idx].name = name; - } - - return name; - }; - - $scope.sortableOptions = { - axis: 'y', - cursor: "move", - handle: ".nested-content__icon--move", - start: function (ev, ui) { - // Yea, yea, we shouldn't modify the dom, sue me - $("#nested-content--" + $scope.model.id + " .umb-rte textarea").each(function () { - tinymce.execCommand('mceRemoveEditor', false, $(this).attr('id')); - $(this).css("visibility", "hidden"); - }); - $scope.$apply(function () { - $scope.sorting = true; - }); - }, - stop: function (ev, ui) { - $("#nested-content--" + $scope.model.id + " .umb-rte textarea").each(function () { - tinymce.execCommand('mceAddEditor', true, $(this).attr('id')); - $(this).css("visibility", "visible"); - }); - $scope.$apply(function () { - $scope.sorting = false; - }); - } - }; - - // Initialize - ncResources.getContentTypeAliasByGuid($scope.model.config.docTypeGuid).then(function (data1) { - contentResource.getScaffold(-20, data1.alias).then(function (data2) { - - // Ignore the generic properties tab - data2.tabs.pop(); - - // Store the scaffold object - $scope.scaffold = data2; - - // Convert stored nodes - if ($scope.model.value) { - for (var i = 0; i < $scope.model.value.length; i++) { - var item = $scope.model.value[i]; - var node = angular.copy($scope.scaffold); - node.id = guid(); - - for (var t = 0; t < node.tabs.length; t++) { - var tab = node.tabs[t]; - for (var p = 0; p < tab.properties.length; p++) { - var prop = tab.properties[p]; - - // Force validation to occur server side as this is the - // only way we can have consistancy between mandatory and - // regex validation messages. Not ideal, but it works. - prop.validation = { - mandatory: false, - pattern: "" - }; - - if (item[prop.alias]) { - prop.value = item[prop.alias]; - } - } - } - - $scope.nodes.push(node); - } - } - - // Enforce min items - if ($scope.nodes.length < $scope.model.config.minItems) { - for (var i = $scope.nodes.length; i < $scope.model.config.minItems; i++) { - var node = angular.copy($scope.scaffold); - node.id = guid(); - $scope.nodes.push(node); - } - } - - // If there is only one item, set it as current node - if ($scope.singleMode) { - $scope.currentNode = $scope.nodes[0]; - } - - inited = true; - - }); - }); - - $scope.$watch("nodes", function () { - if (inited) { - var newValues = []; - for (var i = 0; i < $scope.nodes.length; i++) { - var node = $scope.nodes[i]; - var newValue = { - name: node.name - }; - for (var t = 0; t < node.tabs.length; t++) { - var tab = node.tabs[t]; - for (var p = 0; p < tab.properties.length; p++) { - var prop = tab.properties[p]; - if (typeof prop.value !== "function") { - newValue[prop.alias] = prop.value; - } - } - } - newValues.push(newValue); - } - $scope.model.value = newValues; - } - }, true); - - var guid = function () { - function _p8(s) { - var p = (Math.random().toString(16) + "000000000").substr(2, 8); - return s ? "-" + p.substr(0, 4) + "-" + p.substr(4, 4) : p; - } - return _p8() + _p8(true) + _p8(true) + _p8(); - }; - } - -]); \ No newline at end of file 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 deleted file mode 100644 index 1d1f26f..0000000 --- a/Src/Our.Umbraco.NestedContent/Web/UI/App_Plugins/NestedContent/Views/nestedcontent.doctypepicker.html +++ /dev/null @@ -1,6 +0,0 @@ -
- -
\ No newline at end of file diff --git a/Tools/MSBuildUmbracoTasks/MSBuild.Umbraco.Tasks.dll b/Tools/MSBuildUmbracoTasks/MSBuild.Umbraco.Tasks.dll deleted file mode 100644 index b5241bd..0000000 Binary files a/Tools/MSBuildUmbracoTasks/MSBuild.Umbraco.Tasks.dll and /dev/null differ diff --git a/appveyor.yml b/appveyor.yml new file mode 100644 index 0000000..601a746 --- /dev/null +++ b/appveyor.yml @@ -0,0 +1,46 @@ +# version format +version: 0.2.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= + +build_script: +- build-appveyor.cmd + +artifacts: + - path: artifacts\*.nupkg + - path: artifacts\*.zip + +deploy: + # MyGet 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 + + # GitHub Deployment for releases + - provider: GitHub + auth_token: + secure: pEozEGTqJutQwOidJU6BTB+Ix0NV4vrUnomhfeqheVz4RNwfxjEYLoqR4XabhlPz + artifact: /.*\.zip/ # upload all Zip packages to release assets + draft: false + prerelease: false + on: + branch: master + appveyor_repo_tag: true # deploy on tag push only + + # NuGet Deployment for releases + - provider: NuGet + server: + api_key: + secure: CGzDKxw4QI/z2VSe9ceiYlIabqGXHolgBgVNWWZjVAJ2V5WLF11IFdlp9r5Qp+Sw + artifact: /.*\.nupkg/ + on: + branch: master + appveyor_repo_tag: true diff --git a/build-appveyor.cmd b/build-appveyor.cmd new file mode 100644 index 0000000..34ea589 --- /dev/null +++ b/build-appveyor.cmd @@ -0,0 +1,7 @@ +ECHO APPVEYOR_REPO_BRANCH: %APPVEYOR_REPO_BRANCH% +ECHO APPVEYOR_REPO_TAG: %APPVEYOR_REPO_TAG% +ECHO APPVEYOR_BUILD_NUMBER : %APPVEYOR_BUILD_NUMBER% +ECHO APPVEYOR_BUILD_VERSION : %APPVEYOR_BUILD_VERSION% + +CALL src\.nuget\NuGet.exe restore src\Our.Umbraco.NestedContent.sln +CALL "%WINDIR%\Microsoft.NET\Framework\v4.0.30319\msbuild.exe" build\package.proj \ No newline at end of file diff --git a/build.cmd b/build.cmd new file mode 100644 index 0000000..7e1a2d8 --- /dev/null +++ b/build.cmd @@ -0,0 +1,24 @@ +ECHO off + +SET /P APPVEYOR_BUILD_NUMBER=Please enter a build number (e.g. 134): +SET /P PACKAGE_VERISON=Please enter your package version (e.g. 1.0.5): +SET /P UMBRACO_PACKAGE_PRERELEASE_SUFFIX=Please enter your package release suffix or leave empty (e.g. beta): + +SET /P APPVEYOR_REPO_TAG=If you want to simulate a GitHub tag for a release (e.g. true): + +if "%APPVEYOR_BUILD_NUMBER%" == "" ( + SET APPVEYOR_BUILD_NUMBER=100 +) +if "%PACKAGE_VERISON%" == "" ( + SET PACKAGE_VERISON=0.0.0 +) + +SET APPVEYOR_BUILD_VERSION=%PACKAGE_VERISON%.%APPVEYOR_BUILD_NUMBER% + +build-appveyor.cmd + +@IF %ERRORLEVEL% NEQ 0 GOTO err +@EXIT /B 0 +:err +@PAUSE +@EXIT /B 1 \ No newline at end of file diff --git a/build/package.nuspec b/build/package.nuspec new file mode 100644 index 0000000..fe4ca48 --- /dev/null +++ b/build/package.nuspec @@ -0,0 +1,22 @@ + + + + + 0.0.0 + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/build/package.proj b/build/package.proj new file mode 100644 index 0000000..747d160 --- /dev/null +++ b/build/package.proj @@ -0,0 +1,206 @@ + + + + + + $(MSBuildProjectDirectory)\tools\MSBuildCommunityTasks + $(MSBuildProjectDirectory)\tools\MSBuildUmbracoTasks + $(MSBuildProjectDirectory)\tools\MSBuildNugetTasks + $(MSBuildProjectDirectory)\tools\AppVeyorUmbraco + + + + + + + + + + Our.Umbraco.NestedContent + Nested Content + 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 + MIT license + http://opensource.org/licenses/MIT + https://github.com/leekelleher/umbraco-nested-content + + + + + Our.Umbraco.NestedContent + Umbraco Nested Content + Copyright © 2015 Matt Brailsford, Lee Kelleher, Our Umbraco and other contributors + Matt Brailsford, Lee Kelleher + https://our.umbraco.org/media/wiki/145710/635623752021571595_ncpng.png + umbraco list editor + en-GB + false + + + + + $(APPVEYOR_BUILD_VERSION) + + + + + false + + + + + true + + + + + + + + true + + + + + false + + + + + + + Release + $(MSBuildProjectDirectory)\.. + $(MSBuildProjectDirectory)\_umbraco + $(MSBuildProjectDirectory)\_nuget + $(RootDir)\artifacts + $(RootDir)\src\$(ProjectName) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/build/package.xml b/build/package.xml new file mode 100644 index 0000000..c77d85c --- /dev/null +++ b/build/package.xml @@ -0,0 +1,36 @@ + + + + + + 0.0.0 + + + + 0 + 0 + 0 + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/build/tools/AppVeyorUmbraco/AppVeyorUmbraco.Targets b/build/tools/AppVeyorUmbraco/AppVeyorUmbraco.Targets new file mode 100644 index 0000000..67be406 --- /dev/null +++ b/build/tools/AppVeyorUmbraco/AppVeyorUmbraco.Targets @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Tools/MSBuildCommunityTasks/ICSharpCode.SharpZipLib.dll b/build/tools/MSBuildCommunityTasks/ICSharpCode.SharpZipLib.dll similarity index 100% rename from Tools/MSBuildCommunityTasks/ICSharpCode.SharpZipLib.dll rename to build/tools/MSBuildCommunityTasks/ICSharpCode.SharpZipLib.dll diff --git a/Tools/MSBuildCommunityTasks/MSBuild.Community.Tasks.Targets b/build/tools/MSBuildCommunityTasks/MSBuild.Community.Tasks.Targets similarity index 100% rename from Tools/MSBuildCommunityTasks/MSBuild.Community.Tasks.Targets rename to build/tools/MSBuildCommunityTasks/MSBuild.Community.Tasks.Targets diff --git a/Tools/MSBuildCommunityTasks/MSBuild.Community.Tasks.chm b/build/tools/MSBuildCommunityTasks/MSBuild.Community.Tasks.chm similarity index 100% rename from Tools/MSBuildCommunityTasks/MSBuild.Community.Tasks.chm rename to build/tools/MSBuildCommunityTasks/MSBuild.Community.Tasks.chm diff --git a/Tools/MSBuildCommunityTasks/MSBuild.Community.Tasks.dll b/build/tools/MSBuildCommunityTasks/MSBuild.Community.Tasks.dll similarity index 100% rename from Tools/MSBuildCommunityTasks/MSBuild.Community.Tasks.dll rename to build/tools/MSBuildCommunityTasks/MSBuild.Community.Tasks.dll diff --git a/Tools/MSBuildCommunityTasks/MSBuild.Community.Tasks.xml b/build/tools/MSBuildCommunityTasks/MSBuild.Community.Tasks.xml similarity index 100% rename from Tools/MSBuildCommunityTasks/MSBuild.Community.Tasks.xml rename to build/tools/MSBuildCommunityTasks/MSBuild.Community.Tasks.xml diff --git a/Tools/MSBuildCommunityTasks/Sample.proj b/build/tools/MSBuildCommunityTasks/Sample.proj similarity index 100% rename from Tools/MSBuildCommunityTasks/Sample.proj rename to build/tools/MSBuildCommunityTasks/Sample.proj diff --git a/build/tools/MSBuildNugetTasks/MSBuild.NuGet.Tasks.Targets b/build/tools/MSBuildNugetTasks/MSBuild.NuGet.Tasks.Targets new file mode 100644 index 0000000..83ac6e2 --- /dev/null +++ b/build/tools/MSBuildNugetTasks/MSBuild.NuGet.Tasks.Targets @@ -0,0 +1,12 @@ + + + + + $(MSBuildProjectDirectory)\MSBuildTasks + $(MSBuildNuGetTasksPath)\MSBuild.NuGet.Tasks.dll + + + + + + diff --git a/build/tools/MSBuildNugetTasks/MSBuild.NuGet.Tasks.dll b/build/tools/MSBuildNugetTasks/MSBuild.NuGet.Tasks.dll new file mode 100644 index 0000000..d7bc3f3 Binary files /dev/null and b/build/tools/MSBuildNugetTasks/MSBuild.NuGet.Tasks.dll differ diff --git a/Tools/MSBuildUmbracoTasks/ICSharpCode.SharpZipLib.dll b/build/tools/MSBuildUmbracoTasks/ICSharpCode.SharpZipLib.dll similarity index 100% rename from Tools/MSBuildUmbracoTasks/ICSharpCode.SharpZipLib.dll rename to build/tools/MSBuildUmbracoTasks/ICSharpCode.SharpZipLib.dll diff --git a/Tools/MSBuildUmbracoTasks/MSBuild.Umbraco.Tasks.Targets b/build/tools/MSBuildUmbracoTasks/MSBuild.Umbraco.Tasks.Targets similarity index 88% rename from Tools/MSBuildUmbracoTasks/MSBuild.Umbraco.Tasks.Targets rename to build/tools/MSBuildUmbracoTasks/MSBuild.Umbraco.Tasks.Targets index 20034af..61f3346 100644 --- a/Tools/MSBuildUmbracoTasks/MSBuild.Umbraco.Tasks.Targets +++ b/build/tools/MSBuildUmbracoTasks/MSBuild.Umbraco.Tasks.Targets @@ -2,7 +2,7 @@ - $(MSBuildProjectDirectory)\MSBuildUmbracoTasks + $(MSBuildProjectDirectory)\MSBuildTasks $(MSBuildUmbracoTasksPath)\MSBuild.Umbraco.Tasks.dll diff --git a/build/tools/MSBuildUmbracoTasks/MSBuild.Umbraco.Tasks.dll b/build/tools/MSBuildUmbracoTasks/MSBuild.Umbraco.Tasks.dll new file mode 100644 index 0000000..cb05f27 Binary files /dev/null and b/build/tools/MSBuildUmbracoTasks/MSBuild.Umbraco.Tasks.dll differ diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 0000000..9a85b56 --- /dev/null +++ b/docs/README.md @@ -0,0 +1,4 @@ +# Nested Content +## Documentation + +* [Nested Content - Developers Guide](developers-guide.md) \ No newline at end of file diff --git a/docs/assets/img/icon-delete.png b/docs/assets/img/icon-delete.png new file mode 100644 index 0000000..b05892a Binary files /dev/null and b/docs/assets/img/icon-delete.png differ diff --git a/docs/assets/img/icon-edit.png b/docs/assets/img/icon-edit.png new file mode 100644 index 0000000..951ee47 Binary files /dev/null and b/docs/assets/img/icon-edit.png differ diff --git a/docs/assets/img/icon-move.png b/docs/assets/img/icon-move.png new file mode 100644 index 0000000..d72c84e Binary files /dev/null and b/docs/assets/img/icon-move.png differ diff --git a/docs/assets/img/icon-plus.png b/docs/assets/img/icon-plus.png new file mode 100644 index 0000000..999244d Binary files /dev/null and b/docs/assets/img/icon-plus.png differ diff --git a/docs/assets/img/nested-content.png b/docs/assets/img/nested-content.png new file mode 100644 index 0000000..df45a36 Binary files /dev/null and b/docs/assets/img/nested-content.png differ diff --git a/docs/assets/img/screenshot-01.png b/docs/assets/img/screenshot-01.png new file mode 100644 index 0000000..2d984c2 Binary files /dev/null and b/docs/assets/img/screenshot-01.png differ diff --git a/docs/assets/img/screenshot-02.5.png b/docs/assets/img/screenshot-02.5.png new file mode 100644 index 0000000..62240da Binary files /dev/null and b/docs/assets/img/screenshot-02.5.png differ diff --git a/docs/assets/img/screenshot-02.png b/docs/assets/img/screenshot-02.png new file mode 100644 index 0000000..1712a37 Binary files /dev/null and b/docs/assets/img/screenshot-02.png differ diff --git a/docs/assets/img/screenshot-03.png b/docs/assets/img/screenshot-03.png new file mode 100644 index 0000000..dc51f2c Binary files /dev/null and b/docs/assets/img/screenshot-03.png differ diff --git a/docs/assets/img/screenshot-04.png b/docs/assets/img/screenshot-04.png new file mode 100644 index 0000000..4b98c40 Binary files /dev/null and b/docs/assets/img/screenshot-04.png differ diff --git a/docs/assets/img/screenshot-05.png b/docs/assets/img/screenshot-05.png new file mode 100644 index 0000000..0993c9e Binary files /dev/null and b/docs/assets/img/screenshot-05.png differ diff --git a/docs/assets/pdf/Nested-Content--Developers-Guide-v1.0.pdf b/docs/assets/pdf/Nested-Content--Developers-Guide-v1.0.pdf new file mode 100644 index 0000000..10b3c8c Binary files /dev/null and b/docs/assets/pdf/Nested-Content--Developers-Guide-v1.0.pdf differ diff --git a/docs/developers-guide.md b/docs/developers-guide.md new file mode 100644 index 0000000..9b4ca94 --- /dev/null +++ b/docs/developers-guide.md @@ -0,0 +1,155 @@ +# Nested Content - Developers Guide + +### Contents + +1. [Introduction](#introduction) +2. [Getting Set Up](#getting-set-up) + * [System Requirements](#system-requirements) +3. [Configuring Nested Content](#configuring-nested-content) +4. [Editing Nested Content](#editing-nested-content) + * [Single Item Mode](#single-item-mode) +5. [Rendering Nested Content](#rendering-nested-content) + * [Single Item Mode](#single-item-mode-1) +6. [Useful Links](#useful-links) + +--- + +### Introduction + +**Nested Content** is a new list editing property editor for Umbraco 7+, similar to likes of **Embedded Content** and **Archetype**, however **Nested Content** uses the power of doc types to define the list item blue prints. By using doc types, we get the benefit of an easy / reusable UI we are all familiar with and also get to re-use all the standard data types as our field editors rather than being limited to a subset of "allowed" types. + +**Nested Content** then is probably the last list editor you will need for Umbraco 7+. + +--- + +### Getting Set Up + +#### System Requirements + +Before you get started, there are a number of things you will need: + +1. .NET 4.5+ +2. Umbraco 7.1.4+ +3. The **Nested Content** package installed + +--- + +### Configuring Nested Content + +The **Nested Content** property editor is set-up/configured in the same way as any standard property editor, via the *Data Types* admin interface. To set-up your Nested Content property, create a new *Data Type* and select **Nested Content** from the list of available property editors. + +You should then be presented with the **Nested Content** property editors prevalue editor as shown below. + +![Nested Content - Prevalue Editor](assets/img/screenshot-01.png) + +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 | +| 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. | + +Once your data type has been configured, simply set-up a property on your page doc type using your new data type and you are set to start editing. + +--- + +### Editing Nested Content + +The **Nested Content** editor takes a lot of styling cues from the new Umbraco grid in order to keep consistency and a familiarity to content editors. + +When viewing a **Nested Content** editor for the first time, you’ll be presented with a simple icon and help text to get you started. + +![Nested Content - Add Item](assets/img/screenshot-02.png) + +Simply click the ![Nested Content - Plus Icon](assets/img/icon-plus.png) icon to start creating a new item in the list. + +If your **Nested Content** editor is configured with multiple doc types you will be presented with a dialog window to select which doc type you would like to use. + +![Nested Content - Plus Icon](assets/img/screenshot-02.5.png) + +Simply click the icon of the doc type you wish to use and a new items will be created in the list using that doc type. + +If you only have one doc type configured for your **Nested Content** editor, then clicking the ![Nested Content - Plus Icon](assets/img/icon-plus.png) will not display the dialog and instead will jump straight to inserting an entry in the editor for you ready to edit. + +![Nested Content - New Item](assets/img/screenshot-03.png) + +More items can be added to the list by clicking the ![Nested Content - Plus Icon](assets/img/icon-plus.png) icon for each additional item. + +To close the editor for an item / open the editor for another item in the list, simply click the ![Nested Content - Edit Icon](assets/img/icon-edit.png) icon. + +![Nested Content - New Item](assets/img/screenshot-04.png) + +To reorder the list, simply click and drag the ![Nested Content - Move Icon](assets/img/icon-move.png) icon up and down to place the items in the order you want. + +To delete an item simply click the ![Nested Content - Delete Icon](assets/img/icon-delete.png) icon. If the minimum number of items is reached, then the ![Nested Content - Delete Icon](assets/img/icon-delete.png) icon will appear greyed out to prevent going below the minimum allowed number of items. + +#### Single Item Mode + +If **Nested Content** is configured with a minimum and maximum item of 1, then it goes into single item mode. + +In single item mode, there is no icon displayed to add new items, and the single items editor will be open by default and it’s header bar removed. + +In this mode,** Nested Content** works more like a fieldset than a list editor. + +![Nested Content - Single Item Mode](assets/img/screenshot-05.png) + +--- + +### Rendering Nested Content + +To render the stored value of your **Nested Content** property, a built in value convert is provided for you. Simply call the `GetPropertyValue` method with a generic type of `IEnumerable` and the stored value will be returned as a list of `IPublishedContent` entity. + +Example: + +```csharp +@inherits Umbraco.Web.Mvc.UmbracoViewPage +@{ + var items = Model.GetPropertyValue>("myProperyAlias"); + + foreach(var item in items) + { + // Do your thang... + } +} +``` + +Because we treat each item as a standard `IPublishedContent` entity, that means you can use all the property value converters you are used to using, as-well as the built in `@Umbraco.Field(...)` helper methods. + +Example: +```csharp +@inherits Umbraco.Web.Mvc.UmbracoViewPage +@{ + var items = Model.GetPropertyValue>("myProperyAlias"); + + foreach(var item in items) + { +

@item.GetPropertyValue("name")

+ @Umbraco.Field(item, "bodyText") + } +} +``` + +#### Single Item Mode + +If your **Nested Content** property editor is configured in single item mode then the value converter will automatically know this and will return a single `IPublishedContent` entity rather than an `IEnumerable` list. Therefore, when using **Nested Content** in single item mode, you can simply call `GetPropertyValue` with a generic type of `IPublishedContent` and you can start accessing the entities property straight away, rather than having to then fetch it from a list first. + +Example: +```csharp +@inherits Umbraco.Web.Mvc.UmbracoViewPage +@{ + var item = Model.GetPropertyValue ("myProperyAlias"); +} +

@item.GetPropertyValue("name")

+ @Umbraco.Field(item, "bodyText") +``` + +--- + +### Useful Links + +* [Source Code](https://github.com/leekelleher/umbraco-nested-content) +* [Our Umbraco Project Page](http://our.umbraco.org/projects/backoffice-extensions/nested-content) diff --git a/.nuget/NuGet.Config b/src/.nuget/NuGet.Config similarity index 100% rename from .nuget/NuGet.Config rename to src/.nuget/NuGet.Config diff --git a/.nuget/NuGet.exe b/src/.nuget/NuGet.exe similarity index 100% rename from .nuget/NuGet.exe rename to src/.nuget/NuGet.exe diff --git a/.nuget/NuGet.targets b/src/.nuget/NuGet.targets similarity index 100% rename from .nuget/NuGet.targets rename to src/.nuget/NuGet.targets diff --git a/Our.Umbraco.NestedContent.sln b/src/Our.Umbraco.NestedContent.sln similarity index 53% rename from Our.Umbraco.NestedContent.sln rename to src/Our.Umbraco.NestedContent.sln index fb26052..9362de1 100644 --- a/Our.Umbraco.NestedContent.sln +++ b/src/Our.Umbraco.NestedContent.sln @@ -1,24 +1,32 @@  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 2013 -VisualStudioVersion = 12.0.21005.1 +VisualStudioVersion = 12.0.31101.0 MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nuget", ".nuget", "{CEE9961C-D747-40CD-B0B2-868D6B46833E}" ProjectSection(SolutionItems) = preProject .nuget\NuGet.Config = .nuget\NuGet.Config - .nuget\NuGet.exe = .nuget\NuGet.exe .nuget\NuGet.targets = .nuget\NuGet.targets EndProjectSection EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Package", "Package", "{D588B45D-9E67-4136-8271-2B8724C8165F}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Our.Umbraco.NestedContent", "Our.Umbraco.NestedContent\Our.Umbraco.NestedContent.csproj", "{641D5625-F5B4-4B23-875A-4233C507F37D}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Build Package", "Build Package", "{F74D7367-2138-4CC2-BD71-22C514BC451C}" ProjectSection(SolutionItems) = preProject - Package.build.cmd = Package.build.cmd - Package.build.xml = Package.build.xml - Package.xml = Package.xml - README.markdown = README.markdown + ..\appveyor.yml = ..\appveyor.yml + ..\build-appveyor.cmd = ..\build-appveyor.cmd + ..\build.cmd = ..\build.cmd + ..\build\package.nuspec = ..\build\package.nuspec + ..\build\package.proj = ..\build\package.proj + ..\build\package.xml = ..\build\package.xml EndProjectSection EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Our.Umbraco.NestedContent", "Src\Our.Umbraco.NestedContent\Our.Umbraco.NestedContent.csproj", "{641D5625-F5B4-4B23-875A-4233C507F37D}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{2E1F9B00-9D88-460A-8F74-43F517DD913C}" + ProjectSection(SolutionItems) = preProject + ..\CONTRIBUTING.md = ..\CONTRIBUTING.md + ..\LICENSE.md = ..\LICENSE.md + ..\README.md = ..\README.md + EndProjectSection EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -34,4 +42,8 @@ Global GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {CEE9961C-D747-40CD-B0B2-868D6B46833E} = {2E1F9B00-9D88-460A-8F74-43F517DD913C} + {F74D7367-2138-4CC2-BD71-22C514BC451C} = {2E1F9B00-9D88-460A-8F74-43F517DD913C} + EndGlobalSection EndGlobal diff --git a/Src/Our.Umbraco.NestedContent/Bootstrap.cs b/src/Our.Umbraco.NestedContent/Bootstrap.cs similarity index 80% rename from Src/Our.Umbraco.NestedContent/Bootstrap.cs rename to src/Our.Umbraco.NestedContent/Bootstrap.cs index a6e20c0..311294e 100644 --- a/Src/Our.Umbraco.NestedContent/Bootstrap.cs +++ b/src/Our.Umbraco.NestedContent/Bootstrap.cs @@ -16,12 +16,9 @@ private void ExpireCache(IDataTypeService sender, SaveEventArgs(string.Format("ConvertDataToSource ({0})", propertyType.DataTypeId))) + { + using (DisposableTimer.DebugDuration(string.Format("ConvertDataToSource ({0})", propertyType.DataTypeId))) { if (source != null && !source.ToString().IsNullOrWhiteSpace()) { var rawValue = JsonConvert.DeserializeObject>(source.ToString()); var processedValue = new List(); - var preValue = NestedContentHelper.GetPreValuesDictionaryByDataTypeId(propertyType.DataTypeId); - var contentType = NestedContentHelper.GetContentTypeFromPreValue(propertyType.DataTypeId); - if (contentType == null) - return null; - - var publishedContentType = PublishedContentType.Get(PublishedItemType.Content, contentType.Alias); - if (publishedContentType == null) - return null; + var preValueCollection = NestedContentHelper.GetPreValuesCollectionByDataTypeId(propertyType.DataTypeId); + var preValueDictionary = preValueCollection.AsPreValueDictionary(); for (var i = 0; i < rawValue.Count; i++) { - var o = rawValue[i]; - var propValues = ((JObject)o).ToObject>(); + var item = (JObject)rawValue[i]; + + // Convert from old style (v.0.1.1) data format if necessary + // - Please note: This call has virtually no impact on rendering performance for new style (>v0.1.1). + // Even so, this should be removed eventually, when it's safe to assume that there is + // no longer any need for conversion. + NestedContentHelper.ConvertItemValueFromV011(item, propertyType.DataTypeId, ref preValueCollection); + + var contentTypeAlias = NestedContentHelper.GetContentTypeAliasFromItem(item); + if (string.IsNullOrEmpty(contentTypeAlias)) + { + continue; + } + + var publishedContentType = PublishedContentType.Get(PublishedItemType.Content, contentTypeAlias); + if (publishedContentType == null) + { + continue; + } + + var propValues = item.ToObject>(); var properties = new List(); foreach (var jProp in propValues) @@ -71,13 +85,17 @@ public override object ConvertDataToSource(PublishedPropertyType propertyType, o // Do nothing, we just want to parse out the name if we can } - processedValue.Add(new DetachedPublishedContent(nameObj == null ? null : nameObj.ToString(), publishedContentType, properties.ToArray())); + processedValue.Add(new DetachedPublishedContent( + nameObj == null ? null : nameObj.ToString(), + publishedContentType, + properties.ToArray(), + i)); } // Detect min/max items == 1 and just return a single IPublishedContent int minItems, maxItems; - if (preValue.ContainsKey("minItems") && int.TryParse(preValue["minItems"], out minItems) && minItems == 1 - && preValue.ContainsKey("maxItems") && int.TryParse(preValue["maxItems"], out maxItems) && maxItems == 1) + if (preValueDictionary.ContainsKey("minItems") && int.TryParse(preValueDictionary["minItems"], out minItems) && minItems == 1 + && preValueDictionary.ContainsKey("maxItems") && int.TryParse(preValueDictionary["maxItems"], out maxItems) && maxItems == 1) { return processedValue.FirstOrDefault(); } @@ -85,7 +103,7 @@ public override object ConvertDataToSource(PublishedPropertyType propertyType, o return processedValue; } } - } + } catch (Exception e) { LogHelper.Error("Error converting value", e); @@ -94,4 +112,4 @@ public override object ConvertDataToSource(PublishedPropertyType propertyType, o return null; } } -} +} \ No newline at end of file diff --git a/src/Our.Umbraco.NestedContent/Extensions/ContentTypeServiceExtensions.cs b/src/Our.Umbraco.NestedContent/Extensions/ContentTypeServiceExtensions.cs new file mode 100644 index 0000000..735e651 --- /dev/null +++ b/src/Our.Umbraco.NestedContent/Extensions/ContentTypeServiceExtensions.cs @@ -0,0 +1,7 @@ +namespace Our.Umbraco.NestedContent.Extensions +{ + internal static class ContentTypeServiceExtensions + { + // implementation obsolete as of PR #4 - empty class left for future expansion + } +} \ No newline at end of file diff --git a/Src/Our.Umbraco.NestedContent/Extensions/PreValueCollectionExtensions.cs b/src/Our.Umbraco.NestedContent/Extensions/PreValueCollectionExtensions.cs similarity index 99% rename from Src/Our.Umbraco.NestedContent/Extensions/PreValueCollectionExtensions.cs rename to src/Our.Umbraco.NestedContent/Extensions/PreValueCollectionExtensions.cs index de0f6a5..d6a6de9 100644 --- a/Src/Our.Umbraco.NestedContent/Extensions/PreValueCollectionExtensions.cs +++ b/src/Our.Umbraco.NestedContent/Extensions/PreValueCollectionExtensions.cs @@ -11,4 +11,4 @@ public static IDictionary AsPreValueDictionary(this PreValueColl return preValue.PreValuesAsDictionary.ToDictionary(x => x.Key, x => x.Value.Value); } } -} +} \ No newline at end of file diff --git a/src/Our.Umbraco.NestedContent/Helpers/NestedContentHelper.cs b/src/Our.Umbraco.NestedContent/Helpers/NestedContentHelper.cs new file mode 100644 index 0000000..c0f32ff --- /dev/null +++ b/src/Our.Umbraco.NestedContent/Helpers/NestedContentHelper.cs @@ -0,0 +1,125 @@ +using System; +using System.Linq; +using Newtonsoft.Json.Linq; +using Our.Umbraco.NestedContent.Extensions; +using Our.Umbraco.NestedContent.PropertyEditors; +using Umbraco.Core; +using Umbraco.Core.Models; + +namespace Our.Umbraco.NestedContent.Helpers +{ + internal static class NestedContentHelper + { + public static PreValueCollection GetPreValuesCollectionByDataTypeId(int dtdId) + { + var preValueCollection = (PreValueCollection)ApplicationContext.Current.ApplicationCache.RuntimeCache.GetCacheItem( + string.Concat("Our.Umbraco.NestedContent.GetPreValuesCollectionByDataTypeId_", dtdId), + () => ApplicationContext.Current.Services.DataTypeService.GetPreValuesCollectionByDataTypeId(dtdId)); + + return preValueCollection; + } + + public static string GetContentTypeAliasFromItem(JObject item) + { + var contentTypeAliasProperty = item[NestedContentPropertyEditor.ContentTypeAliasPropertyKey]; + if (contentTypeAliasProperty == null) + { + return null; + } + + return contentTypeAliasProperty.ToObject(); + } + + public static IContentType GetContentTypeFromItem(JObject item) + { + var contentTypeAlias = GetContentTypeAliasFromItem(item); + if (string.IsNullOrEmpty(contentTypeAlias)) + { + return null; + } + + return ApplicationContext.Current.Services.ContentTypeService.GetContentType(contentTypeAlias); + } + + #region Conversion from v0.1.1 data formats + + public static void ConvertItemValueFromV011(JObject item, int dtdId, ref PreValueCollection preValues) + { + var contentTypeAlias = GetContentTypeAliasFromItem(item); + if (contentTypeAlias != null) + { + // the item is already in >v0.1.1 format + return; + } + + // old style (v0.1.1) data, let's attempt a conversion + // - get the prevalues (if they're not loaded already) + preValues = preValues ?? GetPreValuesCollectionByDataTypeId(dtdId); + + // - convert the prevalues (if necessary) + ConvertPreValueCollectionFromV011(preValues); + + // - get the content types prevalue as JArray + var preValuesAsDictionary = preValues.AsPreValueDictionary(); + if (!preValuesAsDictionary.ContainsKey(ContentTypesPreValueKey) || string.IsNullOrEmpty(preValuesAsDictionary[ContentTypesPreValueKey]) != false) + { + return; + } + + var preValueContentTypes = JArray.Parse(preValuesAsDictionary[ContentTypesPreValueKey]); + if (preValueContentTypes.Any()) + { + // the only thing we can really do is assume that the item is the first available content type + item[NestedContentPropertyEditor.ContentTypeAliasPropertyKey] = preValueContentTypes.First().Value("ncAlias"); + } + } + + public static void ConvertPreValueCollectionFromV011(PreValueCollection preValueCollection) + { + if (preValueCollection == null) + { + return; + } + + var persistedPreValuesAsDictionary = preValueCollection.AsPreValueDictionary(); + + // do we have a "docTypeGuid" prevalue and no "contentTypes" prevalue? + if (persistedPreValuesAsDictionary.ContainsKey("docTypeGuid") == false || persistedPreValuesAsDictionary.ContainsKey(ContentTypesPreValueKey)) + { + // the prevalues are already in >v0.1.1 format + return; + } + + // attempt to parse the doc type guid + Guid guid; + if (Guid.TryParse(persistedPreValuesAsDictionary["docTypeGuid"], out guid) == false) + { + // this shouldn't happen... but just in case. + return; + } + + // find the content type + var contentType = ApplicationContext.Current.Services.ContentTypeService.GetAllContentTypes().FirstOrDefault(c => c.Key == guid); + if (contentType == null) + { + return; + } + + // add a prevalue in the format expected by the new (>0.1.1) content type picker/configurator + preValueCollection.PreValuesAsDictionary[ContentTypesPreValueKey] = new PreValue( + string.Format(@"[{{""ncAlias"": ""{0}"", ""ncTabAlias"": ""{1}"", ""nameTemplate"": ""{2}"", }}]", + contentType.Alias, + persistedPreValuesAsDictionary["tabAlias"], + persistedPreValuesAsDictionary["nameTemplate"] + ) + ); + } + + private static string ContentTypesPreValueKey + { + get { return NestedContentPropertyEditor.NestedContentPreValueEditor.ContentTypesPreValueKey; } + } + + #endregion + } +} \ No newline at end of file diff --git a/Src/Our.Umbraco.NestedContent/Models/DetachedPublishedContent.cs b/src/Our.Umbraco.NestedContent/Models/DetachedPublishedContent.cs similarity index 95% rename from Src/Our.Umbraco.NestedContent/Models/DetachedPublishedContent.cs rename to src/Our.Umbraco.NestedContent/Models/DetachedPublishedContent.cs index 1df3a04..d8bd5a6 100644 --- a/Src/Our.Umbraco.NestedContent/Models/DetachedPublishedContent.cs +++ b/src/Our.Umbraco.NestedContent/Models/DetachedPublishedContent.cs @@ -13,16 +13,19 @@ internal class DetachedPublishedContent : PublishedContentBase private readonly string _name; private readonly PublishedContentType _contentType; private readonly IEnumerable _properties; + private readonly int _sortOrder; private readonly bool _isPreviewing; public DetachedPublishedContent(string name, PublishedContentType contentType, IEnumerable properties, + int sortOrder = 0, bool isPreviewing = false) { _name = name; _contentType = contentType; _properties = properties; + _sortOrder = sortOrder; _isPreviewing = isPreviewing; } @@ -96,7 +99,7 @@ public override int TemplateId public override int SortOrder { - get { return 0; } + get { return _sortOrder; } } public override string UrlName @@ -149,4 +152,4 @@ public override int Level get { return 0; } } } -} +} \ No newline at end of file diff --git a/Src/Our.Umbraco.NestedContent/Models/DetachedPublishedProperty.cs b/src/Our.Umbraco.NestedContent/Models/DetachedPublishedProperty.cs similarity index 98% rename from Src/Our.Umbraco.NestedContent/Models/DetachedPublishedProperty.cs rename to src/Our.Umbraco.NestedContent/Models/DetachedPublishedProperty.cs index de73e46..e85dfbc 100644 --- a/Src/Our.Umbraco.NestedContent/Models/DetachedPublishedProperty.cs +++ b/src/Our.Umbraco.NestedContent/Models/DetachedPublishedProperty.cs @@ -15,7 +15,8 @@ internal class DetachedPublishedProperty : IPublishedProperty public DetachedPublishedProperty(PublishedPropertyType propertyType, object value) : this(propertyType, value, false) - { } + { + } public DetachedPublishedProperty(PublishedPropertyType propertyType, object value, bool isPreview) { @@ -43,7 +44,9 @@ public bool HasValue } public object DataValue { get { return _rawValue; } } + public object Value { get { return _objectValue.Value; } } + public object XPathValue { get { return _xpathValue.Value; } } } -} +} \ No newline at end of file diff --git a/Src/Our.Umbraco.NestedContent/Our.Umbraco.NestedContent.csproj b/src/Our.Umbraco.NestedContent/Our.Umbraco.NestedContent.csproj similarity index 61% rename from Src/Our.Umbraco.NestedContent/Our.Umbraco.NestedContent.csproj rename to src/Our.Umbraco.NestedContent/Our.Umbraco.NestedContent.csproj index 11dc177..238a18f 100644 --- a/Src/Our.Umbraco.NestedContent/Our.Umbraco.NestedContent.csproj +++ b/src/Our.Umbraco.NestedContent/Our.Umbraco.NestedContent.csproj @@ -11,7 +11,7 @@ Our.Umbraco.NestedContent v4.5 512 - ..\..\ + ..\ true @@ -33,124 +33,148 @@ - ..\..\packages\AutoMapper.3.0.0\lib\net40\AutoMapper.dll + ..\packages\AutoMapper.3.0.0\lib\net40\AutoMapper.dll + False - ..\..\packages\AutoMapper.3.0.0\lib\net40\AutoMapper.Net4.dll + ..\packages\AutoMapper.3.0.0\lib\net40\AutoMapper.Net4.dll + False - ..\..\packages\UmbracoCms.Core.7.1.4\lib\businesslogic.dll + ..\packages\UmbracoCms.Core.7.1.4\lib\businesslogic.dll + False - ..\..\packages\ClientDependency.1.8.2.1\lib\net45\ClientDependency.Core.dll + ..\packages\ClientDependency.1.8.2.1\lib\net45\ClientDependency.Core.dll + False - ..\..\packages\ClientDependency-Mvc.1.7.0.4\lib\ClientDependency.Core.Mvc.dll + ..\packages\ClientDependency-Mvc.1.7.0.4\lib\ClientDependency.Core.Mvc.dll + False - ..\..\packages\UmbracoCms.Core.7.1.4\lib\cms.dll + ..\packages\UmbracoCms.Core.7.1.4\lib\cms.dll + False - ..\..\packages\UmbracoCms.Core.7.1.4\lib\controls.dll + ..\packages\UmbracoCms.Core.7.1.4\lib\controls.dll + False - ..\..\packages\xmlrpcnet.2.5.0\lib\net20\CookComputing.XmlRpcV2.dll + ..\packages\xmlrpcnet.2.5.0\lib\net20\CookComputing.XmlRpcV2.dll + False - ..\..\packages\UmbracoCms.Core.7.1.4\lib\Examine.dll + ..\packages\UmbracoCms.Core.7.1.4\lib\Examine.dll + False - ..\..\packages\HtmlAgilityPack.1.4.6\lib\Net45\HtmlAgilityPack.dll + ..\packages\HtmlAgilityPack.1.4.6\lib\Net45\HtmlAgilityPack.dll + False - ..\..\packages\SharpZipLib.0.86.0\lib\20\ICSharpCode.SharpZipLib.dll + ..\packages\SharpZipLib.0.86.0\lib\20\ICSharpCode.SharpZipLib.dll + False - ..\..\packages\ImageProcessor.1.9.0.0\lib\ImageProcessor.dll + ..\packages\ImageProcessor.1.9.0.0\lib\ImageProcessor.dll + False - ..\..\packages\ImageProcessor.Web.3.2.3.0\lib\net45\ImageProcessor.Web.dll + ..\packages\ImageProcessor.Web.3.2.3.0\lib\net45\ImageProcessor.Web.dll + False - ..\..\packages\UmbracoCms.Core.7.1.4\lib\interfaces.dll + ..\packages\UmbracoCms.Core.7.1.4\lib\interfaces.dll + False - ..\..\packages\UmbracoCms.Core.7.1.4\lib\log4net.dll + ..\packages\UmbracoCms.Core.7.1.4\lib\log4net.dll + False - ..\..\packages\Lucene.Net.2.9.4.1\lib\net40\Lucene.Net.dll + ..\packages\Lucene.Net.2.9.4.1\lib\net40\Lucene.Net.dll + False - ..\..\packages\UmbracoCms.Core.7.1.4\lib\Microsoft.ApplicationBlocks.Data.dll + ..\packages\UmbracoCms.Core.7.1.4\lib\Microsoft.ApplicationBlocks.Data.dll + False - ..\..\packages\UmbracoCms.Core.7.1.4\lib\Microsoft.Web.Helpers.dll + ..\packages\UmbracoCms.Core.7.1.4\lib\Microsoft.Web.Helpers.dll + False - True - ..\..\packages\Microsoft.Web.Infrastructure.1.0.0.0\lib\net40\Microsoft.Web.Infrastructure.dll + False + ..\packages\Microsoft.Web.Infrastructure.1.0.0.0\lib\net40\Microsoft.Web.Infrastructure.dll - ..\..\packages\Microsoft.AspNet.Mvc.FixedDisplayModes.1.0.0\lib\net40\Microsoft.Web.Mvc.FixedDisplayModes.dll + ..\packages\Microsoft.AspNet.Mvc.FixedDisplayModes.1.0.0\lib\net40\Microsoft.Web.Mvc.FixedDisplayModes.dll + False - ..\..\packages\MiniProfiler.2.1.0\lib\net40\MiniProfiler.dll + ..\packages\MiniProfiler.2.1.0\lib\net40\MiniProfiler.dll + False - ..\..\packages\MySql.Data.6.6.5\lib\net40\MySql.Data.dll + ..\packages\MySql.Data.6.6.5\lib\net40\MySql.Data.dll + False - ..\..\packages\Newtonsoft.Json.6.0.2\lib\net45\Newtonsoft.Json.dll + ..\packages\Newtonsoft.Json.6.0.2\lib\net45\Newtonsoft.Json.dll + False - ..\..\packages\UmbracoCms.Core.7.1.4\lib\SQLCE4Umbraco.dll + ..\packages\UmbracoCms.Core.7.1.4\lib\SQLCE4Umbraco.dll + False - True - ..\..\packages\UmbracoCms.Core.7.1.4\lib\System.Data.SqlServerCe.dll + False + ..\packages\UmbracoCms.Core.7.1.4\lib\System.Data.SqlServerCe.dll - True - ..\..\packages\UmbracoCms.Core.7.1.4\lib\System.Data.SqlServerCe.Entity.dll + False + ..\packages\UmbracoCms.Core.7.1.4\lib\System.Data.SqlServerCe.Entity.dll - True - ..\..\packages\Microsoft.AspNet.WebApi.Client.4.0.30506.0\lib\net40\System.Net.Http.Formatting.dll + False + ..\packages\Microsoft.AspNet.WebApi.Client.4.0.30506.0\lib\net40\System.Net.Http.Formatting.dll - True - ..\..\packages\Microsoft.AspNet.WebPages.2.0.20710.0\lib\net40\System.Web.Helpers.dll + False + ..\packages\Microsoft.AspNet.WebPages.2.0.20710.0\lib\net40\System.Web.Helpers.dll - True - ..\..\packages\Microsoft.AspNet.WebApi.Core.4.0.20710.0\lib\net40\System.Web.Http.dll + False + ..\packages\Microsoft.AspNet.WebApi.Core.4.0.20710.0\lib\net40\System.Web.Http.dll - True - ..\..\packages\Microsoft.AspNet.WebApi.WebHost.4.0.20710.0\lib\net40\System.Web.Http.WebHost.dll + False + ..\packages\Microsoft.AspNet.WebApi.WebHost.4.0.20710.0\lib\net40\System.Web.Http.WebHost.dll False - ..\..\packages\Microsoft.AspNet.Mvc.4.0.20710.0\lib\net40\System.Web.Mvc.dll + ..\packages\Microsoft.AspNet.Mvc.4.0.20710.0\lib\net40\System.Web.Mvc.dll + False - True + False - True - ..\..\packages\Microsoft.AspNet.WebPages.2.0.20710.0\lib\net40\System.Web.WebPages.dll + False + ..\packages\Microsoft.AspNet.WebPages.2.0.20710.0\lib\net40\System.Web.WebPages.dll - True - ..\..\packages\Microsoft.AspNet.WebPages.2.0.20710.0\lib\net40\System.Web.WebPages.Deployment.dll + False + ..\packages\Microsoft.AspNet.WebPages.2.0.20710.0\lib\net40\System.Web.WebPages.Deployment.dll - True - ..\..\packages\Microsoft.AspNet.WebPages.2.0.20710.0\lib\net40\System.Web.WebPages.Razor.dll + False + ..\packages\Microsoft.AspNet.WebPages.2.0.20710.0\lib\net40\System.Web.WebPages.Razor.dll @@ -158,37 +182,48 @@ - ..\..\packages\UmbracoCms.Core.7.1.4\lib\TidyNet.dll + ..\packages\UmbracoCms.Core.7.1.4\lib\TidyNet.dll + False - ..\..\packages\UmbracoCms.Core.7.1.4\lib\umbraco.dll + ..\packages\UmbracoCms.Core.7.1.4\lib\umbraco.dll + False - ..\..\packages\UmbracoCms.Core.7.1.4\lib\Umbraco.Core.dll + ..\packages\UmbracoCms.Core.7.1.4\lib\Umbraco.Core.dll + False - ..\..\packages\UmbracoCms.Core.7.1.4\lib\umbraco.DataLayer.dll + ..\packages\UmbracoCms.Core.7.1.4\lib\umbraco.DataLayer.dll + False - ..\..\packages\UmbracoCms.Core.7.1.4\lib\umbraco.editorControls.dll + ..\packages\UmbracoCms.Core.7.1.4\lib\umbraco.editorControls.dll + False - ..\..\packages\UmbracoCms.Core.7.1.4\lib\umbraco.MacroEngines.dll + ..\packages\UmbracoCms.Core.7.1.4\lib\umbraco.MacroEngines.dll + False - ..\..\packages\UmbracoCms.Core.7.1.4\lib\umbraco.providers.dll + ..\packages\UmbracoCms.Core.7.1.4\lib\umbraco.providers.dll + False - ..\..\packages\UmbracoCms.Core.7.1.4\lib\Umbraco.Web.UI.dll + ..\packages\UmbracoCms.Core.7.1.4\lib\Umbraco.Web.UI.dll + False - ..\..\packages\UmbracoCms.Core.7.1.4\lib\umbraco.XmlSerializers.dll + ..\packages\UmbracoCms.Core.7.1.4\lib\umbraco.XmlSerializers.dll + False - ..\..\packages\UmbracoCms.Core.7.1.4\lib\UmbracoExamine.dll + ..\packages\UmbracoCms.Core.7.1.4\lib\UmbracoExamine.dll + False - ..\..\packages\UmbracoCms.Core.7.1.4\lib\UrlRewritingNet.UrlRewriter.dll + ..\packages\UmbracoCms.Core.7.1.4\lib\UrlRewritingNet.UrlRewriter.dll + False @@ -200,6 +235,7 @@ + @@ -221,9 +257,9 @@ IF %25ComputerName%25 == MBP13-PC-BC ( IF NOT "$(SolutionDir)" == "*Undefined*" ( - xcopy /s /y "$(TargetPath)" "C:\Users\Matt\Work\Sandbox\Umbraco\UmbracoCms.7.2.2\bin" - xcopy /s /y "$(TargetDir)$(ProjectName).pdb" "C:\Users\Matt\Work\Sandbox\Umbraco\UmbracoCms.7.2.2\bin" - xcopy /s /y "$(ProjectDir)Web\UI\*.*" "C:\Users\Matt\Work\Sandbox\Umbraco\UmbracoCms.7.2.2" + xcopy /s /y "$(TargetPath)" "C:\Users\Matt\Work\Sandbox\Umbraco\UmbracoCms.7.2.6\bin" + xcopy /s /y "$(TargetDir)$(ProjectName).pdb" "C:\Users\Matt\Work\Sandbox\Umbraco\UmbracoCms.7.2.6\bin" + xcopy /s /y "$(ProjectDir)Web\UI\*.*" "C:\Users\Matt\Work\Sandbox\Umbraco\UmbracoCms.7.2.6" ) ) diff --git a/src/Our.Umbraco.NestedContent/Properties/AssemblyInfo.cs b/src/Our.Umbraco.NestedContent/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..231ac6e --- /dev/null +++ b/src/Our.Umbraco.NestedContent/Properties/AssemblyInfo.cs @@ -0,0 +1,15 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +[assembly: AssemblyTitle("Our.Umbraco.NestedContent")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("The Umbraco Community")] +[assembly: AssemblyProduct("Our.Umbraco.NestedContent")] +[assembly: AssemblyCopyright("Copyright \xa9 The Umbraco Community 2015")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +[assembly: ComVisible(false)] +[assembly: Guid("8B4FA1D2-118F-497F-B7FB-636F366E245E")] diff --git a/src/Our.Umbraco.NestedContent/Properties/VersionInfo.cs b/src/Our.Umbraco.NestedContent/Properties/VersionInfo.cs new file mode 100644 index 0000000..96a08b4 --- /dev/null +++ b/src/Our.Umbraco.NestedContent/Properties/VersionInfo.cs @@ -0,0 +1,19 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.34209 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +using System; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +[assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyInformationalVersion("1.0.0-local-000100")] + + diff --git a/Src/Our.Umbraco.NestedContent/PropertyEditors/NestedContentPropertyEditor.cs b/src/Our.Umbraco.NestedContent/PropertyEditors/NestedContentPropertyEditor.cs similarity index 81% rename from Src/Our.Umbraco.NestedContent/PropertyEditors/NestedContentPropertyEditor.cs rename to src/Our.Umbraco.NestedContent/PropertyEditors/NestedContentPropertyEditor.cs index 3db2e42..6332798 100644 --- a/Src/Our.Umbraco.NestedContent/PropertyEditors/NestedContentPropertyEditor.cs +++ b/src/Our.Umbraco.NestedContent/PropertyEditors/NestedContentPropertyEditor.cs @@ -1,4 +1,5 @@ -using System.Linq; +using System; +using System.Linq; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.Text.RegularExpressions; @@ -11,6 +12,7 @@ using Umbraco.Core.Models.Editors; using Umbraco.Core.PropertyEditors; using Umbraco.Core.Services; +using umbraco.editorControls.SettingControls; using Umbraco.Web.PropertyEditors; namespace Our.Umbraco.NestedContent.PropertyEditors @@ -18,6 +20,8 @@ namespace Our.Umbraco.NestedContent.PropertyEditors [PropertyEditor(NestedContentPropertyEditor.PropertyEditorAlias, "Nested Content", "/App_Plugins/NestedContent/Views/nestedcontent.html", ValueType = "JSON")] public class NestedContentPropertyEditor : PropertyEditor { + internal const string ContentTypeAliasPropertyKey = "ncContentTypeAlias"; + public const string PropertyEditorAlias = "Our.Umbraco.NestedContent"; private IDictionary _defaultPreValues; @@ -32,10 +36,11 @@ public NestedContentPropertyEditor() // Setup default values _defaultPreValues = new Dictionary { - {"docTypeGuid", ""}, + {NestedContentPreValueEditor.ContentTypesPreValueKey, ""}, {"minItems", 0}, {"maxItems", 0}, - {"confirmDeletes", 1} + {"confirmDeletes", "1"}, + {"showIcons", "1"} }; } @@ -48,14 +53,10 @@ protected override PreValueEditor CreatePreValueEditor() internal class NestedContentPreValueEditor : PreValueEditor { - [PreValueField("docTypeGuid", "Doc Type", "/App_Plugins/NestedContent/Views/nestedcontent.doctypepicker.html", Description = "Select the doc type to use as the data blueprint.")] - public string DocTypeGuid { get; set; } - - [PreValueField("tabAlias", "Tab", "textstring", Description = "Enter the alias of the tab whos properties should be displayed. If left blank, the first tab on the doc type will be used.")] - public string TabAlias { get; set; } + internal const string ContentTypesPreValueKey = "contentTypes"; - [PreValueField("nameTemplate", "Name Template", "textstring", Description = "Enter an angular expression to evaluate against each item for its name.")] - public string NameTemplate { get; set; } + [PreValueField(ContentTypesPreValueKey, "Doc Types", "/App_Plugins/NestedContent/Views/nestedcontent.doctypepicker.html", Description = "Select the doc types to use as the data blueprint.")] + public string[] ContentTypes { get; set; } [PreValueField("minItems", "Min Items", "number", Description = "Set the minimum number of items allowed.")] public string MinItems { get; set; } @@ -66,8 +67,19 @@ internal class NestedContentPreValueEditor : PreValueEditor [PreValueField("confirmDeletes", "Confirm Deletes", "boolean", Description = "Set whether item deletions should require confirming.")] public string ConfirmDeletes { get; set; } + [PreValueField("showIcons", "Show Icons", "boolean", Description = "Set whether to show the items doc type icon in the list.")] + public string ShowIcons { 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; } + + public override IDictionary ConvertDbToEditor(IDictionary defaultPreVals, PreValueCollection persistedPreVals) + { + // re-format old style (v0.1.1) pre values if necessary + NestedContentHelper.ConvertPreValueCollectionFromV011(persistedPreVals); + + return base.ConvertDbToEditor(defaultPreVals, persistedPreVals); + } } #endregion @@ -75,9 +87,9 @@ internal class NestedContentPreValueEditor : PreValueEditor #region Value Editor protected override PropertyValueEditor CreateValueEditor() - { + { return new NestedContentPropertyValueEditor(base.CreateValueEditor()); - } + } internal class NestedContentPropertyValueEditor : PropertyValueEditorWrapper { @@ -119,15 +131,22 @@ public override string ConvertDbToString(Property property, PropertyType propert if (value == null) return string.Empty; - var contentType = NestedContentHelper.GetContentTypeFromPreValue(propertyType.DataTypeDefinitionId); - if (contentType == null) - return string.Empty; - // Process value + PreValueCollection preValues = null; for (var i = 0; i < value.Count; i++) { var o = value[i]; var propValues = ((JObject)o); + + // convert from old style (v0.1.1) data format if necessary + NestedContentHelper.ConvertItemValueFromV011(propValues, propertyType.DataTypeDefinitionId, ref preValues); + + var contentType = NestedContentHelper.GetContentTypeFromItem(propValues); + if (contentType == null) + { + continue; + } + var propValueKeys = propValues.Properties().Select(x => x.Name).ToArray(); foreach (var propKey in propValueKeys) @@ -135,7 +154,7 @@ public override string ConvertDbToString(Property property, PropertyType propert var propType = contentType.PropertyTypes.FirstOrDefault(x => x.Alias == propKey); if (propType == null) { - if (propKey != "name") + if (IsSystemPropertyKey(propKey) == false) { // Property missing so just delete the value propValues[propKey] = null; @@ -177,15 +196,22 @@ public override object ConvertDbToEditor(Property property, PropertyType propert if (value == null) return string.Empty; - var contentType = NestedContentHelper.GetContentTypeFromPreValue(propertyType.DataTypeDefinitionId); - if (contentType == null) - return string.Empty; - // Process value + PreValueCollection preValues = null; for (var i = 0; i < value.Count; i++) { var o = value[i]; var propValues = ((JObject)o); + + // convert from old style (v0.1.1) data format if necessary + NestedContentHelper.ConvertItemValueFromV011(propValues, propertyType.DataTypeDefinitionId, ref preValues); + + var contentType = NestedContentHelper.GetContentTypeFromItem(propValues); + if (contentType == null) + { + continue; + } + var propValueKeys = propValues.Properties().Select(x => x.Name).ToArray(); foreach (var propKey in propValueKeys) @@ -193,7 +219,7 @@ public override object ConvertDbToEditor(Property property, PropertyType propert var propType = contentType.PropertyTypes.FirstOrDefault(x => x.Alias == propKey); if (propType == null) { - if (propKey != "name") + if (IsSystemPropertyKey(propKey) == false) { // Property missing so just delete the value propValues[propKey] = null; @@ -238,15 +264,18 @@ public override object ConvertEditorToDb(ContentPropertyData editorValue, object if (value == null) return string.Empty; - var contentType = NestedContentHelper.GetContentTypeFromPreValue(editorValue.PreValues); - if (contentType == null) - return string.Empty; - // Process value for (var i = 0; i < value.Count; i++) { var o = value[i]; var propValues = ((JObject)o); + + var contentType = NestedContentHelper.GetContentTypeFromItem(propValues); + if (contentType == null) + { + continue; + } + var propValueKeys = propValues.Properties().Select(x => x.Name).ToArray(); foreach (var propKey in propValueKeys) @@ -254,7 +283,7 @@ public override object ConvertEditorToDb(ContentPropertyData editorValue, object var propType = contentType.PropertyTypes.FirstOrDefault(x => x.Alias == propKey); if (propType == null) { - if (propKey != "name") + if (IsSystemPropertyKey(propKey) == false) { // Property missing so just delete the value propValues[propKey] = null; @@ -271,7 +300,7 @@ public override object ConvertEditorToDb(ContentPropertyData editorValue, object // Create a fake content property data object var contentPropData = new ContentPropertyData( - propValues[propKey] == null ? null : propValues[propKey].ToString(), propPreValues, + propValues[propKey], propPreValues, new Dictionary()); // Get the property editor to do it's conversion @@ -298,14 +327,17 @@ public IEnumerable Validate(object rawValue, PreValueCollectio if (value == null) yield break; - var contentType = NestedContentHelper.GetContentTypeFromPreValue(preValues); - if (contentType == null) - yield break; - for (var i = 0; i < value.Count; i++) { var o = value[i]; var propValues = ((JObject)o); + + var contentType = NestedContentHelper.GetContentTypeFromItem(propValues); + if (contentType == null) + { + continue; + } + var propValueKeys = propValues.Properties().Select(x => x.Name).ToArray(); foreach (var propKey in propValueKeys) @@ -349,5 +381,10 @@ public IEnumerable Validate(object rawValue, PreValueCollectio } #endregion + + private static bool IsSystemPropertyKey(string propKey) + { + return propKey == "name" || propKey == ContentTypeAliasPropertyKey; + } } -} +} \ No newline at end of file diff --git a/Src/Our.Umbraco.NestedContent/Web/Controllers/NestedContentApiController.cs b/src/Our.Umbraco.NestedContent/Web/Controllers/NestedContentApiController.cs similarity index 77% rename from Src/Our.Umbraco.NestedContent/Web/Controllers/NestedContentApiController.cs rename to src/Our.Umbraco.NestedContent/Web/Controllers/NestedContentApiController.cs index d7f9838..4df5208 100644 --- a/Src/Our.Umbraco.NestedContent/Web/Controllers/NestedContentApiController.cs +++ b/src/Our.Umbraco.NestedContent/Web/Controllers/NestedContentApiController.cs @@ -11,15 +11,6 @@ namespace Our.Umbraco.NestedContent.Web.Controllers [PluginController("NestedContent")] public class NestedContentApiController : UmbracoAuthorizedJsonController { - [System.Web.Http.HttpGet] - public object GetContentTypeAliasByGuid([ModelBinder] Guid guid) - { - return new - { - alias = Services.ContentTypeService.GetAliasByGuid(guid) - }; - } - [System.Web.Http.HttpGet] public IEnumerable GetContentTypes() { diff --git a/Src/Our.Umbraco.NestedContent/Web/UI/App_Plugins/NestedContent/Css/nestedcontent.css b/src/Our.Umbraco.NestedContent/Web/UI/App_Plugins/NestedContent/Css/nestedcontent.css similarity index 64% rename from Src/Our.Umbraco.NestedContent/Web/UI/App_Plugins/NestedContent/Css/nestedcontent.css rename to src/Our.Umbraco.NestedContent/Web/UI/App_Plugins/NestedContent/Css/nestedcontent.css index fb17c60..5469cad 100644 --- a/Src/Our.Umbraco.NestedContent/Web/UI/App_Plugins/NestedContent/Css/nestedcontent.css +++ b/src/Our.Umbraco.NestedContent/Web/UI/App_Plugins/NestedContent/Css/nestedcontent.css @@ -9,6 +9,13 @@ text-align: left; border-top: solid 1px transparent; background: white; + + +} + +.nested-content__item--active:not(.nested-content__item--single) +{ + background: #f8f8f8; } .nested-content__item.ui-sortable-placeholder @@ -35,6 +42,13 @@ padding: 15px 20px; border-bottom: 1px dashed #e0e0e0; text-align: right; + cursor: pointer; + background-color: white; + + -moz-user-select: none; + -khtml-user-select: none; + -webkit-user-select: none; + -o-user-select: none; } .nested-content__heading @@ -43,6 +57,13 @@ line-height: 20px; } +.nested-content__heading i +{ + vertical-align: text-top; + color: #999; /* same icon color as the icons in the item type picker */ + margin-right: 10px; +} + .nested-content__icons { margin: -6px 0; @@ -126,4 +147,38 @@ color: #555; background: #f8f8f8; border-radius: 15px; -} \ No newline at end of file +} + +.nested-content__doctypepicker table input, .nested-content__doctypepicker table select { + width: 100%; + padding-right: 0; +} + +.nested-content__doctypepicker table td.icon-navigation, .nested-content__doctypepicker i.nested-content__help-icon { + vertical-align: middle; + color: #CCC; +} + +.nested-content__doctypepicker table td.icon-navigation:hover, .nested-content__doctypepicker i.nested-content__help-icon:hover { + color: #343434; +} + +.nested-content__doctypepicker i.nested-content__help-icon { + margin-left: 10px; +} + +.form-horizontal .nested-content .control-label +{ + width: 30%; +} + +.form-horizontal .nested-content .controls-row +{ + margin-left: 40% !important; +} + +.form-horizontal .nested-content .controls-row .umb-textstring, +.form-horizontal .nested-content .controls-row .umb-textarea +{ + width: 95%; +} 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 new file mode 100644 index 0000000..a00887f --- /dev/null +++ b/src/Our.Umbraco.NestedContent/Web/UI/App_Plugins/NestedContent/Js/nestedcontent.controllers.js @@ -0,0 +1,353 @@ +angular.module("umbraco").controller("Our.Umbraco.NestedContent.Controllers.DocTypePickerController", [ + + "$scope", + "Our.Umbraco.NestedContent.Resources.NestedContentResources", + + function ($scope, ncResources) { + + $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: "" + } + ); + } + + $scope.remove = function (index) { + $scope.model.value.splice(index, 1); + } + + $scope.sortableOptions = { + axis: 'y', + cursor: "move", + handle: ".icon-navigation" + }; + + ncResources.getContentTypes().then(function (docTypes) { + $scope.model.docTypes = docTypes; + }); + + if (!$scope.model.value) { + $scope.model.value = []; + $scope.add(); + } + } +]); + +angular.module("umbraco").controller("Our.Umbraco.NestedContent.Controllers.NestedContentPropertyEditorController", [ + + "$scope", + "$interpolate", + "$filter", + "contentResource", + "Our.Umbraco.NestedContent.Resources.NestedContentResources", + + function ($scope, $interpolate, $filter, contentResource, ncResources) { + + //$scope.model.config.contentTypes; + //$scope.model.config.minItems; + //$scope.model.config.maxItems; + //console.log($scope); + + var inited = false; + + _.each($scope.model.config.contentTypes, function (contentType) { + contentType.nameExp = !!contentType.nameTemplate + ? $interpolate(contentType.nameTemplate) + : undefined; + }); + + $scope.nodes = []; + $scope.currentNode = undefined; + $scope.scaffolds = undefined; + $scope.sorting = false; + + $scope.minItems = $scope.model.config.minItems || 0; + $scope.maxItems = $scope.model.config.maxItems || 0; + + if ($scope.maxItems == 0) + $scope.maxItems = 1000; + + $scope.singleMode = $scope.minItems == 1 && $scope.maxItems == 1; + + $scope.showIcons = $scope.model.config.showIcons || true; + + $scope.overlayMenu = { + show: false, + style: {} + }; + + $scope.addNode = function (alias) { + var scaffold = $scope.getScaffold(alias); + + var newNode = initNode(scaffold, null); + + $scope.currentNode = newNode; + + $scope.closeNodeTypePicker(); + }; + + $scope.openNodeTypePicker = function (event) { + if ($scope.nodes.length >= $scope.maxItems) { + return; + } + + // this could be used for future limiting on node types + $scope.overlayMenu.scaffolds = []; + _.each($scope.scaffolds, function (scaffold) { + var icon = scaffold.icon; + // workaround for when no icon is chosen for a doctype + if (icon == ".sprTreeFolder") { + icon = "icon-folder"; + } + $scope.overlayMenu.scaffolds.push({ + alias: scaffold.contentTypeAlias, + name: scaffold.contentTypeName, + icon: icon + }); + }); + + if ($scope.overlayMenu.scaffolds.length == 0) { + return; + } + + if ($scope.overlayMenu.scaffolds.length == 1) { + // only one scaffold type - no need to display the picker + $scope.addNode($scope.scaffolds[0].contentTypeAlias); + 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; + } + $scope.overlayMenu.show = true; + }; + + $scope.closeNodeTypePicker = function () { + $scope.overlayMenu.show = false; + }; + + $scope.editNode = function (idx) { + if ($scope.currentNode && $scope.currentNode.id == $scope.nodes[idx].id) { + $scope.currentNode = undefined; + } else { + $scope.currentNode = $scope.nodes[idx]; + } + }; + + $scope.deleteNode = function (idx) { + if ($scope.nodes.length > $scope.model.config.minItems) { + if ($scope.model.config.confirmDeletes && $scope.model.config.confirmDeletes == 1) { + if (confirm("Are you sure you want to delete this item?")) { + $scope.nodes.splice(idx, 1); + } + } else { + $scope.nodes.splice(idx, 1); + } + } + }; + + $scope.getName = function (idx) { + + var name = "Item " + (idx + 1); + + 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 + if (newName && (newName = $.trim(newName))) { + name = newName; + } + } + + // Update the nodes actual name value + if ($scope.nodes[idx].name != newName) { + $scope.nodes[idx].name = name; + } + + return name; + }; + + $scope.getIcon = function (idx) { + var scaffold = $scope.getScaffold($scope.model.value[idx].ncContentTypeAlias); + return scaffold && scaffold.icon && scaffold.icon !== ".sprTreeFolder" ? scaffold.icon : "icon-folder"; + } + + $scope.sortableOptions = { + axis: 'y', + cursor: "move", + handle: ".nested-content__icon--move", + start: function (ev, ui) { + // Yea, yea, we shouldn't modify the dom, sue me + $("#nested-content--" + $scope.model.id + " .umb-rte textarea").each(function () { + tinymce.execCommand('mceRemoveEditor', false, $(this).attr('id')); + $(this).css("visibility", "hidden"); + }); + $scope.$apply(function () { + $scope.sorting = true; + }); + }, + stop: function (ev, ui) { + $("#nested-content--" + $scope.model.id + " .umb-rte textarea").each(function () { + tinymce.execCommand('mceAddEditor', true, $(this).attr('id')); + $(this).css("visibility", "visible"); + }); + $scope.$apply(function () { + $scope.sorting = false; + }); + } + }; + + $scope.getScaffold = function (alias) { + return _.find($scope.scaffolds, function (scaffold) { + return scaffold.contentTypeAlias == alias; + }); + } + + $scope.getContentTypeConfig = function (alias) { + return _.find($scope.model.config.contentTypes, function (contentType) { + return contentType.ncAlias == alias; + }); + } + + // Initialize + var scaffoldsLoaded = 0; + $scope.scaffolds = []; + _.each($scope.model.config.contentTypes, function (contentType) { + contentResource.getScaffold(-20, contentType.ncAlias).then(function(scaffold) { + // remove all tabs except the specified tab + var tab = _.find(scaffold.tabs, function(tab) { + return tab.id != 0 && (tab.alias == contentType.ncTabAlias || contentType.ncTabAlias == ""); + }) + scaffold.tabs = []; + if (tab) { + scaffold.tabs.push(tab); + } + + // Store the scaffold object + $scope.scaffolds.push(scaffold); + + scaffoldsLoaded++; + initIfAllScaffoldsHaveLoaded(); + }, function(error) { + scaffoldsLoaded++; + initIfAllScaffoldsHaveLoaded(); + }); + }); + + 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) { + contentTypeAliases.push(contentType.ncAlias); + }); + $scope.scaffolds = $filter('orderBy')($scope.scaffolds, function (s) { + return contentTypeAliases.indexOf(s.contentTypeAlias); + }); + + // Convert stored nodes + if ($scope.model.value) { + for (var i = 0; i < $scope.model.value.length; i++) { + var item = $scope.model.value[i]; + var scaffold = $scope.getScaffold(item.ncContentTypeAlias); + if (scaffold == null) { + // No such scaffold - the content type might have been deleted. We need to skip it. + continue; + } + initNode(scaffold, item); + } + } + + // Enforce min items + if ($scope.nodes.length < $scope.model.config.minItems) { + for (var i = $scope.nodes.length; i < $scope.model.config.minItems; i++) { + $scope.addNode($scope.scaffolds[0].contentTypeAlias); + } + } + + // If there is only one item, set it as current node + if ($scope.singleMode) { + $scope.currentNode = $scope.nodes[0]; + } + + inited = true; + } + } + + var initNode = function (scaffold, item) { + var node = angular.copy(scaffold); + + node.id = guid(); + node.ncContentTypeAlias = scaffold.contentTypeAlias; + + for (var t = 0; t < node.tabs.length; t++) { + var tab = node.tabs[t]; + for (var p = 0; p < tab.properties.length; p++) { + var prop = tab.properties[p]; + // Force validation to occur server side as this is the + // only way we can have consistancy between mandatory and + // regex validation messages. Not ideal, but it works. + prop.validation = { + mandatory: false, + pattern: "" + }; + if (item) { + if (item[prop.alias]) { + prop.value = item[prop.alias]; + } + } + } + } + + $scope.nodes.push(node); + + return node; + } + + $scope.$watch("nodes", function () { + if (inited) { + var newValues = []; + for (var i = 0; i < $scope.nodes.length; i++) { + var node = $scope.nodes[i]; + var newValue = { + name: node.name, + ncContentTypeAlias: node.ncContentTypeAlias + }; + for (var t = 0; t < node.tabs.length; t++) { + var tab = node.tabs[t]; + for (var p = 0; p < tab.properties.length; p++) { + var prop = tab.properties[p]; + if (typeof prop.value !== "function") { + newValue[prop.alias] = prop.value; + } + } + } + newValues.push(newValue); + } + $scope.model.value = newValues; + } + }, true); + + var guid = function () { + function _p8(s) { + var p = (Math.random().toString(16) + "000000000").substr(2, 8); + return s ? "-" + p.substr(0, 4) + "-" + p.substr(4, 4) : p; + } + return _p8() + _p8(true) + _p8(true) + _p8(); + }; + } + +]); \ 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 similarity index 100% rename from Src/Our.Umbraco.NestedContent/Web/UI/App_Plugins/NestedContent/Js/nestedcontent.directives.js rename to src/Our.Umbraco.NestedContent/Web/UI/App_Plugins/NestedContent/Js/nestedcontent.directives.js 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 similarity index 58% rename from Src/Our.Umbraco.NestedContent/Web/UI/App_Plugins/NestedContent/Js/nestedcontent.resources.js rename to src/Our.Umbraco.NestedContent/Web/UI/App_Plugins/NestedContent/Js/nestedcontent.resources.js index 2d5a669..192a501 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 @@ -1,13 +1,6 @@ angular.module('umbraco.resources').factory('Our.Umbraco.NestedContent.Resources.NestedContentResources', function ($q, $http, umbRequestHelper) { return { - getContentTypeAliasByGuid: function (guid) { - var url = "/umbraco/backoffice/NestedContent/NestedContentApi/GetContentTypeAliasByGuid?guid=" + guid; - return umbRequestHelper.resourcePromise( - $http.get(url), - 'Failed to retrieve datatype alias by guid' - ); - }, getContentTypes: function () { var url = "/umbraco/backoffice/NestedContent/NestedContentApi/GetContentTypes"; return umbRequestHelper.resourcePromise( diff --git a/Src/Our.Umbraco.NestedContent/Web/UI/App_Plugins/NestedContent/Js/nestedcontent.services.js b/src/Our.Umbraco.NestedContent/Web/UI/App_Plugins/NestedContent/Js/nestedcontent.services.js similarity index 100% rename from Src/Our.Umbraco.NestedContent/Web/UI/App_Plugins/NestedContent/Js/nestedcontent.services.js rename to src/Our.Umbraco.NestedContent/Web/UI/App_Plugins/NestedContent/Js/nestedcontent.services.js 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 new file mode 100644 index 0000000..e0b7e83 --- /dev/null +++ b/src/Our.Umbraco.NestedContent/Web/UI/App_Plugins/NestedContent/Views/nestedcontent.doctypepicker.html @@ -0,0 +1,56 @@ +
+
+ + + + + + + + + + + + + + + + + +
+ + Document type + + Tab alias + + Name template + +
+ + + + + + + + Remove +
+
+ Add + +
+
+
+
+

+ Tab:
+ Enter the alias of the tab who's properties should be displayed. If left blank, the first tab on the doc type will be used. +

+

+ Name template:
+ Enter an angular expression to evaluate against each item for its name. +

+
+
\ No newline at end of file diff --git a/Src/Our.Umbraco.NestedContent/Web/UI/App_Plugins/NestedContent/Views/nestedcontent.editor.html b/src/Our.Umbraco.NestedContent/Web/UI/App_Plugins/NestedContent/Views/nestedcontent.editor.html similarity index 100% rename from Src/Our.Umbraco.NestedContent/Web/UI/App_Plugins/NestedContent/Views/nestedcontent.editor.html rename to src/Our.Umbraco.NestedContent/Web/UI/App_Plugins/NestedContent/Views/nestedcontent.editor.html 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 similarity index 70% rename from Src/Our.Umbraco.NestedContent/Web/UI/App_Plugins/NestedContent/Views/nestedcontent.html rename to src/Our.Umbraco.NestedContent/Web/UI/App_Plugins/NestedContent/Views/nestedcontent.html index 67a48e0..9966bd4 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 @@ -7,7 +7,7 @@ @@ -36,10 +36,26 @@ +
+
+
+ +
+ +
+
+ \ No newline at end of file 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 similarity index 100% rename from Src/Our.Umbraco.NestedContent/Web/UI/App_Plugins/NestedContent/package.manifest rename to src/Our.Umbraco.NestedContent/Web/UI/App_Plugins/NestedContent/package.manifest diff --git a/Src/Our.Umbraco.NestedContent/app.config b/src/Our.Umbraco.NestedContent/app.config similarity index 100% rename from Src/Our.Umbraco.NestedContent/app.config rename to src/Our.Umbraco.NestedContent/app.config diff --git a/Src/Our.Umbraco.NestedContent/packages.config b/src/Our.Umbraco.NestedContent/packages.config similarity index 100% rename from Src/Our.Umbraco.NestedContent/packages.config rename to src/Our.Umbraco.NestedContent/packages.config diff --git a/src/packages/repositories.config b/src/packages/repositories.config new file mode 100644 index 0000000..1de4387 --- /dev/null +++ b/src/packages/repositories.config @@ -0,0 +1,4 @@ + + + + \ No newline at end of file