matjazp Posted April 22, 2015 Share Posted April 22, 2015 I would like to prevent certain page delete (or move to trash) if it is still "in use" in other pages. I would like to hook in admin.php similar to https://processwire.com/talk/topic/5027-hide-page-instead-of-trash-or-delete/ wire()->addHookBefore('Pages::trash', function ($event) { $page = $event->arguments[0]; //do some checking wire('session')->message('just testing'); $event->replace = true; } ); but page is moved to trash and message is not displayed when I'm in the Page list (I click Move then Trash icon on the right). If I delete the page while editing it (In tab Delete and then confirm), it works as expected. What am I doing wrong? In debug tools hook is listed: before Pages::trash() anonymous function() class method 111 Link to comment Share on other sites More sharing options...
LostKobrakai Posted April 22, 2015 Share Posted April 22, 2015 The PageList does initiate trashing via ajax, therefore the missing message. Also your hook does not throw any error or return false, therefore the ajax call can't determine, that you don't want the page to be trashed. The more failsave way of preventing trashing would be by hooking Page::deleteable, as this is checked everywhere throughout ProcessWire no matter from where it's called. Link to comment Share on other sites More sharing options...
matjazp Posted April 22, 2015 Author Share Posted April 22, 2015 I noticed something: when you enable advanced settings in config.php and set "Disable trash option" in templates System tab (API: $template->noTrash = 1; // or 0 to disable) the trash icon is not present in page list (when you click on move), that is ok. But, you can move page to the trash... is this expected behaviour? Link to comment Share on other sites More sharing options...
matjazp Posted April 22, 2015 Author Share Posted April 22, 2015 I tried this: wire()->addHookAfter("Page::trashable", function($event) { $page = $event->object; wire('session')->message('trashable: '.$page->id); $event->return = false; }); With that hook the trash icon in page list is gone (for all pages of course), but I can still move the page to the trash. Also, I can trash the page in Delete tab (while editing the page) unless I hook to Page::deleteable. Link to comment Share on other sites More sharing options...
LostKobrakai Posted April 22, 2015 Share Posted April 22, 2015 Please keep in mind, that the trash is only visible to superusers. Therefore normal users can't move any pages to the trash. The trash is implemented as security measurement, so that users can't permanently destroy content. They just have the option to delete a page, but normally no knowledge about the trash. Under this circumstances it's understandable that there may be quirks with moving a page to the trash and that the delete tab on the edit page responds to deletable() and not trashable(). The deletable() permission really is the function you're looking for. Link to comment Share on other sites More sharing options...
matjazp Posted April 24, 2015 Author Share Posted April 24, 2015 Yeah, trash is just for admins, forgot that. Now I have two hooks: one on Page::trashable intended for admins, to remove trash icon, as a reminder not to shoot myself in the foot, and one on ProcessPageEdit::buildFormDelete, where I can inform the user about possible consequences about deleting the page that is referenced from another page(s). I think it would be good to have that sort of functionality available in the core or installable core module (for example user should not get deleted if he owns pages). LostKobrakai, thank you for your suggestions. Link to comment Share on other sites More sharing options...
mn-martin Posted February 1, 2016 Share Posted February 1, 2016 ... The more failsave way of preventing trashing would be by hooking Page::deleteable ... That's not a hookable method. I'm trying to do the same thing but am failing when it comes to the ajax trashing as superuser. (I can prevent the actual delete/trash but am unable to show some error message to the admin so he knows that he's got cheated by the system) Any help? What i currently have: public function init() { $this->pages->addHookBefore('delete', $this, 'deletePage'); $this->pages->addHookBefore('trash', $this, 'deletePage'); } public function deletePage(HookEvent $event) { // if page has ... some condition $this->error('Deleting this page is forbidden'); $event->replace = true; } Link to comment Share on other sites More sharing options...
LostKobrakai Posted February 1, 2016 Share Posted February 1, 2016 Page::deletable has to be hookable because PagePermissions::init does exactly that to implement the default permission setup. Link to comment Share on other sites More sharing options...
mn-martin Posted February 2, 2016 Share Posted February 2, 2016 Page::deletable has to be hookable because PagePermissions::init does exactly that to implement the default permission setup. You're right I'm sorry. I couldn't find it on https://processwire.com/api/hooks/captain-hook/ Anyways I can't get this working :/ I'm currently using following Hooks to prevent page deletion and trashing. I even remove the delete tab from the page editor: $this->addHookBefore('Pages::trash', $this, 'preparePagesTrash'); $this->addHookBefore('Pages::delete', $this, 'preparePagesDelete'); $this->addHookAfter('ProcessPageEdit::buildFormDelete', $this, "removePageDeleteButton"); This works flawlessly. Anyways I couldn't get the /admin/page/ Page List Ajax trash button to alert the user. (Currently the system tells you that the page has been trashed even thought it didn't. Reload and I can see it's never been trashed) Is this even possible? I've tryed Page::deletable and even Page::trashable. There seems to be no way :-/ Maybe you can provide a brief example of how this should be working? Thank you very much. Link to comment Share on other sites More sharing options...
mn-martin Posted February 5, 2016 Share Posted February 5, 2016 I'm still lookign for a solution to this. Link to comment Share on other sites More sharing options...
LostKobrakai Posted February 5, 2016 Share Posted February 5, 2016 You can find the part, where the pagelist ajax actions are parsed here: https://github.com/ryancramerdesign/ProcessWire/blob/master/wire/modules/Process/ProcessPageList/ProcessPageListActions.php under processAction(). Link to comment Share on other sites More sharing options...
Robin S Posted June 16, 2018 Share Posted June 16, 2018 A hook for anyone still wanting a solution for this: // Prevent the trashing of pages referenced by other pages $pages->addHookBefore('trash', function(HookEvent $event) { $page = $event->arguments(0); // Find non-system Page Reference fields $pr_fields = $this->fields->find("type=FieldtypePage, flags=0"); // Implode for selector string $pr_fields_str = $pr_fields->implode('|', 'name'); // Find any referencing pages $referenced_on = $this->pages->find("$pr_fields_str=$page->id, include=all"); if($referenced_on->count) { // Replace the trash method $event->replace = true; // Link markup for referencing pages $referenced_on_str = $referenced_on->implode(', ', "<a href='{editUrl}'>{name}</a>"); $plural = $referenced_on->count > 1 ? 's' : ''; // Trigger an error message (using $session in case a superuser is trashing from ProcessPageList) $this->session->error("You cannot trash page $page->name because it is referenced in a Page Reference field on page$plural $referenced_on_str.", Notice::allowMarkup); // Don't allow the trashing of this page $event->return = false; } }); When attempting to trash a referenced page from ProcessPageList the page will at first appear to be trashed but in fact it is not, and on the next admin page load a notice will be displayed explaining the situation. I don't think this detail matters much because it only affects superusers. 6 Link to comment Share on other sites More sharing options...
adrian Posted June 16, 2018 Share Posted June 16, 2018 Nice one @Robin S - just wondering about API calls to delete, rather than trash. Also, what about AOS's "delete" page list action button? Link to comment Share on other sites More sharing options...
Robin S Posted June 17, 2018 Share Posted June 17, 2018 1 hour ago, adrian said: just wondering about API calls to delete, rather than trash. Also, what about AOS's "delete" page list action button? The same code will work in a hook to Pages::delete. Adapted to keep it DRY: // Prevent the trashing/deleting of pages referenced by other pages $pages->addHookBefore('trash', null, 'protectReferencedPages'); $pages->addHookBefore('delete', null, 'protectReferencedPages'); function protectReferencedPages(HookEvent $event) { $page = $event->arguments(0); // Find non-system Page Reference fields $pr_fields = wire('fields')->find("type=FieldtypePage, flags=0"); // Implode for selector string $pr_fields_str = $pr_fields->implode('|', 'name'); // Find any referencing pages $referenced_on = wire('pages')->find("$pr_fields_str=$page->id, include=all"); if($referenced_on->count) { // Replace the trash/delete method $event->replace = true; // Link markup for referencing pages $referenced_on_str = $referenced_on->implode(', ', "<a href='{editUrl}'>{name}</a>"); $plural = $referenced_on->count > 1 ? 's' : ''; // Trigger an error message (using $session in case a superuser is trashing/deleting from ProcessPageList) wire('session')->error("You cannot $event->method page $page->name because it is referenced in a Page Reference field on page$plural $referenced_on_str.", Notice::allowMarkup); // Don't allow the trashing/deleting of this page $event->return = false; } } 5 Link to comment Share on other sites More sharing options...
DrQuincy Posted November 8, 2021 Share Posted November 8, 2021 The above is great @Robin S, thanks. The issue I have though is if Page A references Page B, you can delete Page A. But then when you come to delete Page B it won't let you because it sees a reference between Page A, which is in the trash, and page B. I'm struggling to come up with a solution that will always work in a user-friendly way. If you only run the above just on delete (and not trash) then that fixes my initial problem but what could happen is a page could be restored while the page it references is still in the trash. What do you think would be best practice here? Run your code above on delete only and then do a similar check on restore — only on restore simply issue a warning since at that point the reference is broken. I'm just finding when thinking through all the scenarios it gets complicated and I'm wondering if you run that hook above without adding parent!=7 to the selector (to ignore trashed items) it can get messy and prevent legitimate deletes and trashes. Maybe it's just better to enforce on delete and on the edge cases where page are restored issue a warning. Hope that makes sense and I've understood it correctly! Link to comment Share on other sites More sharing options...
Robin S Posted November 10, 2021 Share Posted November 10, 2021 On 11/8/2021 at 10:01 PM, DrQuincy said: The issue I have though is if Page A references Page B, you can delete Page A. But then when you come to delete Page B it won't let you because it sees a reference between Page A, which is in the trash, and page B. I guess you could exclude pages that are in the trash by changing... $referenced_on = wire('pages')->find("$pr_fields_str=$page->id, include=all"); ...to... $referenced_on = wire('pages')->find("$pr_fields_str=$page->id, include=all, status!=trash"); 1 Link to comment Share on other sites More sharing options...
DrQuincy Posted November 10, 2021 Share Posted November 10, 2021 Thanks. ? I'm going to try and implement this when I get the time. I'm thinking use your code running only on Pages::delete with status!=trash added to the selector — and then issuing a warning on Pages::restore if there are any broken references (possibly also setting the page to unpublished). I'll post something once I've had a go. 1 Link to comment Share on other sites More sharing options...
Recommended Posts
Create an account or sign in to comment
You need to be a member in order to leave a comment
Create an account
Sign up for a new account in our community. It's easy!
Register a new accountSign in
Already have an account? Sign in here.
Sign In Now