Configuring site so a role could delete objects but not containing folder

I am developing an Add-On for a site with the following requirements:

  1. From Plone Site root, there should be a FolderA, and within it, FolderB.
  2. Within FolderB, any user with Role1 should be able to create, edit, delete, and change workflow state of objects of TypeAlpha.
  3. FolderB will have only TypeAlpha objects.
  4. Any user with role Role1 should be able to edit, delete and change workflow state of any objects of type TypeAlpha, even if they did not create said objects.
  5. Role1 users should not be able to delete FolderB or FolderA.

That said, I am having a bit of trouble setting the delete permissions.

If I am not wrong, from what I understood from my testing, for an object to be deleted the user should have the "Delete Objects" permission in both the "to be deleted" object and the folder. Those permissions are needed to both show the portal_action item on the Actions menu and to perform the deletion itself.

So I need to give Delete Objects to Role1 in the TypeAlpha objects, in FolderB, but NOT on FolderA.

Considering that FolderA and Folder B would have the same workflow (so they would have the same permissions on the same - published - state), what would be a sane way of doing that?

Roles are really cool but can be overly complicated. Have you thought about
using sharing instead?

This (mostly) looks like a task for Products.CMFPlaceFullWorkflow -- at least for those permissions controlled by the workflow. This extension allows you to apply workflow assignments and definitions not globally but restricted to a subhierarchy ("folderB" in your case). In your specialized worklow definitions, you would control all permissions you want to adapt and assign them to the required roles.

This should work well for everything apart from the "delete" permission. This permission is usually not controlled by workflow. Therefore (more precisely: then), it can be directly mapped to the roles you want. For this, you can use the (now hidden) manage_access view (for permissions controlled by workflow, modifications via manage_access can be lost -- that's the reason, why it is hidden). Important thing about the delete permission: it does not allow for the deletion of the object itself, but the deletion of its descendants. Thus, mapping the delete permission to your roles at "folderB" is sufficient to allow deletion below "folderB" but not the deletion of "folderB" itself (provided that no applicable workflow below "folderB" controls the delete permission).

1 Like

Have a look into collective.deletepermission. It addresses especially this issue. It introduces a new permission "Delete portal content", that is true, if this single object can be deleted. Your container workflow should be configured in a way, users have the permission "Delete objects", but in the workflow for the children content types you give the permission "Delete portal content" individually. It should be working nicely with a custom workflow, that is assigned via a placeful workflow policy for FolderA - as @dieter stated.

We have this add-on in production and are very happy with it. :slight_smile:

Hey, guys, thank you for the answers.

@mikemets, I really didn't understand what your suggestion was. Besides, I'm going to disable the Sharing tab completely, because in the application I am developing, only the Manager should delegate roles.

@dieter Using PFW is a very good suggestion. I've used it in the past, but it didn't occur to me now. So, in this strategy, I could create a, say, folder_full_of_typealphas_workflow, based on simple_publication (which was my original choice for the Folders), in which I could give "Delete Objects" to Role1 in the published state. In simple_publication, Role1 would not have Delete Objects in the published state. And then I assign simple_publication to FolderA in portal_workflows, and I could use PFW to assign folder_full_of_typealphas_workflow to folderB and simple_publication_workflow to folders below FolderB.

The only question to me is that configuration set? I've checked the genericsetup files for Placeful Workflow, but it seems like it does only export and import workflow polciies, and not assignments of such policies to folders. I suppose I will have to write Python code in my import step to configure those assignments?

@pgg Paul, I will check out that add-on too. It seems really reasonable and would require less config for my product. The only point in which it would lose to using Placeful Workflow is that PFW is included in the default Plone install, and I guess this would somehow keep it more to the safe side considering future Plone upgrades.

Thank you all for your replies.

Supposing you are setting the roles per sharing-tab of the folders, it would solve your requests to set the permissions per TypeAlpha-instance, by granting role1 the wished roles (edit/delete/review) with GenericSetup-xml-files of your content-type as default-setting for each fresh born instance. The add permission can be granted on the parent-folder, as you do already.

For performance reasons it's better to create a group, wire it to the role and assign users to it, instead of assigning the role per user (role-perm-lookups in the DB consume more time, than group-perm-lookups, IIRC).

If you want to restrict this behavior to certain locations only (here: folder A and B), listen to the creation and set the props via a Python-script, instead of using the xml-files.

Maybe, I did not understand your requirements; otherwise, the solution above should not work. I understood, that modifications and workflow actions should be granted to "role1" below "folderB". This means that you cannot use the standard "simple_publication_workflow" for content below "folderB" (as there, "role1" is unknown). You do not need workflow to have the proper "delete Objects" permission mapping on "folderB" (you use "../manage_access" for this).

If you use "GenericSetup" to make the workflow assignment on "folderB", you (almost surely) must indeed do it in a special handler, implemented directly in Python.