From a55323176f02e2c93150152da30302582f07f933 Mon Sep 17 00:00:00 2001 From: Shannon Date: Wed, 26 Jun 2019 14:35:36 +1000 Subject: [PATCH] Updates EntityController to support passing in the dataTypeId for the GetChildren methods and updates the mediapicker to pass along that data --- .../src/common/resources/entity.resource.js | 12 +- .../mediaPicker/mediapicker.controller.js | 2 +- src/Umbraco.Web/Editors/EntityController.cs | 126 +++++++++++++----- 3 files changed, 103 insertions(+), 37 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/common/resources/entity.resource.js b/src/Umbraco.Web.UI.Client/src/common/resources/entity.resource.js index 455fcab956..5e6f1095e4 100644 --- a/src/Umbraco.Web.UI.Client/src/common/resources/entity.resource.js +++ b/src/Umbraco.Web.UI.Client/src/common/resources/entity.resource.js @@ -358,14 +358,19 @@ function entityResource($q, $http, umbRequestHelper) { * @returns {Promise} resourcePromise object containing the entity. * */ - getChildren: function (id, type) { + getChildren: function (id, type, options) { + + var args = [{ id: id }, { type: type }]; + if (options.dataTypeId) { + args.push({ dataTypeId: options.dataTypeId }); + } return umbRequestHelper.resourcePromise( $http.get( umbRequestHelper.getApiUrl( "entityApiBaseUrl", "GetChildren", - [{ id: id }, { type: type }])), + args)), 'Failed to retrieve child data for id ' + id); }, @@ -433,7 +438,8 @@ function entityResource($q, $http, umbRequestHelper) { pageSize: options.pageSize, orderBy: options.orderBy, orderDirection: options.orderDirection, - filter: encodeURIComponent(options.filter) + filter: encodeURIComponent(options.filter), + dataTypeId: options.dataTypeId } )), 'Failed to retrieve child data for id ' + parentId); diff --git a/src/Umbraco.Web.UI.Client/src/views/common/overlays/mediaPicker/mediapicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/overlays/mediaPicker/mediapicker.controller.js index 400d3a7bb5..96354d7696 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/overlays/mediaPicker/mediapicker.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/common/overlays/mediaPicker/mediapicker.controller.js @@ -388,7 +388,7 @@ angular.module("umbraco") function getChildren(id) { $scope.loading = true; - return entityResource.getChildren(id, "Media") + return entityResource.getChildren(id, "Media", $scope.searchOptions) .then(function(data) { for (i=0;i /// - /// Some objects such as macros are not based on CMSNode + /// + /// This controller allows resolving basic entity data for various entities without placing the hard restrictions on users that may not have access + /// to the sections these entities entities exist in. This is to allow pickers, etc... of data to work for all users. In some cases such as accessing + /// Members, more explicit security checks are done. + /// + /// Some objects such as macros are not based on CMSNode /// [EntityControllerConfiguration] [PluginController("UmbracoApi")] @@ -88,8 +93,7 @@ namespace Umbraco.Web.Editors [HttpGet] public IEnumerable Search(string query, UmbracoEntityTypes type, string searchFrom = null, Guid? dataTypeId = null) { - //TODO: Should we restrict search results based on what app the user has access to? - // - Theoretically you shouldn't be able to see member data if you don't have access to members right? + // NOTE: Theoretically you shouldn't be able to see member data if you don't have access to members right? ... but there is a member picker, so can't really do that if (string.IsNullOrEmpty(query)) return Enumerable.Empty(); @@ -195,6 +199,9 @@ namespace Umbraco.Web.Editors /// Int id of the entity to fetch URL for /// The tpye of entity such as Document, Media, Member /// The URL or path to the item + /// + /// We are not restricting this with security because there is no sensitive data + /// public HttpResponseMessage GetUrl(int id, UmbracoEntityTypes type) { var returnUrl = string.Empty; @@ -428,9 +435,52 @@ namespace Umbraco.Web.Editors return GetResultForKeys(ids, type); } - public IEnumerable GetChildren(int id, UmbracoEntityTypes type) + public IEnumerable GetChildren(int id, UmbracoEntityTypes type, Guid? dataTypeId = null) { - return GetResultForChildren(id, type); + var objectType = ConvertToObjectType(type); + if (objectType.HasValue) + { + //TODO: Need to check for Object types that support hierarchy here, some might not. + + int[] startNodes = null; + switch (type) + { + case UmbracoEntityTypes.Document: + startNodes = Security.CurrentUser.CalculateContentStartNodeIds(Services.EntityService); + break; + case UmbracoEntityTypes.Media: + startNodes = Security.CurrentUser.CalculateMediaStartNodeIds(Services.EntityService); + break; + } + + var ignoreUserStartNodes = IsDataTypeIgnoringUserStartNodes(dataTypeId); + + // root is special: we reduce it to start nodes if the user's start node is not the default, then we need to return their start nodes + if (id == Constants.System.Root && startNodes.Length > 0 && startNodes.Contains(Constants.System.Root) == false && !ignoreUserStartNodes) + { + var nodes = Services.EntityService.GetAll(objectType.Value, startNodes).ToArray(); + if (nodes.Length == 0) + return Enumerable.Empty(); + var pr = new List(nodes.Select(Mapper.Map)); + return pr; + } + + // else proceed as usual + + return Services.EntityService.GetChildren(id, objectType.Value) + .WhereNotNull() + .Select(Mapper.Map); + } + //now we need to convert the unknown ones + switch (type) + { + case UmbracoEntityTypes.Domain: + case UmbracoEntityTypes.Language: + case UmbracoEntityTypes.User: + case UmbracoEntityTypes.Macro: + default: + throw new NotSupportedException("The " + typeof(EntityController) + " does not currently support data for the type " + type); + } } /// @@ -451,7 +501,8 @@ namespace Umbraco.Web.Editors int pageSize, string orderBy = "SortOrder", Direction orderDirection = Direction.Ascending, - string filter = "") + string filter = "", + Guid? dataTypeId = null) { int intId; @@ -480,7 +531,7 @@ namespace Umbraco.Web.Editors //the EntityService can search paged members from the root intId = -1; - return GetPagedChildren(intId, type, pageNumber, pageSize, orderBy, orderDirection, filter); + return GetPagedChildren(intId, type, pageNumber, pageSize, orderBy, orderDirection, filter, dataTypeId); } //the EntityService cannot search members of a certain type, this is currently not supported and would require @@ -513,7 +564,8 @@ namespace Umbraco.Web.Editors int pageSize, string orderBy = "SortOrder", Direction orderDirection = Direction.Ascending, - string filter = "") + string filter = "", + Guid? dataTypeId = null) { if (pageNumber <= 0) throw new HttpResponseException(HttpStatusCode.NotFound); @@ -523,8 +575,40 @@ namespace Umbraco.Web.Editors var objectType = ConvertToObjectType(type); if (objectType.HasValue) { + IEnumerable entities; long totalRecords; - var entities = Services.EntityService.GetPagedChildren(id, objectType.Value, pageNumber - 1, pageSize, out totalRecords, orderBy, orderDirection, filter); + + int[] startNodes = null; + switch (type) + { + case UmbracoEntityTypes.Document: + startNodes = Security.CurrentUser.CalculateContentStartNodeIds(Services.EntityService); + break; + case UmbracoEntityTypes.Media: + startNodes = Security.CurrentUser.CalculateMediaStartNodeIds(Services.EntityService); + break; + } + + var ignoreUserStartNodes = IsDataTypeIgnoringUserStartNodes(dataTypeId); + + // root is special: we reduce it to start nodes if the user's start node is not the default, then we need to return their start nodes + if (id == Constants.System.Root && startNodes.Length > 0 && startNodes.Contains(Constants.System.Root) == false && !ignoreUserStartNodes) + { + if (pageNumber > 0) + return new PagedResult(0, 0, 0); + var nodes = Services.EntityService.GetAll(objectType.Value, startNodes).ToArray(); + if (nodes.Length == 0) + return new PagedResult(0, 0, 0); + if (pageSize < nodes.Length) pageSize = nodes.Length; // bah + var pr = new PagedResult(nodes.Length, pageNumber, pageSize) + { + Items = nodes.Select(Mapper.Map) + }; + return pr; + } + + // else proceed as usual + entities = Services.EntityService.GetPagedChildren(id, objectType.Value, pageNumber - 1, pageSize, out totalRecords, orderBy, orderDirection, filter); if (totalRecords == 0) { @@ -653,30 +737,6 @@ namespace Umbraco.Web.Editors return _treeSearcher.ExamineSearch(Umbraco, query, entityType, 200, 0, out total, searchFrom, ignoreUserStartNodes); } - - private IEnumerable GetResultForChildren(int id, UmbracoEntityTypes entityType) - { - var objectType = ConvertToObjectType(entityType); - if (objectType.HasValue) - { - //TODO: Need to check for Object types that support hierarchic here, some might not. - - return Services.EntityService.GetChildren(id, objectType.Value) - .WhereNotNull() - .Select(Mapper.Map); - } - //now we need to convert the unknown ones - switch (entityType) - { - case UmbracoEntityTypes.Domain: - case UmbracoEntityTypes.Language: - case UmbracoEntityTypes.User: - case UmbracoEntityTypes.Macro: - default: - throw new NotSupportedException("The " + typeof(EntityController) + " does not currently support data for the type " + entityType); - } - } - private IEnumerable GetResultForAncestors(int id, UmbracoEntityTypes entityType, Guid? dataTypeId = null) { var objectType = ConvertToObjectType(entityType);