Jump to content
matjazp

Prevent page delete

Recommended Posts

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

Share this post


Link to post
Share on other sites

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.

Share this post


Link to post
Share on other sites

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?  

Share this post


Link to post
Share on other sites

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. 

Share this post


Link to post
Share on other sites

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.

Share this post


Link to post
Share on other sites

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.

Share this post


Link to post
Share on other sites

...

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;
}

Share this post


Link to post
Share on other sites

Page::deletable has to be hookable because PagePermissions::init does exactly that to implement the default permission setup.

Share this post


Link to post
Share on other sites

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.

Share this post


Link to post
Share on other sites

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.

  • Like 5

Share this post


Link to post
Share on other sites

Nice one @Robin S - just wondering about API calls to delete, rather than trash. Also, what about AOS's "delete" page list action button?

Share this post


Link to post
Share on other sites
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;
    }
}

 

  • Like 4

Share this post


Link to post
Share on other sites

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 account

Sign in

Already have an account? Sign in here.

Sign In Now

  • Recently Browsing   0 members

    No registered users viewing this page.

×
×
  • Create New...