Virtual pages from image gallery
Intro
If we want to show single files on a page together with some additional information, we can create a parent page with subpages, and upload a single image to each subpage. However, in the Panel those pages would be pretty empty when the information about the image is actually stored in the image meta data. Wouldn't it be cool and less time consuming if we could just upload a bunch of images to the parent page, fill in the meta data—maybe even automatically add some EXIF data at upload (no, that's not part of this recipe)—,and the child pages would be automagically created from these images?
Thanks to virtual pages in Kirby, we can, and that with little more code than what we would need for the parent/subpage approach. Let's work out how.
Prerequisites
- A working Kirby installation. In this example, we use a Starterkit to make use of the existing styles, but you might as well use a Plainkit or your own setup.
- A code editor of your choice
- Optionally, some music that gets you into coding mood
Files we will create in this recipe
Blueprints
- /site/blueprint/pages/gallery.yml
- /site/blueprint/files/gallery-image.yml
Templates
- /site/templates/gallery.php
- /site/templates/gallery-image.php
Models
- /site/models/gallery.php
- /site/models/gallery-image.php
Plugin (optional)
- /site/plugins/virtual-gallery/index.php
Gallery parent page
Let's start with creating a blueprint for our new gallery overview page with a files section to which we can upload the images and assign a files blueprint.
In our site.yml
, we have to adapt the pages
section and add the gallery
template as a new template option. We also change the create
option, so that this part of the blueprint now looks like this:
Now, log in to Panel and create a new gallery page using the gallery.yml
blueprint. Upload some files (if you use the Starterkit, you can borrow somes images from the subpages of the photography
page). Then publish the page.
Gallery template
Before we visit the image gallery on the frontend, we need a new template—gallery.php
—with the following code:
We are ready to view our spectacular little gallery on the frontend.
If you look closely, you will notice that we currently output the images of the page and the href
attribute points to the image URL itself. If you click on any of the links, the browser will open the single file in a new tab or window, but not in the context of a new page. That's not really what we want, and we'll of course fix that later.
So next, let's go tell Kirby that we want these images to be children of the gallery page.
Gallery model
We can fetch child pages of a given page in Kirby with the children()
method, and by default this method returns the subfolders of the given page.
In a Page Model, we can however overwrite this method and thus change what Kirby regards as children. Let's create such a model in the site/models
folder.
We loop through the images, store the page properties for each image in the $images
array and pass it to the Pages::factory()
.
Note that we use the file's name
instead of the filename
(i.e. without the extension) for the page slug. This can have certain downsides if you happen to have multiple files with the same filename but a different extension, which also use the same file template. On the other hand, we probably don't want to use the extension in the URL, particularly if we want to provide different file formats, e.g. support for webp
images.
It's up to you to adapt the slug as fits your use case. If you change the slug, you will have to adapt the image method in the gallery-image.php
page model, though.
If you want to use the unsluggified filename with extension (e.g. attentions-sharks.jpg
), you will need an additional route to prevent Kirby redirecting the URL to the media folder.
Modify gallery template
With this model in place, we can now modify our gallery.php
template and output the child pages instead of the images. Let's do it!
Instead of looping through the images as before, we now loop through the children (and just output the URL for the moment).
But if we open this page in the browser, all we see is some url strings (or black rectangles if you used the Starterkit) instead of the images we had before. But if we click on one of the links, the new virtual subpage opens with the file's name as title (and otherwise almost empty). We are getting there…
Fetch image
How do we get the images back? We need another page model for the child pages that fetches the correct image from the images of the parent page based on the page slug.
Here we redefine the image()
method and return the image that matches the page slug if no file name is passed to the method. Otherwise, we fall back to the parent method. If necessary, you can modify this code to also filter by file type or extension (e.g. if you use different versions of the same file).
We can now adjust the gallery.php
template again:
And hurray! The images show up on the frontend again.
File blueprint
Let's quickly set up the file blueprint with some fields so that we can add some meta data to the images.
Update gallery model
Since we want to access the file meta data as if it was page data, we have to map each image's meta data to the page content object, which we do inside the children
method of the gallery.php
model from above.
Child page template
With the page model complete, let's set out to create a template for the children pages.
Provided you have filled in some data for each image, you can now lean back and admire the result. Feel free to adjust the styles or the content fields to your liking.
There's only one thing left to do…
File component
At this point, when we update a file's metadata, we cannot preview the resulting page like a normal page. If we click on the Open
button in the file view, we are just redirected to the file. We could now add a pages section to our gallery.yml
together with a blueprint for the subpages and extend the model, but that would only complicate matters without really adding any advantages.
Instead, we can use a file::url
component that changes the file URL depending on the request header. In this case, we want to modify the URL only if the visitor wants a JSON response.
This component might actually interfere with JSON requests from the frontend, so only add this component if it works for you.
Inside the /plugins
folder, create a folder virtual-gallery
with the following index.php
Final result
Done! The virtual pages from files in the parent page are now working exactly like we wanted.
Here is the final code again, this time all nicely tucked up into a plugin.