Move blocks from page assets without Delete permission

Use Dojo trickery to allow users moving blocks without Delete permissions on local assets folder.

Move blocks from page assets without Delete permission

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:

  • Editors should not be able to create and delete pages - only admins are allowed to do it,
  • Editors can edit existing pages,
  • Editors can create, delete and move blocks between folders.

To achieve these requirements the administrator configured permissions in a way that editors have:

  • Read, Create, Change, and Publish permissions to content pages
  • Read, Create, Change, Delete, Publish permissions to assets

For demonstration purposes, this is how this could look like in the Alloy sample site. Permissions for the start page and all inheriting pages:

Editor permissions to content pages

Permissions for content assets folders: Editor permissions to assets

Move block from a local folder to a shared folder issue

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:

DND error

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?

Disable delete command in the UI

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:

    • if the user has admin access to the content item,
    • in case the user does not have admin access, checks if the item is not a page nor a content folder

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:

Final solution - blocks can be moved

Final thoughts

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