From 42d86f147f30be7e09ebde69760ea8b098057364 Mon Sep 17 00:00:00 2001 From: Shannon Date: Mon, 1 Jul 2019 18:23:36 +1000 Subject: [PATCH] gets content picker ignore start nodes working, fixes descriptions --- .../mediapicker/mediapicker.controller.js | 115 +++++++++--------- .../treepicker/treepicker.controller.js | 6 + .../treepicker/treepicker.html | 1 + .../contentpicker/contentpicker.controller.js | 5 +- src/Umbraco.Web/Editors/EntityController.cs | 1 + .../ContentPickerConfiguration.cs | 6 +- .../PropertyEditors/GridConfiguration.cs | 4 +- .../MediaPickerConfiguration.cs | 4 +- .../MultiNodePickerConfiguration.cs | 4 +- .../MultiUrlPickerConfiguration.cs | 4 +- .../PropertyEditors/RichTextConfiguration.cs | 4 +- .../Trees/ContentTreeControllerBase.cs | 28 +++-- 12 files changed, 103 insertions(+), 79 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/mediapicker/mediapicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/mediapicker/mediapicker.controller.js index 23d9cef9a1..015a452e67 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/mediapicker/mediapicker.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/mediapicker/mediapicker.controller.js @@ -1,7 +1,7 @@ //used for the media picker dialog angular.module("umbraco") .controller("Umbraco.Editors.MediaPickerController", - function($scope, mediaResource, entityResource, userService, mediaHelper, mediaTypeHelper, eventsService, treeService, localStorageService, localizationService, editorService) { + function ($scope, mediaResource, entityResource, userService, mediaHelper, mediaTypeHelper, eventsService, treeService, localStorageService, localizationService, editorService) { if (!$scope.model.title) { localizationService.localizeMany(["defaultdialogs_selectMedia", "general_includeFromsubFolders"]) @@ -47,7 +47,7 @@ angular.module("umbraco") $scope.acceptedMediatypes = []; mediaTypeHelper.getAllowedImagetypes($scope.startNodeId) - .then(function(types) { + .then(function (types) { $scope.acceptedMediatypes = types; }); @@ -96,7 +96,7 @@ angular.module("umbraco") //media object so we need to look it up var id = $scope.target.udi ? $scope.target.udi : $scope.target.id; var altText = $scope.target.altText; - entityResource.getById(id, "Media") + entityResource.getById(id, "Media") .then(function (node) { $scope.target = node; if (ensureWithinStartNode(node)) { @@ -106,28 +106,28 @@ angular.module("umbraco") $scope.openDetailsDialog(); } }, - gotoStartNode); + gotoStartNode); } } - $scope.upload = function(v) { + $scope.upload = function (v) { angular.element(".umb-file-dropzone .file-select").trigger("click"); }; - $scope.dragLeave = function(el, event) { + $scope.dragLeave = function (el, event) { $scope.activeDrag = false; }; - $scope.dragEnter = function(el, event) { + $scope.dragEnter = function (el, event) { $scope.activeDrag = true; }; - $scope.submitFolder = function() { + $scope.submitFolder = function () { if ($scope.model.newFolderName) { $scope.model.creatingFolder = true; mediaResource .addFolder($scope.model.newFolderName, $scope.currentFolder.id) - .then(function(data) { + .then(function (data) { //we've added a new folder so lets clear the tree cache for that specific item treeService.clearCache({ cacheKey: "__media", //this is the main media tree cache key @@ -143,14 +143,14 @@ angular.module("umbraco") } }; - $scope.enterSubmitFolder = function(event) { + $scope.enterSubmitFolder = function (event) { if (event.keyCode === 13) { $scope.submitFolder(); event.stopPropagation(); } }; - $scope.gotoFolder = function(folder) { + $scope.gotoFolder = function (folder) { if (!$scope.multiPicker) { deselectAllImages($scope.model.selection); } @@ -161,15 +161,15 @@ angular.module("umbraco") if (folder.id > 0) { entityResource.getAncestors(folder.id, "media", { dataTypeId: $scope.model.dataTypeId }) - .then(function(anc) { + .then(function (anc) { $scope.path = _.filter(anc, - function(f) { + function (f) { return f.path.indexOf($scope.startNodeId) !== -1; }); }); mediaTypeHelper.getAllowedImagetypes(folder.id) - .then(function(types) { + .then(function (types) { $scope.acceptedMediatypes = types; }); } else { @@ -183,7 +183,7 @@ angular.module("umbraco") return getChildren(folder.id); }; - $scope.clickHandler = function(image, event, index) { + $scope.clickHandler = function (image, event, index) { if (image.isFolder) { if ($scope.disableFolderSelect) { $scope.gotoFolder(image); @@ -211,7 +211,7 @@ angular.module("umbraco") } }; - $scope.clickItemName = function(item) { + $scope.clickItemName = function (item) { if (item.isFolder) { $scope.gotoFolder(item); } @@ -243,8 +243,8 @@ angular.module("umbraco") images.length = 0; } - $scope.onUploadComplete = function(files) { - $scope.gotoFolder($scope.currentFolder).then(function() { + $scope.onUploadComplete = function (files) { + $scope.gotoFolder($scope.currentFolder).then(function () { if (files.length === 1 && $scope.model.selection.length === 0) { var image = $scope.images[$scope.images.length - 1]; $scope.target = image; @@ -254,7 +254,7 @@ angular.module("umbraco") }); }; - $scope.onFilesQueue = function() { + $scope.onFilesQueue = function () { $scope.activeDrag = false; }; @@ -287,12 +287,12 @@ angular.module("umbraco") $scope.gotoFolder({ id: $scope.startNodeId, name: "Media", icon: "icon-folder" }); } - $scope.openDetailsDialog = function() { + $scope.openDetailsDialog = function () { $scope.mediaPickerDetailsOverlay = {}; $scope.mediaPickerDetailsOverlay.show = true; - $scope.mediaPickerDetailsOverlay.submit = function(model) { + $scope.mediaPickerDetailsOverlay.submit = function (model) { $scope.model.selection.push($scope.target); $scope.model.submit($scope.model); @@ -300,42 +300,42 @@ angular.module("umbraco") $scope.mediaPickerDetailsOverlay = null; }; - $scope.mediaPickerDetailsOverlay.close = function(oldModel) { + $scope.mediaPickerDetailsOverlay.close = function (oldModel) { $scope.mediaPickerDetailsOverlay.show = false; $scope.mediaPickerDetailsOverlay = null; }; }; - var debounceSearchMedia = _.debounce(function() { - $scope.$apply(function() { - if ($scope.searchOptions.filter) { - searchMedia(); - } else { - // reset pagination - $scope.searchOptions = { - pageNumber: 1, - pageSize: 100, - totalItems: 0, - totalPages: 0, - filter: '', - dataTypeId: $scope.model.dataTypeId - }; - getChildren($scope.currentFolder.id); - } - }); - }, 500); + var debounceSearchMedia = _.debounce(function () { + $scope.$apply(function () { + if ($scope.searchOptions.filter) { + searchMedia(); + } else { + // reset pagination + $scope.searchOptions = { + pageNumber: 1, + pageSize: 100, + totalItems: 0, + totalPages: 0, + filter: '', + dataTypeId: $scope.model.dataTypeId + }; + getChildren($scope.currentFolder.id); + } + }); + }, 500); - $scope.changeSearch = function() { + $scope.changeSearch = function () { $scope.loading = true; debounceSearchMedia(); }; - $scope.toggle = function() { + $scope.toggle = function () { // Make sure to activate the changeSearch function everytime the toggle is clicked $scope.changeSearch(); } - $scope.changePagination = function(pageNumber) { + $scope.changePagination = function (pageNumber) { $scope.loading = true; $scope.searchOptions.pageNumber = pageNumber; searchMedia(); @@ -344,9 +344,9 @@ angular.module("umbraco") function searchMedia() { $scope.loading = true; entityResource.getPagedDescendants($scope.currentFolder.id, "Media", $scope.searchOptions) - .then(function(data) { + .then(function (data) { // update image data to work with image grid - angular.forEach(data.items, function(mediaItem) { + angular.forEach(data.items, function (mediaItem) { setMediaMetaData(mediaItem); }); // update images @@ -389,10 +389,9 @@ angular.module("umbraco") function getChildren(id) { $scope.loading = true; return entityResource.getChildren(id, "Media", $scope.searchOptions) - .then(function(data) { - - for (i=0;i 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 8fbed8cb9c..f8ecb898ae 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 @@ -133,7 +133,7 @@ function contentPickerController($scope, entityResource, editorState, iconHelper multiPicker: $scope.model.config.multiPicker, entityType: entityType, filterCssClass: "not-allowed not-published", - startNodeId: null, + startNodeId: null, currentNode: editorState ? editorState.current : null, callback: function (data) { if (angular.isArray(data)) { @@ -155,6 +155,8 @@ function contentPickerController($scope, entityResource, editorState, iconHelper // pre-value config on to the dialog options angular.extend(dialogOptions, $scope.model.config); + dialogOptions.dataTypeId = $scope.model.dataTypeId; + // if we can't pick more than one item, explicitly disable multiPicker in the dialog options if ($scope.model.config.maxNumber && parseInt($scope.model.config.maxNumber) === 1) { dialogOptions.multiPicker = false; @@ -211,7 +213,6 @@ function contentPickerController($scope, entityResource, editorState, iconHelper //dialog $scope.openCurrentPicker = function () { $scope.currentPicker = dialogOptions; - $scope.contentPickerOverlay.dataTypeId = $scope.model.dataTypeId; $scope.currentPicker.submit = function (model) { if (angular.isArray(model.selection)) { diff --git a/src/Umbraco.Web/Editors/EntityController.cs b/src/Umbraco.Web/Editors/EntityController.cs index b0868bd2a6..573bb2b872 100644 --- a/src/Umbraco.Web/Editors/EntityController.cs +++ b/src/Umbraco.Web/Editors/EntityController.cs @@ -607,6 +607,7 @@ namespace Umbraco.Web.Editors { context.SetCulture(culture); }); + //TODO: Why is this here and not in the mapping? target.AdditionalData["hasChildren"] = source.HasChildren; return target; }) diff --git a/src/Umbraco.Web/PropertyEditors/ContentPickerConfiguration.cs b/src/Umbraco.Web/PropertyEditors/ContentPickerConfiguration.cs index d7f7ca9551..021d416781 100644 --- a/src/Umbraco.Web/PropertyEditors/ContentPickerConfiguration.cs +++ b/src/Umbraco.Web/PropertyEditors/ContentPickerConfiguration.cs @@ -5,13 +5,15 @@ namespace Umbraco.Web.PropertyEditors { public class ContentPickerConfiguration : IIgnoreUserStartNodesConfig { - [ConfigurationField("showOpenButton", "Show open button (this feature is in beta!)", "boolean", Description = "Opens the node in a dialog")] + [ConfigurationField("showOpenButton", "Show open button", "boolean", Description = "Opens the node in a dialog")] public bool ShowOpenButton { get; set; } [ConfigurationField("startNodeId", "Start node", "treepicker")] // + config in configuration editor ctor public Udi StartNodeId { get; set; } - [ConfigurationField(Core.Constants.DataTypes.ReservedPreValueKeys.IgnoreUserStartNodes, "Selecting this option allows a user to choose nodes that they normally don't have access to.", "boolean")] + [ConfigurationField(Core.Constants.DataTypes.ReservedPreValueKeys.IgnoreUserStartNodes, + "Ignore User Start Nodes", "boolean", + Description = "Selecting this option allows a user to choose nodes that they normally don't have access to.")] public bool IgnoreUserStartNodes { get; set; } } } diff --git a/src/Umbraco.Web/PropertyEditors/GridConfiguration.cs b/src/Umbraco.Web/PropertyEditors/GridConfiguration.cs index 80f09d3f8f..b7a76ac960 100644 --- a/src/Umbraco.Web/PropertyEditors/GridConfiguration.cs +++ b/src/Umbraco.Web/PropertyEditors/GridConfiguration.cs @@ -16,7 +16,9 @@ namespace Umbraco.Web.PropertyEditors [ConfigurationField("rte", "Rich text editor", "views/propertyeditors/rte/rte.prevalues.html", Description = "Rich text editor configuration")] public JObject Rte { get; set; } - [ConfigurationField(Core.Constants.DataTypes.ReservedPreValueKeys.IgnoreUserStartNodes, "Selecting this option allows a user to choose nodes that they normally don't have access to.", "boolean")] + [ConfigurationField(Core.Constants.DataTypes.ReservedPreValueKeys.IgnoreUserStartNodes, + "Ignore User Start Nodes", "boolean", + Description = "Selecting this option allows a user to choose nodes that they normally don't have access to.")] public bool IgnoreUserStartNodes { get; set; } } } diff --git a/src/Umbraco.Web/PropertyEditors/MediaPickerConfiguration.cs b/src/Umbraco.Web/PropertyEditors/MediaPickerConfiguration.cs index 3a9a9e25ac..b8b9476184 100644 --- a/src/Umbraco.Web/PropertyEditors/MediaPickerConfiguration.cs +++ b/src/Umbraco.Web/PropertyEditors/MediaPickerConfiguration.cs @@ -20,7 +20,9 @@ namespace Umbraco.Web.PropertyEditors [ConfigurationField("startNodeId", "Start node", "mediapicker")] public Udi StartNodeId { get; set; } - [ConfigurationField(Core.Constants.DataTypes.ReservedPreValueKeys.IgnoreUserStartNodes, "Selecting this option allows a user to choose nodes that they normally don't have access to.", "boolean")] + [ConfigurationField(Core.Constants.DataTypes.ReservedPreValueKeys.IgnoreUserStartNodes, + "Ignore User Start Nodes", "boolean", + Description = "Selecting this option allows a user to choose nodes that they normally don't have access to.")] public bool IgnoreUserStartNodes { get; set; } } } diff --git a/src/Umbraco.Web/PropertyEditors/MultiNodePickerConfiguration.cs b/src/Umbraco.Web/PropertyEditors/MultiNodePickerConfiguration.cs index 310211ab2b..b099573b9f 100644 --- a/src/Umbraco.Web/PropertyEditors/MultiNodePickerConfiguration.cs +++ b/src/Umbraco.Web/PropertyEditors/MultiNodePickerConfiguration.cs @@ -23,7 +23,9 @@ namespace Umbraco.Web.PropertyEditors [ConfigurationField("showOpenButton", "Show open button (this feature is in preview!)", "boolean", Description = "Opens the node in a dialog")] public bool ShowOpen { get; set; } - [ConfigurationField(Core.Constants.DataTypes.ReservedPreValueKeys.IgnoreUserStartNodes, "Selecting this option allows a user to choose nodes that they normally don't have access to.", "boolean")] + [ConfigurationField(Core.Constants.DataTypes.ReservedPreValueKeys.IgnoreUserStartNodes, + "Ignore User Start Nodes", "boolean", + Description = "Selecting this option allows a user to choose nodes that they normally don't have access to.")] public bool IgnoreUserStartNodes { get; set; } } } diff --git a/src/Umbraco.Web/PropertyEditors/MultiUrlPickerConfiguration.cs b/src/Umbraco.Web/PropertyEditors/MultiUrlPickerConfiguration.cs index 8b6a4c15d2..16aff6e0bf 100644 --- a/src/Umbraco.Web/PropertyEditors/MultiUrlPickerConfiguration.cs +++ b/src/Umbraco.Web/PropertyEditors/MultiUrlPickerConfiguration.cs @@ -11,7 +11,9 @@ namespace Umbraco.Web.PropertyEditors [ConfigurationField("maxNumber", "Maximum number of items", "number")] public int MaxNumber { get; set; } - [ConfigurationField(Core.Constants.DataTypes.ReservedPreValueKeys.IgnoreUserStartNodes, "Selecting this option allows a user to choose nodes that they normally don't have access to.", "boolean")] + [ConfigurationField(Core.Constants.DataTypes.ReservedPreValueKeys.IgnoreUserStartNodes, + "Ignore User Start Nodes", "boolean", + Description = "Selecting this option allows a user to choose nodes that they normally don't have access to.")] public bool IgnoreUserStartNodes { get; set; } diff --git a/src/Umbraco.Web/PropertyEditors/RichTextConfiguration.cs b/src/Umbraco.Web/PropertyEditors/RichTextConfiguration.cs index 30d3ee2113..bd153c8e2f 100644 --- a/src/Umbraco.Web/PropertyEditors/RichTextConfiguration.cs +++ b/src/Umbraco.Web/PropertyEditors/RichTextConfiguration.cs @@ -15,7 +15,9 @@ namespace Umbraco.Web.PropertyEditors [ConfigurationField("hideLabel", "Hide Label", "boolean")] public bool HideLabel { get; set; } - [ConfigurationField(Core.Constants.DataTypes.ReservedPreValueKeys.IgnoreUserStartNodes, "Selecting this option allows a user to choose nodes that they normally don't have access to.", "boolean")] + [ConfigurationField(Core.Constants.DataTypes.ReservedPreValueKeys.IgnoreUserStartNodes, + "Ignore User Start Nodes", "boolean", + Description = "Selecting this option allows a user to choose nodes that they normally don't have access to.")] public bool IgnoreUserStartNodes { get; set; } } } diff --git a/src/Umbraco.Web/Trees/ContentTreeControllerBase.cs b/src/Umbraco.Web/Trees/ContentTreeControllerBase.cs index 091c3a8f8d..2ee3d07c83 100644 --- a/src/Umbraco.Web/Trees/ContentTreeControllerBase.cs +++ b/src/Umbraco.Web/Trees/ContentTreeControllerBase.cs @@ -88,9 +88,10 @@ namespace Umbraco.Web.Trees /// /// internal TreeNode GetSingleTreeNodeWithAccessCheck(IEntitySlim e, string parentId, FormDataCollection queryStrings, - int[] startNodeIds, string[] startNodePaths, bool ignoreUserStartNodes) + int[] startNodeIds, string[] startNodePaths) { var entityIsAncestorOfStartNodes = ContentPermissionsHelper.IsInBranchOfStartNode(e.Path, startNodeIds, startNodePaths, out var hasPathAccess); + var ignoreUserStartNodes = IgnoreUserStartNodes(queryStrings); if (ignoreUserStartNodes == false && entityIsAncestorOfStartNodes == false) return null; @@ -101,7 +102,7 @@ namespace Umbraco.Web.Trees //the node so we need to return null; return null; } - if (ignoreUserStartNodes == false && hasPathAccess == false) + if (!ignoreUserStartNodes && !hasPathAccess) { treeNode.AdditionalData["noAccess"] = true; } @@ -182,7 +183,7 @@ namespace Umbraco.Web.Trees //get the current user start node/paths GetUserStartNodes(out var userStartNodes, out var userStartNodePaths); - nodes.AddRange(entities.Select(x => GetSingleTreeNodeWithAccessCheck(x, id, queryStrings, userStartNodes, userStartNodePaths, ignoreUserStartNodes)).Where(x => x != null)); + nodes.AddRange(entities.Select(x => GetSingleTreeNodeWithAccessCheck(x, id, queryStrings, userStartNodes, userStartNodePaths)).Where(x => x != null)); // if the user does not have access to the root node, what we have is the start nodes, // but to provide some context we also need to add their topmost nodes when they are not @@ -193,7 +194,7 @@ namespace Umbraco.Web.Trees if (topNodeIds.Length > 0) { var topNodes = Services.EntityService.GetAll(UmbracoObjectType, topNodeIds.ToArray()); - nodes.AddRange(topNodes.Select(x => GetSingleTreeNodeWithAccessCheck(x, id, queryStrings, userStartNodes, userStartNodePaths, ignoreUserStartNodes)).Where(x => x != null)); + nodes.AddRange(topNodes.Select(x => GetSingleTreeNodeWithAccessCheck(x, id, queryStrings, userStartNodes, userStartNodePaths)).Where(x => x != null)); } } @@ -217,7 +218,7 @@ namespace Umbraco.Web.Trees { // try to parse id as an integer else use GetEntityFromId // which will grok Guids, Udis, etc and let use obtain the id - if (int.TryParse(id, out var entityId) == false) + if (!int.TryParse(id, out var entityId)) { var entity = GetEntityFromId(id); if (entity == null) @@ -226,11 +227,13 @@ namespace Umbraco.Web.Trees entityId = entity.Id; } + var ignoreUserStartNodes = IgnoreUserStartNodes(queryStrings); + IEntitySlim[] result; // if a request is made for the root node but user has no access to // root node, return start nodes instead - if (entityId == Constants.System.Root && UserStartNodes.Contains(Constants.System.Root) == false) + if (!ignoreUserStartNodes && entityId == Constants.System.Root && UserStartNodes.Contains(Constants.System.Root) == false) { result = UserStartNodes.Length > 0 ? Services.EntityService.GetAll(UmbracoObjectType, UserStartNodes).ToArray() @@ -535,6 +538,8 @@ namespace Umbraco.Web.Trees private readonly ConcurrentDictionary _entityCache = new ConcurrentDictionary(); + private bool? _ignoreUserStartNodes; + /// /// If the request should allows a user to choose nodes that they normally don't have access to /// @@ -542,13 +547,12 @@ namespace Umbraco.Web.Trees /// internal bool IgnoreUserStartNodes(FormDataCollection queryStrings) { - var dataTypeId = queryStrings.GetValue(TreeQueryStringParameters.DataTypeId); - if (dataTypeId.HasValue) - { - return Services.DataTypeService.IsDataTypeIgnoringUserStartNodes(dataTypeId.Value); - } + if (_ignoreUserStartNodes.HasValue) return _ignoreUserStartNodes.Value; - return false; + var dataTypeId = queryStrings.GetValue(TreeQueryStringParameters.DataTypeId); + _ignoreUserStartNodes = dataTypeId.HasValue ? Services.DataTypeService.IsDataTypeIgnoringUserStartNodes(dataTypeId.Value) : false; + + return _ignoreUserStartNodes.Value; } } }