Jump to content

Check the template of the page which will be added


doolak
 Share

Recommended Posts

It seems that as long the page is not added it does not have an own ID until the ProcessPageAdd is finished, so i have to check it somehow like:

public function addScript(HookEvent $event) {
		 $this->page = $this->pages->get($this->input->get->parent_id); 
		 if($this->page->template == 'mitglied') {
	     $this->config->scripts->add($this->config->urls->PopulateTitle . "populate_title.js"); 
	     }
	}

The code above does not work, so i tried to solve it with jQuery and found a solution:

jQuery(document).ready(function() {
if($('input[name=parent_id]').val() == '1015') {
// do something	
}
});

But i am sure there will be a possibility using the PW API and it would be nice to know what i made wrong in the first code above - would be great if someone could help me to find the mistake i made there.

Link to comment
Share on other sites

For the most part, you only want to hook into Process modules if you are manipulating something on the admin interface side. Process modules don't get used for anything else. So if a page is added from some other method in the API, your ProcessPageAdd hook would never get called. Instead, you'd want to hook Pages::added to get a $page that was just added. Or you'd want to hook into Pages::saveReady if you wanted to get it before it was added. If using saveReady, you'd just confirm that it's a new page being added by it not having a $page->id (already existing pages will have a $page->id). If you find you still need to hook ProcessPageAdd, tell me more about what you are trying to do and I can add an appropriate hook. 

public function init() {
  $this->pages->addHook('added', $this, 'hookPageAdded');
  $this->pages->addHook('saveReady', $this, 'hookSaveReady'); 
}

public function hookPageAdded(HookEvent $event) {
  $page = $event->arguments[0];
  $template = $page->template; // here's your template
}

public function hookSaveReady(HookEvent $event) {
  $page = $event->arguments[0];
  if(!$page->id) {
    // new page about to be added
    $template = $page->template; // here's your template
  }
}
Link to comment
Share on other sites

Yes, I want to manipulate something on the admin interface side:

Each member of a sports club is saved as page, i don't want the website editor to have put in name and first name as title when adding a new member, maybe with a number if there are two members having the same first and last name - and then entereing first name and last name again when editing the member page to have both values seperatley.

So i first had the idea to populate the title field of a new added page during ProcessPageEdit dynamically, e.g. with a increasing number.

As i couldn't manage it to check the template of the new added page or the number of existing pages using this template - at least i was not able to check any parameters of the pages.

I found some information in this post, but this did not work for ProcessPageEdit (or i just made something wrong).

So i began to use just Jquery (as described above in my second post) and made a little module which populates the title dynamically with the name and the date of birth after they are entered in the edit page:

<?php

class PopulateTitle extends WireData implements Module {

	public static function getModuleInfo() {

		return array(

			'title' => 'Populate Title', 
			'version' => 101, 
			'summary' => 'Populate the title field dynamically',
			'singular' => true, 
			'autoload' => true, 
			);
	}

	public function init() {
		
		$this->addHookAfter('ProcessPageAdd::execute', $this, 'addScript'); 
		$this->addHookAfter('ProcessPageEdit::execute', $this, 'addScript2'); 

		
	}

	public function addScript(HookEvent $event) {
	     $this->config->scripts->add($this->config->urls->PopulateTitle . "populate_title.js"); 
	}
	
	
	public function addScript2(HookEvent $event) {
		 $this->config->scripts->add($this->config->urls->PopulateTitle . "populate_title2.js"); 
	}
		
}

populate_title.js:

During ProcessPageAdd this script checks if the page which will be added is in a child of the "Members" page, if it is, the timestamp is added as title to keep it individual. Same for the page name. It hides both fields and triggers a submit, so the page is added automatically.

jQuery(document).ready(function() {
if($('input[name=parent_id]').val() == '1015') {
	$('input[name=title]').val(new Date().getTime());
	$('input[name=_pw_page_name]').val(new Date().getTime());
	$('#wrap_Inputfield_title').hide();
	$('#wrap_Inputfield_name').hide();	
	$('#submit_save').trigger('click');
}
});

populate_title2.js:

When editing a page it checks again if the page is a child of the "Members" page, empties the title, checks if the members name is already entered (mitglied_name) and displays "Neues Mitglied" (New member) as title if it is not.

If there is a members name entered (which is a required field) it concenates name, first name and date of birth (all required fields) and uses them as page title and, a filtered version as page name:

