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 @@ +