Skip to content

Implement Recycle Bin #4168

Closed
rohnsha0 wants to merge 64 commits intomasterfrom
rohnsha0-plip-recyclebin
Closed

Implement Recycle Bin #4168
rohnsha0 wants to merge 64 commits intomasterfrom
rohnsha0-plip-recyclebin

Conversation

@rohnsha0
Copy link
Member

@rohnsha0 rohnsha0 commented Apr 29, 2025

ref #2966

to be merged together:-

  • plone.base - #81
  • plone.app.discussion - #264

RecycleBinView
RecycleBinItemView

replaces #4166

@mister-roboto
Copy link

@rohnsha0 thanks for creating this Pull Request and helping to improve Plone!

TL;DR: Finish pushing changes, pass all other checks, then paste a comment:

@jenkins-plone-org please run jobs

To ensure that these changes do not break other parts of Plone, the Plone test suite matrix needs to pass, but it takes 30-60 min. Other CI checks are usually much faster and the Plone Jenkins resources are limited, so when done pushing changes and all other checks pass either start all Jenkins PR jobs yourself, or simply add the comment above in this PR to start all the jobs automatically.

Happy hacking!

@rohnsha0 rohnsha0 mentioned this pull request Apr 29, 2025
Copy link
Contributor

@stevepiercy stevepiercy left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Refactor recyclerbin to recyclebin. I missed this on my previous pass.

@stevepiercy
Copy link
Contributor

@rohnsha0 can you check the failing CI checks from meta? Let us know if you have any questions about it.

@stevepiercy
Copy link
Contributor

stevepiercy commented Apr 29, 2025

@rohnsha0 here's one problem.

https://github.com/plone/Products.CMFPlone/actions/runs/14733219302/job/41352593835?pr=4168#step:7:70

error: cannot format Products/CMFPlone/browser/recyclebin.py: Cannot parse for target version Python 3.8: 77:67:         message = f"{restored_count} item{'s' if deleted_count != 1} restored successfully."

I think we need to change that, as it appears to have support only since Python 3.12, according to PEP 701. Bummer.

Confirmed by https://github.com/plone/Products.CMFPlone/actions/runs/14733219302/job/41352596237?pr=4168#step:7:773.

@jensens
Copy link
Member

jensens commented May 9, 2025

@ale-rt i noticed only core presentations are placed in p.a.layout. i did not find any controlpanel thing in there... i am not super sure what to do!

Controlpanel goes into https://github.com/plone/Products.CMFPlone/tree/master/Products/CMFPlone/controlpanel/browser

@davisagli
Copy link
Member

Hi @rohnsha0 -- thanks for your diligent work so far.

Tomorrow I plan to test some edge cases. I will also record a video to show to a UX expert our company works with, to see if he has any suggestions.

try:
settings = self._get_settings()
return settings.recycling_enabled
except (KeyError, AttributeError):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can raise zope.interface.interfaces.ComponentLookupError when this is called while the Plone site is being deleted, so we should also catch that here.

Traceback (innermost last):
  Module ZPublisher.WSGIPublisher, line 181, in transaction_pubevents
  Module ZPublisher.WSGIPublisher, line 390, in publish_module
  Module ZPublisher.WSGIPublisher, line 284, in publish
  Module ZPublisher.mapply, line 98, in mapply
  Module ZPublisher.WSGIPublisher, line 68, in call_object
  Module OFS.ObjectManager, line 560, in manage_delObjects
  Module OFS.ObjectManager, line 417, in _delObject
  Module zope.event, line 33, in notify
  Module zope.component.event, line 27, in dispatch
  Module zope.component._api, line 146, in subscribers
  Module zope.interface.registry, line 458, in subscribers
  Module zope.interface.adapter, line 918, in subscribers
  Module zope.component.event, line 37, in objectEventNotify
  Module zope.component._api, line 146, in subscribers
  Module zope.interface.registry, line 458, in subscribers
  Module zope.interface.adapter, line 918, in subscribers
  Module Products.CMFPlone.events, line 60, in handle_content_removal
  Module Products.CMFPlone.recyclebin, line 164, in is_enabled
  Module Products.CMFPlone.recyclebin, line 156, in _get_settings
  Module zope.component._api, line 180, in getUtility
zope.interface.interfaces.ComponentLookupError: (<InterfaceClass plone.registry.interfaces.IRegistry>, '')

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done in fa502d9

@rohnsha0 rohnsha0 requested a review from davisagli May 30, 2025 02:02
Copy link
Contributor

@stevepiercy stevepiercy left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

minor tweaks and a general question about translations and localization of various forms of plurality


