Jump to content

Hook to output warning from site tree


DrQuincy
 Share

Recommended Posts

I need to check some data when pages are moved or saved. It runs fine when I save a page (Pages::saved) but nothing happens when I move a page (e.g. to and from trash). It only appears to be the lack of warning that is the issue. The hook definitely runs when I move pages in the site tree as I set up a simple bit of text file logging.

wire()->addHookAfter('Pages::moved, Pages::saved', function($event) {
	
    $event->warning('Warning');
	
});	

Is it because from the site tree moving pages is done with AJAX? Is there a way to output a warning when sorting or moving pages in the site tree or is it not possible?

Thanks.

P.S. I have noticed on Pages::saved if you change the status from the tree view (using Unpub, Hide buttons) the hook calls but the messages are not displayed. So really I guess my question is: how can hooks send messages, errors and warnings in the tree view?

Link to comment
Share on other sites

On 5/14/2021 at 7:26 PM, DrQuincy said:

So really I guess my question is: how can hooks send messages, errors and warnings in the tree view?

There's no simple way to do this because when a page is moved the page list is updated via AJAX, whereas the core message() / warning() / error() methods require a normal page load in order to appear.

But I had fun exploring some workarounds. Maybe one of these will suit or maybe not. In these examples a message is shown whenever a page is moved but of course you would add your own logic show your message more selectively.

 

Option 1: queue a warning() to appear at the next page load via $session->warning(), and use some JS to automatically reload ProcessPageList whenever a page is sorted or moved.

In some custom admin JS (you can add this with a hook or by using AdminOnSteroids):

// When a page is moved in ProcessPageList
$(document).on('pageMoved', '.PageListItem', function() {
	// Reload
	location.reload();
});

In /site/ready.php

// When a page is moved
$wire->addHookAfter('Pages::moved', function(HookEvent $event) {
	$page = $event->arguments(0);
	// Show a warning
	$event->wire()->session->warning("Page '{$page->title}' was moved to parent '{$page->parent->title}'.");
});

Result:

warning-1.gif.96913f4ebdb485660d68dcf015c561c0.gif

 

Option 2: Make use of the fact that when an exception is thrown ProcessPageList will show this as an alert. The below assumes that you only want to show a warning when a page is moved to a new parent and not when it is sorted within its existing parent. If that distinction doesn't matter then you could simplify this by only hooking after ProcessPageSort::execute().

In /site/ready.php

// Optional: use Vex for nicer alerts in ProcessPageList
$wire->addHookBefore('ProcessPageList::execute', function(HookEvent $event) {
	$event->wire()->modules->get('JqueryUI')->use('vex');
});

// Before ProcessPageSort::execute
$wire->addHookBefore('ProcessPageSort::execute', function(HookEvent $event) {
	$pps = $event->object;
	$move_id = (int) $event->wire()->input->post->id;
	$moved = $event->wire()->pages->get($move_id);
	// Store original parent ID on the ProcessPageSort object
	$pps->original_parent_id = $moved->parent->id;
});

// After ProcessPageSort::execute
$wire->addHookAfter('ProcessPageSort::execute', function(HookEvent $event) {
	$pps = $event->object;
	$input = $event->wire()->input;
	$pages = $event->wire()->pages;
	$parent_id = (int) $input->post->parent_id;
	$move_id = (int) $input->post->id;
	$parent = $pages->get($parent_id);
	$moved = $pages->get($move_id);
	// Check if the parent ID has changed from the original (i.e. the page has moved)
	if($parent->id !== $pps->original_parent_id) {
		// Show an alert by making use of how exceptions are handled in ProcessPageList
		throw new WireException("Page '{$moved->title}' was moved to parent '{$parent->title}'.");
	}
});

Result:

warning-2.gif.97030d7a41156651097e6c8b32cf11e0.gif

 

Option 3: do it all in JavaScript. Whether this is viable depends on what sort of logic you need. With a bit of extra faffing around (not shown here) you can get things like the page IDs and template names from the class names of the page list items which might help.

Optionally add the Vex library as in option 2. And then in some custom admin JS:

// When a page is moved in ProcessPageList
$(document).on('pageMoved', '.PageListItem', function() {
	var moved_title = $(this).find('.label_title').text();
	var $parent = $(this).parent('.PageList').prev('.PageListItem');
	var parent_title = $parent.find('.label_title').text();
	var $from = $('#PageListMoveFrom').prev('.PageListItem');
	if($from.length) {
		// Page was moved to a different parent
		var from_title = $from.find('.label_title').text();
		ProcessWire.alert('Page "' + moved_title + '" was moved to parent "' + parent_title + '" from parent "' + from_title + '"');
	} else {
		// Page was sorted within its existing parent
		ProcessWire.alert('Page "' + moved_title + '" was sorted within parent "' + parent_title + '"');
	}
});

Result:

warning-3.gif.38c7a3336b353be1afc6524a8f2129c9.gif

  • Like 9
Link to comment
Share on other sites

Wow, thanks @Robin S, I was not expecting such detailed examples! Thanks so much for the effort you have put in.

I'll have a think about it this week as to the best way to do what I want but you have given me some excellent starting points. ?

  • Like 1
Link to comment
Share on other sites

@Robin S Thanks again, this has been extremely helpful.

 

I think if I use Pages::saved and ProcessPageSort::execute per your example it should cover everything I need (I am detecting non-unique values in pages/templates mimicking a database unique index). Pages::saved can be called in the site tree (AJAX) or via a standard POST when editing a page (changing status from the site tree calls this) — or even the API. As you have demonstrated if using AJAX then throwing an Exception can do the job. However, I would rather output a warning if a standard POST request and only use Vex/Exception if AJAX as the former is more flexible.

If there any way in the Pages::saved hook to know whether it is an AJAX call or not? Is there a property in the API or do I just have to check the $_SERVER['REQUEST_PATH'] or something?

Thank you!

Link to comment
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
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...