Kirby 2.1 Hook Usage- Creating Subpages

Hi,

I’m looking at two hooks recently released in Kirby 2.1. Specifically: panel.page.create and panel.page.update.

Here’s a summary of what I’m trying to achieve:

Using a structure field in calendar, whenever an administrator adds a new course and clicks “Save” in the structure field, a new subpage for the course is also created in calendar/events/newcourse and populated with data from the structure field in the calendar folder.

Q: How can I use panel.page.create to create a new subpage in calendar/events when a new course is added via the Panel? This is the hook I need to modify and use- thoughts?

kirby()->hook('panel.page.create', function($page) {
// your hook code
});

Thanks for your help!

mind me asking, since when a user has panel access?

have you taken a look in the cheatsheet? i haven’t been working with the hooks yet, but however had quite a chance playing with creating pages… so you mind try out something with this

try {

$newPage = page(‘blog’)->children()->create(‘my-new-article’, ‘article’, array(
‘title’ => ‘My new article’,
‘date’ => ‘2012-12-12 22:33’,
‘text’ => ‘This is my new article’,
‘tags’ => ‘article, text, readable’
));

echo ‘The new page has been created’;

} catch(Exception $e) {

echo $e->getMessage();

}

http://getkirby.com/docs/cheatsheet/pages/create

Good catch, @carstengrimm. The administrator will be accessing the panel, not the user- thanks!

