Probably almost any EPiserver user knows that to move blocks between folders in EPiserver you need to able to delete items from the source folder. And it’s fine for most of the time and we don’t think too much about it. Until we run into an issue, of course. Things can get tricky when we are faced with some specific requirements, which do not fit into the out-of-the-box capabilities of EPiserver.
Let’s say we have following requirements for permissions in the system:
To achieve these requirements the administrator configured permissions in a way that editors have:
For demonstration purposes, this is how this could look like in the Alloy sample site. Permissions for the start page and all inheriting pages:
Permissions for content assets folders:
When editors build the pages they can run into a problem, where they are unable to move a block from the local folder (For this page) to any other, shared folder. Once the editor drags the block and drops it into a different folder, the following error message gets displayed:
Users have been able to move the blocks between different folders without issues. The problem only occurred for blocks in local folders.
This is happening because local folder permissions are the same as permissions of the page that the user is currently editing. This is documented in the EPiserver User Guide here. Taking into account the access configuration that has been applied to the pages, it is clear that the user does not have Delete permission on the local folder. However, Delete is necessary for the user to be able to move content between folders. Without it, the move operation can’t be executed.
A simple solution to this problem is to give the editors Delete permissions on the pages. But that violates the requirement we put in place: Editors should not be able to create and delete pages, only admins are allowed. So what now?
Fortunately, due to EPiserver’s flexibility, we can easily adjust the system to meet the requirements. For this, we will have to override some UI commands to prevent the editors from using them. After some poking around I have been able to locate the command called epi-cms/command/DeleteContent
, which is sitting in the CMS protected module. I made a copy of the uncompressed version of the file into ClientResources/Scripts/Commands
folder in my solution, and altered the logic a little.
I only edited couple of lines of the _onModelChange
function around the canDelete
function:
// source code redacted for brevity
_onModelChange: function () {
var model = this.model,
selection = this.selection.data,
canDelete = function (item) {
var content = item.data;
var hasAdminAccess = ContentActionSupport.hasAccess(
content.accessMask, ContentActionSupport.accessLevel[ContentActionSupport.action.Administer]);
return content && !content.isDeleted && model.canDelete(content)
&& (hasAdminAccess || (!content.capabilities.isPage && !content.capabilities.isContentFolder));
}
}
// source code redacted for brevity
First main thing is to determine whether user is an administrator with this piece of code:
var hasAdminAccess = ContentActionSupport.hasAccess(
content.accessMask, ContentActionSupport.accessLevel[ContentActionSupport.action.Administer]);
We are calling ContentActionSupport.hasAccess()
helper which checks whether the current user has Administer
permission level for the specified content item. Then we have a return statement which has two parts:
content && !content.isDeleted && model.canDelete(content)
is a 1-1 copy from the original EPiserver implementation of the command, becaue we want our additional logic to be exectud on top of the default EPiserver behavior,
&& (hasAdminAccess || (!content.capabilities.isPage && !content.capabilities.isContentFolder))
checks:
With that last check, we ensure that admins can delete everything (pages, folders & blocks), and editors can’t remove pages and folders but can still remove blocks - even though they now have Delete
permissions which we gave them to let them move blocks around.
In order to make this code to work we need to have a module initializer that references our new command:
define([
'dojo/_base/declare',
'epi/_Module',
// set our command as dependency
'alloy/Commands/AdminDeleteContent'
],
function (declare, _Module) {
return declare([_Module], {
initialize: function () {
this.inherited(arguments);
}
});
}
);
We also need to ensure our initializer is called when EPiserver is loading by adding clientModule
entry to the module.config:
<?xml version="1.0" encoding="utf-8"?>
<module>
<!-- Redacted for brevity -->
<clientModule initializer="alloy/Initializer">
<moduleDependencies>
<add dependency="Shell" type="RunAfter" />
</moduleDependencies>
</clientModule>
</module>
As a result when we login into EPiserver as an editor we can’t delete a page but we can move the block from local folder to a shared folder:
Depending on specific use cases and requirements, it might be needed to provide a custom implementation for other commands, like the Cut command.
Also, since I’m not a Dojo expert, the custom delete command implementation is pretty straight forward. But it gets the job done. It’s worth keeping in mind that with new versions of EPiserver this might stop to work due to changes in the implementation of the command, so I suggest testing the feature when upgrading to newer versions.
Happy coding!
Cover photo by Chase Clark on Unsplash