Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Products/CMFPlone/browser/configure.zcml
Original file line number Diff line number Diff line change
Expand Up @@ -316,14 +316,14 @@
name="recyclebin"
for="plone.base.interfaces.siteroot.IPloneSiteRoot"
class=".recyclebin.RecycleBinView"
permission="cmf.ManagePortal"
permission="Products.CMFPlone.ManageRecycleBin"
/>

<browser:page
name="recyclebin-item"
for="plone.base.interfaces.siteroot.IPloneSiteRoot"
class=".recyclebin.RecycleBinItemView"
permission="cmf.ManagePortal"
permission="Products.CMFPlone.ManageRecycleBin"
/>

</configure>
22 changes: 22 additions & 0 deletions Products/CMFPlone/browser/recyclebin.py
Original file line number Diff line number Diff line change
Expand Up @@ -318,6 +318,10 @@ def get_items(self):
filter_type = self.get_filter_type()
search_query = self.get_search_query().lower()

# Check if the user has full access to the recycle bin
# (includes permissions check and Manager/Site Administrator roles)
has_full_access = self.recycle_bin.check_permission(check_roles=True)

# Create a list of all items that are children of a parent in the recycle bin
child_items_to_exclude = []
for item in items:
Expand All @@ -334,6 +338,11 @@ def get_items(self):

for item in items:
if item.get("id") not in child_items_to_exclude:
# If user doesn't have full access, show only items they can restore
if not has_full_access:
if not self.recycle_bin.check_permission():
continue

# Apply type filtering
if filter_type and item.get("type") != filter_type:
continue
Expand Down Expand Up @@ -411,6 +420,19 @@ def update(self):
)
return

# Check if the user has permission to access this item
if not self.recycle_bin.check_permission():
logger.debug(f"User does not have permission to view item {self.item_id}")
message = translate(
_("You don't have permission to access this item in the recycle bin."),
context=self.request,
)
IStatusMessage(self.request).addStatusMessage(message, type="error")
self.request.response.redirect(
f"{self.context.absolute_url()}/@@recyclebin"
)
return

# Handle restoration of children
if "restore.child" in self.request.form:
self._handle_child_restoration()
Expand Down
2 changes: 1 addition & 1 deletion Products/CMFPlone/controlpanel/browser/configure.zcml
Original file line number Diff line number Diff line change
Expand Up @@ -354,7 +354,7 @@
name="plone-recyclebin"
for="Products.CMFPlone.interfaces.IPloneSiteRoot"
class="Products.CMFPlone.controlpanel.browser.recyclebin.RecyclebinControlPanelView"
permission="cmf.ManagePortal"
permission="Products.CMFPlone.ManageRecycleBin"
/>

</configure>
5 changes: 5 additions & 0 deletions Products/CMFPlone/controlpanel/permissions.zcml
Original file line number Diff line number Diff line change
Expand Up @@ -89,4 +89,9 @@
title="Manage Context Aliases"
/>

<permission
id="Products.CMFPlone.ManageRecycleBin"
title="Manage recycle bin"
/>

</configure>
2 changes: 1 addition & 1 deletion Products/CMFPlone/profiles/default/actions.xml
Original file line number Diff line number Diff line change
Expand Up @@ -508,7 +508,7 @@
<property name="icon_expr">string:plone-delete</property>
<property name="available_expr">portal/@@recyclebin-enabled|nothing</property>
<property name="permissions">
<element value="Manage portal" />
<element value="Manage recycle bin" />
</property>
<property name="visible">True</property>
</object>
Expand Down
2 changes: 1 addition & 1 deletion Products/CMFPlone/profiles/default/controlpanel.xml
Original file line number Diff line number Diff line change
Expand Up @@ -362,6 +362,6 @@
url_expr="string:${portal_url}/@@plone-recyclebin"
visible="True"
>
<permission>Manage portal</permission>
<permission>Manage recycle bin</permission>
</configlet>
</object>
6 changes: 6 additions & 0 deletions Products/CMFPlone/profiles/default/rolemap.xml
Original file line number Diff line number Diff line change
Expand Up @@ -378,5 +378,11 @@
<role name="Site Administrator" />
<role name="Owner" />
</permission>
<permission acquire="True"
name="Manage recycle bin"
>
<role name="Manager" />
<role name="Site Administrator" />
</permission>
</permissions>
</rolemap>
12 changes: 12 additions & 0 deletions Products/CMFPlone/recyclebin.py
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,9 @@ def get_items_sorted_by_date(self, reverse=True):
class RecycleBin:
"""Stores deleted content items"""

# Permission for managing recycle bin
MANAGE_RECYCLEBIN = "Manage recycle bin"

def __init__(self):
"""Initialize the recycle bin utility

Expand Down Expand Up @@ -166,6 +169,15 @@ def is_enabled(self):
except (KeyError, AttributeError):
return False

def check_permission(self):
"""Check if the current user has permission to manage the recycle bin

Returns:
Boolean indicating permission
"""
context = self._get_context()
return getSecurityManager().checkPermission(self.MANAGE_RECYCLEBIN, context)

def _get_item_title(self, obj, item_type=None):
"""Helper method to get a meaningful title for an item"""
if hasattr(obj, "objectIds") or item_type == "Collection":
Expand Down
13 changes: 13 additions & 0 deletions Products/CMFPlone/tests/test_recyclebin.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,19 @@ def test_recyclebin_settings(self):
self.assertEqual(settings.retention_period, 30)
self.assertEqual(settings.maximum_size, 100)

def test_recyclebin_permission(self):
"""Test permission checks for the recycle bin"""
# As Manager role, should have access
self.assertTrue(self.recyclebin.check_permission())

self.portal.acl_users._doAddUser("testuser", "password", ["Member"], [])

# Log in as the test user using plone.app.testing login function
login(self.portal, "testuser")

# Check permission - should be False for a regular member
self.assertFalse(self.recyclebin.check_permission())


class RecycleBinContentTests(RecycleBinTestCase):
"""Tests for deleting and restoring basic content items"""
Expand Down