How to use a new workflow to define view and deletion of an object

Hello,

I have a collection that i want to control who can see and delete the collection itself, without preventing logged in users from see the content of the collection.

so, I created a very simple workflow and assigned to "collections".

I want this WF to:

  1. makes collection only visible to admin but the contents to be viewed by all signed in users.
  2. makes collection can't be deleted except by admin.

In the WF, i create the usual WF features and for the (initial and only) state "private" i went to "permission" for that state these are the current permissions (I know i can add more if i find the correct permission)

*Access contents information, *Delete objects, *Modify portal content, *View

my assumption is if i control "Delete objects" and "View" i can control the deleting and viewing of the collection. However as @espenmn pointed out, this my apply to what is contained IN the collection and not the collection itself. My testing shows this is correct.

so, how do i block these viewing and deleting for the collection itself.

Plone 6 Classic.

You will made a list of objects only visible for managers, but the objects itself can access by all? Sorry that make no sense. That is security through obscurity.

well, either I didn't explain well enough, or maybe you're right and it's a dumb idea. Let me try again.

On my site i have a collection inside a folder. i want to ensure the collection can't be deleted. This accidental deletion is foreseeable because I will need to give permission to a small group (project manger) to be able to go into "contents" of the folder containing the collection and be able to delete everything inside the folder Except the collection.
If you look at "contents' and see the "select all" button, selecting this would include the collection. it is easy to foresee the user, who wants to empty the folder, will 'select all' and delete. thus 'upsetting the apple cart" by deleting the collection.

So, my thought was to prevent this by hiding the collection and removing 'delete' as an option except for admin.

I realize this may not make sense to others; however, it will fit my use case.

So, back to my technical question above: 1) "can this be done in the WF permissions settings".; or 2) can i modify the permissions on this object - not thru worklfow?

Wayne Glover via Plone Community wrote at 2024-4-30 21:28 +0000:

...
On my site i have a collection inside a folder. i want to ensure the collection can't be deleted. This accidental deletion is foreseeable because I will need to give permission to a small group (project manger) to be able to go into "contents" of the folder containing the collection and be able to delete everything inside the folder Except the collection.

As you are aware deletion is not easy to control via a permission,
because the deletion is seen as a modification of the container
not the deleted object and therefore, the "deletion permission"
applies to the container independent of any object to be deleted.

You need something more specific.
One approach is to register a
zope.lifecycleevent.interfaces.IObjectRemoved event handler
(look at OFS.ObjectManager.ObjectManager._delObject to learn
about alternative event handler based approaches).
Such an event is notified by Zope when an object is
removed from a container.
In your event handler, you could check additional conditions
for an allowed deletion and raise an exception if they are not met.

Your event handler could e.g. look for an attribute
"object deletion permission" on the object to be deleted
and (if it finds such an attribute) check that the current user
has this permission on the object to be deleted.
If you only want to prevent accidental deletion, you might
implement the "attribute" via an object property.

The approach sketched above might not fulfill all your wishes:
if someone selects all items and tries the deletion,
the result will be an exception, not the deletion of all items
which the current user can delete.
If you want such a functionality, you will need to customize
the deletion action to translate "delete all items" into
"delete all items you have permission to delete".
Likely, you have the event handler in addition, to prevent
the deletion via ananticipated routes.

Isn't this 'after' the object is removed?

Slighly related: It would be useful if there was a check for 'is default page', or maybe that a default page could not be deleted (maybe with a warning: set another default page/view first)

Espen via Plone Community wrote at 2024-5-1 13:38 +0000:

Isn't this 'after' the object is removed?

It is -- but when the event handler raises an exception, it does not matter:
the transaction will get aborted and persistent data is not changed.

There is also an ObjectWillBeRemovedEvent
(see OFS.ObjectManager.ObjectManager._delObject), notified before
the object is actually removed. But, in order to prevent the deletion,
the handler will need to raise an exception - and that gives almost
the same net effect as before.

1 Like

Hi Dieter, @dieter

thanks for the explanation. Frankly i had no idea delete permissions don't apply to the object.

Also, i appreciate your help with a solution. since @espenmn is helping me with the Plone programming I'll talk with him about it later.

Thanks again and have a good day

Can't you just move the collection out of the folder?

Yes, this works, did a quick test.
I think it would be useful to have a 'check for default page', either refuse it (or maybe redirect to 'set new default page'.
For the last appoach (and adding status messages), I have not figured out how to do that, since there is no 'request avalable for object/event (?))

def check_defaultpage(object, event):
    if object.aq_parent.default_page == object.id:
        #alternatively check for 'workflow state or something similar'
        # messages = IStatusMessage(event)
        # messages.addStatusMessage(u"You can not delete the default view of a folder.", type="info")
        raise Unauthorized("You are not allowed to delete the default view of a folder")

Espen via Plone Community wrote at 2024-5-3 11:53 +0000:

...
For the last appoach (and adding status messages), I have not figured out how to do that, since there is no 'request avalable for object/event (?))

Usually, you can access the request via acquisition from an
object obj (in the object file system) via obj.REQUEST.

using 'object' for statusmessage gives me an error (could not adapt), but (for some reason) it looks like this works:

    messages = IStatusMessage(object.REQUEST)
    messages.addStatusMessage(u"You can not delete the default view of a folder.", type="info")

But I am not sure how to combine this with raising an exception (since transactions gets aborted). Maybe I need to do this 'earlier' (?)

Espen via Plone Community wrote at 2024-5-6 08:23 +0000:

...
messages = IStatusMessage(object.REQUEST)
messages.addStatusMessage(u"You can not delete the default view of a folder.", type="info")

But I am not sure how to combine this with raising an exception (since transactions gets aborted). Maybe I need to do this 'earlier' (?)

When you look at the StatusMessage definition, you will see
that it sets a cookie.
Cookie setting is not controlled by the transaction management,
i.e. the following request can see it (and display the message).
You can use raise zExceptions.Redirect(<redirect_to>)
to combine raising an exception and redirect to a specific page.

1 Like

Thanks A LOT (would never found that)

I paste code here for reference:

  <!-- Disable deletion of default page -->
  <subscriber
    for="*       
       zope.lifecycleevent.interfaces.IObjectRemovedEvent"
       handler=".subscribers.check_defaultpage"
    />

def check_defaultpage(object, event):
    if object.aq_parent.default_page == object.id:
        messages = IStatusMessage(object.REQUEST)
        messages.addStatusMessage(u"You can not delete the default view of a folder.", type="error")
        raise Redirect(object.absolute_url())

Espen via Plone Community wrote at 2024-5-6 09:29 +0000:

...
def check_defaultpage(object, event):
if object.aq_parent.default_page == object.id:

This does not look correct: aq_parent gives you the acquisition
parent -- not necessarily the container (even though, it often is
the container).
obj.aq_inner.aq_parent gives you obj's container.

Thanks

PS: I will also have a check to see if it has a parent (I assume content types (etc) does not have one)

Espen via Plone Community wrote at 2024-5-6 21:19 +0000:

...
PS: I will also have a check to see if it has a parent (I assume content types (etc) does not have one)

Typically, all OFS (= "Object File System", i.e. the Zope object
hierarchy) with the exception of the root object have a container
(and thus a parent).