jQuery(document).ready(function() {
if($('input[name=parent_id]').val() == '1015') {
	
	$('#title').empty();
	if($('input[name=mitglied_name]').val() == '') {
	$('#title').append('Neues Mitglied');
	}
	else {
	$unfiltered = $('input[name=mitglied_name]').val() + ", " + $('input[name=mitglied_vorname]').val() + ", " + $('input[name=mitglied_geburtsdatum]').val();
	$('#title').append($unfiltered);
	$filtered = $unfiltered.toLowerCase().replace(/[\*\^\'\!\,]/g, '').replace(/[\.]/g, '-').split(' ').join('-');
	$('input[name=title]').val($unfiltered);
	$('input[name=_pw_page_name]').val($filtered);
	}
}
});

So this does exactly what i want, even more (as it corrects the page name dynamically after name, first name or date of birth are changed later).

But i am sure that one could have done the same with PW API - without using just jQuery and that would make it possible to write a module which offers the possibility to choose the fields which should be used as title dynamically.

Hmm...

Maybe one could use a modified Fieldtype Concatenate for this? 

  • Like 1
Link to comment
Share on other sites

Another approach you might take is to copy ProcessPageAdd.module to /site/modules/ProcessPageAddDoolak.module. Edit the file, and rename the class to be the same too. Then go to Modules > Check for new modules, and add your version of it. Go and edit the 'Add' page (Which is here: Admin > Page > Add). Change the 'Process' field from ProcessPageAdd to ProcessPageAddDoolak. Now you have full control over the page adding process. 

  • Like 1
Link to comment
Share on other sites

Wow, that offers new possibilities! Would this have the effect that the original ProcessPageAdd.module will not be used anymore or could i verify in the ProcessPageAddDoolak.module the parent of the added page and just then use the new module? 

Link to comment
Share on other sites

Yes this would totally replace the ProcessPageAdd, so it would be using yours every time you clicked "new". Of course, you can make your ProcessPageAddDoolak do anything, perform any additional checks, etc. 

Link to comment
Share on other sites

  • 10 months later...

Hi again,

in the examples above I have used jQuery to check the template of the page which should be added. Actually I didn't check the template, I checked the parent id...

Now I really need to check the template which will be added and I would like to to this in the module and I tried this:

<?php

class PopulateTitle extends WireData implements Module {

	public static function getModuleInfo() {

		return array(

			'title' => 'Populate Title', 
			'version' => 101, 
			'summary' => 'Populate the title field dynamically',
			'singular' => true, 
			'autoload' => true, 
			);
	}

	public function init() {
		$this->page = $this->pages->get($this->input->get->id);
		if ($this->page->template == produkt) {
		$this->addHookAfter('ProcessPageAdd::execute', $this, 'addScript'); 
		}
		
	}

	
	public function addScript(HookEvent $event) {
		 $this->config->scripts->add($this->config->urls->PopulateTitle . "populate_title.js"); 
	}
		
}

Of course this does not work, because it (probably) checks the template of the actual page, not of the page which should be added.

I could find just one part in the source of the admin page which references the template which will be added:

<ul id='notices' class='ui-widget'>

		<li class='ui-state-highlight NoticeMessage'><div class='container'><p><span class='ui-icon ui-icon-info'></span>You are adding a page using the 'produkt' template</p></div></li>
	</ul><!--/notices-->

So it would be possible to check the content of the ul "notices" for the string "produkt" - but I would prefer to check this directly in the modul file by changing the part below:

public function init() {
		$this->page = $this->pages->get($this->input->get->id);
		if ($this->page->template == produkt) {
		$this->addHookAfter('ProcessPageAdd::execute', $this, 'addScript'); 
		}
		
	}

Already tried different things with $this->getAllowedTemplates() - but unfortunately I have no idea how to get it work and would appreciate your help...

Link to comment
Share on other sites

So for the moment I have solved this again with jQuery - almost solved...

Here the module file:

<?php

class PopulateTitle extends WireData implements Module {

	public static function getModuleInfo() {

		return array(

			'title' => 'Populate Title', 
			'version' => 101, 
			'summary' => 'Populate the title field dynamically',
			'singular' => true, 
			'autoload' => true, 
			);
	}

	public function init() {

		$this->addHookAfter('ProcessPageAdd::execute', $this, 'addScript'); 
		$this->addHookAfter('save', $this, 'addScript2'); 
		
	}

	
	public function addScript(HookEvent $event) {
		 $this->config->scripts->add($this->config->urls->PopulateTitle . "populate_title.js"); 
	}
	
	public function addScript2(HookEvent $event) {
	$this->page = $this->pages->get($this->input->get->id);
			if ($this->page->template == produkt) {
		 $this->config->scripts->add($this->config->urls->PopulateTitle . "populate_title2.js"); 
		 }
	}
		
}

And here the two scripts:

populate_title.js:

jQuery(document).ready(function() {
if($("#notices div.container:contains('produkt')").length) {
		$('input[name=title]').val(new Date().getTime());
		$('input[name=_pw_page_name]').val(new Date().getTime());
		$('#wrap_Inputfield_title').hide();
		$('#wrap_Inputfield_name').hide();	
		$('#submit_save').trigger('click');
}
});

populate_title2.js:

jQuery(document).ready(function() {
if($('input[name=produkt_marke]').val() == '') {
	$('#title').empty();
	$('#title').append('Neues Produkt');
	}
	else {
	$unfiltered = $('input[name=produkt_marke]').val() + " " + $('input[name=produkt_art]').val() + " " + $('input[name=produkt_typ]').val();
	$('#title').empty();
	$('#title').append($unfiltered);
	$filtered = $unfiltered.toLowerCase().replace(/[\*\^\'\!\,]/g, '').replace(/[\.]/g, '-').split(' ').join('-');
	$('input[name=title]').val($unfiltered);
	$('input[name=_pw_page_name]').val($filtered);
	$('#Inputfield_name_path').find('strong').text($filtered);
	}
});

Almost everything works fine, just one problem resists:

After changing one of the fields "produkt_marke", "produkt_art" or "produkt"typ" and saving the page, the path of the page is not changed (the page is saved under the old path) and even if the page title is changed it is displayed under the page tree with the old name.

Any idea how to solve this?



If I save the page manually again - everything is fine. So it has to be saved two times after editing.

This would be a quick solution (of course not the nicest) too - but I don't know how to have it saved once again after saving it manually one time...

Link to comment
Share on other sites

There so many ways and use cases.

I think with creating your own ProcessPageAdd module like Ryan mentioned would be a way to go, though depends on the case and you might need to maintain it in case the core one has changes that need to be implemented in your copy too. 

To use existing ProcessPageAdd you can hook into execute or buildForm. The latter being better as it returns the form not rendered so you can manipulate it.

The ProcessPageAdd module also stores the template ($this->template) in case the Template family settings is set to only one specific, but this property is protected so you can't read it from outside the module.

But you can add your own checks to get the parent and check for the family settings of its template. $template->childTemplates. Then use this info to check if it's your template you want to make changes.

If you get this far you can also add fields to the form you wish to. These may even existing fields you are using on your page you create, and they will get saved along with the page add form.

Just as an example to do what I said above:

$this->addHookAfter('ProcessPageAdd::buildForm', $this, 'hookProcessPageAdd');
public function hookProcessPageAdd(HookEvent $event){

    $process = $event->object;
    $form = $event->return;

    // get the parent page
    if(isset($_POST['parent_id'])) {
        $this->parent_id = (int) $_POST['parent_id'];
    } else {
        $this->parent_id = isset($_GET['parent_id']) ? (int) $_GET['parent_id'] : 1;
    }

    $this->parent = $this->pages->get($this->parent_id);

    // get the template via the child templates
    // if only 1 defined
    if(count($this->parent->template->childTemplates) == 1) {
        $childTemplates = $this->parent->template->childTemplates;
        $this->template = $this->templates->get(reset($childTemplates));
    }

    if($this->template == "post"){

        // load an existing field and get its inputfield to add to the form
        // this will be save to the page if the created page has the field
        $f = $this->fields->get("date_published");
        $inputfield = $f->getInputfield(new Page(),null);

        // add the field after the page name field
        $form->insertAfter($inputfield, $form->get("_pw_page_name"));

        // populate page name field with a timestamp
        $form->get("_pw_page_name")->attr("value", time());
    }
}

So far so good. 

The page title field is global and is also used on the page add process, it's also required, so you can't ignore it. It would be possible to remove the title field from your template. To do this you have to make the "title" field not global (field advanced settings). Once removed it will not get added to newly created template, and also you can remove it from any template.

BUT there's some drawback, since the title is not global anymore, it will never be shown anymore in the page add process also for other normal pages! This gets kinda tricky. You could remove the title field and after that make it global again, but then it will get again shown at the page add process even if the template has no title field anymore. ProcessWire doesn't know. Also when opening the template you removed the title field previously, it is added again to the fieldgroup, and you can't remove it. See?

So maybe best bet would be to leave it alone and just remove it from the add page form in the hook, or just populate it with some value, and later use hooks to concat values on page save to set the title dynamic.

$form->remove($form->get("title"));

So with all this in mind you could add "firstname", "lastname" fields to the add form with ease. Then use a Pages::added and/or Pages::saveReady to populate the title after adding the page.

Or you could also "hide" the title field on the template. When editing the template, click on the title field to open context editor, select for visibility "Hidden, not shown in the editor". This will "remove" the title field on the edit screen. But you can still populate it via API if needed.

  • Like 2
Link to comment
Share on other sites

Thank you very much for your help, Soma!

I am just still stuck into one problem: I cannot manage to change the path of the edited page, unless I save it twice.

I am still using jQuery for that part, can it be that the API doesn't recognize the changed title and page name, because this happens in the DOM?

public function addScript2(HookEvent $event) {
	$this->page = $this->pages->get($this->input->get->id);
			if ($this->page->template == produkt) {
		 $this->config->scripts->add($this->config->urls->PopulateTitle . "populate_title2.js"); 
		 }
	}

How could I provide the API the new value of the page name field within that function?

Link to comment
Share on other sites

Just to be sure that you know what I mean:

I am adding a new page - the title is added automatically as a timestamp. I am redirected directly to the Edit page.

There, I have to enter some fields which are combined to fill the title field and the page name field via jQuery.

When I save the page, title and page name are correct saved, but the page is saved under the old path (with timestamp).

So I have to save the page manually another time - then the path is corrected and uses the current page name.

I would have to combine the values of the fields in the module file, or - what I would prefer - get the jQuery manipulated page name value and provide this to the API through the function above. If somebody could help me doing this would be fantastic...

Link to comment
Share on other sites

Hm I guess it does not have anything to do with DOM - the title field and the page name field are changed after saving the page - but I would have to find a possibility to change them before the page is saved... is this possible?

Link to comment
Share on other sites

Puhh - maybe not the nicest solution, but it works:

jQuery(document).ready(function() {

if($('input[name=produkt_marke]').val() == '') {
	$('#title').empty();
	$('#title').append('Neues Produkt');
}

$unfiltered = '';
$filtered = '';

$("#Inputfield_produkt_marke").keyup(function() {
	$unfiltered = $('input[name=produkt_marke]').val() + " " + $('input[name=produkt_art]').val() + " " + $('input[name=produkt_typ]').val();
	$filtered = $unfiltered.toLowerCase().replace(/[\*\^\'\!\,]/g, '').replace(/[\.]/g, '-').split(' ').join('-');
	$('input[name=title]').val($unfiltered);
	$('input[name=_pw_page_name]').val($filtered);
	$('#title').text($unfiltered);
	$('#Inputfield_name_path').find('strong').text($filtered);
});

$("#Inputfield_produkt_art").keyup(function() {
	$unfiltered = $('input[name=produkt_marke]').val() + " " + $('input[name=produkt_art]').val() + " " + $('input[name=produkt_typ]').val();
	$filtered = $unfiltered.toLowerCase().replace(/[\*\^\'\!\,]/g, '').replace(/[\.]/g, '-').split(' ').join('-');
	$('input[name=title]').val($unfiltered);
	$('input[name=_pw_page_name]').val($filtered);
	$('#title').text($unfiltered);
	$('#Inputfield_name_path').find('strong').text($filtered);
});

$("#Inputfield_produkt_typ").keyup(function() {
	$unfiltered = $('input[name=produkt_marke]').val() + " " + $('input[name=produkt_art]').val() + " " + $('input[name=produkt_typ]').val();
	$filtered = $unfiltered.toLowerCase().replace(/[\*\^\'\!\,]/g, '').replace(/[\.]/g, '-').split(' ').join('-');
	$('input[name=title]').val($unfiltered);
	$('input[name=_pw_page_name]').val($filtered);
	$('#title').text($unfiltered);
	$('#Inputfield_name_path').find('strong').text($filtered);
});
	
	
});

Title, page name and path are changed live now and so they are saved correctly after editing the page.

For me it's fine now, but if somebody can tell me how it could have been solved easier and more elegant, I will be happy to learn!

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