Jump to content
Brmm

Doing additional logic after saving a new page.

Recommended Posts

Hi, I have a simple question.

I need to do additional logic right after a new page has been created/saved, to be more specific I need to call an API with the value of some of its fields, is this possible in process wire?

Thank you!

  • Like 1

Share this post


Link to post
Share on other sites

You'd want to hook after $pages->save(); If you want your function to execute on every page save, any time, then you'd want to create an autoload module. http://wiki.processwire.com/index.php/Module_Creation

If you want to capture newly added pages separately, then you'd also want to hook $pages->added(), which is called after a new page has been added. This example shows both:

/site/modules/HelloWorld.module

class HelloWorld extends WireData implements Module {
 public static function getModuleInfo() {
   return array(
     'title' => 'Hello World',
     'summary' => 'Just an example module class.',
     'version' => 1 
     'autoload' => true,
   );
 }
 public function init() {
   $this->pages->addHookAfter('save', $this, 'hookSave'); 
   $this->pages->addHookAfter('added', $this, 'hookAdded'); 
 }
 public function hookSave(HookEvent $event) {
   // this function called any time a page is saved (whether new or not)
   $page = $event->object; 
   // now you can access anything from the $page
 }
 public function hookAdded(HookEvent $event) {
   // this function only called after a new page added
   $page = $event->object; 
 }
}
  • Like 4

Share this post


Link to post
Share on other sites

I'm working on a small project which happens to be a photographer portfolio.

I have template which represents an album containing image field with unlimited items with some additional description fields.

Is it possible to automatically create an unpublished blog post (announcing new photos) every time an album is saved with new images added?

Share this post


Link to post
Share on other sites
Is it possible to automatically create an unpublished blog post (announcing new photos) every time an album is saved with new images added?

This should be possible. You'd use a Pages::saveReady hook. You'll want to have your hook do something along these lines:

if($page->template == 'album' && $page->isChanged('images')) {
 $newItems = $page->images->getItemsAdded(); 
 if(count($newItems)) {
   // images were added, create your new unpublished blog post
   // the $newItems WireArray contains the images that were added
 }
}
  • Like 3

Share this post


Link to post
Share on other sites

Greetings,

Glad to find this! I was searching for an answer to a similar question.

Would this same technique be effective in a ProcessWire-based e-commerce application? I'm looking to use ProcessWire to build product pages, and allow customers to place orders as pages, then send the details of that order to FoxyCart.

I'm looking at other techniques as well.

Thanks,

Matthew

Share this post


Link to post
Share on other sites

How do you wanna solve this Matthew?

By module or by template file?

If (by template)

echo 'Luis got code snippet'

  • Like 1

Share this post


Link to post
Share on other sites

Is it possible to manipulate the fieldgroup of an template before it's saved?

Like so:

public function addProcessField(HookEvent $event) {
	$template = $event->object;
	$template->fields->add('process');
}

Share this post


Link to post
Share on other sites
Is it possible to manipulate the fieldgroup of an template before it's saved?

You can hook before Fieldgroups::save or Templates::save. 

public function init() {
  $fieldgroups->addHookBefore('Fieldgroups::save', $this, 'addProcessField'); 
}
public function addProcessField(HookEvent $event) {
  $fieldgroup = $event->arguments[0];
  if(!$fieldgroup->hasField('process')) $fieldgroup->add('process'); 
} 
  • Like 4

Share this post


Link to post
Share on other sites

How about if you wan't to add logic before saving new page?

I'd like to prefill page title and name fields when new product page is added. I've tried hook it to ProcessPageAdd::execute but don't know how to set field values here before form is rendered. If I hook before Inputfield::render I'm able to pre-set values for both title and _pw_page_name but this doesn't look right...

public function createItem(HookEvent $event) {
	
	// limit action to admin pages only
	if (wire('page')->template->flags & Template::flagSystem) {
		
		// get parent page
		$parent = isset($_GET['parent_id']) ? wire('pages')->get($_GET['parent_id']) : null; 

		// add this to item pages only
		if ($parent && $parent->template == 'items') {

			// get the current field
			$field = $event->object;

			// proceed on (empty) title and page name fields only
			if (($field->name == 'title' || $field->name == '_pw_page_name') && $field->value == '' ) {

				// finally, set the value
				$field->set('value', 'some_val');
			}
		}
	}
}

Share this post


