Kirby Opener is a Kirby CMS Panel Field button that allows you to use placeholders to create dynamic urls which are called with and without ajax response or start downloads.
NOTE: This is not a free plugin. In order to use it on a production server, you need to buy a license. For details on Kirby Opener's license model, scroll down to the License section of this document.
- open any URL from within the panel
- add custom data within the URL using placeholders
- easily call Routes or Page Models Functions
- display custom JSON response status messages at the button label
- trigger downloading of files
- trigger clipboard copy of url
- trigger refresh of page on success
- trigger browser confirmation dialog
- easily extendable placeholders
- configural parsing of json response
- Kirby 2.3+
kirby plugin:install bnomei/kirby-opener
$ git submodule add https://github.com/bnomei/kirby-opener.git site/plugins/kirby-opener
- Download the contents of this repository as ZIP-file.
- Rename the extracted folder to
kirby-opener
and copy it into thesite/plugins/
directory in your Kirby project.
Start the Kirby Panel and create a new page with the template openerexample
this plugin provides. The plugin also comes with some example fields to get you started. You can find their their global field definitions in the kirby-opener/blueprints/fields
folder.
To use the plugin to the fullest you will have to define your own urls using placeholders and maybe even create the controllers and/or templates to respond with the JSON.
example1: openeropenuser
example2: openeropenexternal
example3: openeropenpagefield
example4: openerpopup
example5: openerdownload
example6: openersuccess
example7: openererror
example8: openercontroller
Add this field definition to any blueprint and open the page in the panel.
example2explained:
type: opener
command: 'https://www.google.com/?q={field.title}/open:yes'
text: 'Search for Title in Google'
The {field.title}
is called a placeholder. It will be replaced with something context related on the panel page. In this case with the title
field of the current $page
-object.
Add this field definition to a blueprint. It will create a new opener
-button in the panel with the label Download fileXY
. While waiting for the response the ...
will be displayed. Once the called page responds with JSON it will be parsed. Unless there is a different message
in the JSON the textsuccess
from the blueprint will be displayed.
example5explained:
type: opener
command: '/{page.url}/fileparam:fileXY/download:yes'
text: 'Download a file'
textprogress: '...'
textsuccess: 'Download...'
The {page.url}
within the command
is a placeholder and will be replaced by the url of the current page. There are couple of predefined placeholders but you probably will want define your own. Which properties of the root JSON object are parsed to determin success, message and url of the file can be configured. These topics will be described later in this readme.
The download:yes
-parameter can also be configured. It tells the plugins javascript code to download the file and not open it in a popup window (since most browser would block that by default).
For this example let's respond with downloading the kirby license file. In your template code you need to build and return a JSON response.
if(param('fileparam') == 'fileXY') {
$code = f::exists(kirby()->roots()->index().DS.'license.md') ? 200 : 400;
$json = [
'code' => $code,
'fileurl' => kirby()->urls()->index().'/license.md',
];
sleep(5); // wait a bit for example purposes
die(response::json($json, $code));
}
Please note that this is a very basic implementation of returning JSON. The Kirby Cookbook and Kirby Forum are a good sources to do better.
Now open your page in the panel and press the Download fileXY
button. The download dialog of your browser for the kirby license.md file should appear. unless you removed the license – you little scoundrel.
To make sure the command can only be called from within the panel you need to add some sort protection. Let's assume you have an api
controller (or just a template) prepared. Add the following field definition to any blueprint you want to trigger the api.
exampleController:
type: opener
command: '/api/{field.autoid}/{page.diruri.encoded}/{page.secret}/mycmd:dowork'
text: 'Do Work'
textprogress: 'working...'
textsuccess: 'Done.'
texterror: 'Failed.'
So on any page within the panel that has this field you now have a Do Work
-button. Pressing it will start an ajax request to the api
page with additional parameters. Since these parameters also contain some placeholders as well these will be replaced with context specific values.
Now you need some logic to handle the request. I prefer using a controller in combination with templates, so paste this to your api
controller. This plugin comes with and example controller to help you get started. But let's take a look at how the controller works.
<?php
return function($site, $pages, $page) {
// prepare json response
$json = ['code' => 400, 'message' => '', 'fileurl' => ''];
// #1: optional security...
// require a user to be logged in and
// the request has to come from the panel opener plugin and
// it has to be an proper ajax call
if( !$site->user() ||
!boolval(param('panel')) || // added by plugin automatically
!r::ajax()
) {
die(response::json($json, 400));
}
// #2: now check if work need to be done at all
if(param('mycmd') == 'dowork') {
// #3: get page to work at
$pageToWork = null;
// #3.1: try autoid
if($autoid = param('autoid')) {
// left for you to implement
$pageToWork = myGetPageByAutoIdFunction($autoid);
}
// #3.2: try diruri
else if($diruri = param('diruri')) {
// plugin provides a pages method to get page from encoded uri
// why encode the uri? because it could contain multiple '/' and that would break the parameters.
$pageToWork = $pages->openerDiruriEncodedGetPage($diruri);
}
// #4: found a page? then validate with secret and start working
// why a secret? to add an extra layout of security so creating a valid
// request is something only you can do and nobody from the outside.
if($pageToWork && $pageToWork->openerSecret() == param('secret')) {
// do work
sleep(5);
// then respond...
$json['code'] = 200;
$json['message'] = 'Lunchtime!';
}
}
// for the sake of simplicity just exit now
die(response::json($json, intval($json['code'])));
// normaly a controller would return some values to the template
//return compact('json');
};
The placeholders help you build commands quickly. Why did I implement placeholders instead of parsing the command directly? They help you avoid mistakes in sticking to the DRY-principle.
replace the wildcard with any blueprint fieldname to get the value of the field. only numbers and strings are supported.
will get the fields and call urlencode()
on its value.
$page->url()
in template
$page->parent()->url()
in template
token you can check in template/controller if request is valid. limited to page.
token you can check in template/controller if request is valid. wildcard version.
urlencoded($page->diruri())
to forward this page to any other. helper functions available – see controller example.
Using the autoid-plugin is a good alternative to diruri
if you implement a fast lookup-method maybe with a cache. Since just using $site->index()
or $site->search()
might be slow if you have many pages.
You can also define your own by creating a site/config/config.php
setting. This plugin grants you access to $site
and $page
.
c::set('plugin.opener.placeholder', [
'myfield' => '$page->myfield()->fieldmethod()',
'another' => '$page->parent()->diruri()',
'yetmore' => '$site->otherfield()',
]);
You can set these in your site/config/config.php
.
- default: ''
- add your license here and the widget reminding you to buy one will disappear from the Panel.
- default: unique SALT for your webserver
- this value is used to create the
secret
and you should set your own value to improve security but it is not required.
- default: true
- if disabled the plugin does not install any
blueprints, templates, controllers, hooks and routes
that are used by its examples. use this setting in production enviroment.
- default:
code
- use this setting to define a json root-object property which will be used to parse the status code.
- default:
message
- use this setting to define a json root-object property which will be used to parse the response message.
- default:
fileurl
- use this setting to define a json root-object property which will be used to parse the url of the file to be downloaded.
- default:
5000
in ms - after that delay the button is reset from displaying the message to its initial state.
- default: false
- downloads are opend via bowser dialog if possible and not as popup windows which most browsers block
- default: 'download:yes'
- command part to tell the plugin js script to trigger download for content of JSON response (see
json.fileurl
).
- default: 'open:yes'
- command part to tell the plugin js script to trigger a new window/tab with command as url. There will be no ajax call.
- default: 'copy:yes'
- command part to tell the plugin js script to trigger clipboard copy of the url. There will be no ajax call. If browser blocks it behaves like with
open:yes
.
- default: 'refresh:yes'
- command part to tell the plugin js script to trigger a page refresh on success.
- default: false
- if enabled you can use
$pageModel
in your placeholders to access functions defined in your Kirby Page Models.
- default: false
- commands only allow you to chain
$page
or$site
and their methods but without parameters. If you enabledallow-eval
you can go crazy with your placeholders up to 100 chars and a single statement. But sinceeval()
is dangerous this setting is disabled by default. Please be aware of the risks of you enable this setting.
Placeholders like the following becomes possible if enabled:
c::set('plugin.opener.placeholder', [
'crazy' => 'panel()->page("some/wicked/uri")->mymethod($page->somefield()->value())', // less than 100 chars
]);
This plugin is provided "as is" with no guarantee. Use it at your own risk and always test it yourself before using it in a production environment. If you find any issues, please create a new issue.
Kirby Opener can be evaluated as long as you want on how many private servers you want. To deploy Kirby Opener on any public server, you need to buy a license. You need one unique license per public server (just like Kirby does). See license.md
for terms and conditions.
However, even with a valid license code, it is discouraged to use it in any project, that promotes racism, sexism, homophobia, animal abuse or any other form of hate-speech.
Technical support is provided on GitHub only. No representations or guarantees are made regarding the response time in which support questions are answered. But you can also join the discussions in the Kirby Forum.
Kirby Opener is developed and maintained by Bruno Meilick, a game designer & web developer from Germany. I want to thank Fabian Michael for inspiring me a great deal and Julian Kraan for telling me about Kirby in the first place.