Jump to content

Automatically generating new page names.


netcarver
 Share

Recommended Posts

I've just started working on a basic URL shortener in PW and am about 95% of the way there with just the existing features. The one missing part of the puzzle is assigning a short random string I have generated to be the page name for each new page added under a certain parent ('Short URLs') in the page tree.

Would hooking ProcessPageAdd::execute allow such an assignment to be made automatically?

Link to comment
Share on other sites

I think the best hook might be Pages::setupNew. That function gets a new $page (before its been saved) as the 1 argument. If the page doesn't have a name yet, then that function creates one. So it's the perfect spot for you to assign a name.

public function init() {
 $this->pages->addHookBefore('setupNew', $this, 'hookSetupNew');
}

public function hookSetupNew(HookEvent $event) {
 $page = $event->arguments[0];
 if($page->parent->id == $the_parent_you_want) {
   $page->name = 'the-page-name-you-want';
 }
}
  • Like 2
Link to comment
Share on other sites

Ryan,

Thank you for the quick reply. Yes, whilst that does work it is a little late for what I'm really after. I'd prefer to have the name I auto-generate actually appear pre-populated in the name field in the "Add New" page. This will allow a user to override the random string that was created in case they want to replace it with something with more meaning before they commit to adding the page.

Link to comment
Share on other sites

In that case, I think you'd want to hook before InputfieldPageName::render and adjust set the value attribute. I haven't tried this, but think this would work:

public function init() {
 $this->addHookBefore('InputfieldPageName::render', $this, 'hookRender'); 
}

public function hookRender(HookEvent $event) {
 // if process isn't ProcessPageAdd, exit now
 if($this->process != 'ProcessPageAdd') return;

 // if the GET var 'parent_id' isn't the one we want, exit now
 if($this->input->get->parent_id != $the_parent_id_you_want) return;

 $inputfield = $event->object;

 // if the input already has a populated value, exit now
 if(strlen($inputfield->attr('value'))) return; 

 // if we made it here, populate the value attribute
 $inputfield->attr('value', 'the-page-name-you-want');  
}
  • Like 1
Link to comment
Share on other sites

Ryan, I haven't tried your 2nd suggestion yet but I have figured out another way to do this by cheating a little.

My approach is to add a new method into processPageAdd called ___getDefaultPageName() which returns an empty string by default. processPageAdd::buildForm then calls this to set the page name. Any module that wants can just hook it.

class ProcessPageAdd extends Process {
...
// new function generates an empty string. Can be hooked if needed.
protected function ___getDefaultPageName( $parent, $template ) {
 return "";
}
protected function buildForm() {
 ...
 $field = $this->modules->get('InputfieldPageName');
 $field->parentPage = $this->parent;
 $field->required = true;
 $field->value = $this->getDefaultPageName( $this->parent, $this->template ); // Get the page name -- this is new
 $form->append($field);
 ...
}
...
}

Code in the module then reduces to...

public function init() {
   $this->addHookAfter( 'processPageAdd::getDefaultPageName', $this, 'hookPagename');
}

public function hookPagename(HookEvent $event) {
   $template = $event->arguments[1];
   if( $template->name == "URLShortener" ) $event->return = self::generateUnusedRandomPagename();
}

Probably not implemented quite right, but it seems to work.

Link to comment
Share on other sites

Looks good! Though I think you are probably better off with a solution that doesn't require modifying a core file (but I'll be happy to update the core if you think this is going to have broader usefulness).

I modified my previous example a bit, just so that it's a little smarter about when it decides to add the hook (slightly less overhead). Basically, it moves some of the logic out of the hook function and instead uses it to determine whether to even attempt the hook.

public function ready() {
 if($this->page->template != 'admin') return;
 if($this->input->get->parent_id != $the_parent_id_you_want) return;
 // only add our hook if template is admin and there a parent_id GET variable with the value we want
 $this->addHookBefore('InputfieldPageName::render', $this, 'hookRender'); 
}

public function hookRender(HookEvent $event) {
 // if process isn't ProcessPageAdd, exit now
 if($this->process != 'ProcessPageAdd') return;

 $inputfield = $event->object;

 // if the input already has a populated value (possibly from another hook?), exit now
 if(strlen($inputfield->attr('value'))) return; 

 // if we made it here, populate the value attribute
 $inputfield->attr('value', 'the-page-name-you-want');  
}
  • Like 1
Link to comment
Share on other sites

  • 1 year later...