Link to post
Share on other sites

I think what you want is to hook after ProcessPageAdd::buildForm. That returns an InputfieldForm object, that you could manipulate with an 'after' hook. The only problem is that method isn't currently hookable. But you can make it hookable by editing the file and prepending 3 underscores to it, i.e. "buildForm" => "___buildForm". I will make this same change in the core source and you'll see it in the next batch of dev commits. 

  • Like 2

Share this post


Link to post
Share on other sites

Muchas gracias Ryan! I'm just bit lost how to continue from here.. How can I get fields from this specific form:

public function createItem(HookEvent $event) {
	$form = $event->object;
	die(var_dump($form->fields));
}

I get data from all existing fields. I've also tried:

// to get from
$form = $event->object;
$form = $event->arguments(0);

// to get fields
$fields = $form->getInputfieldsForm();
$fields = $form->processor()->getInputfieldsForm();

Share this post


Link to post
Share on other sites

OK, had some time to get hands dirty and here's what came up:

module init:

public function init() {
	$this->addHookAfter('ProcessPageAdd::buildForm', $this, 'newItem');
}

function to prefill values on new items:

public function newItem(HookEvent $event) {
			
	// get parent page
	$parent = wire('pages')->get((int)$_GET['parent_id']);
	
	// continue only if new item page
	if ($parent->template->name != 'items') return;
			
	// hook to page inputfields
	$this->addHookBefore('Inputfield::render', function($event) {
		
		$field = $event->object; // current field
		$fields = array('title','_pw_page_name'); // fields to modify
		
		if (in_array($field->name, $fields) && empty($field->value)) {
			$field->set('value', 'some_val');
		}
		
	});
	
}

Looks much better now - doesn't it?

  • Like 1

Share this post


Link to post
Share on other sites

What I did in the past is to hook on InputfieldForm::render and check for if the form is the add page form. Works fine.

  • Like 1

Share this post


Link to post
Share on other sites

I'm trying to add a hook which manipulates a particular page's name on save(). When I save the page using the admin, this works as expected. When creating or saving a page from the API, however, it doesn't seem to work. Why is that?

Share this post


Link to post
Share on other sites

Sorry, I didn't make it clear: My question is whether this is the intended behavior. If it's not the intended behavior, I'll be happy to post some code.

Share this post


Link to post
Share on other sites

The hook should work whether it's in the admin or via the API, but if the code that sets up the hook is in a module that isn't autoloaded or called via the API then it won't work. So it would really help to see exactly how you have things set up.

Share this post


Link to post
Share on other sites
class AbcDatabase extends WireData implements Module {
	
	public static function getModuleInfo() {
		return array(
		        'title' => 'ABC Database', 
		        'version' => 001,
		        'summary' => 'Base module for the Database features',
		        'singular' => true,
		        'autoload' => true,
		        'installs' => array(),
		        'requires' => array(),
		        );
	}
	public function __construct() {
	}
	public function init() {
		$this->pages->addHookBefore('Pages::save', $this, "hookAutoPageName");
	}
	
	public function ready() { // PW API is ready
		// Controllers go here		
	}
	
	public function hookAutoPageName(HookEvent $event) {
	  $pageid = $this->sanitizer->selectorValue($this->input->get->id);
	  $page = $this->pages->get($pageid);
	  
	  // Bases the name of the company template off of the company_name field and keeps them in sync
	  if($page->template == 'company') {
	  	if($page->company_name != '') {
			  $newName = wire('sanitizer')->pageName($page->company_name, true);
			  if($page->name == $newName) return;
			  
			  $pageMatch = $page->parent->child("name=$newName,include=all") ?: '';
			  if($pageMatch->id && $page->id != $pageMatch->id) { //This is a duplicate name, so we have to add an increment to avoid throwing error
			  	
				  while($pageMatch->id) {
				  	$newName = $pageMatch->name . "-1";
				  	$pageMatch = $page->parent->child("name=$newName,include=all") ?: '';
				  }
				  
				  $page->name = $newName;
				}
			  else {
			  	$page->name = $newName;
			  }
			}
		}
	}

Below that is the install and uninstall functions and closing brace.

 

Share this post


Link to post
Share on other sites

Replace:

$pageid = $this->sanitizer->selectorValue($this->input->get->id);
$page = $this->pages->get($pageid);

with:

$page = $event->arguments[0];

Remember the id get variable won't be available on the front-end via the API.

EDIT: Actually, better yet, don't use $page at all, use $p instead - you don't want to overwrite the current $page with the one being saved.

