How do I add a custom options page to the panel?

I created a plugin which registers a custom widget. But I got stuck by adding some kind of settings page/modal to the widget.

// my-plugin/my-plugin.php
$kirby->set( 'widget', 'widget', __DIR__ . '/widget' );
$kirby->set( 'route', array(
    'pattern' => 'panel/gearsdigital/settings',
    'action'  => function () {
        return tpl::load( __DIR__ . DS . 'templates/settings.php');
    }
) );

// my-plugin/widget/widget.php
return array(
    'title' => array(
        'text'       => 'Gearsdigital',
        'compressed' => true
    ),
    'options' => array(
        array(
            'text' => 'Settings',
            'icon' => 'pencil',
            'link' => 'panel/gearsdigital/settings'
        )
    ),
    'html'  => function () {
        return tpl::load( __DIR__ . DS . 'template.php' );
    }
);

It’s totally possible to do what you want but unfortunately it not documented anywhere. I’m using panel routes, modals, and forms, for an upcoming plugin update. The best way to learn how to use them is reading the Panel source code.

I’ll briefly try to explain the process:

Panel Routes

Use panel()->routes() to register routes:

<?php // site/plugins/plugin-name/routes.php

panel()->routes([
    [
        'pattern' => 'my/plugin/route',
        'method'  => 'GET|POST',
        'filter'  => 'auth',
        'action'  => 'PluginName\Controller::config'
    ]
]);

The route pattern will be automatically prefixed with panel/. You’ll find route examples at panel/app/config/routes.php.

Controllers

Although one could use modal and forms straight inside a route closure, using a controller makes it easier in my opinion. The controller needs to extend Kirby\Panel\Controllers\Base and implement the view and form methods.

<?php // site/plugins/plugin-name/controller.php

namespace PluginName;

class Controller extends \Kirby\Panel\Controllers\Base
{
    public function config()
    {
        $configuration = 'fetch configuration';

        $form = $this->form('config', compact('configuration'), function ($form) use ($configuration) {
            // this will be called only when the form POSTs to this action
            try {
                // save configuration…

                panel()->notify(':)');
            } catch (Exception $e) {
                panel()->alert('Something went wrong!');
            }

            panel()->redirect(); // go back to dashboard
        });

        return $this->modal('config', compact('configuration', 'form'));
    }

    public function form($id, $data = array(), $submit = null)
    {
        $file = kirby()->roots()->plugins() . DS . 'plugin-name' . DS . 'forms' . DS . $id . '.php';

        return panel()->form($file, $data, $submit);
    }

    public function view($file, $data = array())
    {
        return new View($file, $data);
    }
}

You can add as many methods as needed. You’ll find controller examples at panel/app/controllers.

Take a look at panel/app/src/panel/controllers/base.php to understand what the base controller does under the hood.

Forms

The form method tells the controller where to look for forms, in this case it’s a form folder inside the plugins folder: site/plugins/plugin-name/forms.

You’ll find many form examples at panel/app/forms, just copy one of those to site/plugins/plugin-name/forms/config.php and tweak it to your needs.

Views

You’ll need to create a class that extends Kirby\Panel\View to tell the controller where to look for views:

<?php // site/plugins/plugin-name/view.php

namespace PluginName;

class View extends \Kirby\Panel\View
{
    public function __construct($file, $data = [])
    {
        $this->_root = kirby()->roots()->plugins() . DS . 'plugin-name' . DS . 'views';
        $this->_file = $file;
        $this->_data = $data;
    }
}

In this case it’s a views folder inside the plugins folder: site/plugins/plugin-name/views. This is where you can put modal views:

<!-- site/plugins/plugin-name/views/config.php-->

<div class="modal-content modal-content-medium">
  <?php echo $form ?>
</div>

Note that we are passing the $form variable to this view on the controller config() method. You can pass more variables too.

Conclusion

Next up, let’s load these files:

<?php // site/plugins/plugin-name/plugin-name.php

load([
  'pluginname\\controller' => __DIR__ . DS . 'controller.php',
  'pluginname\\view'  => __DIR__ . DS . 'view.php'
]);

require 'routes.php';

I just copied these examples from my plugin (not releases yet) and changed them a bit. Let me know if something doesn’t work as expected.

8 Likes

WHOAAA! What an awesome answer :smile: l’ll try that out and come back to you later this day.

1 Like

Just as a complement to the amazing answer by @pedroborges, you can find some undocumented documentation here:

github.com/jenstornell/kirby-secrets

There is a panel section there as well.

2 Likes

Also there is this boilerplate - https://github.com/LCD344/kirby-panel-extension-boilerplate

It has in it setup everything to add your own views and forms to the panel… It needs a bit of an update (to enable the text area to work properly) but except for that it functions.

Nur

1 Like

Is there, in any way, a possibility to save all the data of this page to a custom .txt file in the content directory?

Yes, that should be possible. You might, however, just as well create a page, then, instead of a custom view? Maybe one you access directly via a widget, but hidden from the rest?

That 's indeed a better way of doing it, is there any example or documentation on how this could be done?

There are a lot of widget examples here. But all you really need to do is a simple widget with a link in it. Here are the docs: https://getkirby.com/docs/developer-guide/panel/widgets

Thanks… I tried it but I found no solution for the controller in my route based on tpl::load so I switched back to the old method ?key=value

@pedroborges I got this error:
10

Thanks for your help!

@tylerlumen Could you please post more context?

Well, the error message means exactly what is says, that you are using a $this variable outside of an object context (i.e. outside of a class instance), for example in a function…

Hey @texnixe , thanks for you answer!

I have use the code 1:1 from @pedroborges. See above: How do I add a custom options page to the panel?

Just that and nothing else?

And is there any more information of what file and line throws the error, maybe in your php error logs?

Now I can see the error:

But I don’t have a plan what I can do. :open_mouth:

I have only make a test with this code:

namespace newssystem;

class Controller extends \Kirby\Panel\Controllers\Base
{
    public function config()
    {
        return $this->modal('config');
    }
}

But the same error… Why I can’t use “$this” in my controller?..

Because the config function is called statically in the route action.