Resurrecting this slightly, but I did something along these lines just now with help from this thread (actually approached it netcarver's suggested way from the first post). It's one where I wanted to skip the first page of adding a name (for events in a calendar) but didn't want to use a repeater - which would achieve the same, but I wanted separate pages.

Code as follows:

In init function:

$this->pages->addHookBefore('ProcessPageAdd::execute', $this, 'generateName');

The function that does the work (specific to my case):

public function generateName() {
        if ($this->input->get->parent_id == 1019) { // 1019 = some page where we want to auto-generate child page names
            $page = new Page();
            $page->parent = $this->input->get->parent_id;
            $page->name = $this->pages->get($this->input->get->parent_id)->count()+1;
            $page->template = 'child-template-name';
            $page->addStatus(Page::statusUnpublished);
            $page->save();
            $this->session->redirect("../edit/?id=$page");
        }
    }

So it checks we're creating a page under a certain parent, sets the right template, creates a page using an integer as the name based on the count of pages under the parent page and then takes you to the edit form for that page. The beauty of it is that since there has been no title entered so far, the user has to fill out the page fields - well title at least - to continue.

So why did I do something so convoluted here? Because for what I'm using the title field for I needed to allow duplicate titles, so different names is key to this. The individual pages themselves will never be visited - they're just being pulled into a table into the parent page.

Of course I could have used repeaters, but on this occasion I didn't want to.

I think that with a little work, and the addition of multiple config lines like in my ProcessEmailToPage module you could expand this to monitor certain parent pages, hook into new page creation and create the page with a specific child template nice and neatly instead of hardcoding it as above. When I get time I might just do that... which might not be any time soon :)

For now the above code works though for anyone wanting to adapt it to do something similar.

  • Like 9
Link to comment
Share on other sites

  • 1 month later...

@pete - many thanks for your example; i used your code and made a module, and this works really well, eliminates that first screen and auto-generates the page name as the id; i'm also using another example you posted to then build the name from various fields, once the user clicks save..

  • Like 2
Link to comment
Share on other sites

I think leoric's needs are better overcome by overriding that functionality in particular rather than working around it with something from this thread.

In particular, leoric could do with simply having the option of removing those restrictions on characters in page names. Perhaps one for Ryan?

Link to comment
Share on other sites

  • 2 weeks later...

Resurrecting this slightly, but I did something along these lines just now with help from this thread (actually approached it netcarver's suggested way from the first post). It's one where I wanted to skip the first page of adding a name (for events in a calendar) but didn't want to use a repeater - which would achieve the same, but I wanted separate pages.

Code as follows:

In init function:

$this->pages->addHookBefore('ProcessPageAdd::execute', $this, 'generateName');

The function that does the work (specific to my case):

public function generateName() {
        if ($this->input->get->parent_id == 1019) { // 1019 = some page where we want to auto-generate child page names
            $page = new Page();
            $page->parent = $this->input->get->parent_id;
            $page->name = $this->pages->get($this->input->get->parent_id)->count()+1;
            $page->template = 'child-template-name';
            $page->addStatus(Page::statusUnpublished);
            $page->save();
            $this->session->redirect("../edit/?id=$page");
        }
    }

So it checks we're creating a page under a certain parent, sets the right template, creates a page using an integer as the name based on the count of pages under the parent page and then takes you to the edit form for that page. The beauty of it is that since there has been no title entered so far, the user has to fill out the page fields - well title at least - to continue.

So why did I do something so convoluted here? Because for what I'm using the title field for I needed to allow duplicate titles, so different names is key to this. The individual pages themselves will never be visited - they're just being pulled into a table into the parent page.

Of course I could have used repeaters, but on this occasion I didn't want to.

I think that with a little work, and the addition of multiple config lines like in my ProcessEmailToPage module you could expand this to monitor certain parent pages, hook into new page creation and create the page with a specific child template nice and neatly instead of hardcoding it as above. When I get time I might just do that... which might not be any time soon :)

For now the above code works though for anyone wanting to adapt it to do something similar.

@Pete, I was looking to do something similar for an events and news pages. I was wondering how would you deal with the URLs, since these are generated from the real title and not the user's generated title. Maybe this was not meant to work with URLs, but maybe it is. 

Link to comment
Share on other sites

@landitus, in my case i'm generating the page name initially because that will be overwritten when the person changes the fields on the page; in this case it's more of a database entry and not a page on the site. to change the page name and title on save (from a combination of fields) i use a module that was also demonstrated by Pete called 'buildUrl' i think and then i expanded it a lot

  • Like 1
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...