I changed your code to match my needs. One question- when you say 'my-new-article', 'article', is the my-new-article the UID of the page? And is article the template of the page? If so then the code I have below should be right.

    try {

$newPage = page('calendar/events')->children()->create('course', 'event', array(
'title' => '{{course}}',
'description' => '{{description}}',
'instructor' => '{{instructor}}',
'begin_date' => '{{_begin_date}}'
));

Finally, where should I place this code so the panel triggers it every time I enter event info in calendar/events and creates a new subpage?

Thanks!

Hey @aftereffectsmagic,

as I said I haven’t been working with the panel hooks so far so I can’t say for sure.

I guess the Code goes between here where it says to put the code to:

kirby()->hook('panel.page.create', function($page) { // your hook code });

I have been using the create page on other occations in my template/snippet to create subfiles to… like:

$newPage = $page->children()->create($time, 'kommentar', array( 'name' => get('name'), 'username' => get('email'), 'text' => get('kommentar'), 'key' => $rand ));

In your example you would always try to create the same page with the uid “course”, so that would fail on the second attempt, you need to use a variable here.

Also, I don’t see how you get the information for the title, description etc. from using syntax from the yaml file. You’d have to fetch that information from the structure field of the text files. I guess, you would have to go through the whole array and check for each entry if the page already exists and if not, create a new one.

Thanks, @carstengrimm and @texnixe!

Realize it might be helpful to see my blueprint at calendar/events, specifically the structure field. Here it is:

<?php if(!defined('KIRBY')) exit ?>

title: New Event Creation
pages: true
files: true
fields:
  title:
    label: Course or Activity
  calendar:
    label: Courses and Activities
    type: structure
    entry: >
      <strong>{{course}}</strong> with {{instructor}}
      <br>{{description}}<br>
      Beginning: {{_begin_date}} at {{_begin_time}}
      <br>End: {{_end_date}} at {{_end_time}}
    fields:
      course:
        label: Event, Course or Activity Title
        type: text
      description:
            label: Description
            type: textarea
            size: medium
            required: true
      instructor:
            label: Select an Instructor
            type: select
            options: query
            query:
                page: calendar/instructors
                fetch: children
                text: '{{title}}'
                value: '{{title}}'
            required: true
      _begin_date:
            label: Beginning date
            type: date
            format: MM/DD/YYYY
            width: 1/4
            default: today
      _begin_time:
            label: Beginning time
            type: time
            interval: 30
            width: 1/4
            required: true
      _end_date:
            label: Ending date
            type: date
            format: MM/DD/YYYY
            width: 1/4
            default: today
      _end_time:
            label: Ending time
            type: time
            interval: 30
            width: 1/4
            required: true
      description:
        label: Description of Event
        type: textarea
        size: medium
        required: true
      location:
        label: Location
        type: place
        center:
          lat: 40.73530960000001
          lng: -73.99439289999998
          zoom: 1
        help: >
          Move the pin wherever you'd like, or search for a location!

So, after the structure field has been completed by the administrator in the calendar\events panel, a subpage should be created with the course title as the URL, i.e., calendar\events\coursename with all of that information present in that page.

I changed the snippet @carstengrimm provided to this, based on your change, @texnixe - this is what it looks like:

    try {

$newPage = page('calendar/events')->children()->create('title', 'event', array(
'title' => {{course}},
'description' => {{description}},
'instructor' => {{instructor}},
'begin_date' => {{_begin_date}}
<....>
    ));

Does this work as far as calling each field properly? And again, I’m not sure where to place this section of code- in a page controller? in a template? in a snippet? If you could clarify where I need to put this code, that would be really, really great.

Thanks, as always!

When I think about it, it would probably even be easier to create the course pages and then append that data to the structure field.

Anyhow, currently there is no documentation on how to best use the new hooks, in the changelog it says they should be registered either in the config file or in a plugin, so I’d go for the plugin, I guess.

Still, in the above code you would have to get the information from the text files and you can’t do that with those curly bracket tags but need to fetch that information from an array of the structure field ($page->calendar()->yaml() or using the cool new toStructure() method ($page->calendar()->toStructure()).

Have a look at this post for a method on how to update a structure field.

Good night for now :sleeping:

So, I briefly tested this in a new Starterkit. Now here some basic steps:

  1. Create a new file in plugins, e.g. hooks.php

  2. Register a hook like this:

    <?php
    kirby()->hook('panel.page.create', function($page) {
    // your hook code
    });
    

    Now, every time any page is created in the panel, the hook is triggered and the code inside is executed.

  3. For test puposes, I created a structure field in the projects blueprint like this

    ```text
    projects:
     label: Projects
     type:  structure
    fields:
      projecttitle:
        label: Project Title
        type: text
     projectyear:
       label: Project Year
       type: text
    
    
    
  4. Next, in the hook.php file I added the following code:

<?php
kirby()->hook('panel.page.create', function($page) {

//check if page is child of projects

if($page->isChildOf(page('projects'))) {
	$newPage = $page;
}
// this is the page which has the structure field type
$projectpage = site()->find('projects');

// get existing entries in an array
$projectarray = $projectpage->projects()->yaml();

// add a new entry to the array
$projectarray[] = ['projecttitle' => $newPage->title(), 'projectyear' => $newPage->year()];

// update the structure field type
page('projects')->update([
   'projects' => yaml::encode($projectarray)
]);

});

So, what this does, every time a page is created, the new page is added to the structure field (code based on a suggestion in this post Add method to append to structure field. Guess, we can now use the new toStructure() method here as well and add to the collection.

Note: This is just to show the basic functionality.

You would have to create a second hook with the same code but panel.page.update instead of panel.page.create to update the field when the page is updated as well.

There are some downsides to this basic code, in the first step (when the page is created) nothing but the title will be added to the structure field. Also, every time you update (with the second hook), a new entry will be created, so the code needs to be refined to prevent that.

But in general, the above works, which is a starting point, I hope …

2 Likes

And here an example of the other way round, i.e. create a new page when the structure field is updated (reference is the blueprint for the projects page in the previous post).

<?php

function seoUrl($string) {
    //Lower case everything
    $string = strtolower($string);
    //Make alphanumeric (removes all other characters)
    $string = preg_replace("/[^a-z0-9_\s-]/", "", $string);
    //Clean up multiple dashes or whitespaces
    $string = preg_replace("/[\s-]+/", " ", $string);
    //Convert whitespaces and underscore to dash
    $string = preg_replace("/[\s_]/", "-", $string);
    return $string;
}

kirby()->hook('panel.page.update', function($page) {

foreach(page('projects')->projects()->yaml() as $projectpage) :
    $title = seoUrl($projectpage['projecttitle']);
    //check if page exists	
    $children = page('projects')->children();
    if(!$children->has($title)) {
	try {

  $newPage = $children->create($title, 'project', array(
    'title' => $projectpage['projecttitle'],
    'year'  => $projectpage['projectyear'],
    
  ));

  echo 'The new page has been created';

} catch(Exception $e) {

  //echo "The page already exists";

 }
}
endforeach;
});

Known problems with this code:

  • the catch exception doesn’t work, therefore I commented it out; otherwise it throws an error message and the page cannot be saved (any solution?)
  • The existing pages are not updated if changes are made to the existing content, so a page update function is also required
  • if the title of the structure field is changed, a new page will be created, which doesn’t make sense, so a solution is needed for this problem as well
  • and also, if a structure field is deleted, the page would have to be deleted as well
  • maybe some more issues that I haven’t considered yet … But as I said, consider it a starting point.
2 Likes

Thanks for posting your findings @texnixe. I’m hoping to play with the hooks a little later today and your examples provide a nice leg up.

Well, its nice to use this opportunity to play around with the hooks, cause that’s really great, however, I’m not convinced, that duplicating content like this is really the best solution to @aftereffectsmagic original problem.

I’d rather change the input that is expected by the plugin or create my own to handle events rather than doing this. To me it seems a bit complicated to find out which structure fields have been deleted vs. just changed, and then delete or update or create new pages.

Thanks for the outline of the two methods, @texnixe!

I decided to try out the first solution as that was provided via a step-by-step solution, but when I save the hook under plugins, every time I access the panel or attempt to create a new page, the text of the hook appears in the header of the panel window and inserts itself in the URL field of the page creation pop-up window.

And when I save an entry to my structure field, nothing happens… the Kirby search icon in the top right corner turns into a spinning icon and it stays like that until I refresh the page.

So just to make sure I have everything right, I’ll show you my step by step process:

  1. Create projects.php blueprint with the same structure field you provided above. There is no blueprint set up for the children / subpages that will be created. Should there be?

  2. Create top-level projects folder titled Projects

  3. Create events folder under Projects titled Events (projects/events)

  4. Save panel.page.create hook as hook.php to plugins folder.

  5. Create new structure field entry in projects/events

  6. Click “save”. The structure field now has the new entry, but nothing else happens.

I want to make sure that this basic function works before including a separate hook for panel.page.update. Thoughts on why this is not working?

My bad, I was lazy and left out the “<?php” bit at the beginning of the file, I’ll add it above …

The first step-by-step procedure is supposed to create new structure entries when a new page is created, so that won’t work with your current setup. If you want to create new pages when you create a new structure field entry, then you have to go for the second method.

I used the Kirby Starterkit as a base, it has a projects folder and some project subfolders and the corresponding blueprints and only added the structure field to the projects.php blueprint.

@aftereffectsmagic:
I have perhaps a stupid question: “Why are you making this so complicated?”
For this I would like to outline another way to create and maintain an event calendar in Kirby.

Some time ago I have developed a Kirby’s website (http://www.sav-barchfeld.de/) with a calendar of events (http://www.sav-barchfeld.de/kalender). There I have chosen in my view an easier way with the need to use the panel by the editors, which is here the default for all pages. But this could certainly be replaced by appropriate input page.

In my understanding, the main difference is something else:

I have created a content type “event” together with the associated blueprint, that contains all the fields, that are required for the representation of an event. In the blueprint of the calendar I have enabled the page type event as a possible child of the calendar.
I had to add the representation of all events, that “visible” children of the events are, in the template of the calendar.

The editor now has the ability in the panel to create a new event, then to fill in the fields and to upload photos or other files as needed or, as usual, to turn any event “visible” or “invisible” or update or change specific individual fields from existing events, like:

(screenshot of an event in the panel / here in English language / !click two times in this picture to grow it here!)

The uploaded files will have to be taken either into rendering in the template, being assigned in the panel possibly by some fields provided or be inserted in a textarea as an image or similar. But that is normal building a Kirby website.
In the above-mentioned website, we have been decided, if at all, to integrate pictures on the field Text of the event (using kirbytext).

Note: My way to store this data is commonly known as the relational model of data (https://en.wikipedia.org/wiki/Relational_database).

Hint: If you want you can protect the events from being viewed at an own webpage.

Good luck!

[last edited: 2015-05-22 16:05 / German time]

1 Like

Ha, removing the text from the header section of the panel was simple. I should’ve thought to check to make sure the php bit was there. Thanks, @texnixe. That’s part of the problem solved! Testing the rest now.

Could you post the blueprint for this screenshot? I really couldn’t understand what you’ve meant.

Thanks

I don’t quite see what these events have in common with a relational database? We are talking files here not databases.

1 Like

@texnixe:
The answer to your questions depends on the approach. The data in the Kirby directory “/content” is, if one looks at the information theory, a data structure that is denoted by “relational model of data”. In my possibly incomplete knowledge of this subject many studies have been published on the subject “relational database”. Therefore I have taken the liberty for readers, who want to deepen their knowledge of this subject, to provide a link for “relational database” at Wikipedia. The second link in this Wikipedia article leads to the page “relational model of data”.

For the purpose of this method it once does not matter what kind of storage medium (e.g. files or databases) we are dealing here, as long as the direct access (sometimes called “random access”) is ensured to the data. If the storage medium clumsy selected (this does not apply to Kirby), you can apply this system though, but may have to contend with runtime problems.

Furthermore the linked Wikipedia article is an excellent introduction to this topic in more extensive data collections, which I can recommend (not just for websites) all very novice in the design of data storage. All I can recommend is a profound knowledge of the normalization of a database (see link below at the linked Wikipedia page).
To make these considerations is with me by the way the first step to begin the development of a bigger new website.

In order not to be misunderstood: I as an engineer have this now shown here only so extensively to answer your questions as completely as possible. This is not intended to be a affront of the importance of design or designers and please should not be understood wrong. Thank you!
I therefore hope that this information theory oriented part of the topic is now completed. We should be devoted to the actual topic of this page again.

Now I have translated the blueprints using Google to English.

The blueprint “events.php” (for the calendar) looks like:


<?php if(!defined('KIRBY')) exit ?>

title: Calendar of events
pages:
  sortable: true
  num: date
  field: date
  template:
    - event
files: true
deletable: false
fields:
  title:
    label: Title
    type:  text
    help:  Please enter the "name" of this page, is also used in the navigation (menu etc.).
  text1:
    label: Starting text
    type:  textarea
    help:  Enter the "starting text" of the page. The starting text will be added automatically under the title and above the list of events.
  catchstatistics:
    label: Submission of catch statistics
    type: date
    width: 1/2
    default: 31.12.2015
    format: DD.MM.YYYY
    required: true
    help:  Please select the "Submission of catch statistics".
  membershipfeepayment:
    label: Date of the membership fee payment
    type: date
    width: 1/2
    default: 31.03.2015
    format: DD.MM.YYYY
    required: true
    help:  Please select the "Date of the membership fee payment".
  text:
    label: Final text
    type:  textarea
    help:  Enter the "final text" of the page. The final text will be added automatically under the list of events.

The blueprint “event.php” (my screenshot) looks like:


<?php if(!defined('KIRBY')) exit ?>

title: Event
pages: false
files: true
fields:
  title:
    label: Event
    type: title
    help:  Please enter the "name" of this event, is also used in the navigation (menu etc.).
  categories:
    label: Typ of event
    type: radio
    default: 6
    options:
      1: Official events
      2: Fishing events
      3: Meetings
      4: Work assignments
      5: Events of the association
      6: Others
  date:
    label: Date (beginning)
    type: date
    width: 1/2
    default: today
    format: DD.MM.YYYY
    required: true
    help:  Please enter the "beginning" of the event.
  time:
    label: Time (beginning)
    type: time
    interval: 15
    width: 1/2
    help:  Please enter the "beginning" of the event (Enter "00:00" for full-time events)!
  dateto:
    label: Date (end)
    type: date
    width: 1/2
    format: DD.MM.YYYY
    help:  Please enter the "end" of the event (Select only if it differs from the beginning).
  location:
    label: Meeting point
    type: text
    help:  Please enter the "meeting point" of the event.
  text:
    label: Text
    type: textarea
    help:  Please enter the "description" of the event.
    

Good luck!

1 Like

Thank you @HeinerEF , much appreciated.

I was doing something similar and got curious about how you’d tackle this.