A Micropub endpoint you can connect to your Kirby 3 setup.
This is heavily inspired by sebsel's solution in the Indieweb Toolkit for Kirby 2 and follows its philosophy.
The code in this repository is intended to serve as an example of how to use the Micropub Request for Kirby 3 class.
This is not a fully functional Kirby 3 plugin. A Micropub endpoint is a hard thing to keep generic. The intention of the Micropub standard is to provide a uniform way to publish all kinds of content to a website. However, every site has a different way of storing that content, especially when a flexible CMS like Kirby is used.
Therefore, it is probably a good idea to customize a Micropub setup to the project at hand. The idea behind this code is to streamline the things that are the same on a Micropub server. It will process incoming Micropub requests, handle authentication, manage all communication with the client, and provide you with consistent data that you know how to work with when you know how to work with Kirby. You can then use this data to create, update or delete pages, write content to a database, JSON
or YAML
files, etc— whatever fits the site in question.
- Micropub Endpoint that supports creating, updating, and deleting posts
- Media endpoint to handle attachments
- Supports some Micropub extensions: slug, post status, and visibility properties; query for supported vocabulary and category/tag list
- Post Type Discovery algorithm
- All features of the Micropub Request for Kirby 3 class
Load the classes in lib/
using your favourite method, for example using Kirby's built-in autoloader:
load([
'mof\\Micropub\\Endpoint' => 'lib/Endpoint.php',
'mof\\Micropub\\Request' => 'lib/Request.php',
'mof\\Micropub\\IndieAuth' => 'lib/IndieAuth.php',
'mof\\Micropub\\Error' => 'lib/Error.php'
], __DIR__);
To make the endpoint work, pass it a config
array with your Micropub endpoint configuration information, and, at minimum, a create
callback function that returns an URL of the newly created content (This endpoint can also handle update
and delete
if you choose to implement them).
Here is a minimal example how to do it in a route:
'routes' => [
[
'pattern' => 'micropub',
'method' => 'GET|POST',
'action' => function () {
return \mof\Micropub\Endpoint::micropub([
'config' => [
'media-endpoint' => kirby()->urls()->base() . '/' . 'micropub-media-endpoint',
],
'create' => function ($post) {
// Create a new page, store content in a database,
// etc. from $post
return $url;
}
]);
}
]
]
A more detailed example can be found in the included example.php
. It is advisable to create a Kirby plugin instead of storing the whole endpoint setup in config.php
.
In general, what you want to do in the create
function is to ‘map’ the incoming Micropub post to your Kirby setup's content structure. Say you want incoming note
posts to go to your blog, which stores its entries in a content folder called blog
. Photos should be published as new subpages of your photo page, which is in the photography
folder. In order to map the incoming posts to your existing Kirby pages, you therefore need a parent
and a blueprint
for these two post types:
$postmap = [
'note' => [
'parent' => 'blog',
'blueprint' => 'article'
],
'photo' => [
'parent' => 'photography',
'blueprint' => 'image'
]
];
Now, say you try to post a bookmark
to your Micropub endpoint, a type of post you have not defined. You could reject such posts:
if(!isset($postmap[$post->type()])) return false;
Alternatively, you could publish all unknown types to a page you have called stream
:
if(!isset($postmap[$post->type()])) {
$postmap[$post->type()] = [
'parent' => 'stream',
'blueprint' => 'default'
];
}
Note: In this example, we rely on $post->type()
to match the incoming content to our content structure. $post->type()
contains the suggestion of the Post Type Discovery algorithm, which tries to find an 'implied post type' based on properties of the content submitted to the endpoint. This is not the only way. If what you want to publish to your site doesn't fit the predefined 'types' of posts,
you can simply ignore $post->type()
and come up with your own matching criteria by inspecting the incoming content. Maybe you don't have a separate photography
page and publish everything to blog
, but would like to tag posts that have a photo with 'photo'. Or you want to publish note
(a post without a title in Micropub parlance) and article
(a post with a title) using the same blueprint.
After you have matched the incoming post to your content structure, you can create a new page:
// Careful, overwrites existing pages which have the same slug!
$newpage = page($postmap[$post->type()]['parent'])->createChild([
'slug' => $post->slug() ?? \Kirby\Toolkit\Str::random(8),
'template' => $postmap[$post->type()]['blueprint'],
'draft' => ($post->status() == 'listed' || $post->status() == 'unlisted') ? false : true
]);
Please note this will overwrite existing pages with the same slug. This is just an example.
The next step would then be to match the $post->fields()
array to the blueprint's content fields and populate $newpage
's content, handle the attachments, and so on (see the reference below and example.php
).
However you choose to store your content, create
should return the URL of that content in the end.
The media endpoint needs two things:
- a directory to (temporarily) store uploads in
- a publicly accessible URL of that directory, so it can tell the Micropub client where to find the uploaded attachment.
A good choice is probably Kirby's media
folder. Create a new directory in there, e.g., temp
, and set up the media endpoint with it. Again, you can do so with a route:
'routes' => [
[
'pattern' => 'micropub-media-endpoint',
'method' => 'POST',
'action' => function () {
return \mof\Micropub\Endpoint::media(
kirby()->root('media') . '/temp',
kirby()->urls()->media() . '/temp'
);
}
]
]
(You should probably clear out that folder from time to time, or delete the upload after you finished adding the file to your newly created page)
For testing, download and copy this repository to your project folder (or a fresh Starterkit). You should then be able to send test requests to the included example.php
. I recommend Insomnia for these kinds of things. Once you move closer to production, micropub.rocks helps to thoroughly test your implementation.
When you copy this repository to /site/plugins/{{ your-plugin-name }}/vendor
, make sure to correctly load the classes, for example using Kirby's built-in autoloader.
You can customize some options of the Micropub Request for Kirby 3 class. See the documentation there.
Gets a Kirby\Toolkit\Obj
object that includes the following:
/*
* A suggested 'implied post type' based on the properties of the request.
* You can use it to map the post to your existing content structure.
*
* @return string
*/
$post->type()
/*
* An array of the post's content fields
*
* @return array
*/
$post->fields()
/* For your head start, here is what that might look like:
Array
(
[name] => Title of my blog post, but there might not always be one
[content] => <b>Hello</b> World. My blog post text.
[category] => Array
(
[0] => foo
[1] => bar
)
)
*/
/*
* A visibility property, if none was suggested by the client it defaults to 'listed'
*
* @return string
*/
$post->status()
/*
* A slug for the new post, if one was suggested by the client
*
* @return string|false
*/
$post->slug()
/*
* An object with the attachments of the post
*
* @return Kirby\Toolkit\Obj
*/
$post->files()
/*
* An array listing the content fields which include HTML
*
* @return array|false
*/
$post->html()
/*
* The client used to post the content
*
* @return string|null
*/
$post->client()
Gets a Kirby\Toolkit\Obj
object that includes the following:
/*
* The Kirby page object of the page to be updated.
* The endpoint has already checked if this page exists.
*
* @return Kirby\Cms\Page
*/
$post->page()
/*
* An array of properties to replace on the page
*
* @return array|null
*/
$post->replace()
/*
* An array of properties to add to the page
*
* @return array|null
*/
$post->add()
/*
* An array of properties to remove from the page
*
* @return array|null
*/
$post->remove()
/*
* An array listing the content fields which include HTML
*
* @return array|false
*/
$post->html()
Gets a Kirby\Toolkit\Obj
object that includes the following:
/*
* The Kirby page object of the page to be deleted.
* The endpoint has already checked if this page exists.
*
* @return Kirby\Cms\Page
*/
$post->page()
Please report any problems you encounter, as well as your thoughts and comments as issues, or send a pull request!
- kirby3-micropub-request, a class for Kirby 3 development providing a simple API to inspect incoming Micropub requests.
- kirby3-micropublisher, a fully functioning Micropub endpoint plugin with a lot of customization options
Inspiration and some code from