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
});
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’
));
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.
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.
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:
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.
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.
So, I briefly tested this in a new Starterkit. Now here some basic steps:
Create a new file in plugins, e.g. hooks.php
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.
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
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 …
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.
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:
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?
Create top-level projects folder titled Projects
Create events folder under Projects titled Events (projects/events)
Save panel.page.create hook as hook.php to plugins folder.
Create new structure field entry in projects/events
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).
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.
@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.