  • Like 2

Share this post


Link to post
Share on other sites

There's also this:

$this->pages->addHookBefore('Pages::save', $this, "hookAutoPageName");
// to either/or
$this->addHookBefore('Pages::save', $this, "hookAutoPageName");
$this->pages->addHookBefore('save', $this, "hookAutoPageName");
  • Like 1

Share this post


Link to post
Share on other sites

Sorry if this is mentioned well elsewhere, but just in case not and in case someone else might find it handy, this example helped me, in a few minutes, get emails sent out, with custom messages, when certain pages were saved.

https://processwire-recipes.com/recipes/extending-page-save-process/

I am repeatedly amazed by the elegance and power of ProcessWire... To Ryan and all fellow PWers—THANK YOU.

  • Like 1

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.


  • Recently Browsing   0 members

    No registered users viewing this page.

  • Similar Content

    • By VeiJari
      Hello forum,
      This is really a weird one, because front end editing works in a earlier website we did to a customer. 
      When I check the source code for current website it does initiate front end edit: 
      <span id=pw-edit-1 class='pw-edit pw-edit-InputfieldPageTitle' data-name=title data-page=1021 data-lang='1017' style='position:relative'><span class=pw-edit-orig>Tekijät</span><span class=pw-edit-copy id=pw-editor-title-1021 style='display:none;-webkit-user-select:text;user- select:text;' contenteditable>Tekijät</span></span>  But when I double click nothing happens (yes I'm 100% sure I'm superuser and logged in)
      I also tried to apply the front end with other methods than:
      $page->edit('title'); But didn't work either.
      We are using jquery 2.2.4, so it should not be a problem.
      Is this a bug related to current master or something else?
      Someone else having this problem as well?
    • By jom
      Hi everyone
      It seems that I don't fully understand the wireTempPath() function and I need some help.
      I use wireTempPath() to create a new location in assets/cache/WireTempDir and than copy a pdf from the assets/files/page folder to the new folder. I want the file to be accessible only for a limited time, that's why I use wireTempPath.
      The file seems to be copied to the right location, but gets deleted right afterwards, according to 
      As mentioned in the topic above, 
      $wireTempDir->setRemove(false); prevents the file to be deleted. But I like the file to be automatically deleted after a few days. So, how can I do that?
      My code so far (everything works, but the automatic removal of the tempDir folder):
      //generate and show download link $folder = time(); // timestamp as temporary folder $maxAge = (int) $settings->options_downloadlink_valid_hours * 3600; //tempDir wants maxAge as seconds $options = array( 'maxAge' => $maxAge ); $wireTempDir = wireTempDir($folder, $options); $wireTempDir->setRemove(false); $src_file = $page->ebook_download->filename; // Create a new directory in ProcessWire's cache dir if(wire('files')->mkdir($wireTempDir, $recursive = true)) { if(wire('files')->copy($src_file, $wireTempDir)){ //get subdirs from tempDir: $pos = strpos($wireTempDir, "WireTempDir"); $subdir = substr($wireTempDir, $pos, 100); $out .= "<p><a href='" . wire('pages')->get('template=passthrough')->httpUrl . "?file=" . $subdir . $page->ebook_download->basename . "' target='_blank'>$page->title</a></p>"; } } I appreciate any ideas - thanks!
      Oliver
    • By VeiJari
      Hello forum, this is my first security related post, so I'm a bit of a newbie.
      I understand that when I have direct front-input from user I should sanitize the input, but how about when I use a secret key for showing a API for a third-party supplier? Should I sanitize the input->get() key?
      I've tested this issue and I tried ?key=<?php echo $page->field; ?> And without adding any sanitization it comes back: /?key=<?php%20echo%20$page->field;%20?>
      So can I rely on this, or should I still use $sanitizer just in case?
       
      Thanks for the help!
    • By EyeDentify
      I have been experimenting with the new $page->meta() method and find it useful.

      Once i figured out that the data i "save" with it is tied to the page where i called the method from.

      So this is not obvious at least not for me in the documentation:
      https://processwire.com/api/ref/page/meta/
       
      So i just wanted to share that revelation with the community so you don´t get as confused as i was.

      Happy Coding Everyone.
×
×
  • Create New...