U4-4700 List view doesn't respect user permissions - makes the buttons show/hide based on the selected items instead of the current item.

This commit is contained in:
Shannon
2016-03-08 15:53:22 +01:00
parent c7a3efadb1
commit 372a768dca
9 changed files with 387 additions and 269 deletions

View File

@@ -509,6 +509,16 @@ function contentResource($q, $http, umbDataFormatter, umbRequestHelper) {
'Failed to check permission for item ' + id);
},
getPermissions: function (nodeIds) {
return umbRequestHelper.resourcePromise(
$http.post(
umbRequestHelper.getApiUrl(
"contentApiBaseUrl",
"GetPermissions"),
nodeIds),
'Failed to get permissions');
},
/**
* @ngdoc method
* @name umbraco.resources.contentResource#save

View File

@@ -1,288 +1,319 @@
(function() {
'use strict';
(function () {
'use strict';
function listViewHelper(localStorageService) {
function listViewHelper(localStorageService) {
var firstSelectedIndex = 0;
var localStorageKey = "umblistViewLayout";
var firstSelectedIndex = 0;
var localStorageKey = "umblistViewLayout";
function getLayout(nodeId, availableLayouts) {
function getLayout(nodeId, availableLayouts) {
var storedLayouts = [];
var storedLayouts = [];
if(localStorageService.get(localStorageKey)) {
storedLayouts = localStorageService.get(localStorageKey);
}
if (storedLayouts && storedLayouts.length > 0) {
for (var i = 0; storedLayouts.length > i; i++) {
var layout = storedLayouts[i];
if (layout.nodeId === nodeId) {
return setLayout(nodeId, layout, availableLayouts);
}
}
}
return getFirstAllowedLayout(availableLayouts);
}
function setLayout(nodeId, selectedLayout, availableLayouts) {
var activeLayout = {};
var layoutFound = false;
for (var i = 0; availableLayouts.length > i; i++) {
var layout = availableLayouts[i];
if (layout.path === selectedLayout.path) {
activeLayout = layout;
layout.active = true;
layoutFound = true;
} else {
layout.active = false;
}
}
if(!layoutFound) {
activeLayout = getFirstAllowedLayout(availableLayouts);
}
saveLayoutInLocalStorage(nodeId, activeLayout);
return activeLayout;
}
function saveLayoutInLocalStorage(nodeId, selectedLayout) {
var layoutFound = false;
var storedLayouts = [];
if(localStorageService.get(localStorageKey)) {
storedLayouts = localStorageService.get(localStorageKey);
}
if(storedLayouts.length > 0) {
for(var i = 0; storedLayouts.length > i; i++) {
var layout = storedLayouts[i];
if(layout.nodeId === nodeId) {
layout.path = selectedLayout.path;
layoutFound = true;
}
}
}
if(!layoutFound) {
var storageObject = {
"nodeId": nodeId,
"path": selectedLayout.path
};
storedLayouts.push(storageObject);
}
localStorageService.set(localStorageKey, storedLayouts);
}
function getFirstAllowedLayout(layouts) {
var firstAllowedLayout = {};
for (var i = 0; layouts.length > i; i++) {
var layout = layouts[i];
if (layout.selected === true) {
firstAllowedLayout = layout;
break;
if (localStorageService.get(localStorageKey)) {
storedLayouts = localStorageService.get(localStorageKey);
}
}
return firstAllowedLayout;
}
if (storedLayouts && storedLayouts.length > 0) {
for (var i = 0; storedLayouts.length > i; i++) {
var layout = storedLayouts[i];
if (layout.nodeId === nodeId) {
return setLayout(nodeId, layout, availableLayouts);
}
}
function selectHandler(selectedItem, selectedIndex, items, selection, $event) {
}
var start = 0;
var end = 0;
var item = null;
return getFirstAllowedLayout(availableLayouts);
if ($event.shiftKey === true) {
}
if(selectedIndex > firstSelectedIndex) {
function setLayout(nodeId, selectedLayout, availableLayouts) {
start = firstSelectedIndex;
end = selectedIndex;
var activeLayout = {};
var layoutFound = false;
for (; end >= start; start++) {
item = items[start];
selectItem(item, selection);
}
for (var i = 0; availableLayouts.length > i; i++) {
var layout = availableLayouts[i];
if (layout.path === selectedLayout.path) {
activeLayout = layout;
layout.active = true;
layoutFound = true;
} else {
layout.active = false;
}
}
if (!layoutFound) {
activeLayout = getFirstAllowedLayout(availableLayouts);
}
saveLayoutInLocalStorage(nodeId, activeLayout);
return activeLayout;
}
function saveLayoutInLocalStorage(nodeId, selectedLayout) {
var layoutFound = false;
var storedLayouts = [];
if (localStorageService.get(localStorageKey)) {
storedLayouts = localStorageService.get(localStorageKey);
}
if (storedLayouts.length > 0) {
for (var i = 0; storedLayouts.length > i; i++) {
var layout = storedLayouts[i];
if (layout.nodeId === nodeId) {
layout.path = selectedLayout.path;
layoutFound = true;
}
}
}
if (!layoutFound) {
var storageObject = {
"nodeId": nodeId,
"path": selectedLayout.path
};
storedLayouts.push(storageObject);
}
localStorageService.set(localStorageKey, storedLayouts);
}
function getFirstAllowedLayout(layouts) {
var firstAllowedLayout = {};
for (var i = 0; layouts.length > i; i++) {
var layout = layouts[i];
if (layout.selected === true) {
firstAllowedLayout = layout;
break;
}
}
return firstAllowedLayout;
}
function selectHandler(selectedItem, selectedIndex, items, selection, $event) {
var start = 0;
var end = 0;
var item = null;
if ($event.shiftKey === true) {
if (selectedIndex > firstSelectedIndex) {
start = firstSelectedIndex;
end = selectedIndex;
for (; end >= start; start++) {
item = items[start];
selectItem(item, selection);
}
} else {
start = firstSelectedIndex;
end = selectedIndex;
for (; end <= start; start--) {
item = items[start];
selectItem(item, selection);
}
}
} else {
start = firstSelectedIndex;
end = selectedIndex;
if (selectedItem.selected) {
deselectItem(selectedItem, selection);
} else {
selectItem(selectedItem, selection);
}
for (; end <= start; start--) {
item = items[start];
selectItem(item, selection);
}
firstSelectedIndex = selectedIndex;
}
} else {
}
if(selectedItem.selected) {
deselectItem(selectedItem, selection);
} else {
selectItem(selectedItem, selection);
function selectItem(item, selection) {
var isSelected = false;
for (var i = 0; selection.length > i; i++) {
var selectedItem = selection[i];
if (item.id === selectedItem.id) {
isSelected = true;
}
}
if (!isSelected) {
selection.push({ id: item.id });
item.selected = true;
}
}
function deselectItem(item, selection) {
for (var i = 0; selection.length > i; i++) {
var selectedItem = selection[i];
if (item.id === selectedItem.id) {
selection.splice(i, 1);
item.selected = false;
}
}
}
function clearSelection(items, folders, selection) {
var i = 0;
selection.length = 0;
if (angular.isArray(items)) {
for (i = 0; items.length > i; i++) {
var item = items[i];
item.selected = false;
}
}
firstSelectedIndex = selectedIndex;
}
}
function selectItem(item, selection) {
var isSelected = false;
for (var i = 0; selection.length > i; i++) {
var selectedItem = selection[i];
if (item.id === selectedItem.id) {
isSelected = true;
if (angular.isArray(items)) {
for (i = 0; folders.length > i; i++) {
var folder = folders[i];
folder.selected = false;
}
}
}
if(!isSelected) {
selection.push({id: item.id});
item.selected = true;
}
}
}
function deselectItem(item, selection) {
for (var i = 0; selection.length > i; i++) {
var selectedItem = selection[i];
if (item.id === selectedItem.id) {
selection.splice(i, 1);
item.selected = false;
function selectAllItems(items, selection, $event) {
var checkbox = $event.target;
var clearSelection = false;
if (!angular.isArray(items)) {
return;
}
}
}
function clearSelection(items, folders, selection) {
selection.length = 0;
var i = 0;
for (var i = 0; i < items.length; i++) {
selection.length = 0;
var item = items[i];
if (checkbox.checked) {
selection.push({ id: item.id });
} else {
clearSelection = true;
}
item.selected = checkbox.checked;
if(angular.isArray(items)) {
for(i = 0; items.length > i; i++) {
var item = items[i];
item.selected = false;
}
}
if(angular.isArray(items)) {
for(i = 0; folders.length > i; i++) {
var folder = folders[i];
folder.selected = false;
if (clearSelection) {
selection.length = 0;
}
}
}
function selectAllItems(items, selection, $event) {
}
var checkbox = $event.target;
var clearSelection = false;
function isSelectedAll(items, selection) {
if (!angular.isArray(items)) {
return;
}
var numberOfSelectedItem = 0;
selection.length = 0;
for (var itemIndex = 0; items.length > itemIndex; itemIndex++) {
var item = items[itemIndex];
for (var i = 0; i < items.length; i++) {
for (var selectedIndex = 0; selection.length > selectedIndex; selectedIndex++) {
var selectedItem = selection[selectedIndex];
var item = items[i];
if (item.id === selectedItem.id) {
numberOfSelectedItem++;
}
}
if (checkbox.checked) {
selection.push({id: item.id});
} else {
clearSelection = true;
}
}
item.selected = checkbox.checked;
if (numberOfSelectedItem === items.length) {
return true;
}
}
}
if (clearSelection) {
selection.length = 0;
}
function setSortingDirection(col, direction, options) {
return options.orderBy.toUpperCase() === col.toUpperCase() && options.orderDirection === direction;
}
}
function setSorting(field, allow, options) {
if (allow) {
options.orderBy = field;
function isSelectedAll(items, selection) {
if (options.orderDirection === "desc") {
options.orderDirection = "asc";
} else {
options.orderDirection = "desc";
}
}
}
//This takes in a dictionary of Ids with Permissions and determines
// the intersect of all permissions to return an object representing the
// listview button permissions
function getButtonPermissions(unmergedPermissions, currentIdsWithPermissions) {
if (currentIdsWithPermissions == null) {
currentIdsWithPermissions = {};
}
var numberOfSelectedItem = 0;
//merge the newly retrieved permissions to the main dictionary
_.each(unmergedPermissions, function (value, key, list) {
currentIdsWithPermissions[key] = value;
});
for(var itemIndex = 0; items.length > itemIndex; itemIndex++) {
var item = items[itemIndex];
//get the intersect permissions
var arr = [];
_.each(currentIdsWithPermissions, function (value, key, list) {
arr.push(value);
});
for(var selectedIndex = 0; selection.length > selectedIndex; selectedIndex++) {
var selectedItem = selection[selectedIndex];
//we need to use 'apply' to call intersection with an array of arrays,
//see: http://stackoverflow.com/a/16229480/694494
var intersectPermissions = _.intersection.apply(_, arr);
if(item.id === selectedItem.id) {
numberOfSelectedItem++;
}
}
return {
canCopy: _.contains(intersectPermissions, 'O'), //Magic Char = O
canCreate: _.contains(intersectPermissions, 'C'), //Magic Char = C
canDelete: _.contains(intersectPermissions, 'D'), //Magic Char = D
canMove: _.contains(intersectPermissions, 'M'), //Magic Char = M
canPublish: _.contains(intersectPermissions, 'U'), //Magic Char = U
canUnpublish: _.contains(intersectPermissions, 'U'), //Magic Char = Z (however UI says it can't be set, so if we can publish 'U' we can unpublish)
};
}
}
var service = {
getLayout: getLayout,
getFirstAllowedLayout: getFirstAllowedLayout,
setLayout: setLayout,
saveLayoutInLocalStorage: saveLayoutInLocalStorage,
selectHandler: selectHandler,
selectItem: selectItem,
deselectItem: deselectItem,
clearSelection: clearSelection,
selectAllItems: selectAllItems,
isSelectedAll: isSelectedAll,
setSortingDirection: setSortingDirection,
setSorting: setSorting,
getButtonPermissions: getButtonPermissions
};
if(numberOfSelectedItem === items.length) {
return true;
}
return service;
}
}
function setSortingDirection(col, direction, options) {
return options.orderBy.toUpperCase() === col.toUpperCase() && options.orderDirection === direction;
}
function setSorting(field, allow, options) {
if (allow) {
options.orderBy = field;
if (options.orderDirection === "desc") {
options.orderDirection = "asc";
} else {
options.orderDirection = "desc";
}
}
}
var service = {
getLayout: getLayout,
getFirstAllowedLayout: getFirstAllowedLayout,
setLayout: setLayout,
saveLayoutInLocalStorage: saveLayoutInLocalStorage,
selectHandler: selectHandler,
selectItem: selectItem,
deselectItem: deselectItem,
clearSelection: clearSelection,
selectAllItems: selectAllItems,
isSelectedAll: isSelectedAll,
setSortingDirection: setSortingDirection,
setSorting: setSorting
};
return service;
}
angular.module('umbraco.services').factory('listViewHelper', listViewHelper);
angular.module('umbraco.services').factory('listViewHelper', listViewHelper);
})();

View File

@@ -21,6 +21,7 @@ function packageHelper(assetsService, treeService, eventsService, $templateCache
}
angular.module('umbraco.services').factory('packageHelper', packageHelper);
//TODO: I believe this is obsolete
function umbPhotoFolderHelper($compile, $log, $timeout, $filter, imageHelper, mediaHelper, umbRequestHelper) {
return {
/** sets the image's url, thumbnail and if its a folder */
@@ -319,7 +320,6 @@ function umbPhotoFolderHelper($compile, $log, $timeout, $filter, imageHelper, me
}
};
}
angular.module("umbraco.services").factory("umbPhotoFolderHelper", umbPhotoFolderHelper);
/**

View File

@@ -58,37 +58,53 @@ function listViewController($rootScope, $scope, $routeParams, $injector, $cookie
totalPages: 0,
items: []
};
$scope.currentNodePermissions = {}
//Just ensure we do have an editorState
if(editorState.current){
//Get Current User to check if their usertype is an admin (overwrites all permissions!)
userService.getCurrentUser().then(function(data){
//Fetch current node allowed actions for the current user
//This is the current node & not each individual child node in the list
var currentUserPermissions = editorState.current.allowedActions;
//Create a nicer model rather than the funky & hard to remember permissions strings
$scope.currentNodePermissions = {
"isAdminUser": data.userType.toLowerCase() === "admin",
"canCopy": _.contains(currentUserPermissions, 'O'), //Magic Char = O
"canCreate": _.contains(currentUserPermissions, 'C'), //Magic Char = C
"canDelete": _.contains(currentUserPermissions, 'D'), //Magic Char = D
"canMove": _.contains(currentUserPermissions, 'M'), //Magic Char = M
"canPublish": _.contains(currentUserPermissions, 'U'), //Magic Char = U
"canUnpublish": _.contains(currentUserPermissions, 'U'), //Magic Char = Z (however UI says it can't be set, so if we can publish 'U' we can unpublish)
"rawPermissions": currentUserPermissions
};
//when this is null, we don't check permissions
$scope.buttonPermissions = null;
//When we are dealing with 'content', we need to deal with permissions on child nodes.
// Currently there is no real good way to
if ($scope.entityType = "content") {
var idsWithPermissions = null;
var intersectPermissions = null;
$scope.buttonPermissions = {
canCopy: true,
canCreate: true,
canDelete: true,
canMove: true,
canPublish: true,
canUnpublish: true
};
$scope.$watch(function() {
return $scope.selection.length;
}, function(newVal, oldVal) {
if ((idsWithPermissions == null && newVal > 0) || (idsWithPermissions != null)) {
//go get the permissions for the selected items
var ids = _.map($scope.selection, function(i) {
return i.id;
});
//remove the dictionary items that don't have matching ids
var filtered = {};
_.each(idsWithPermissions, function (value, key, list) {
if (_.contains(ids, Number(key))) {
filtered[key] = value;
}
});
idsWithPermissions = filtered;
contentResource.getPermissions(ids).then(function (p) {
$scope.buttonPermissions = listViewHelper.getButtonPermissions(p, idsWithPermissions);
});
}
});
}
}
$scope.options = {
displayAtTabNumber: $scope.model.config.displayAtTabNumber ? $scope.model.config.displayAtTabNumber : 1,

View File

@@ -82,7 +82,7 @@
<umb-editor-sub-header-section ng-if="isAnythingSelected()">
<umb-button
ng-if="options.allowBulkPublish && (currentNodePermissions.canPublish || currentNodePermissions.isAdminUser)"
ng-if="options.allowBulkPublish && (buttonPermissions == null || buttonPermissions.canPublish)"
type="button"
button-style="link"
label="Publish"
@@ -93,7 +93,7 @@
</umb-button>
<umb-button
ng-if="options.allowBulkUnpublish && (currentNodePermissions.canUnpublish || currentNodePermissions.isAdminUser)"
ng-if="options.allowBulkUnpublish && (buttonPermissions == null || buttonPermissions.canUnpublish)"
type="button"
button-style="link"
label="Unpublish"
@@ -104,7 +104,7 @@
</umb-button>
<umb-button
ng-if="options.allowBulkCopy && (currentNodePermissions.canCopy || currentNodePermissions.isAdminUser)"
ng-if="options.allowBulkCopy && (buttonPermissions == null || buttonPermissions.canCopy)"
type="button"
button-style="link"
label="Copy"
@@ -115,7 +115,7 @@
</umb-button>
<umb-button
ng-if="(options.allowBulkMove) && (currentNodePermissions.canMove || currentNodePermissions.isAdminUser)"
ng-if="options.allowBulkMove && (buttonPermissions == null || buttonPermissions.canMove)"
type="button"
button-style="link"
label="Move"
@@ -126,7 +126,7 @@
</umb-button>
<umb-button
ng-if="options.allowBulkDelete && (currentNodePermissions.canDelete || currentNodePermissions.isAdminUser)"
ng-if="options.allowBulkDelete && (buttonPermissions == null || buttonPermissions.canDelete)"
type="button"
button-style="link"
label="Delete"

View File

@@ -5,7 +5,8 @@ var app = angular.module('umbraco', [
'umbraco.services',
'umbraco.mocks',
'umbraco.security',
'ngCookies'
'ngCookies',
'LocalStorageModule'
]);
/* For Angular 1.2: we need to load in Routing separately

View File

@@ -26,7 +26,7 @@ module.exports = function(karma) {
'lib/../build/belle/lib/underscore/underscore-min.js',
'lib/umbraco/Extensions.js',
'lib/../build/belle/lib/rgrove-lazyload/lazyload.js',
'lib/../build/belle/lib/angular-local-storage/angular-local-storage.min.js',
'test/config/app.unit.js',
'src/common/mocks/umbraco.servervariables.js',

View File

@@ -0,0 +1,48 @@
describe('list view helper tests', function () {
var $scope, listViewHelper;
beforeEach(module('LocalStorageModule'));
beforeEach(module('umbraco.services'));
beforeEach(inject(function ($injector) {
$scope = $injector.get('$rootScope');
listViewHelper = $injector.get('listViewHelper');
}));
describe('getButtonPermissions', function () {
it('should update the currentIdsWithPermissions dictionary', function () {
var currentIdsWithPermissions = {};
var result = listViewHelper.getButtonPermissions({ "1234": ["A", "B", "C"] }, currentIdsWithPermissions);
expect(_.has(currentIdsWithPermissions, "1234")).toBe(true);
expect(_.keys(currentIdsWithPermissions).length).toEqual(1);
});
it('returns button permissions', function () {
var currentIdsWithPermissions = {};
var result1 = listViewHelper.getButtonPermissions({ "1234": ["O", "C", "D", "M", "U"] }, currentIdsWithPermissions);
expect(result1["canCopy"]).toBe(true);
expect(result1["canCreate"]).toBe(true);
expect(result1["canDelete"]).toBe(true);
expect(result1["canMove"]).toBe(true);
expect(result1["canPublish"]).toBe(true);
expect(result1["canUnpublish"]).toBe(true);
var result2 = listViewHelper.getButtonPermissions({ "1234": ["A", "B"] }, currentIdsWithPermissions);
expect(result2["canCopy"]).toBe(false);
expect(result2["canCreate"]).toBe(false);
expect(result2["canDelete"]).toBe(false);
expect(result2["canMove"]).toBe(false);
expect(result2["canPublish"]).toBe(false);
expect(result2["canUnpublish"]).toBe(false);
});
});
});