Merge branch 'v7/dev' into v8/dev

# Conflicts:
#	src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs
#	src/Umbraco.Core/Persistence/Repositories/Interfaces/IContentRepository.cs
#	src/Umbraco.Core/Services/ContentService.cs
#	src/Umbraco.Core/Services/IContentService.cs
#	src/Umbraco.Core/UriExtensions.cs
#	src/Umbraco.Web.UI.Client/src/common/resources/content.resource.js
#	src/Umbraco.Web.UI.Client/src/common/resources/entity.resource.js
#	src/Umbraco.Web.UI.Client/src/common/resources/media.resource.js
#	src/Umbraco.Web.UI.Client/src/common/services/search.service.js
#	src/Umbraco.Web.UI.Client/src/views/common/overlays/contentpicker/contentpicker.html
#	src/Umbraco.Web.UI.Client/src/views/common/overlays/linkpicker/linkpicker.controller.js
#	src/Umbraco.Web.UI.Client/src/views/common/overlays/linkpicker/linkpicker.html
#	src/Umbraco.Web.UI.Client/src/views/common/overlays/mediaPicker/mediapicker.controller.js
#	src/Umbraco.Web.UI.Client/src/views/common/overlays/treepicker/treepicker.controller.js
#	src/Umbraco.Web.UI.Client/src/views/common/overlays/treepicker/treepicker.html
#	src/Umbraco.Web.UI.Client/src/views/components/content/umb-content-node-info.html
#	src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.controller.js
#	src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/editors/media.controller.js
#	src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/editors/rte.controller.js
#	src/Umbraco.Web.UI.Client/src/views/propertyeditors/mediapicker/mediapicker.controller.js
#	src/Umbraco.Web.UI.Client/src/views/propertyeditors/multiurlpicker/multiurlpicker.controller.js
#	src/Umbraco.Web.UI.Client/src/views/propertyeditors/relatedlinks/relatedlinks.controller.js
#	src/Umbraco.Web.UI.Client/src/views/propertyeditors/rte/rte.controller.js
#	src/Umbraco.Web.UI.Client/src/views/propertyeditors/rte/rte.prevalues.html
#	src/Umbraco.Web/Cache/UnpublishedPageCacheRefresher.cs
#	src/Umbraco.Web/Editors/ContentController.cs
#	src/Umbraco.Web/Editors/EntityController.cs
#	src/Umbraco.Web/Editors/MediaController.cs
#	src/Umbraco.Web/PropertyEditors/ContentPicker2PropertyEditor.cs
#	src/Umbraco.Web/PropertyEditors/GridPropertyEditor.cs
#	src/Umbraco.Web/PropertyEditors/MediaPicker2PropertyEditor.cs
#	src/Umbraco.Web/PropertyEditors/MultiNodeTreePicker2PropertyEditor.cs
#	src/Umbraco.Web/PropertyEditors/MultiUrlPickerPropertyEditor.cs
#	src/Umbraco.Web/PropertyEditors/RelatedLinks2PropertyEditor.cs
#	src/Umbraco.Web/PropertyEditors/RichTextPreValueEditor.cs
#	src/Umbraco.Web/PublishedCache/ContextualPublishedCache.cs
#	src/Umbraco.Web/PublishedCache/ContextualPublishedContentCache.cs
#	src/Umbraco.Web/PublishedCache/ContextualPublishedMediaCache.cs
#	src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedContentCache.cs
#	src/Umbraco.Web/Search/UmbracoTreeSearcher.cs
#	src/Umbraco.Web/Trees/ContentTreeControllerBase.cs
#	src/Umbraco.Web/Trees/TreeControllerBase.cs
#	src/Umbraco.Web/Trees/TreeQueryStringParameters.cs
#	src/Umbraco.Web/WebApi/Filters/EnsureUserPermissionForContentAttribute.cs
#	src/Umbraco.Web/WebApi/Filters/FilterAllowedOutgoingMediaAttribute.cs
#	src/Umbraco.Web/umbraco.presentation/content.cs
#	src/Umbraco.Web/umbraco.presentation/umbraco/preview/PreviewContent.cs
This commit is contained in:
Sebastiaan Janssen
2019-04-16 10:11:16 +02:00
parent 0cf609fdce
commit 7db0440b5c
33 changed files with 449 additions and 121 deletions

View File

