Jump to content

How to insert additional checkbox into page edit form


FlorianA
 Share

Recommended Posts

Hi,

I would like to extend the (backend) edit form for some pages: Above the "Save" button there should be an additional checkbox "Notify users about changes". If this checkbox is checked, an e-mail notification about the page's changes should be sent to other users. My plans for implementation are the following:

  1. Anyhow add a hook on rendering the form and insert a checkbox at the appropriate place.
  2. Hook the saving of the form, check the state of the checkbox and send the e-mail, if wanted.

However, I'm struggling with the details. Which method should be hooked for step 1? InputFieldForm::render? How can I get the current form data? How can I extend it? Is there already a module which does all this stuff for me? (Not really sending the e-mail but all the hooking stuff, that's a pattern I would like to reuse form something else later.)

Thanks in advance for any help.

Link to comment
Share on other sites

36 minutes ago, FlorianA said:
  • Anyhow add a hook on rendering the form and insert a checkbox at the appropriate place.
  •  Hook the saving of the form, check the state of the checkbox and send the e-mail, if wanted.

My normal take on this is adding a normal checkbox field to the template (even though I guess it's an overkill ). That way on Pages::saved I can check for the value and reset it to unchecked saving only the field. Probably adding the field through an "after" hook on ProcessPageEdit::buildForm (or buildFormContent) as it returns the object that has the form fields so you can go through them and add other stuff you might need. Though I wouldn't know on which other hook I could catch it to process the input. (for example, I don't know if the data from this fake field would reach Pages::saved or even create an error). 

  • Like 1
Link to comment
Share on other sites

A couple of hooks for /site/ready.php:

$wire->addHookAfter('ProcessPageEdit::buildFormContent', function(HookEvent $event) {
	/* @var InputfieldWrapper $form */
	$form = $event->return;

	// Maybe return early if page does not use a particular template
	/* @var ProcessPageEdit $ppe */
	$ppe = $event->object;
	/* @var Page $page */
	$page = $ppe->getPage();
	if($page->template != 'basic_page') return;

	// Add checkbox field
	/* @var InputfieldCheckbox $f */
	$f = $event->wire('modules')->InputfieldCheckbox;
	$f->name = 'notify';
	$f->label = 'Notify users about changes';
	$form->add($f);
});

$wire->addHookAfter('ProcessPageEdit::processInput', function(HookEvent $event) {
	/* @var InputfieldWrapper $form */
	$form = $event->arguments(0);

	// We only care about the top-level form
	$level = $event->arguments(1);
	if($level) return;

	// Get the notify field
	$notify = $form->getChildByName('notify');
	// Return early if notify field doesn't exist or isn't checked
	if(!$notify || !$notify->value) return;

	// Now send the email notification...
	// Your code here
});

 

  • Like 4
Link to comment
Share on other sites

9 hours ago, Robin S said:

// We only care about the top-level form $level = $event->arguments(1); if($level) return;

Hey @Robin S could you please explain this part. I know I've seen this $level check somewhere in the core but I've never come to a situation where I needed it for myself (and I've hooked processInput often, so maybe I'm missing something?). Thx

Link to comment
Share on other sites

1 hour ago, bernhard said:

Hey @Robin S could you please explain this part.

ProcessPageEdit::processInput() calls itself recursively for every instance of an InputfieldWrapper found within the form, incrementing $level for each nesting. But because we know we have added the notify checkbox field at the top level there's no point in looking for it within any nested InputfieldWrapper.

Link to comment
Share on other sites

3 hours ago, Robin S said:

ProcessPageEdit::processInput() calls itself recursively for every instance of an InputfieldWrapper found within the form, incrementing $level for each nesting. But because we know we have added the notify checkbox field at the top level there's no point in looking for it within any nested InputfieldWrapper.

Ok that's what I was guessing. And I understand why it has never been an issue for me:

$wire->addHookAfter('ProcessPageEdit::processInput', function(HookEvent $event) {
	/* @var InputfieldWrapper $form */
	$form = $event->arguments(0);
    if($form->name != 'myform') return;

	...
});

I'm usually checking for the name, so $level is not issue ? 

Link to comment
Share on other sites

Thanks @Robin S for this really nice piece of code. Works out of the box  ?  One more question: When exactly the ProcessPageEdit::processInput hook is being triggered? I want to be sure that users are notified only after page data really has been saved.

Link to comment
Share on other sites

3 hours ago, FlorianA said:

When exactly the ProcessPageEdit::processInput hook is being triggered?

As the name would suggest, it's triggered when the input values in the Page Edit form are processed. ?

3 hours ago, FlorianA said:

I want to be sure that users are notified only after page data really has been saved.

There aren't really any circumstances when page data will not be saved when the Page Edit form is submitted, apart from a fatal PHP error due to some mistake in your code. The situations where a page save might be attempted but not actually occur are outlined in this code, but these are only likely to happen when a page is being saved via the API.

Are you thinking of warnings caused by empty required fields? Because page data is still saved in that case. But if you don't want an email to be sent if there are empty required fields you can check to see if there are any form errors:

// Return early if there are any form errors
if(count($form->getErrors()) return;

 

  • Like 1
Link to comment
Share on other sites

Taking Robins example I'd also add some feedback for the user:

$wire->addHookAfter('ProcessPageEdit::processInput', function(HookEvent $event) {
	/* @var InputfieldWrapper $form */
	$form = $event->arguments(0);

	// We only care about the top-level form
	$level = $event->arguments(1);
	if($level) return;

	// Get the notify field
	$notify = $form->getChildByName('notify');
	// Return early if notify field doesn't exist or isn't checked
	if(!$notify || !$notify->value) return;

	// Now send the email notification...
	if($form->getErrors()) {
        $this->error('Mail NOT sent');
        return;
    }
    else {
        $mail = new WireMail();
        $mail->to(...)->from(...)->...
        $sent = $mail->send();
        if($sent) $this->message('Mail was sent successfully to ...');
        else $this->error('Error sending mail to ...');
    }
});

 

Link to comment
Share on other sites

I'm just considering, if I should rather hook Pages::saved than ProcessPageEdit::processInput. The advantage is that I can get information about which fields have changed. So I can send the e-mail notification only if some important fields have changed. It seems that I can still access the "notify" value of my checkbox - it is contained by the "changes" parameter exactly if it differs from the default.

  • Like 1
Link to comment
Share on other sites

Sounds good ? 

PS: I recommend using conditional hooks as they make it very obvious what's going on when:

$wire->addHookAfter('Pages::saved(template=xyz)', function ... );

PPS: I think even this should work:

$wire->addHookAfter('Pages::saved(template=xyz,notify=1)', function($event) {
  $page = $event->arguments(0);

  // send email
  
  // reset checkbox
  $page->setAndSave('notify', 0);
});

 

Link to comment
Share on other sites

On 8/30/2019 at 6:05 PM, bernhard said:

PPS: I think even this should work:


$wire->addHookAfter('Pages::saved(template=xyz,notify=1)', function($event) {
  $page = $event->arguments(0);

  // send email
  
  // reset checkbox
  $page->setAndSave('notify', 0);
});

 

Yes, it works indeed  ?  But I wonder if it's really necessary to reset the 'notify' flag, as it does not belong to the page template but has just been injected into the form by the buildFormContent hook above. I'm lucky enough still finding the field in the Pages::saved hook, but it isn't saved with the page, is it?

  • Like 1
Link to comment
Share on other sites

10 hours ago, FlorianA said:

But I wonder if it's really necessary to reset the 'notify' flag, as it does not belong to the page template but has just been injected into the form by the buildFormContent hook above. I'm lucky enough still finding the field in the Pages::saved hook, but it isn't saved with the page, is it?

Of course, you're right ? Forget about that! And for situations like this it's really nice to add the inputfield via hook so that no field (and table) is created unnecessarily in the database.

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...