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:
@@ -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
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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 },
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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];
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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 : [];
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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)) {
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
});
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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]);
|
||||
|
||||
@@ -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" /> ×
|
||||
|
||||
BIN
src/Umbraco.Web.UI/favicon.ico
Normal file
BIN
src/Umbraco.Web.UI/favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 15 KiB |
@@ -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");
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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";
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user