@@ -12,6 +12,7 @@ function treeSearchBox(localizationService, searchService, $q) {
searchFromName: "@",
showSearch: "@",
section: "@",
ignoreUserStartNodes: "@",
hideSearchCallback: "=",
searchCallback: "="
},
@@ -34,6 +35,7 @@ function treeSearchBox(localizationService, searchService, $q) {
scope.showSearch = "false";
}
//used to cancel any request in progress if another one needs to take it's place
var canceler = null;
@@ -60,6 +62,11 @@ function treeSearchBox(localizationService, searchService, $q) {
searchArgs["searchFrom"] = scope.searchFromId;
}
//append ignoreUserStartNodes value if there is one
if (scope.ignoreUserStartNodes) {
searchArgs["ignoreUserStartNodes"] = scope.ignoreUserStartNodes;
}
searcher(searchArgs).then(function (data) {
scope.searchCallback(data);
//set back to null so it can be re-created

View File

@@ -365,17 +365,28 @@ function contentResource($q, $http, umbDataFormatter, umbRequestHelper) {
* </pre>
*
* @param {Int} id id of content item to return
* @param {Int} culture optional culture to retrieve the item in
* @param {Bool} options.ignoreUserStartNodes set to true to allow a user to choose nodes that they normally don't have access to
* @returns {Promise} resourcePromise object containing the content item.
*
*/
getById: function (id) {
getById: function (id, options) {
var defaults = {
ignoreUserStartNodes: false
};
if (options === undefined) {
options = {};
}
//overwrite the defaults if there are any specified
angular.extend(defaults, options);
//now copy back to the options we will use
options = defaults;
return umbRequestHelper.resourcePromise(
$http.get(
umbRequestHelper.getApiUrl(
"contentApiBaseUrl",
"GetById",
{ id: id })),
[{ id: id }, { ignoreUserStartNodes: options.ignoreUserStartNodes }])),
'Failed to retrieve data for content id ' + id)
.then(function (result) {
return $q.when(umbDataFormatter.formatContentGetData(result));

View File

@@ -284,15 +284,31 @@ function entityResource($q, $http, umbRequestHelper) {
* @returns {Promise} resourcePromise object containing the entity.
*
*/
getAncestors: function (id, type, culture) {
getAncestors: function (id, type, culture, options) {
var defaults = {
ignoreUserStartNodes: false
};
if (options === undefined) {
options = {};
}
//overwrite the defaults if there are any specified
angular.extend(defaults, options);
//now copy back to the options we will use
options = defaults;
if (culture === undefined) culture = "";
return umbRequestHelper.resourcePromise(
$http.get(
umbRequestHelper.getApiUrl(
"entityApiBaseUrl",
"GetAncestors",
[{ id: id }, { type: type }, { culture: culture }])),
'Failed to retrieve ancestor data for id ' + id);
[
{ id: id },
{ type: type },
{ culture: culture },
{ ignoreUserStartNodes: options.ignoreUserStartNodes }
])),
'Failed to retrieve ancestor data for id ' + id);
},
/**
@@ -424,7 +440,8 @@ function entityResource($q, $http, umbRequestHelper) {
pageNumber: 1,
filter: '',
orderDirection: "Ascending",
orderBy: "SortOrder"
orderBy: "SortOrder",
ignoreUserStartNodes: false
};
if (options === undefined) {
options = {};
@@ -453,7 +470,8 @@ function entityResource($q, $http, umbRequestHelper) {
pageSize: options.pageSize,
orderBy: options.orderBy,
orderDirection: options.orderDirection,
filter: encodeURIComponent(options.filter)
filter: encodeURIComponent(options.filter),
ignoreUserStartNodes: options.ignoreUserStartNodes
}
)),
'Failed to retrieve child data for id ' + parentId);
@@ -481,12 +499,19 @@ function entityResource($q, $http, umbRequestHelper) {
* @returns {Promise} resourcePromise object containing the entity array.
*
*/
search: function (query, type, searchFrom, canceler) {
search: function (query, type, options, canceler) {
var args = [{ query: query }, { type: type }];
if (searchFrom) {
args.push({ searchFrom: searchFrom });
if(options !== undefined) {
if (options.searchFrom) {
args.push({ searchFrom: options.searchFrom });
}
if (options.ignoreUserStartNodes) {
args.push({ ignoreUserStartNodes: options.ignoreUserStartNodes });
}
}
var httpConfig = {};
if (canceler) {

View File

@@ -329,7 +329,8 @@ function mediaResource($q, $http, umbDataFormatter, umbRequestHelper) {
filter: '',
orderDirection: "Ascending",
orderBy: "SortOrder",
orderBySystemField: true
orderBySystemField: true,
ignoreUserStartNodes: false
};
if (options === undefined) {
options = {};
@@ -367,6 +368,7 @@ function mediaResource($q, $http, umbDataFormatter, umbRequestHelper) {
"GetChildren",
[
{ id: parentId },
{ ignoreUserStartNodes: options.ignoreUserStartNodes },
{ pageNumber: options.pageNumber },
{ pageSize: options.pageSize },
{ orderBy: options.orderBy },

View File

@@ -42,7 +42,11 @@ angular.module('umbraco.services')
throw "args.term is required";
}
return entityResource.search(args.term, "Member", args.searchFrom).then(function (data) {
var options = {
searchFrom: args.searchFrom
}
return entityResource.search(args.term, "Member", options).then(function (data) {
_.each(data, function (item) {
searchResultFormatter.configureMemberResult(item);
});
@@ -67,7 +71,12 @@ angular.module('umbraco.services')
throw "args.term is required";
}
return entityResource.search(args.term, "Document", args.searchFrom, args.canceler).then(function (data) {
var options = {
searchFrom: args.searchFrom,
ignoreUserStartNodes: args.ignoreUserStartNodes
}
return entityResource.search(args.term, "Document", options, args.canceler).then(function (data) {
_.each(data, function (item) {
searchResultFormatter.configureContentResult(item);
});
@@ -92,7 +101,12 @@ angular.module('umbraco.services')
throw "args.term is required";
}
return entityResource.search(args.term, "Media", args.searchFrom).then(function (data) {
var options = {
searchFrom: args.searchFrom,
ignoreUserStartNodes: args.ignoreUserStartNodes
}
return entityResource.search(args.term, "Media", options).then(function (data) {
_.each(data, function (item) {
searchResultFormatter.configureMediaResult(item);
});

View File

@@ -1143,11 +1143,25 @@ function tinyMceService($rootScope, $q, imageHelper, $locale, $http, $timeout, s
let self = this;
function getIgnoreUserStartNodes(args) {
var ignoreUserStartNodes = false;
// Most property editors have a "config" property with ignoreUserStartNodes on then
if (args.model.config) {
ignoreUserStartNodes = Object.toBoolean(args.model.config.ignoreUserStartNodes);
}
// EXCEPT for the grid's TinyMCE editor, that one wants to be special and the config is called "configuration" instead
else if (args.model.configuration) {
ignoreUserStartNodes = Object.toBoolean(args.model.configuration.ignoreUserStartNodes);
}
return ignoreUserStartNodes;
}
//create link picker
self.createLinkPicker(args.editor, function (currentTarget, anchorElement) {
var linkPicker = {
currentTarget: currentTarget,
anchors: editorState.current ? self.getAnchorNames(JSON.stringify(editorState.current.properties)) : [],
ignoreUserStartNodes: getIgnoreUserStartNodes(args),
submit: function (model) {
self.insertLinkInEditor(args.editor, model.target, anchorElement);
editorService.close();
@@ -1161,13 +1175,25 @@ function tinyMceService($rootScope, $q, imageHelper, $locale, $http, $timeout, s
//Create the insert media plugin
self.createMediaPicker(args.editor, function (currentTarget, userData) {
var startNodeId = userData.startMediaIds.length !== 1 ? -1 : userData.startMediaIds[0];
var startNodeIsVirtual = userData.startMediaIds.length !== 1;
var ignoreUserStartNodes = getIgnoreUserStartNodes(args);
if (ignoreUserStartNodes) {
ignoreUserStartNodes = true;
startNodeId = -1;
startNodeIsVirtual = true;
}
var mediaPicker = {
currentTarget: currentTarget,
onlyImages: true,
showDetails: true,
disableFolderSelect: true,
startNodeId: userData.startMediaIds.length !== 1 ? -1 : userData.startMediaIds[0],
startNodeIsVirtual: userData.startMediaIds.length !== 1,
startNodeId: startNodeId,
startNodeIsVirtual: startNodeIsVirtual,
ignoreUserStartNodes: ignoreUserStartNodes,
submit: function (model) {
self.insertMediaInEditor(args.editor, model.selection[0]);
editorService.close();

View File

@@ -28,9 +28,11 @@ angular.module("umbraco").controller("Umbraco.Editors.LinkPickerController",
searchFromName: null,
showSearch: false,
results: [],
selectedSearchResults: []
selectedSearchResults: [],
ignoreUserStartNodes: dialogOptions.ignoreUserStartNodes
};
$scope.customTreeParams = dialogOptions.ignoreUserStartNodes ? "ignoreUserStartNodes=" + dialogOptions.ignoreUserStartNodes : "";
$scope.showTarget = $scope.model.hideTarget !== true;
// this ensures that we only sync the tree once and only when it's ready
@@ -73,7 +75,11 @@ angular.module("umbraco").controller("Umbraco.Editors.LinkPickerController",
});
// get the content properties to build the anchor name list
contentResource.getById(id).then(function (resp) {
var options = {};
options.ignoreUserStartNodes = dialogOptions.ignoreUserStartNodes;
contentResource.getById(id, options).then(function (resp) {
$scope.anchorValues = tinyMceService.getAnchorNames(JSON.stringify(resp.properties));
$scope.model.target.url = resp.urls[0].text;
});
@@ -119,7 +125,10 @@ angular.module("umbraco").controller("Umbraco.Editors.LinkPickerController",
if (args.node.id < 0) {
$scope.model.target.url = "/";
} else {
contentResource.getById(args.node.id).then(function (resp) {
var options = {};
options.ignoreUserStartNodes = dialogOptions.ignoreUserStartNodes;
contentResource.getById(args.node.id, options).then(function (resp) {
$scope.anchorValues = tinyMceService.getAnchorNames(JSON.stringify(resp.properties));
$scope.model.target.url = resp.urls[0].text;
});
@@ -139,9 +148,17 @@ angular.module("umbraco").controller("Umbraco.Editors.LinkPickerController",
$scope.switchToMediaPicker = function () {
userService.getCurrentUser().then(function (userData) {
var startNodeId = userData.startMediaIds.length !== 1 ? -1 : userData.startMediaIds[0];
var startNodeIsVirtual = userData.startMediaIds.length !== 1;
if (dialogOptions.ignoreUserStartNodes) {
startNodeId = -1;
startNodeIsVirtual = true;
}
var mediaPicker = {
startNodeId: userData.startMediaIds.length !== 1 ? -1 : userData.startMediaIds[0],
startNodeIsVirtual: userData.startMediaIds.length !== 1,
startNodeId: startNodeId,
startNodeIsVirtual: startNodeIsVirtual,
ignoreUserStartNodes: dialogOptions.ignoreUserStartNodes,
submit: function (model) {
var media = model.selection[0];

View File

@@ -68,6 +68,7 @@
search-from-id="{{searchInfo.searchFromId}}"
search-from-name="{{searchInfo.searchFromName}}"
show-search="{{searchInfo.showSearch}}"
ignore-user-start-nodes="{{searchInfo.ignoreUserStartNodes}}"
section="{{section}}">
</umb-tree-search-box>
@@ -84,6 +85,7 @@
section="content"
hideheader="true"
hideoptions="true"
customtreeparams="{{customTreeParams}}"
api="dialogTreeApi"
on-init="onTreeInit()"
enablelistviewexpand="true"

View File

@@ -20,11 +20,13 @@ angular.module("umbraco")
$scope.showDetails = dialogOptions.showDetails;
$scope.multiPicker = (dialogOptions.multiPicker && dialogOptions.multiPicker !== "0") ? true : false;
$scope.startNodeId = dialogOptions.startNodeId ? dialogOptions.startNodeId : -1;
$scope.ignoreUserStartNodes = Object.toBoolean(dialogOptions.ignoreUserStartNodes);
$scope.cropSize = dialogOptions.cropSize;
$scope.lastOpenedNode = localStorageService.get("umbLastOpenedMediaNodeId");
$scope.lockedFolder = true;
$scope.allowMediaEdit = dialogOptions.allowMediaEdit ? dialogOptions.allowMediaEdit : false;
var userStartNodes = [];
var umbracoSettings = Umbraco.Sys.ServerVariables.umbracoSettings;
var allowedUploadFiles = mediaHelper.formatFileTypes(umbracoSettings.allowedUploadFiles);
if ($scope.onlyImages) {
@@ -54,7 +56,8 @@ angular.module("umbraco")
pageSize: 100,
totalItems: 0,
totalPages: 0,
filter: ''
filter: "",
ignoreUserStartNodes: $scope.model.ignoreUserStartNodes
};
//preload selected item
@@ -66,7 +69,7 @@ angular.module("umbraco")
function onInit() {
if ($scope.startNodeId !== -1) {
entityResource.getById($scope.startNodeId, "media")
.then(function (ent) {
.then(function(ent) {
$scope.startNodeId = ent.id;
run();
});
@@ -143,7 +146,7 @@ angular.module("umbraco")
}
};
$scope.gotoFolder = function(folder) {
$scope.gotoFolder = function (folder) {
if (!$scope.multiPicker) {
deselectAllImages($scope.model.selection);
}
@@ -152,8 +155,10 @@ angular.module("umbraco")
folder = { id: -1, name: "Media", icon: "icon-folder" };
}
var options = {};
if (folder.id > 0) {
entityResource.getAncestors(folder.id, "media")
options.ignoreUserStartNodes = $scope.model.ignoreUserStartNodes;
entityResource.getAncestors(folder.id, "media", options)
.then(function(anc) {
$scope.path = _.filter(anc,
function(f) {
@@ -169,13 +174,26 @@ angular.module("umbraco")
$scope.path = [];
}
$scope.lockedFolder = folder.id === -1 && $scope.model.startNodeIsVirtual;
$scope.lockedFolder = (folder.id === -1 && $scope.model.startNodeIsVirtual) || hasFolderAccess(folder) === false;
$scope.currentFolder = folder;
localStorageService.set("umbLastOpenedMediaNodeId", folder.id);
return getChildren(folder.id);
options.ignoreUserStartNodes = $scope.ignoreUserStartNodes;
return getChildren(folder.id, options);
};
function hasFolderAccess(node) {
var nodePath = node.path ? node.path.split(',') : [node.id];
for (var i = 0; i < nodePath.length; i++) {
if (userStartNodes.indexOf(parseInt(nodePath[i])) !== -1)
return true;
}
return false;
}
$scope.clickHandler = function(image, event, index) {
if (image.isFolder) {
if ($scope.disableFolderSelect) {
@@ -299,7 +317,8 @@ angular.module("umbraco")
pageSize: 100,
totalItems: 0,
totalPages: 0,
filter: ''
filter: "",
ignoreUserStartNodes: $scope.model.ignoreUserStartNodes
};
getChildren($scope.currentFolder.id);
}
@@ -367,9 +386,9 @@ angular.module("umbraco")
}
}
function getChildren(id) {
function getChildren(id, options) {
$scope.loading = true;
return mediaResource.getChildren(id)
return mediaResource.getChildren(id, options)
.then(function(data) {
$scope.searchOptions.filter = "";
$scope.images = data.items ? data.items : [];

View File

@@ -36,6 +36,7 @@ angular.module("umbraco").controller("Umbraco.Editors.TreePickerController",
selectedSearchResults: []
}
vm.startNodeId = $scope.model.startNodeId;
vm.ignoreUserStartNodes = $scope.model.ignoreUserStartNodes;
//Used for toggling an empty-state message
//Some trees can have no items (dictionary & forms email templates)
vm.hasItems = true;
@@ -171,6 +172,9 @@ angular.module("umbraco").controller("Umbraco.Editors.TreePickerController",
if (vm.startNodeId) {
queryParams["startNodeId"] = $scope.model.startNodeId;
}
if (vm.ignoreUserStartNodes) {
queryParams["ignoreUserStartNodes"] = $scope.model.ignoreUserStartNodes;
}
if (vm.selectedLanguage && vm.selectedLanguage.id) {
queryParams["culture"] = vm.selectedLanguage.culture;
}

View File

@@ -27,7 +27,7 @@
<a ng-click="vm.selectLanguage(language)" ng-repeat="language in vm.languages" href="">{{language.name}}</a>
</div>
</div>
<div class="umb-control-group">
<umb-tree-search-box
ng-if="vm.enableSearh"
@@ -36,6 +36,7 @@
search-from-id="{{vm.searchInfo.searchFromId}}"
search-from-name="{{vm.searchInfo.searchFromName}}"
show-search="{{vm.searchInfo.showSearch}}"
ignore-user-start-nodes="{{vm.ignoreUserStartNodes}}"
section="{{vm.section}}">
</umb-tree-search-box>
</div>

View File

@@ -81,6 +81,7 @@ function contentPickerController($scope, entityResource, editorState, iconHelper
showOpenButton: false,
showEditButton: false,
showPathOnHover: false,
ignoreUserStartNodes: false,
maxNumber: 1,
minNumber: 0,
startNode: {
@@ -118,7 +119,8 @@ function contentPickerController($scope, entityResource, editorState, iconHelper
$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);
$scope.model.config.ignoreUserStartNodes = Object.toBoolean($scope.model.config.ignoreUserStartNodes);
var entityType = $scope.model.config.startNode.type === "member"
? "Member"
: $scope.model.config.startNode.type === "media"
@@ -134,6 +136,7 @@ function contentPickerController($scope, entityResource, editorState, iconHelper
entityType: entityType,
filterCssClass: "not-allowed not-published",
startNodeId: null,
ignoreUserStartNodes: $scope.model.config.ignoreUserStartNodes,
currentNode: editorState ? editorState.current : null,
callback: function (data) {
if (angular.isArray(data)) {

View File

@@ -14,6 +14,7 @@
sortable="!sortableOptions.disabled"
allow-remove="allowRemoveButton"
allow-open="model.config.showOpenButton && allowOpenButton && !dialogEditor"
ignore-user-startnodes="model.config.ignoreUserStartNodes"
on-remove="remove($index)"
on-open="openContentEditor(node)">
</umb-node-preview>

View File

@@ -1,25 +1,32 @@
angular.module("umbraco")
.controller("Umbraco.PropertyEditors.Grid.MediaController",
function ($scope, $timeout, userService, editorService) {
var ignoreUserStartNodes = Object.toBoolean($scope.model.config.ignoreUserStartNodes);
$scope.thumbnailUrl = getThumbnailUrl();
if (!$scope.model.config.startNodeId) {
userService.getCurrentUser().then(function (userData) {
$scope.model.config.startNodeId = userData.startMediaIds.length !== 1 ? -1 : userData.startMediaIds[0];
$scope.model.config.startNodeIsVirtual = userData.startMediaIds.length !== 1;
});
if (ignoreUserStartNodes === true) {
$scope.model.config.startNodeId = -1;
$scope.model.config.startNodeIsVirtual = true;
} else {
userService.getCurrentUser().then(function (userData) {
$scope.model.config.startNodeId = userData.startMediaIds.length !== 1 ? -1 : userData.startMediaIds[0];
$scope.model.config.startNodeIsVirtual = userData.startMediaIds.length !== 1;
});
}
}
$scope.setImage = function(){
var startNodeId = $scope.model.config && $scope.model.config.startNodeId ? $scope.model.config.startNodeId : undefined;
var startNodeIsVirtual = startNodeId ? $scope.model.config.startNodeIsVirtual : undefined;
var mediaPicker = {
startNodeId: startNodeId,
startNodeIsVirtual: startNodeIsVirtual,
ignoreUserStartNodes: ignoreUserStartNodes,
cropSize: $scope.control.editor.config && $scope.control.editor.config.size ? $scope.control.editor.config.size : undefined,
showDetails: true,
disableFolderSelect: true,

View File

@@ -7,9 +7,12 @@ angular.module('umbraco').controller("Umbraco.PropertyEditors.MediaPickerControl
var multiPicker = $scope.model.config.multiPicker && $scope.model.config.multiPicker !== '0' ? true : false;
var onlyImages = $scope.model.config.onlyImages && $scope.model.config.onlyImages !== '0' ? true : false;
var disableFolderSelect = $scope.model.config.disableFolderSelect && $scope.model.config.disableFolderSelect !== '0' ? true : false;
var ignoreUserStartNodes = Object.toBoolean($scope.model.config.ignoreUserStartNodes);
$scope.allowEditMedia = false;
$scope.allowAddMedia = false;
function setupViewModel() {
$scope.mediaItems = [];
$scope.ids = [];
@@ -90,26 +93,31 @@ angular.module('umbraco').controller("Umbraco.PropertyEditors.MediaPickerControl
// reload. We only reload the images that is already picked but has been updated.
// We have to get the entities from the server because the media
// can be edited without being selected
_.each($scope.images, function (image, i) {
if (updatedMediaNodes.indexOf(image.udi) !== -1) {
image.loading = true;
entityResource.getById(image.udi, "media")
.then(function (mediaEntity) {
angular.extend(image, mediaEntity);
image.thumbnail = mediaHelper.resolveFileFromEntity(image, true);
image.loading = false;
});
}
})
_.each($scope.images,
function (image, i) {
if (updatedMediaNodes.indexOf(image.udi) !== -1) {
image.loading = true;
entityResource.getById(image.udi, "media")
.then(function (mediaEntity) {
angular.extend(image, mediaEntity);
image.thumbnail = mediaHelper.resolveFileFromEntity(image, true);
image.loading = false;
});
}
});
}
function init() {
userService.getCurrentUser().then(function (userData) {
if (!$scope.model.config.startNodeId) {
$scope.model.config.startNodeId = userData.startMediaIds.length !== 1 ? -1 : userData.startMediaIds[0];
$scope.model.config.startNodeIsVirtual = userData.startMediaIds.length !== 1;
}
if (ignoreUserStartNodes === true) {
$scope.model.config.startNodeId = -1;
$scope.model.config.startNodeIsVirtual = true;
}
// only allow users to add and edit media if they have access to the media section
var hasAccessToMedia = userData.allowedSections.indexOf("media") !== -1;
$scope.allowEditMedia = hasAccessToMedia;
@@ -167,12 +175,13 @@ angular.module('umbraco').controller("Umbraco.PropertyEditors.MediaPickerControl
var mediaPicker = {
startNodeId: $scope.model.config.startNodeId,
startNodeIsVirtual: $scope.model.config.startNodeIsVirtual,
ignoreUserStartNodes: ignoreUserStartNodes,
multiPicker: multiPicker,
onlyImages: onlyImages,
disableFolderSelect: disableFolderSelect,
allowMediaEdit: true,
submit: function(model) {
submit: function (model) {
editorService.close();
@@ -182,7 +191,7 @@ angular.module('umbraco').controller("Umbraco.PropertyEditors.MediaPickerControl
media.thumbnail = mediaHelper.resolveFileFromEntity(media, true);
}
$scope.mediaItems.push(media);
$scope.mediaItems.push(media);
if ($scope.model.config.idType === "udi") {
$scope.ids.push(media.udi);
@@ -205,7 +214,7 @@ angular.module('umbraco').controller("Umbraco.PropertyEditors.MediaPickerControl
};
$scope.sortableOptions = {
disabled: !$scope.isMultiPicker,
@@ -217,7 +226,7 @@ angular.module('umbraco').controller("Umbraco.PropertyEditors.MediaPickerControl
// content picker. Then we don't have to worry about setting ids, render models, models, we just set one and let the
// watch do all the rest.
$timeout(function () {
angular.forEach($scope.mediaItems, function(value, key) {
angular.forEach($scope.mediaItems, function (value, key) {
r.push($scope.model.config.idType === "udi" ? value.udi : value.id);
});
$scope.ids = r;
@@ -236,5 +245,5 @@ angular.module('umbraco').controller("Umbraco.PropertyEditors.MediaPickerControl
};
init();
});

View File

@@ -69,9 +69,10 @@ function multiUrlPickerController($scope, angularHelper, localizationService, en
url: link.url,
target: link.target
} : null;
var linkPicker = {
currentTarget: target,
ignoreUserStartNodes: Object.toBoolean($scope.model.config.ignoreUserStartNodes),
submit: function (model) {
if (model.target.url || model.target.anchor) {
// if an anchor exists, check that it is appropriately prefixed

View File

@@ -25,6 +25,7 @@
section: "content",
treeAlias: "content",
multiPicker: false,
ignoreUserStartNodes: Object.toBoolean($scope.model.config.ignoreUserStartNodes),
idType: $scope.model.config.idType ? $scope.model.config.idType : "int",
submit: function (model) {
select(model.selection[0]);
@@ -47,6 +48,7 @@
section: "content",
treeAlias: "content",
multiPicker: false,
ignoreUserStartNodes: Object.toBoolean($scope.model.config.ignoreUserStartNodes),
idType: $scope.model.config.idType ? $scope.model.config.idType : "udi",
submit: function (model) {
select(model.selection[0]);

View File

@@ -28,6 +28,14 @@
</div>
</umb-control-group>
<umb-control-group label="Ignore user start nodes" description="Selecting this option allows a user to choose nodes that they normally don't have access to.<br /> <em>Note: this applies only to rich text editors in this grid editor.</em>">
<div>
<label>
<input type="checkbox" ng-model="model.value.ignoreUserStartNodes" />
</label>
</div>
</umb-control-group>
<umb-control-group label="Dimensions" description="Set the editor dimensions">
<div class="vertical-align-items">
<input type="number" min="0" ng-model="model.value.dimensions.width" class="umb-property-editor-small" title="Width" placeholder="Width" /> &times;

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

View File

@@ -99,8 +99,25 @@ namespace Umbraco.Web.Editors
/// A starting point for the search, generally a node id, but for members this is a member type alias
/// </param>
/// <returns></returns>
[Obsolete("This method is obsolete, use the overload with ignoreUserStartNodes instead", false)]
[HttpGet]
public IEnumerable<EntityBasic> Search(string query, UmbracoEntityTypes type, string searchFrom = null)
{
return Search(query, type, ignoreUserStartNodes: false, searchFrom);
}
/// <summary>
/// Searches for results based on the entity type
/// </summary>
/// <param name="query"></param>
/// <param name="type"></param>
/// <param name="searchFrom">
/// A starting point for the search, generally a node id, but for members this is a member type alias
/// </param>
/// <param name="ignoreUserStartNodes">If set to true, user and group start node permissions will be ignored.</param>
/// <returns></returns>
[HttpGet]
public IEnumerable<EntityBasic> Search(string query, UmbracoEntityTypes type, bool? ignoreUserStartNodes, string searchFrom = 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?
@@ -110,7 +127,7 @@ namespace Umbraco.Web.Editors
//TODO: This uses the internal UmbracoTreeSearcher, this instead should delgate to the ISearchableTree implementation for the type
return ExamineSearch(query, type, searchFrom);
return ExamineSearch(query, type, searchFrom, ignoreUserStartNodes != null && ignoreUserStartNodes.Value);
}
/// <summary>
@@ -534,6 +551,7 @@ namespace Umbraco.Web.Editors
}
}
[Obsolete("This method is obsolete, use the overload with ignoreUserStartNodes instead", false)]
public PagedResult<EntityBasic> GetPagedDescendants(
int id,
UmbracoEntityTypes type,
@@ -542,6 +560,20 @@ namespace Umbraco.Web.Editors
string orderBy = "SortOrder",
Direction orderDirection = Direction.Ascending,
string filter = "")
{
return GetPagedDescendants(id, type, pageNumber, pageSize,
ignoreUserStartNodes: false, orderBy, orderDirection, filter);
}
public PagedResult<EntityBasic> GetPagedDescendants(
int id,
UmbracoEntityTypes type,
int pageNumber,
int pageSize,
bool ignoreUserStartNodes,
string orderBy = "SortOrder",
Direction orderDirection = Direction.Ascending,
string filter = "")
{
if (pageNumber <= 0)
throw new HttpResponseException(HttpStatusCode.NotFound);
@@ -569,7 +601,7 @@ namespace Umbraco.Web.Editors
break;
}
entities = aids == null || aids.Contains(Constants.System.Root)
entities = aids == null || aids.Contains(Constants.System.Root) || ignoreUserStartNodes
? Services.EntityService.GetPagedDescendants(objectType.Value, pageNumber - 1, pageSize, out totalRecords,
SqlContext.Query<IUmbracoEntity>().Where(x => x.Name.Contains(filter)),
Ordering.By(orderBy, orderDirection), includeTrashed: false)
@@ -611,9 +643,15 @@ namespace Umbraco.Web.Editors
}
}
public IEnumerable<EntityBasic> GetAncestors(int id, UmbracoEntityTypes type, [ModelBinder(typeof(HttpQueryStringModelBinder))]FormDataCollection queryStrings)
[Obsolete("This method is obsolete, use the overload with ignoreUserStartNodes instead", false)]
public IEnumerable<EntityBasic> GetAncestors(int id, UmbracoEntityTypes type, [ModelBinder(typeof(HttpQueryStringModelBinder))] FormDataCollection queryStrings)
{
return GetResultForAncestors(id, type, queryStrings);
return GetResultForAncestors(id, type, queryStrings, ignoreUserStartNodes: false);
}
public IEnumerable<EntityBasic> GetAncestors(int id, UmbracoEntityTypes type, [ModelBinder(typeof(HttpQueryStringModelBinder))]FormDataCollection queryStrings, bool ignoreUserStartNodes)
{
return GetResultForAncestors(id, type, queryStrings, ignoreUserStartNodes);
}
/// <summary>
@@ -622,10 +660,11 @@ namespace Umbraco.Web.Editors
/// <param name="query"></param>
/// <param name="entityType"></param>
/// <param name="searchFrom"></param>
/// <param name="ignoreUserStartNodes">If set to true, user and group start node permissions will be ignored.</param>
/// <returns></returns>
private IEnumerable<SearchResultEntity> ExamineSearch(string query, UmbracoEntityTypes entityType, string searchFrom = null)
private IEnumerable<SearchResultEntity> ExamineSearch(string query, UmbracoEntityTypes entityType, string searchFrom = null, bool ignoreUserStartNodes = false)
{
return _treeSearcher.ExamineSearch(query, entityType, 200, 0, out _, searchFrom);
return _treeSearcher.ExamineSearch(query, entityType, 200, 0, out _, ignoreUserStartNodes, searchFrom);
}
private IEnumerable<EntityBasic> GetResultForChildren(int id, UmbracoEntityTypes entityType)
@@ -651,7 +690,7 @@ namespace Umbraco.Web.Editors
}
}
private IEnumerable<EntityBasic> GetResultForAncestors(int id, UmbracoEntityTypes entityType, FormDataCollection queryStrings = null)
private IEnumerable<EntityBasic> GetResultForAncestors(int id, UmbracoEntityTypes entityType, FormDataCollection queryStrings = null, bool ignoreUserStartNodes = false)
{
var objectType = ConvertToObjectType(entityType);
if (objectType.HasValue)
@@ -660,35 +699,38 @@ namespace Umbraco.Web.Editors
var ids = Services.EntityService.Get(id).Path.Split(',').Select(int.Parse).Distinct().ToArray();
int[] aids = null;
switch (entityType)
if (ignoreUserStartNodes == false)
{
case UmbracoEntityTypes.Document:
aids = Security.CurrentUser.CalculateContentStartNodeIds(Services.EntityService);
break;
case UmbracoEntityTypes.Media:
aids = Security.CurrentUser.CalculateMediaStartNodeIds(Services.EntityService);
break;
}
if (aids != null)
{
var lids = new List<int>();
var ok = false;
foreach (var i in ids)
int[] aids = null;
switch (entityType)
{
if (ok)
{
lids.Add(i);
continue;
}
if (aids.Contains(i))
{
lids.Add(i);
ok = true;
}
case UmbracoEntityTypes.Document:
aids = Security.CurrentUser.CalculateContentStartNodeIds(Services.EntityService);
break;
case UmbracoEntityTypes.Media:
aids = Security.CurrentUser.CalculateMediaStartNodeIds(Services.EntityService);
break;
}
if (aids != null)
{
var lids = new List<int>();
var ok = false;
foreach (var i in ids)
{
if (ok)
{
lids.Add(i);
continue;
}
if (aids.Contains(i))
{
lids.Add(i);
ok = true;
}
}
ids = lids.ToArray();
}
ids = lids.ToArray();
}
var culture = queryStrings?.GetValue<string>("culture");

View File

@@ -246,6 +246,7 @@ namespace Umbraco.Web.Editors
/// </summary>
[FilterAllowedOutgoingMedia(typeof(IEnumerable<ContentItemBasic<ContentPropertyBasic>>), "Items")]
public PagedResult<ContentItemBasic<ContentPropertyBasic>> GetChildren(int id,
bool ignoreUserStartNodes,
int pageNumber = 0,
int pageSize = 0,
string orderBy = "SortOrder",
@@ -255,7 +256,7 @@ namespace Umbraco.Web.Editors
{
//if a request is made for the root node data but the user's start node is not the default, then
// we need to return their start nodes
if (id == Constants.System.Root && UserStartNodes.Length > 0 && UserStartNodes.Contains(Constants.System.Root) == false)
if (id == Constants.System.Root && UserStartNodes.Length > 0 && (UserStartNodes.Contains(Constants.System.Root) == false && ignoreUserStartNodes == false))
{
if (pageNumber > 0)
return new PagedResult<ContentItemBasic<ContentPropertyBasic>>(0, 0, 0);
@@ -311,6 +312,7 @@ namespace Umbraco.Web.Editors
}
/// <summary>
/// This method is obsolete, use the overload with ignoreUserStartNodes instead
/// Returns the child media objects - using the entity GUID id
/// </summary>
/// <param name="id"></param>
@@ -321,8 +323,34 @@ namespace Umbraco.Web.Editors
/// <param name="orderBySystemField"></param>
/// <param name="filter"></param>
/// <returns></returns>
[Obsolete("This method is obsolete, use the overload with ignoreUserStartNodes instead", false)]
[FilterAllowedOutgoingMedia(typeof(IEnumerable<ContentItemBasic<ContentPropertyBasic>>), "Items")]
public PagedResult<ContentItemBasic<ContentPropertyBasic>> GetChildren(Guid id,
int pageNumber = 0,
int pageSize = 0,
string orderBy = "SortOrder",
Direction orderDirection = Direction.Ascending,
bool orderBySystemField = true,
string filter = "")
{
return GetChildren(id, ignoreUserStartNodes: false, pageNumber, pageSize, orderBy, orderDirection, orderBySystemField, filter);
}
/// <summary>
/// Returns the child media objects - using the entity GUID id
/// </summary>
/// <param name="id"></param>
/// /// <param name="ignoreUserStartNodes">If set to true, user and group start node permissions will be ignored.</param>
/// <param name="pageNumber"></param>
/// <param name="pageSize"></param>
/// <param name="orderBy"></param>
/// <param name="orderDirection"></param>
/// <param name="orderBySystemField"></param>
/// <param name="filter"></param>
/// <returns></returns>
[FilterAllowedOutgoingMedia(typeof(IEnumerable<ContentItemBasic<ContentPropertyBasic>>), "Items")]
public PagedResult<ContentItemBasic<ContentPropertyBasic>> GetChildren(Guid id,
bool ignoreUserStartNodes,
int pageNumber = 0,
int pageSize = 0,
string orderBy = "SortOrder",
@@ -333,12 +361,13 @@ namespace Umbraco.Web.Editors
var entity = Services.EntityService.Get(id);
if (entity != null)
{
return GetChildren(entity.Id, pageNumber, pageSize, orderBy, orderDirection, orderBySystemField, filter);
return GetChildren(entity.Id, ignoreUserStartNodes, pageNumber, pageSize, orderBy, orderDirection, orderBySystemField, filter);
}
throw new HttpResponseException(HttpStatusCode.NotFound);
}
/// <summary>
/// This method is obsolete, use the overload with ignoreUserStartNodes instead
/// Returns the child media objects - using the entity UDI id
/// </summary>
/// <param name="id"></param>
@@ -349,6 +378,7 @@ namespace Umbraco.Web.Editors
/// <param name="orderBySystemField"></param>
/// <param name="filter"></param>
/// <returns></returns>
[Obsolete("This method is obsolete, use the overload with ignoreUserStartNodes instead", false)]
[FilterAllowedOutgoingMedia(typeof(IEnumerable<ContentItemBasic<ContentPropertyBasic>>), "Items")]
public PagedResult<ContentItemBasic<ContentPropertyBasic>> GetChildren(Udi id,
int pageNumber = 0,
@@ -357,6 +387,31 @@ namespace Umbraco.Web.Editors
Direction orderDirection = Direction.Ascending,
bool orderBySystemField = true,
string filter = "")
{
return GetChildren(id, ignoreUserStartNodes: false, pageNumber, pageSize, orderBy, orderDirection, orderBySystemField, filter);
}
/// <summary>
/// Returns the child media objects - using the entity UDI id
/// </summary>
/// <param name="id"></param>
/// <param name="ignoreUserStartNodes">If set to true, user and group start node permissions will be ignored.</param>
/// <param name="pageNumber"></param>
/// <param name="pageSize"></param>
/// <param name="orderBy"></param>
/// <param name="orderDirection"></param>
/// <param name="orderBySystemField"></param>
/// <param name="filter"></param>
/// <returns></returns>
[FilterAllowedOutgoingMedia(typeof(IEnumerable<ContentItemBasic<ContentPropertyBasic>>), "Items")]
public PagedResult<ContentItemBasic<ContentPropertyBasic>> GetChildren(Udi id,
bool ignoreUserStartNodes,
int pageNumber = 0,
int pageSize = 0,
string orderBy = "SortOrder",
Direction orderDirection = Direction.Ascending,
bool orderBySystemField = true,
string filter = "")
{
var guidUdi = id as GuidUdi;
if (guidUdi != null)
@@ -364,7 +419,7 @@ namespace Umbraco.Web.Editors
var entity = Services.EntityService.Get(guidUdi.Guid);
if (entity != null)
{
return GetChildren(entity.Id, pageNumber, pageSize, orderBy, orderDirection, orderBySystemField, filter);
return GetChildren(entity.Id, ignoreUserStartNodes, pageNumber, pageSize, orderBy, orderDirection, orderBySystemField, filter);
}
}

View File

@@ -10,5 +10,8 @@ namespace Umbraco.Web.PropertyEditors
[ConfigurationField("startNodeId", "Start node", "treepicker")] // + config in configuration editor ctor
public Udi StartNodeId { get; set; }
[ConfigurationField("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; }
}
}

View File

@@ -15,5 +15,8 @@ namespace Umbraco.Web.PropertyEditors
// TODO: Make these strongly typed, for now this works though
[ConfigurationField("rte", "Rich text editor", "views/propertyeditors/rte/rte.prevalues.html", Description = "Rich text editor configuration")]
public JObject Rte { get; set; }
[ConfigurationField("ignoreUserStartNodes", "Ignore user start nodes", "boolean", Description = "Selecting this option allows a user to choose nodes that they normally don't have access to.<br /> <em>Note: this applies to all editors in this grid editor except for the rich text editor, which has it's own option for that.</em>")]
public bool IgnoreUserStartNodes { get; set; }
}
}

View File

@@ -19,5 +19,8 @@ namespace Umbraco.Web.PropertyEditors
[ConfigurationField("startNodeId", "Start node", "mediapicker")]
public Udi StartNodeId { get; set; }
[ConfigurationField("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; }
}
}

View File

@@ -22,5 +22,8 @@ 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("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; }
}
}

View File

@@ -9,5 +9,8 @@ namespace Umbraco.Web.PropertyEditors
[ConfigurationField("maxNumber", "Maximum number of items", "number")]
public int MaxNumber { get; set; }
[ConfigurationField("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; }
}
}

View File

@@ -14,5 +14,8 @@ namespace Umbraco.Web.PropertyEditors
[ConfigurationField("hideLabel", "Hide Label", "boolean")]
public bool HideLabel { get; set; }
[ConfigurationField("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; }
}
}

View File

@@ -39,6 +39,7 @@ namespace Umbraco.Web.Search
}
/// <summary>
/// This method is obsolete, use the overload with ignoreUserStartNodes instead
/// Searches for results based on the entity type
/// </summary>
/// <param name="query"></param>
@@ -50,11 +51,39 @@ namespace Umbraco.Web.Search
/// <param name="pageSize"></param>
/// <param name="pageIndex"></param>
/// <returns></returns>
[Obsolete("This method is obsolete, use the overload with ignoreUserStartNodes instead", false)]
public IEnumerable<SearchResultEntity> ExamineSearch(
string query,
UmbracoEntityTypes entityType,
int pageSize,
long pageIndex, out long totalFound, string searchFrom = null)
long pageIndex,
out long totalFound,
string searchFrom = null)
{
return ExamineSearch(query, entityType, pageSize, pageIndex, out totalFound, ignoreUserStartNodes: false, searchFrom);
}
/// <summary>
/// Searches for results based on the entity type
/// </summary>
/// <param name="query"></param>
/// <param name="entityType"></param>
/// <param name="totalFound"></param>
/// <param name="searchFrom">
/// A starting point for the search, generally a node id, but for members this is a member type alias
/// </param>
/// <param name="pageSize"></param>
/// <param name="pageIndex"></param>
/// <param name="ignoreUserStartNodes">If set to true, user and group start node permissions will be ignored.</param>
/// <returns></returns>
public IEnumerable<SearchResultEntity> ExamineSearch(
string query,
UmbracoEntityTypes entityType,
int pageSize,
long pageIndex,
out long totalFound,
bool ignoreUserStartNodes,
string searchFrom = null)
{
var sb = new StringBuilder();
@@ -85,12 +114,12 @@ namespace Umbraco.Web.Search
case UmbracoEntityTypes.Media:
type = "media";
var allMediaStartNodes = _umbracoContext.Security.CurrentUser.CalculateMediaStartNodeIds(_entityService);
AppendPath(sb, UmbracoObjectTypes.Media, allMediaStartNodes, searchFrom, _entityService);
AppendPath(sb, UmbracoObjectTypes.Media, allMediaStartNodes, searchFrom, ignoreUserStartNodes, _entityService);
break;
case UmbracoEntityTypes.Document:
type = "content";
var allContentStartNodes = _umbracoContext.Security.CurrentUser.CalculateContentStartNodeIds(_entityService);
AppendPath(sb, UmbracoObjectTypes.Document, allContentStartNodes, searchFrom, _entityService);
AppendPath(sb, UmbracoObjectTypes.Document, allContentStartNodes, searchFrom, ignoreUserStartNodes, _entityService);
break;
default:
throw new NotSupportedException("The " + typeof(UmbracoTreeSearcher) + " currently does not support searching against object type " + entityType);
@@ -288,7 +317,7 @@ namespace Umbraco.Web.Search
}
}
private void AppendPath(StringBuilder sb, UmbracoObjectTypes objectType, int[] startNodeIds, string searchFrom, IEntityService entityService)
private void AppendPath(StringBuilder sb, UmbracoObjectTypes objectType, int[] startNodeIds, string searchFrom, bool ignoreUserStartNodes, IEntityService entityService)
{
if (sb == null) throw new ArgumentNullException(nameof(sb));
if (entityService == null) throw new ArgumentNullException(nameof(entityService));
@@ -311,7 +340,7 @@ namespace Umbraco.Web.Search
// make sure we don't find anything
sb.Append("+__Path:none ");
}
else if (startNodeIds.Contains(-1) == false) // -1 = no restriction
else if (startNodeIds.Contains(-1) == false && ignoreUserStartNodes == false) // -1 = no restriction
{
var entityPaths = entityService.GetAllPaths(objectType, startNodeIds);

View File

@@ -69,7 +69,7 @@ namespace Umbraco.Web.Trees
{
var node = base.CreateRootNode(queryStrings);
if (IsDialog(queryStrings) && UserStartNodes.Contains(Constants.System.Root) == false)
if (IsDialog(queryStrings) && UserStartNodes.Contains(Constants.System.Root) == false && IgnoreUserStartNodes(queryStrings) == false)
{
node.AdditionalData["noAccess"] = true;
}
@@ -90,11 +90,11 @@ namespace Umbraco.Web.Trees
internal TreeNode GetSingleTreeNodeWithAccessCheck(IEntitySlim e, string parentId, FormDataCollection queryStrings)
{
var entityIsAncestorOfStartNodes = Security.CurrentUser.IsInBranchOfStartNode(e, Services.EntityService, RecycleBinId, out var hasPathAccess);
if (entityIsAncestorOfStartNodes == false)
if (IgnoreUserStartNodes(queryStrings) == false && entityIsAncestorOfStartNodes == false)
return null;
var treeNode = GetSingleTreeNode(e, parentId, queryStrings);
if (hasPathAccess == false)
if (IgnoreUserStartNodes(queryStrings) == false && hasPathAccess == false)
{
treeNode.AdditionalData["noAccess"] = true;
}
@@ -134,7 +134,7 @@ namespace Umbraco.Web.Trees
// ensure that the user has access to that node, otherwise return the empty tree nodes collection
// TODO: in the future we could return a validation statement so we can have some UI to notify the user they don't have access
if (HasPathAccess(id, queryStrings) == false)
if (IgnoreUserStartNodes(queryStrings) == false && HasPathAccess(id, queryStrings) == false)
{
Logger.Warn<ContentTreeControllerBase>("User {Username} does not have access to node with id {Id}", Security.CurrentUser.Username, id);
return nodes;
@@ -255,8 +255,9 @@ namespace Umbraco.Web.Trees
/// </remarks>
protected sealed override TreeNodeCollection GetTreeNodes(string id, FormDataCollection queryStrings)
{
var ignoreUserStartNodes = queryStrings.GetValue<bool>(TreeQueryStringParameters.IgnoreUserStartNodes);
//check if we're rendering the root
if (id == Constants.System.RootString && UserStartNodes.Contains(Constants.System.Root))
if (id == Constants.System.RootString && UserStartNodes.Contains(Constants.System.Root) || ignoreUserStartNodes)
{
var altStartId = string.Empty;

View File

@@ -369,6 +369,16 @@ namespace Umbraco.Web.Trees
return queryStrings.GetValue<string>(TreeQueryStringParameters.Use) == "dialog";
}
/// <summary>
/// If the request should allows a user to choose nodes that they normally don't have access to
/// </summary>
/// <param name="queryStrings"></param>
/// <returns></returns>
protected bool IgnoreUserStartNodes(FormDataCollection queryStrings)
{
return queryStrings.GetValue<bool>(TreeQueryStringParameters.IgnoreUserStartNodes);
}
/// <summary>
/// An event that allows developers to modify the tree node collection that is being rendered
/// </summary>

View File

@@ -8,6 +8,7 @@
public const string Use = "use";
public const string Application = "application";
public const string StartNodeId = "startNodeId";
public const string IgnoreUserStartNodes = "ignoreUserStartNodes";
//public const string OnNodeClick = "OnNodeClick";
//public const string RenderParent = "RenderParent";
}

View File

@@ -11,6 +11,7 @@ using Umbraco.Core.Models;
using Umbraco.Web.Actions;
using Umbraco.Core.Security;
using System.Net;
using System.Web;
namespace Umbraco.Web.WebApi.Filters
{
@@ -66,7 +67,7 @@ namespace Umbraco.Web.WebApi.Filters
//not logged in
throw new HttpResponseException(System.Net.HttpStatusCode.Unauthorized);
}
int nodeId;
if (_nodeId.HasValue == false)
{
@@ -116,24 +117,29 @@ namespace Umbraco.Web.WebApi.Filters
nodeId = _nodeId.Value;
}
var permissionResult = ContentPermissionsHelper.CheckPermissions(nodeId,
Current.UmbracoContext.Security.CurrentUser,
Current.Services.UserService,
Current.Services.ContentService,
Current.Services.EntityService,
out var contentItem,
_permissionToCheck.HasValue ? new[] { _permissionToCheck.Value } : null);
if (permissionResult == ContentPermissionsHelper.ContentAccess.NotFound)
throw new HttpResponseException(HttpStatusCode.NotFound);
if (permissionResult == ContentPermissionsHelper.ContentAccess.Denied)
throw new HttpResponseException(actionContext.Request.CreateUserNoAccessResponse());
if (contentItem != null)
var queryStringCollection = HttpUtility.ParseQueryString(actionContext.Request.RequestUri.Query);
var ignoreUserStartNodes = bool.Parse(queryStringCollection["ignoreUserStartNodes"]);
if (ignoreUserStartNodes == false)
{
//store the content item in request cache so it can be resolved in the controller without re-looking it up
actionContext.Request.Properties[typeof(IContent).ToString()] = contentItem;
var permissionResult = ContentPermissionsHelper.CheckPermissions(nodeId,
Current.UmbracoContext.Security.CurrentUser,
Current.Services.UserService,
Current.Services.ContentService,
Current.Services.EntityService,
out var contentItem,
_permissionToCheck.HasValue ? new[] {_permissionToCheck.Value} : null);
if (permissionResult == ContentPermissionsHelper.ContentAccess.NotFound)
throw new HttpResponseException(HttpStatusCode.NotFound);
if (permissionResult == ContentPermissionsHelper.ContentAccess.Denied)
throw new HttpResponseException(actionContext.Request.CreateUserNoAccessResponse());
if (contentItem != null)
{
//store the content item in request cache so it can be resolved in the controller without re-looking it up
actionContext.Request.Properties[typeof(IContent).ToString()] = contentItem;
}
}
base.OnActionExecuting(actionContext);

View File

@@ -3,12 +3,14 @@ using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Web;
using System.Web.Http.Filters;
using Umbraco.Core;
using Umbraco.Core.Models;
using Umbraco.Core.Models.Membership;
using Umbraco.Core.Composing;
using Umbraco.Core.Security;
using Umbraco.Web.Trees;
namespace Umbraco.Web.WebApi.Filters
{
@@ -72,7 +74,12 @@ namespace Umbraco.Web.WebApi.Filters
protected virtual void FilterItems(IUser user, IList items)
{
FilterBasedOnStartNode(items, user);
bool.TryParse(HttpContext.Current.Request.QueryString.Get(TreeQueryStringParameters.IgnoreUserStartNodes), out var ignoreUserStartNodes);
if (ignoreUserStartNodes == false)
{
FilterBasedOnStartNode(items, user);
}
}
internal void FilterBasedOnStartNode(IList items, IUser user)