From fddea8c815e893ea4a219f7ebe2646e33933f2cd Mon Sep 17 00:00:00 2001 From: Shannon Date: Thu, 12 Apr 2018 21:29:36 +1000 Subject: [PATCH] Fixes data type pre-value issues with MNTP, removes unecessary contentpicker.html and memberpicker.html overlays since treepicker.html is all that should be used, cleans up the treepicker one to be more inline with our current angular standards. Removes uneeded event handling from treepicker --- .../ConfigurationEditorOfTConfiguration.cs | 22 +- .../PropertyEditors/ConfigurationField.cs | 9 +- .../lib/umbraco/Extensions.js | 15 +- .../overlays/contentpicker/contentpicker.html | 48 -- .../overlays/memberpicker/memberpicker.html | 47 -- .../querybuilder/querybuilder.controller.js | 6 +- .../treepicker/treepicker.controller.js | 433 +++++++++--------- .../overlays/treepicker/treepicker.html | 66 +-- .../contentblueprints/create.controller.js | 5 +- .../src/views/datatypes/create.controller.js | 5 +- .../views/membertypes/create.controller.js | 6 +- .../prevalueeditors/mediapicker.controller.js | 4 +- .../prevalueeditors/treepicker.controller.js | 4 +- .../contentpicker/contentpicker.controller.js | 29 +- .../memberpicker/memberpicker.controller.js | 4 +- .../relatedlinks/relatedlinks.controller.js | 28 +- .../src/views/users/group.controller.js | 8 +- .../src/views/users/user.controller.js | 4 +- .../MultiNodePickerConfiguration.cs | 9 +- .../MultiNodePickerConfigurationEditor.cs | 4 +- .../MultiNodePickerConfigurationTreeSource.cs | 20 + .../Trees/DataTypeTreeController.cs | 2 +- src/Umbraco.Web/Umbraco.Web.csproj | 1 + 23 files changed, 388 insertions(+), 391 deletions(-) delete mode 100644 src/Umbraco.Web.UI.Client/src/views/common/overlays/contentpicker/contentpicker.html delete mode 100644 src/Umbraco.Web.UI.Client/src/views/common/overlays/memberpicker/memberpicker.html create mode 100644 src/Umbraco.Web/PropertyEditors/MultiNodePickerConfigurationTreeSource.cs diff --git a/src/Umbraco.Core/PropertyEditors/ConfigurationEditorOfTConfiguration.cs b/src/Umbraco.Core/PropertyEditors/ConfigurationEditorOfTConfiguration.cs index 15676f134c..2145a580e4 100644 --- a/src/Umbraco.Core/PropertyEditors/ConfigurationEditorOfTConfiguration.cs +++ b/src/Umbraco.Core/PropertyEditors/ConfigurationEditorOfTConfiguration.cs @@ -44,6 +44,7 @@ namespace Umbraco.Core.PropertyEditors Key = string.IsNullOrWhiteSpace(attribute.Key) ? property.Name : attribute.Key, Name = attribute.Name, PropertyName = property.Name, + PropertyType = property.PropertyType, Description = attribute.Description, HideLabel = attribute.HideLabel, View = attribute.View @@ -67,6 +68,7 @@ namespace Umbraco.Core.PropertyEditors fields.Add(field); field.PropertyName = property.Name; + field.PropertyType = property.PropertyType; if (!string.IsNullOrWhiteSpace(attribute.Key)) field.Key = attribute.Key; @@ -137,7 +139,25 @@ namespace Umbraco.Core.PropertyEditors // only keep fields that have a non-null/empty value // rest will fall back to default during ToObject() if (editorValues.TryGetValue(field.Key, out var value) && value != null && (!(value is string stringValue) || !string.IsNullOrWhiteSpace(stringValue))) - o[field.PropertyName] = value is JToken jtoken ? jtoken : JToken.FromObject(value); + { + if (value is JToken jtoken) + { + //if it's a jtoken then set it + o[field.PropertyName] = jtoken; + } + else if (field.PropertyType == typeof(bool) && value is string sBool) + { + //if it's a boolean property type but a string is found, try to do a conversion + var converted = sBool.TryConvertTo(); + if (converted) + o[field.PropertyName] = converted.Result; + } + else + { + //default behavior + o[field.PropertyName] = JToken.FromObject(value); + } + } } return o.ToObject(); diff --git a/src/Umbraco.Core/PropertyEditors/ConfigurationField.cs b/src/Umbraco.Core/PropertyEditors/ConfigurationField.cs index f959e50063..3acd67b0c2 100644 --- a/src/Umbraco.Core/PropertyEditors/ConfigurationField.cs +++ b/src/Umbraco.Core/PropertyEditors/ConfigurationField.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Linq; using Newtonsoft.Json; using Umbraco.Core.IO; @@ -63,6 +64,12 @@ namespace Umbraco.Core.PropertyEditors [JsonIgnore] public string PropertyName { get; set; } + /// + /// Gets or sets the property clr type of the field. + /// + [JsonIgnore] + public Type PropertyType { get; set; } + /// /// Gets or sets the description of the field. /// diff --git a/src/Umbraco.Web.UI.Client/lib/umbraco/Extensions.js b/src/Umbraco.Web.UI.Client/lib/umbraco/Extensions.js index b70a6b12bc..17105a7d7a 100644 --- a/src/Umbraco.Web.UI.Client/lib/umbraco/Extensions.js +++ b/src/Umbraco.Web.UI.Client/lib/umbraco/Extensions.js @@ -330,6 +330,19 @@ return false; }; } + + if (!Object.toBoolean) { + /** Converts a string/integer/bool to true/false */ + Object.toBoolean = function (obj) { + if ((typeof obj) === "boolean") { + return obj; + } + if (obj === "1" || obj === 1 || obj === "true") { + return true; + } + return false; + }; + } -})(); \ No newline at end of file +})(); diff --git a/src/Umbraco.Web.UI.Client/src/views/common/overlays/contentpicker/contentpicker.html b/src/Umbraco.Web.UI.Client/src/views/common/overlays/contentpicker/contentpicker.html deleted file mode 100644 index 43eab532d4..0000000000 --- a/src/Umbraco.Web.UI.Client/src/views/common/overlays/contentpicker/contentpicker.html +++ /dev/null @@ -1,48 +0,0 @@ -
- -
- -
- - -
- - - - -
- - -
- -
- - - - -
\ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/views/common/overlays/memberpicker/memberpicker.html b/src/Umbraco.Web.UI.Client/src/views/common/overlays/memberpicker/memberpicker.html deleted file mode 100644 index 00475dddfe..0000000000 --- a/src/Umbraco.Web.UI.Client/src/views/common/overlays/memberpicker/memberpicker.html +++ /dev/null @@ -1,47 +0,0 @@ -
- -
- -
- - -
- - - - -
- - -
-
- - - - -
diff --git a/src/Umbraco.Web.UI.Client/src/views/common/overlays/querybuilder/querybuilder.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/overlays/querybuilder/querybuilder.controller.js index ea9ad6ca97..176f23f687 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/overlays/querybuilder/querybuilder.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/common/overlays/querybuilder/querybuilder.controller.js @@ -82,7 +82,9 @@ function chooseSource(query) { vm.contentPickerOverlay = { - view: "contentpicker", + view: "treepicker", + section: "content", + treeAlias: "content", show: true, submit: function(model) { @@ -210,4 +212,4 @@ angular.module("umbraco").controller("Umbraco.Overlays.QueryBuilderController", QueryBuilderOverlayController); -})(); \ No newline at end of file +})(); diff --git a/src/Umbraco.Web.UI.Client/src/views/common/overlays/treepicker/treepicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/overlays/treepicker/treepicker.controller.js index 3eb34d63cc..8c14b422de 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/overlays/treepicker/treepicker.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/common/overlays/treepicker/treepicker.controller.js @@ -14,175 +14,177 @@ angular.module("umbraco").controller("Umbraco.Overlays.TreePickerController", mediaResource, memberResource, languageResource) { - - var tree = null; - var dialogOptions = $scope.model; - $scope.treeReady = false; - $scope.dialogTreeEventHandler = $({}); - $scope.section = dialogOptions.section; - $scope.treeAlias = dialogOptions.treeAlias; - $scope.multiPicker = dialogOptions.multiPicker; - $scope.hideHeader = (typeof dialogOptions.hideHeader) === "boolean" ? dialogOptions.hideHeader : true; + + //used as the result selection + $scope.model.selection = []; + + //the tree object when it loads + var tree = null; + + // Search and listviews is only working for content, media and member section + var searchableSections = ["content", "media", "member"]; + + var vm = this; + vm.treeReady = false; + vm.dialogTreeEventHandler = $({}); + vm.section = $scope.model.section; + vm.treeAlias = $scope.model.treeAlias; + vm.multiPicker = $scope.model.multiPicker; + vm.hideHeader = (typeof $scope.model.hideHeader) === "boolean" ? $scope.model.hideHeader : true; // if you need to load a not initialized tree set this value to false - default is true - $scope.onlyInitialized = dialogOptions.onlyInitialized; - $scope.searchInfo = { - searchFromId: dialogOptions.startNodeId, + vm.onlyInitialized = $scope.model.onlyInitialized; + vm.searchInfo = { + searchFromId: $scope.model.startNodeId, searchFromName: null, showSearch: false, results: [], selectedSearchResults: [] - } - - $scope.model.selection = []; - + } + vm.startNodeId = $scope.model.startNodeId; //Used for toggling an empty-state message //Some trees can have no items (dictionary & forms email templates) - $scope.hasItems = true; - $scope.emptyStateMessage = dialogOptions.emptyStateMessage; + vm.hasItems = true; + vm.emptyStateMessage = $scope.model.emptyStateMessage; + vm.languages = []; + vm.selectedLanguage = {}; + vm.languageSelectorIsOpen = false; + vm.showLanguageSelector = $scope.model.showLanguageSelector; + // Allow the entity type to be passed in but defaults to Document for backwards compatibility. + vm.entityType = $scope.model.entityType ? $scope.model.entityType : "Document"; + vm.enableSearh = searchableSections.indexOf(vm.section) !== -1; + + + vm.toggleLanguageSelector = toggleLanguageSelector; + vm.selectLanguage = selectLanguage; + vm.onSearchResults = onSearchResults; + vm.hideSearch = hideSearch; + vm.closeMiniListView = closeMiniListView; + vm.selectListViewNode = selectListViewNode; + + /** + * Performs the initialization of this component + */ + function onInit () { + + // load languages + languageResource.getAll().then(function (languages) { + vm.languages = languages; - $scope.languages = []; - $scope.selectedLanguage = {}; - $scope.page = {}; - $scope.page.languageSelectorIsOpen = false; - - var evts = []; - - // Listen for language updates - evts.push(eventsService.on("editors.languages.languageDeleted", - function (e, args) { - languageResource.getAll().then(function (languages) { - $scope.languages = languages; + // set the default language + vm.languages.forEach(function (language) { + if (language.isDefault) { + vm.selectedLanguage = language; + vm.languageSelectorIsOpen = false; + } }); - })); + }); - evts.push(eventsService.on("editors.languages.languageCreated", - function (e, args) { - languageResource.getAll().then(function (languages) { - $scope.languages = languages; - }); - })); - - // load languages - languageResource.getAll().then(function (languages) { - $scope.languages = languages; - - // select the default language - $scope.languages.forEach(function (language) { - if (language.isDefault) { - $scope.selectLanguage(language); - } - }); - }); - - $scope.selectLanguage = function (language) { - $scope.selectedLanguage = language; - // close the language selector - $scope.page.languageSelectorIsOpen = false; - }; - - $scope.toggleLanguageSelector = function () { - $scope.page.languageSelectorIsOpen = !$scope.page.languageSelectorIsOpen; - }; - - - //This is called from ng-init - //it turns out it is called from the angular html : / Have a look at views/common / overlays / contentpicker / contentpicker.html you'll see ng-init. - //this is probably an anti pattern IMO and shouldn't be used - $scope.init = function (contentType) { - - if (contentType === "content") { - $scope.entityType = "Document"; + if (vm.treeAlias === "content") { + vm.entityType = "Document"; if (!$scope.model.title) { $scope.model.title = localizationService.localize("defaultdialogs_selectContent"); } - } else if (contentType === "member") { - $scope.entityType = "Member"; + } + else if (vm.treeAlias === "member" || vm.section) { + vm.entityType = "Member"; if (!$scope.model.title) { $scope.model.title = localizationService.localize("defaultdialogs_selectMember"); } - } else if (contentType === "media") { - $scope.entityType = "Media"; + } + else if (vm.treeAlias === "media" || vm.section === "media") { + vm.entityType = "Media"; if (!$scope.model.title) { $scope.model.title = localizationService.localize("defaultdialogs_selectMedia"); } + } + + //var searchText = "Search..."; + //localizationService.localize("general_search").then(function (value) { + // searchText = value + "..."; + //}); + + //min / max values + //TODO: Where are these used? + if ($scope.model.minNumber) { + $scope.model.minNumber = parseInt($scope.model.minNumber, 10); + } + if ($scope.model.maxNumber) { + $scope.model.maxNumber = parseInt($scope.model.maxNumber, 10); } - } - var searchText = "Search..."; - localizationService.localize("general_search").then(function (value) { - searchText = value + "..."; - }); - - // Allow the entity type to be passed in but defaults to Document for backwards compatibility. - $scope.entityType = dialogOptions.entityType ? dialogOptions.entityType : "Document"; - - - //min / max values - if (dialogOptions.minNumber) { - dialogOptions.minNumber = parseInt(dialogOptions.minNumber, 10); - } - if (dialogOptions.maxNumber) { - dialogOptions.maxNumber = parseInt(dialogOptions.maxNumber, 10); - } - - if (dialogOptions.section === "member") { - $scope.entityType = "Member"; - } else if (dialogOptions.section === "media") { - $scope.entityType = "Media"; - } - - // Search and listviews is only working for content, media and member section - var searchableSections = ["content", "media", "member"]; - - $scope.enableSearh = searchableSections.indexOf($scope.section) !== -1; - - //if a alternative startnode is used, we need to check if it is a container - if ($scope.enableSearh && - dialogOptions.startNodeId && - dialogOptions.startNodeId !== -1 && - dialogOptions.startNodeId !== "-1") { - entityResource.getById(dialogOptions.startNodeId, $scope.entityType).then(function (node) { - if (node.metaData.IsContainer) { - openMiniListView(node); - } + //if a alternative startnode is used, we need to check if it is a container + if (vm.enableSearh && + vm.startNodeId && + vm.startNodeId !== -1 && + vm.startNodeId !== "-1") { + entityResource.getById(vm.startNodeId, vm.entityType).then(function (node) { + if (node.metaData.IsContainer) { + openMiniListView(node); + } + initTree(); + }); + } + else { initTree(); - }); - } else { - initTree(); - } + } - //Configures filtering - if (dialogOptions.filter) { + //Configures filtering + if ($scope.model.filter) { - dialogOptions.filterExclude = false; - dialogOptions.filterAdvanced = false; - - //used advanced filtering - if (angular.isFunction(dialogOptions.filter)) { - dialogOptions.filterAdvanced = true; - } else if (angular.isObject(dialogOptions.filter)) { - dialogOptions.filterAdvanced = true; - } else { - if (dialogOptions.filter.startsWith("!")) { - dialogOptions.filterExclude = true; - dialogOptions.filter = dialogOptions.filter.substring(1); - } + $scope.model.filterExclude = false; + $scope.model.filterAdvanced = false; //used advanced filtering - if (dialogOptions.filter.startsWith("{")) { - dialogOptions.filterAdvanced = true; - //convert to object - dialogOptions.filter = angular.fromJson(dialogOptions.filter); + if (angular.isFunction($scope.model.filter)) { + $scope.model.filterAdvanced = true; + } + else if (angular.isObject($scope.model.filter)) { + $scope.model.filterAdvanced = true; + } + else { + if ($scope.model.filter.startsWith("!")) { + $scope.model.filterExclude = true; + $scope.model.filter = $scope.model.filter.substring(1); + } + + //used advanced filtering + if ($scope.model.filter.startsWith("{")) { + $scope.model.filterAdvanced = true; + //convert to object + $scope.model.filter = angular.fromJson($scope.model.filter); + } } } } - + + /** + * Updates the tree's query parameters + */ function initTree() { - //create the custom query string param for this tree - $scope.customTreeParams = dialogOptions.startNodeId ? "startNodeId=" + dialogOptions.startNodeId : ""; - $scope.customTreeParams += dialogOptions.customTreeParams ? "&" + dialogOptions.customTreeParams : ""; - $scope.treeReady = true; + //create the custom query string param for this tree + var queryParams = {}; + if (vm.startNodeId) { + queryParams["startNodeId"] = $scope.model.startNodeId; + } + if (vm.selectedLanguage && vm.selectedLanguage.id) { + queryParams["languageId"] = vm.selectedLanguage.id; + } + var queryString = $.param(queryParams); //create the query string from the params object + vm.customTreeParams = queryString + (queryString ? "&" : "") + ($scope.model.customTreeParams ? $scope.model.customTreeParams : ""); + + vm.treeReady = true; } + + function selectLanguage(language) { + vm.selectedLanguage = language; + // close the language selector + vm.languageSelectorIsOpen = false; + initTree(); + }; + + function toggleLanguageSelector() { + vm.languageSelectorIsOpen = !vm.languageSelectorIsOpen; + }; function nodeExpandedHandler(ev, args) { @@ -199,7 +201,7 @@ angular.module("umbraco").controller("Umbraco.Overlays.TreePickerController", //now we need to look in the already selected search results and // toggle the check boxes for those ones that are listed - var exists = _.find($scope.searchInfo.selectedSearchResults, + var exists = _.find(vm.searchInfo.selectedSearchResults, function (selected) { return child.id == selected.id; }); @@ -216,7 +218,7 @@ angular.module("umbraco").controller("Umbraco.Overlays.TreePickerController", //gets the tree object when it loads function treeLoadedHandler(ev, args) { //args.tree contains children (args.tree.root.children) - $scope.hasItems = args.tree.root.children.length > 0; + vm.hasItems = args.tree.root.children.length > 0; tree = args.tree; } @@ -240,11 +242,12 @@ angular.module("umbraco").controller("Umbraco.Overlays.TreePickerController", }); //remove it from the custom tracked search result list - $scope.searchInfo.selectedSearchResults = _.reject($scope.searchInfo.selectedSearchResults, + vm.searchInfo.selectedSearchResults = _.reject(vm.searchInfo.selectedSearchResults, function (i) { return i.id == args.node.id; }); - } else { + } + else { eventsService.emit("dialogs.treePickerController.select", args); if (args.node.filtered) { @@ -255,7 +258,8 @@ angular.module("umbraco").controller("Umbraco.Overlays.TreePickerController", //from the server in this method. if ($scope.model.select) { $scope.model.select(args.node); - } else { + } + else { select(args.node.name, args.node.id); //toggle checked state args.node.selected = args.node.selected === true ? false : true; @@ -276,40 +280,46 @@ angular.module("umbraco").controller("Umbraco.Overlays.TreePickerController", name: text }; - if ($scope.multiPicker) { + if (vm.multiPicker) { if (entity) { multiSelectItem(entity); - } else { + } + else { multiSelectItem(rootNode); } - } else { + } + else { $scope.model.selection.push(rootNode); $scope.model.submit($scope.model); } - } else { + } + else { - if ($scope.multiPicker) { + if (vm.multiPicker) { if (entity) { multiSelectItem(entity); - } else { + } + else { //otherwise we have to get it from the server - entityResource.getById(id, $scope.entityType).then(function (ent) { + entityResource.getById(id, vm.entityType).then(function (ent) { multiSelectItem(ent); }); } - } else { + } + else { - $scope.hideSearch(); + hideSearch(); //if an entity has been passed in, use it if (entity) { $scope.model.selection.push(entity); $scope.model.submit($scope.model); - } else { + } + else { //otherwise we have to get it from the server - entityResource.getById(id, $scope.entityType).then(function (ent) { + entityResource.getById(id, vm.entityType).then(function (ent) { $scope.model.selection.push(ent); $scope.model.submit($scope.model); }); @@ -335,7 +345,8 @@ angular.module("umbraco").controller("Umbraco.Overlays.TreePickerController", if (found) { $scope.model.selection.splice(foundIndex, 1); - } else { + } + else { $scope.model.selection.push(item); } @@ -343,7 +354,7 @@ angular.module("umbraco").controller("Umbraco.Overlays.TreePickerController", function performFiltering(nodes) { - if (!dialogOptions.filter) { + if (!$scope.model.filter) { return; } @@ -354,52 +365,57 @@ angular.module("umbraco").controller("Umbraco.Overlays.TreePickerController", return !angular.isObject(n.metaData.listViewNode); }); - if (dialogOptions.filterAdvanced) { + if ($scope.model.filterAdvanced) { //filter either based on a method or an object - var filtered = angular.isFunction(dialogOptions.filter) - ? _.filter(nodes, dialogOptions.filter) - : _.where(nodes, dialogOptions.filter); + var filtered = angular.isFunction($scope.model.filter) + ? _.filter(nodes, $scope.model.filter) + : _.where(nodes, $scope.model.filter); angular.forEach(filtered, function (value, key) { value.filtered = true; - if (dialogOptions.filterCssClass) { + if ($scope.model.filterCssClass) { if (!value.cssClasses) { value.cssClasses = []; } - value.cssClasses.push(dialogOptions.filterCssClass); + value.cssClasses.push($scope.model.filterCssClass); } }); - } else { - var a = dialogOptions.filter.toLowerCase().replace(/\s/g, '').split(','); + } + else { + var a = $scope.model.filter.toLowerCase().replace(/\s/g, '').split(','); angular.forEach(nodes, function (value, key) { var found = a.indexOf(value.metaData.contentType.toLowerCase()) >= 0; - if (!dialogOptions.filterExclude && !found || dialogOptions.filterExclude && found) { + if (!$scope.model.filterExclude && !found || $scope.model.filterExclude && found) { value.filtered = true; - if (dialogOptions.filterCssClass) { + if ($scope.model.filterCssClass) { if (!value.cssClasses) { value.cssClasses = []; } - value.cssClasses.push(dialogOptions.filterCssClass); + value.cssClasses.push($scope.model.filterCssClass); } } }); } } + + function openMiniListView(node) { + vm.miniListView = node; + } - $scope.multiSubmit = function (result) { - entityResource.getByIds(result, $scope.entityType).then(function (ents) { + function multiSubmit(result) { + entityResource.getByIds(result, vm.entityType).then(function (ents) { $scope.submit(ents); }); - }; + } /** method to select a search result */ - $scope.selectResult = function (evt, result) { + function selectResult(evt, result) { if (result.filtered) { return; @@ -412,9 +428,10 @@ angular.module("umbraco").controller("Umbraco.Overlays.TreePickerController", //add/remove to our custom tracked list of selected search results if (result.selected) { - $scope.searchInfo.selectedSearchResults.push(result); - } else { - $scope.searchInfo.selectedSearchResults = _.reject($scope.searchInfo.selectedSearchResults, + vm.searchInfo.selectedSearchResults.push(result); + } + else { + vm.searchInfo.selectedSearchResults = _.reject(vm.searchInfo.selectedSearchResults, function (i) { return i.id == result.id; }); @@ -427,10 +444,9 @@ angular.module("umbraco").controller("Umbraco.Overlays.TreePickerController", found.selected = result.selected; } } + } - }; - - $scope.hideSearch = function () { + function hideSearch() { //Traverse the entire displayed tree and update each node to sync with the selected search results if (tree) { @@ -441,7 +457,7 @@ angular.module("umbraco").controller("Umbraco.Overlays.TreePickerController", _.each(children, function (child) { //check if the id is in the selection, if so ensure it's flagged as selected - var exists = _.find($scope.searchInfo.selectedSearchResults, + var exists = _.find(vm.searchInfo.selectedSearchResults, function (selected) { return child.id == selected.id; }); @@ -470,7 +486,7 @@ angular.module("umbraco").controller("Umbraco.Overlays.TreePickerController", return c === 'tree-node-slide-up-hide-active'; }); - var listViewResults = _.filter($scope.searchInfo.selectedSearchResults, + var listViewResults = _.filter(vm.searchInfo.selectedSearchResults, function (i) { return i.parentId == child.id; }); @@ -510,13 +526,13 @@ angular.module("umbraco").controller("Umbraco.Overlays.TreePickerController", } - $scope.searchInfo.showSearch = false; - $scope.searchInfo.searchFromId = dialogOptions.startNodeId; - $scope.searchInfo.searchFromName = null; - $scope.searchInfo.results = []; + vm.searchInfo.showSearch = false; + vm.searchInfo.searchFromId = vm.startNodeId; + vm.searchInfo.searchFromName = null; + vm.searchInfo.results = []; } - $scope.onSearchResults = function (results) { + function onSearchResults(results) { //filter all items - this will mark an item as filtered performFiltering(results); @@ -527,10 +543,10 @@ angular.module("umbraco").controller("Umbraco.Overlays.TreePickerController", return !item.filtered; }); - $scope.searchInfo.results = results; + vm.searchInfo.results = results; //sync with the curr selected results - _.each($scope.searchInfo.results, + _.each(vm.searchInfo.results, function (result) { var exists = _.find($scope.model.selection, function (selectedId) { @@ -541,40 +557,31 @@ angular.module("umbraco").controller("Umbraco.Overlays.TreePickerController", } }); - $scope.searchInfo.showSearch = true; - }; - - $scope.dialogTreeEventHandler.bind("treeLoaded", treeLoadedHandler); - $scope.dialogTreeEventHandler.bind("treeNodeExpanded", nodeExpandedHandler); - $scope.dialogTreeEventHandler.bind("treeNodeSelect", nodeSelectHandler); - - $scope.$on('$destroy', - function () { - $scope.dialogTreeEventHandler.unbind("treeLoaded", treeLoadedHandler); - $scope.dialogTreeEventHandler.unbind("treeNodeExpanded", nodeExpandedHandler); - $scope.dialogTreeEventHandler.unbind("treeNodeSelect", nodeSelectHandler); - }); - - $scope.selectListViewNode = function (node) { + vm.searchInfo.showSearch = true; + } + + function selectListViewNode(node) { select(node.name, node.id); //toggle checked state node.selected = node.selected === true ? false : true; - }; - - $scope.closeMiniListView = function () { - $scope.miniListView = undefined; - }; - - function openMiniListView(node) { - $scope.miniListView = node; } - //ensure to unregister from all events! + function closeMiniListView() { + vm.miniListView = undefined; + } + + vm.dialogTreeEventHandler.bind("treeLoaded", treeLoadedHandler); + vm.dialogTreeEventHandler.bind("treeNodeExpanded", nodeExpandedHandler); + vm.dialogTreeEventHandler.bind("treeNodeSelect", nodeSelectHandler); + $scope.$on('$destroy', function () { - for (var e in evts) { - eventsService.unsubscribe(evts[e]); - } + vm.dialogTreeEventHandler.unbind("treeLoaded", treeLoadedHandler); + vm.dialogTreeEventHandler.unbind("treeNodeExpanded", nodeExpandedHandler); + vm.dialogTreeEventHandler.unbind("treeNodeSelect", nodeSelectHandler); }); + + //initialize + onInit(); }); diff --git a/src/Umbraco.Web.UI.Client/src/views/common/overlays/treepicker/treepicker.html b/src/Umbraco.Web.UI.Client/src/views/common/overlays/treepicker/treepicker.html index 4321a1d2d8..fd198bb0fb 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/overlays/treepicker/treepicker.html +++ b/src/Umbraco.Web.UI.Client/src/views/common/overlays/treepicker/treepicker.html @@ -1,59 +1,59 @@ -
+
-
-
-
{{selectedLanguage.name}}
-   +
+
+
{{vm.selectedLanguage.name}}
+  
-
- +
- + - - {{ emptyStateMessage }} + + {{ vm.emptyStateMessage }} -
- + + enablecheckboxes="{{vm.multiPicker}}">
- +
diff --git a/src/Umbraco.Web.UI.Client/src/views/contentblueprints/create.controller.js b/src/Umbraco.Web.UI.Client/src/views/contentblueprints/create.controller.js index 31089f4281..a2a1f00117 100644 --- a/src/Umbraco.Web.UI.Client/src/views/contentblueprints/create.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/contentblueprints/create.controller.js @@ -6,10 +6,11 @@ * @description * The controller for creating content blueprints */ -function ContentBlueprintCreateController($scope, $location, contentTypeResource, navigationService) { +function ContentBlueprintCreateController($scope, $location, contentTypeResource, navigationService, appState) { var vm = this; var node = $scope.dialogOptions.currentNode; + var section = appState.getSectionState("currentSection"); vm.createBlueprint = createBlueprint; @@ -25,7 +26,7 @@ function ContentBlueprintCreateController($scope, $location, contentTypeResource } function createBlueprint(documentType) { - $location.path("/settings/contentBlueprints/edit/" + node.id).search("create", "true").search("doctype", documentType.alias); + $location.path("/" + section + "/contentBlueprints/edit/" + node.id).search("create", "true").search("doctype", documentType.alias); navigationService.hideMenu(); } diff --git a/src/Umbraco.Web.UI.Client/src/views/datatypes/create.controller.js b/src/Umbraco.Web.UI.Client/src/views/datatypes/create.controller.js index 79c3380964..39770ad4e0 100644 --- a/src/Umbraco.Web.UI.Client/src/views/datatypes/create.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/datatypes/create.controller.js @@ -14,6 +14,7 @@ function DataTypeCreateController($scope, $location, navigationService, dataType }; var node = $scope.dialogOptions.currentNode; + var section = appState.getSectionState("currentSection"); $scope.showCreateFolder = function() { $scope.model.creatingFolder = true; @@ -29,8 +30,6 @@ function DataTypeCreateController($scope, $location, navigationService, dataType formHelper.resetForm({ scope: $scope }); - var section = appState.getSectionState("currentSection"); - }, function(err) { //TODO: Handle errors @@ -40,7 +39,7 @@ function DataTypeCreateController($scope, $location, navigationService, dataType $scope.createDataType = function() { $location.search('create', null); - $location.path("/developer/datatypes/edit/" + node.id).search("create", "true"); + $location.path("/" + section + "/datatypes/edit/" + node.id).search("create", "true"); navigationService.hideMenu(); } } diff --git a/src/Umbraco.Web.UI.Client/src/views/membertypes/create.controller.js b/src/Umbraco.Web.UI.Client/src/views/membertypes/create.controller.js index c2bc087c28..ea72b23560 100644 --- a/src/Umbraco.Web.UI.Client/src/views/membertypes/create.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/membertypes/create.controller.js @@ -14,7 +14,7 @@ function MemberTypesCreateController($scope, $location, navigationService, membe }; var node = $scope.dialogOptions.currentNode; - + var section = appState.getSectionState("currentSection"); $scope.showCreateFolder = function() { $scope.model.creatingFolder = true; @@ -33,8 +33,6 @@ function MemberTypesCreateController($scope, $location, navigationService, membe formHelper.resetForm({ scope: $scope }); - var section = appState.getSectionState("currentSection"); - }, function(err) { //TODO: Handle errors @@ -44,7 +42,7 @@ function MemberTypesCreateController($scope, $location, navigationService, membe $scope.createMemberType = function() { $location.search('create', null); - $location.path("/settings/membertypes/edit/" + node.id).search("create", "true"); + $location.path("/" + section + "/membertypes/edit/" + node.id).search("create", "true"); navigationService.hideMenu(); } } diff --git a/src/Umbraco.Web.UI.Client/src/views/prevalueeditors/mediapicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/prevalueeditors/mediapicker.controller.js index 95b2c64a6f..3e1999cfb0 100644 --- a/src/Umbraco.Web.UI.Client/src/views/prevalueeditors/mediapicker.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/prevalueeditors/mediapicker.controller.js @@ -14,6 +14,7 @@ function mediaPickerController($scope, dialogService, entityResource, $log, icon $scope.sortable = false; var dialogOptions = { + view: "treepicker", multiPicker: false, entityType: "Media", section: "media", @@ -28,7 +29,6 @@ function mediaPickerController($scope, dialogService, entityResource, $log, icon $scope.openContentPicker = function() { $scope.contentPickerOverlay = dialogOptions; - $scope.contentPickerOverlay.view = "treePicker"; $scope.contentPickerOverlay.show = true; $scope.contentPickerOverlay.submit = function(model) { @@ -117,4 +117,4 @@ function mediaPickerController($scope, dialogService, entityResource, $log, icon } -angular.module('umbraco').controller("Umbraco.PrevalueEditors.MediaPickerController",mediaPickerController); \ No newline at end of file +angular.module('umbraco').controller("Umbraco.PrevalueEditors.MediaPickerController",mediaPickerController); diff --git a/src/Umbraco.Web.UI.Client/src/views/prevalueeditors/treepicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/prevalueeditors/treepicker.controller.js index 757429ef46..75852281b6 100644 --- a/src/Umbraco.Web.UI.Client/src/views/prevalueeditors/treepicker.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/prevalueeditors/treepicker.controller.js @@ -11,7 +11,8 @@ angular.module('umbraco') $scope.allowEdit = true; $scope.sortable = false; - var config = { + var config = { + view: "treepicker", multiPicker: false, entityType: "Document", type: "content", @@ -52,7 +53,6 @@ angular.module('umbraco') $scope.openContentPicker = function () { $scope.treePickerOverlay = config; $scope.treePickerOverlay.section = config.type; - $scope.treePickerOverlay.view = "treePicker"; $scope.treePickerOverlay.show = true; $scope.treePickerOverlay.submit = function (model) { diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.controller.js index 161f989d10..492cd44a7e 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.controller.js @@ -1,6 +1,18 @@ -//this controller simply tells the dialogs service to open a mediaPicker window -//with a specified callback, this callback will receive an object with a selection on it +/** + * The controller that is used for a couple different Property Editors: Multi Node Tree Picker, Content Picker, + * since this is used by MNTP and it supports content, media and members, there is code to deal with all 3 of those types + * @param {any} $scope + * @param {any} entityResource + * @param {any} editorState + * @param {any} iconHelper + * @param {any} $routeParams + * @param {any} angularHelper + * @param {any} navigationService + * @param {any} $location + * @param {any} miniEditorHelper + * @param {any} localizationService + */ function contentPickerController($scope, entityResource, editorState, iconHelper, $routeParams, angularHelper, navigationService, $location, miniEditorHelper, localizationService) { var unsubscribe; @@ -89,10 +101,10 @@ function contentPickerController($scope, entityResource, editorState, iconHelper } //Umbraco persists boolean for prevalues as "0" or "1" so we need to convert that! - $scope.model.config.multiPicker = ($scope.model.config.multiPicker === "1" ? true : false); - $scope.model.config.showOpenButton = ($scope.model.config.showOpenButton === "1" ? true : false); - $scope.model.config.showEditButton = ($scope.model.config.showEditButton === "1" ? true : false); - $scope.model.config.showPathOnHover = ($scope.model.config.showPathOnHover === "1" ? true : false); + $scope.model.config.multiPicker = Object.toBoolean($scope.model.config.multiPicker); + $scope.model.config.showOpenButton = Object.toBoolean($scope.model.config.showOpenButton); + $scope.model.config.showEditButton = Object.toBoolean($scope.model.config.showEditButton); + $scope.model.config.showPathOnHover = Object.toBoolean($scope.model.config.showPathOnHover); var entityType = $scope.model.config.startNode.type === "member" ? "Member" @@ -105,6 +117,7 @@ function contentPickerController($scope, entityResource, editorState, iconHelper //the dialog options for the picker var dialogOptions = { + view: "treepicker", multiPicker: $scope.model.config.multiPicker, entityType: entityType, filterCssClass: "not-allowed not-published", @@ -128,7 +141,7 @@ function contentPickerController($scope, entityResource, editorState, iconHelper //since most of the pre-value config's are used in the dialog options (i.e. maxNumber, minNumber, etc...) we'll merge the // pre-value config on to the dialog options angular.extend(dialogOptions, $scope.model.config); - + //We need to manually handle the filter for members here since the tree displayed is different and only contains // searchable list views if (entityType === "Member") { @@ -155,7 +168,6 @@ function contentPickerController($scope, entityResource, editorState, iconHelper return false; } } - if ($routeParams.section === "settings" && $routeParams.tree === "documentTypes") { //if the content-picker is being rendered inside the document-type editor, we don't need to process the startnode query dialogOptions.startNodeId = -1; @@ -173,7 +185,6 @@ function contentPickerController($scope, entityResource, editorState, iconHelper //dialog $scope.openContentPicker = function() { $scope.contentPickerOverlay = dialogOptions; - $scope.contentPickerOverlay.view = "treepicker"; $scope.contentPickerOverlay.show = true; $scope.contentPickerOverlay.submit = function(model) { diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/memberpicker/memberpicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/memberpicker/memberpicker.controller.js index efa05ae882..454d35b99c 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/memberpicker/memberpicker.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/memberpicker/memberpicker.controller.js @@ -10,7 +10,8 @@ function memberPickerController($scope, dialogService, entityResource, $log, ico $scope.renderModel = []; $scope.allowRemove = true; - var dialogOptions = { + var dialogOptions = { + view: "treepicker", multiPicker: false, entityType: "Member", section: "member", @@ -40,7 +41,6 @@ function memberPickerController($scope, dialogService, entityResource, $log, ico $scope.openMemberPicker = function() { $scope.memberPickerOverlay = dialogOptions; - $scope.memberPickerOverlay.view = "memberPicker"; $scope.memberPickerOverlay.show = true; $scope.memberPickerOverlay.submit = function(model) { diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/relatedlinks/relatedlinks.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/relatedlinks/relatedlinks.controller.js index 51c5baf55b..30cab066dd 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/relatedlinks/relatedlinks.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/relatedlinks/relatedlinks.controller.js @@ -21,11 +21,14 @@ $scope.internal = function($event) { $scope.currentEditLink = null; - $scope.contentPickerOverlay = {}; - $scope.contentPickerOverlay.view = "contentpicker"; - $scope.contentPickerOverlay.multiPicker = false; - $scope.contentPickerOverlay.show = true; - $scope.contentPickerOverlay.idType = $scope.model.config.idType ? $scope.model.config.idType : "int"; + $scope.contentPickerOverlay = { + view: "treepicker", + section: "content", + treeAlias: "content", + multiPicker: false, + show: true, + idType: $scope.model.config.idType ? $scope.model.config.idType : "int" + }; $scope.contentPickerOverlay.submit = function(model) { @@ -46,12 +49,15 @@ $scope.selectInternal = function ($event, link) { $scope.currentEditLink = link; - $scope.contentPickerOverlay = {}; - $scope.contentPickerOverlay.view = "contentpicker"; - $scope.contentPickerOverlay.multiPicker = false; - $scope.contentPickerOverlay.show = true; - $scope.contentPickerOverlay.idType = $scope.model.config.idType ? $scope.model.config.idType : "int"; - + $scope.contentPickerOverlay = { + view: "treepicker", + section: "content", + treeAlias: "content", + multiPicker: false, + show: true, + idType: $scope.model.config.idType ? $scope.model.config.idType : "int" + }; + $scope.contentPickerOverlay.submit = function(model) { select(model.selection[0]); diff --git a/src/Umbraco.Web.UI.Client/src/views/users/group.controller.js b/src/Umbraco.Web.UI.Client/src/views/users/group.controller.js index 0132991768..06f1a5cc23 100644 --- a/src/Umbraco.Web.UI.Client/src/views/users/group.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/users/group.controller.js @@ -124,7 +124,9 @@ function openContentPicker() { vm.contentPicker = { title: vm.labels.selectContentStartNode, - view: "contentpicker", + view: "treepicker", + section: "content", + treeAlias: "content", hideSubmitButton: true, hideHeader: false, show: true, @@ -215,7 +217,9 @@ function openGranularPermissionsPicker() { vm.contentPicker = { title: vm.labels.selectNode, - view: "contentpicker", + view: "treepicker", + section: "content", + treeAlias: "content", hideSubmitButton: true, show: true, submit: function (model) { diff --git a/src/Umbraco.Web.UI.Client/src/views/users/user.controller.js b/src/Umbraco.Web.UI.Client/src/views/users/user.controller.js index b2aa6c55be..2f7e69e312 100644 --- a/src/Umbraco.Web.UI.Client/src/views/users/user.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/users/user.controller.js @@ -230,7 +230,9 @@ function openContentPicker() { vm.contentPicker = { title: vm.labels.selectContentStartNode, - view: "contentpicker", + view: "treepicker", + section: "content", + treeAlias: "content", multiPicker: true, selection: vm.user.startContentIds, hideHeader: false, diff --git a/src/Umbraco.Web/PropertyEditors/MultiNodePickerConfiguration.cs b/src/Umbraco.Web/PropertyEditors/MultiNodePickerConfiguration.cs index 0e82a7179b..b6333c3140 100644 --- a/src/Umbraco.Web/PropertyEditors/MultiNodePickerConfiguration.cs +++ b/src/Umbraco.Web/PropertyEditors/MultiNodePickerConfiguration.cs @@ -1,4 +1,5 @@ -using Umbraco.Core.PropertyEditors; +using Newtonsoft.Json.Linq; +using Umbraco.Core.PropertyEditors; namespace Umbraco.Web.PropertyEditors { @@ -8,7 +9,7 @@ namespace Umbraco.Web.PropertyEditors public class MultiNodePickerConfiguration { [ConfigurationField("startNode", "Node type", "treesource")] - public string TreeSource { get; set; } + public MultiNodePickerConfigurationTreeSource TreeSource { get; set; } [ConfigurationField("filter", "Allow items of type", "textstring", Description = "Separate with comma")] public string Filter { get; set; } @@ -19,7 +20,7 @@ namespace Umbraco.Web.PropertyEditors [ConfigurationField("maxNumber", "Maximum number of items", "number")] public int MaxNumber { get; set; } - [ConfigurationField("showOpenButtom", "Show open button (this feature is in preview!)", "boolean", Description = "Opens the node in a dialog")] + [ConfigurationField("showOpenButton", "Show open button (this feature is in preview!)", "boolean", Description = "Opens the node in a dialog")] public bool ShowOpen { get; set; } } -} \ No newline at end of file +} diff --git a/src/Umbraco.Web/PropertyEditors/MultiNodePickerConfigurationEditor.cs b/src/Umbraco.Web/PropertyEditors/MultiNodePickerConfigurationEditor.cs index 48b0d7327a..a52ee2138e 100644 --- a/src/Umbraco.Web/PropertyEditors/MultiNodePickerConfigurationEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/MultiNodePickerConfigurationEditor.cs @@ -23,7 +23,7 @@ namespace Umbraco.Web.PropertyEditors // sanitize configuraiton var output = base.ToConfigurationEditor(configuration); - output["multiPicker"] = configuration.MaxNumber > 1 ? "1" : "0"; + output["multiPicker"] = configuration.MaxNumber > 1 ? true : false; return output; } @@ -39,4 +39,4 @@ namespace Umbraco.Web.PropertyEditors return d; } } -} \ No newline at end of file +} diff --git a/src/Umbraco.Web/PropertyEditors/MultiNodePickerConfigurationTreeSource.cs b/src/Umbraco.Web/PropertyEditors/MultiNodePickerConfigurationTreeSource.cs new file mode 100644 index 0000000000..ffedf6c7dc --- /dev/null +++ b/src/Umbraco.Web/PropertyEditors/MultiNodePickerConfigurationTreeSource.cs @@ -0,0 +1,20 @@ +using Newtonsoft.Json; + +namespace Umbraco.Web.PropertyEditors +{ + /// + /// Represents the 'startNode' value for the + /// + [JsonObject] + public class MultiNodePickerConfigurationTreeSource + { + [JsonProperty("type")] + public string ObjectType {get;set;} + + [JsonProperty("query")] + public string StartNodeQuery {get;set;} + + [JsonProperty("id")] + public int? StartNodeId {get;set;} + } +} diff --git a/src/Umbraco.Web/Trees/DataTypeTreeController.cs b/src/Umbraco.Web/Trees/DataTypeTreeController.cs index ce2367191d..991f87d196 100644 --- a/src/Umbraco.Web/Trees/DataTypeTreeController.cs +++ b/src/Umbraco.Web/Trees/DataTypeTreeController.cs @@ -17,7 +17,7 @@ using Constants = Umbraco.Core.Constants; namespace Umbraco.Web.Trees { [UmbracoTreeAuthorize(Constants.Trees.DataTypes)] - [Tree(Constants.Applications.Developer, Constants.Trees.DataTypes, null, sortOrder:1)] + [Tree(Constants.Applications.Settings, Constants.Trees.DataTypes, null, sortOrder:1)] [PluginController("UmbracoTrees")] [CoreTree] public class DataTypeTreeController : TreeController, ISearchableTree diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index 4d1e5fe13f..eb057193e4 100644 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -313,6 +313,7 @@ +