message = translate(
_(
"${count} item(s) restored successfully.",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know there's a way to use plurals intelligently in translations, but I don't know what that would be. Here's my clueless English attempt.

There might also be something else to handle plurals in translations, but my memory fails me. Here is some reference material.

Perhaps @erral has a better idea of how to support localized singular and plural forms?

Suggested change
"${count} item(s) restored successfully.",
"${count} item{count != 1 ? "s"} restored successfully.",

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This pluralisation does not work. I tried it, and I just literally get this in the browser:

1 item${count != 1 ? 's'} restored successfully.

When I extract messages with i18ndude, the po files contain this:

msgid "${count} item${count != 1 ? 's'} restored successfully."

You can't really translate that.

zope.i18n has support for plural messages, but this has not been hooked up in Plone as far as I know. Also, it would need changes in i18ndude.

So: it would be nice to have pluralisation at some point, but currently it does not work.


message = translate(
_(
"${count} item(s) permanently deleted.",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Flagging for pluralization.


message = translate(
_(
"Recycle bin emptied. ${count} item(s) permanently deleted.",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Flagging for pluralization.

<div class="d-flex justify-content-between align-items-center flex-wrap gap-2">
<div class="btn-group" role="group">
<button type="submit" name="form.buttons.restore" class="btn btn-primary" i18n:translate="">
<span class="ms-1">Restore Selected</span>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
<span class="ms-1">Restore Selected</span>
<span class="ms-1">Restore selected</span>

<span class="ms-1">Restore Selected</span>
</button>
<button type="submit" name="form.buttons.delete" class="btn btn-danger" i18n:translate="">
<span class="ms-1">Delete Selected</span>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
<span class="ms-1">Delete Selected</span>
<span class="ms-1">Delete selected</span>

class="btn btn-outline-danger"
onclick="return confirm(recyclebinEmptyMessage);"
i18n:translate="">
<span class="ms-1">Empty Recycle Bin</span>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
<span class="ms-1">Empty Recycle Bin</span>
<span class="ms-1">Empty recycle bin</span>

Comment on lines +205 to +207
return f'Comment thread: "{comment_preview}" ({comment_count} comments)'
else:
return f"Comment thread ({comment_count} comments)"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have two possible plurals to handle here.

… bin.

This avoids having a strange acquisition chain after the item is restored,
which then causes a 'has already been deleted' message if you try to delete it again.
@davisagli
Copy link
Member

@rohnsha0 Here's another thing that didn't quite work like I expected: I created a folder with a page inside, and deleted the folder. Then in the recycle bin I selected the folder and clicked the "Delete Selected" button to delete it permanently. I expected that this would include all children, but afterward the recycle bin still contained the page.

@rohnsha0
Copy link
Member Author

rohnsha0 commented Jun 17, 2025

@rohnsha0 Here's another thing that didn't quite work like I expected: I created a folder with a page inside, and deleted the folder. Then in the recycle bin I selected the folder and clicked the "Delete Selected" button to delete it permanently. I expected that this would include all children, but afterward the recycle bin still contained the page.

@davisagli I fixed it, tested it locally and it now works

edit: also included a testcase covering the same!

@rohnsha0
Copy link
Member Author

@jenkins-plone-org please run jobs

@davisagli
Copy link
Member

@rohnsha0 I discussed your implementation with our UX expert, Dominique Steppeler. He had the following suggestions:

  • When deleting an item, if the recycle bin is enabled, the delete confirmation dialog should have a message saying that the content can be restored by admins, but will be deleted permanently after X days.
  • The UI would be a bit easier to understand if we didn't always show the option to restore to a different path. Instead we could do one of the following options:
    • a. Only prompt the user for a different path after they try to restore to the original location but it is not available.
    • b. Always restore content to a “restored content” folder instead of to where it was deleted
  • In some cases, it might be best if content is restored in its initial workflow state (usually "draft") rather than the workflow state it was in when it was deleted. This could be a setting in the recycle bin control panel.
  • To avoid confusion, we should change the icon for the Undo control panel so that it is more of a normal undo icon, not a trash bin.
  • Additional information that could be useful to show for each item:
    • preview image for content that has one
    • deleted by (username who deleted the item)
    • sub-items (number of contained items)
    • workflow state (i.e. published, private)
  • Additional filters that could be useful for finding the right items to restore:
    • date range for when it was deleted
    • “deleted by” (user who deleted it)
    • checkbox for whether it has sub-items
    • language
    • workflow state

What do you think?

…alue-error gracefully if parent is deleted (#4175)

* feat(recyclebin): enhance restoration process with error handling for missing target containers

* feat(recyclebin): enhance redirect logic after restore with URL validation

* fix failing tests

* refactor(recyclebin): lint

* changelog

* fix(recyclebin): improve parent existence check for comments and discussion items

* refactor(recyclebin): remove obsolete bugfix for ValueError handling and redirection after parent deletion

* refactor(recyclebin): simplify redirection logic after item restoration by using context state

* Store object without acquisition wrapper; reindex it when restored

---------

Co-authored-by: David Glick <david@glicksoftware.com>
@rohnsha0
Copy link
Member Author

replaced by #4223

@rohnsha0 rohnsha0 closed this Sep 21, 2025
@rohnsha0
Copy link
Member Author

@davisagli I have implemented your suggested changes, please verify those in the new PR

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

9 participants