Jump to content
VeiJari

Basics of hooking

Recommended Posts

Hello forum! 

I started to write my first hook for Processwire but I'm pretty confused how you should write these. My idea is to hook after publishing in init.php (called before templates)  for a certain template. 

 

Here's the code: 

image.png.696daf301396a34832a7d6a24495b693.png

Trying to reset the checkbox (ajasta) and then saving it so it shows unchecked in the admin page when publishing "article" page. 

But the code isn't doing anything. Not even dumping anything

What seems to be the problem? And have you made a similar hook for this usage or am I doing it totally wrong? 😄

Thanks for the support in advance!

Share this post


Link to post
Share on other sites

Hi @VeiJari

It's usual practice, but not compulsory, to put hooks into /site/ready.php. site/ready.php loads before _init.php.

You're changing the saved page data so you may need to turn off outputting formatting first if saving the entire page, or simply set-and-save the 'ajasta' field, eg (untested):

wire()->addHookAfter('Pages::published', function($event) {
    $page = $event->arguments('page');

    $t = wire()->templates->get($page->template);
    if($t->name == 'artikkeli' && $t->hasField("ajasta") {
        $page->setAndSave('ajasta', 0);
    }   
    $event->return = $page;
});

 

  • Like 3

Share this post


Link to post
Share on other sites

I guess $pages is not "ready" in _init.php, since _init.php is loaded pretty early. When PW is up and running and before the template is executed, it runs site/ready.php. So placing hooks in ready.php is the recommended practice.

28 minutes ago, psy said:

site/ready.php loads before _init.php

sorry, no.

  • Like 1

Share this post


Link to post
Share on other sites

I'd support the /site/ready.php solution as well. It seems to be the better place for hooks that don't end in a module.

But there is another thing.

Your hooks is tied to the published state or the action of publishing it. While testing this you have to unplublish the page first to test your hook.
Had this testing issue a while back, too. 😉

 

  • Like 1

Share this post


Link to post
Share on other sites

Sure, init.php runs before ready.php as well, but thanks for the link, which explains this more precisely:

Quote

/site/init.php
This file is included during ProcessWire's boot initialization, immediately after autoload modules have been loaded and had their init() methods called. Anything you do in here will behave the same as an init() method on a module. When this file is called, the current $page has not yet been determined. This is an excellent place to attach hooks that don't need to know anything about the current page.

 

  • Like 3

Share this post


Link to post
Share on other sites

Good pick-up @wbmnfktr!

/site/init.php is different to /site/templates/_init.php

I guess the custom of using ready.php is that by that time, PW knows about the current page which in this case, is referenced in the hook.

  • Like 2

Share this post


Link to post
Share on other sites
On 4/11/2019 at 3:27 PM, psy said:

Hi @VeiJari

It's usual practice, but not compulsory, to put hooks into /site/ready.php. site/ready.php loads before _init.php.

You're changing the saved page data so you may need to turn off outputting formatting first if saving the entire page, or simply set-and-save the 'ajasta' field, eg (untested):


wire()->addHookAfter('Pages::published', function($event) {
    $page = $event->arguments('page');

    $t = wire()->templates->get($page->template);
    if($t->name == 'artikkeli' && $t->hasField("ajasta") {
        $page->setAndSave('ajasta', 0);
    }   
    $event->return = $page;
});

 

Tested and the code works!

Thank you for this.

Our customer also wants the field to be disabled when the page is published, how do you achieve this? 

Quick browsing of the API, there doesn't seem to be a disable() function for a field.

Share this post


Link to post
Share on other sites
On 4/11/2019 at 4:02 PM, wbmnfktr said:

Be careful here @Autofahrn and @psy.

@VeiJari is talking about init.php and not _init.php.

  1. init.php
  2. ready.php
  3. finished.php

Just to keep this in mind.

https://processwire.com/blog/posts/processwire-2.6.7-core-updates-and-more/

I was actually talking about /site/templates/_init.php, but after reading the difference of /site/init.php and /site/templates/_init.php, I decided to include psys' solution to site/init.php.

Thank you for this!

  • Like 1

Share this post


Link to post
Share on other sites
3 minutes ago, VeiJari said:

Our customer also wants the field to be disabled when the page is published, how do you achieve this? 

Quick browsing of the API, there doesn't seem to be a disable() function for a field.

I have never used it but you could try to hook into the fields visibility via API in some way.

https://processwire.com/docs/fields/dependencies/

 

Share this post


Link to post
Share on other sites
7 minutes ago, VeiJari said:

Our customer also wants the field to be disabled when the page is published, how do you achieve this? 

Quick browsing of the API, there doesn't seem to be a disable() function for a field.

You are probably looking for the "collapsed" field and its constants.

(collapsedNoLocked to be precise)

  • Like 1

Share this post


Link to post
Share on other sites
46 minutes ago, Autofahrn said:

You are probably looking for the "collapsed" field and its constants.

(collapsedNoLocked to be precise)

I think the solution lies somewhere in that topic, but I haven't figured out how to integrate Robin S' solution for my Pages::published hook.

Share this post


Link to post
Share on other sites
$wire->addHookAfter('ProcessPageEdit::buildForm', function($event) {
  $page = $event->object->getPage();
  if($page->template != 'home') return; // example
  if($page->isUnpublished()) return;
  $form = $event->arguments(0);
  $field = $form->getChildByName('title'); // example
  $field->collapsed = Inputfield::collapsedNoLocked;
});

🙂 

  • Like 5

Share this post


Link to post
Share on other sites
58 minutes ago, bernhard said:

$wire->addHookAfter('ProcessPageEdit::buildForm', function($event) {
  $page = $event->object->getPage();
  if($page->template != 'home') return; // example
  if($page->isUnpublished()) return;
  $form = $event->arguments(0);
  $field = $form->getChildByName('title'); // example
  $field->collapsed = Inputfield::collapsedNoLocked;
});

🙂 

Works like a charm! 

Thank you for this!

And also, I want to thank every one for motivating me to write hooks and seeing how practical and easy they're to make! 🙂

  • Like 5

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.

  • Similar Content

    • By Spyros
      Hello
      I'm having a strange issue with the $page->find(), for some reason I'm missing some of the pages from the results. I found then that I was missing all the pages with the same "PAGE NAME". Is it a bug or am I missing something?
      PS 
      If I change the "PAGE NAME" of one of the missing ones then I'm retrieving the page without any problem.
      Thank you
    • By gebeer
      Hello all,
      wasn't sure where to put this, so it goes in General section.
      Ryan shows a hook that we can use to mirror files on demand from live server to development environment to be up to date with the files on the server without having to download complete site/assets/files folder.
      I just implemented this but had problems getting files to load from a site in development that is secured with user/password via htaccess.
      First I tried to use WireHttp setHeader method for basic authentication like this
      function mirrorFilesfromLiveServer(HookEvent $event) { $config = $event->wire('config'); $file = $event->return; if ($event->method == 'url') { // convert url to disk path $file = $config->paths->root . substr($file, strlen($config->urls->root)); } if (!file_exists($file)) { // download file from source if it doesn't exist here $src = 'http://mydomain.com/site/assets/files/'; $url = str_replace($config->paths->files, $src, $file); $http = new WireHttp(); // basic authentication $u = 'myuser'; $pw = 'mypassword'; $http->setHeader('Authorization: Basic', base64_encode("$u:$pw")); $http->download($url, $file); } } But, unfortunately this didn't work.
      So now I am using curl to do the download. My hook function now looks like this
      function mirrorFilesfromLiveServer(HookEvent $event) { $config = $event->wire('config'); $file = $event->return; if ($event->method == 'url') { // convert url to disk path $file = $config->paths->root . substr($file, strlen($config->urls->root)); } if (!file_exists($file)) { // download file from source if it doesn't exist here $src = 'http://mydomain.com/site/assets/files/'; $fp = fopen($file, 'w+'); // init file pointer $url = str_replace($config->paths->files, $src, $file); $u = 'myuser'; $pw = 'mypassword'; $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_TIMEOUT, 50); // crazy high timeout just in case there are very large files curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_USERPWD, "$u:$pw"); // authentication curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_BASIC); // authentication curl_setopt($ch, CURLOPT_FILE, $fp); // give curl the file pointer so that it can write to it curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); $data = curl_exec($ch); curl_close($ch); } } Now I can load files and images from the htaccess protected development server 🙂
      If anyone knows how to get this to work with WireHttp, please let me know. Thank you.
    • By Guy Incognito
      This short script loops through some images from an XML feed and pushes new ones to an image field. It all works perfectly, except for some reason the last image (only) in the loop each time doesn't receive the image description... can everyone spot why? TIA! 🙂 
      foreach ($propertyImages as $img) { $fileName = trim($img[0]); if ( !empty($fileName) ) { $imgPath = '../property_data/'.$fileName; if(file_exists($imgPath) && !in_array(strtolower($fileName),$currentImages)) { $p->property_images->add($imgPath); $p->save(); $newImg = $p->property_images->last(); $newImg->description = $img[1]; $p->save(); } } }  
    • By MateThemes
      Hello everyone!
      I am working with Processwire since some time. But some topics are quite hard for me.
      I have a Portfolio (Gallery) Page.
      I am build a template with Portfolio Index and pages with portfolio entries.
      Structure:
      Portfolio Index
      -- Portfolio Entry
      -- Portfolio Entry
      and so on.
      Portfolio Entry has an Image field with max 12 images and are accessible Templates. 
      Now I want to display the single Portfolio Entry on the Portfolio Index and Paginate them. In the index page all images of a single Entry page should be displayed (I should not be organized as albums, where a random image of the portfolio entry should be displayed). 
      I have no clue to achieve this. May someone could give me an advice.
      Thank you in advance!
    • By ngrmm
      I have a page with a table. Each table row has a page-reference field and a checkbox.
      The Page sends emails to all users (page-refrence->email-field) and change the value of the checkbox in a row to 1.
      It works with this:
      <?php // event ID fron url query $eventID = $input->get('eventID','int'); // get event-page $event = $pages->get($eventID); // config $fromEmail = $event->event_mail_from; $fromName = $event->event_mail_from_name; $emailSubject = $event->event_subject; // email html body ob_start(); include('./_inc/emailbody.inc'); $emailBody = ob_get_clean(); // make event-page editable $event->of(false); // loop through table and send out emails foreach($event->event_clients_list as $event_table_row) { // get client page $clientPage = $event_table_row->client_name; // get client email $clientEmail = $clientPage->email; // if client isn't invited yet (checkbox not checked) if($event_table_row->client_invited == '') { // send email $m = new WireMail(); $m->to($clientEmail); $m->from($fromEmail, $fromName); $m->subject($emailSubject); $m->bodyHTML($emailBody); $m->send(); // mark client as invited $event_table_row->client_invited = 1; $event->save('event_clients_list'); } } ?> But i have to use a variable in my emailbody.inc which i'm able to get in the table-loop.
      So i do the including of the body inside my loop. But this doesn't work anymore. Page sends out the emails but is unable to change the value of the checkbox.
      I get no errors!
      I'm using ProTable
      <?php // event ID fron url query $eventID = $input->get('eventID','int'); // get event-page $event = $pages->get($eventID); // config $fromEmail = $event->event_mail_from; $fromName = $event->event_mail_from_name; $emailSubject = $event->event_subject; // loop through table and send out emails foreach($event->event_clients_list as $event_table_row) { // get client page $clientPage = $event_table_row->client_name; // get client email $clientEmail = $clientPage->email; // email html body ob_start(); include('./_inc/emailbody.inc'); $emailBody = ob_get_clean(); // make event-page editable $event->of(false); // if client isn't invited yet (checkbox not checked) if($event_table_row->client_invited == '') { // send email $m = new WireMail(); $m->to($clientEmail); $m->from($fromEmail, $fromName); $m->subject($emailSubject); $m->bodyHTML($emailBody); $m->send(); // mark client as invited $event_table_row->client_invited = 1; $event->save('event_clients_list'); } } ?>  
×
×
  • Create New...