Kirby3 Janitor - a panel button to run jobs defined in php

I am creating posts for my plugins to make it easier to find them using the forum search and not just the docs search.

Kirby 3 Plugin for running jobs.

  • It is a Panel Button!
  • It has jobs build-in for cleaning the cache, sessions, create zip-backup, pre-generate thumbs, open URLs, refresh the current Panel page, download a file, copy to clipboard and more.
  • You can define your own jobs (call API hooks, play a game, hack a server, …)
  • It can be triggered in your frontend code and with CRON.
  • It can also be used as a CLI with fancy output.
  • It can also create logs of what it did.

register and define a job as callback aka closure

site/config/config.php

'bnomei.janitor.jobs' => [
    'aweSomeItCouldBe' => function (Kirby\Cms\Page $page = null, string $data = null) {
        // $page => page object where the button as pressed
        // $data => 'my custom data'
        return [
            'status' => 200,
            'label' => $page->title() . ' ' . $data,
        ];
    },
    'query' => function (Kirby\Cms\Page $page = null, string $data = null) {
        return [
            'status' => 200,
            'label' => $data, // this is the current panel users email
        ];
    },
]

add a panel button

site/blueprints/pages/default.yml

  janitor:
    type: janitor
    label: Awesome
    progress: Awesome...
    job: aweSomeItCouldBe

  janitor_query:
    type: janitor
    label: Query '{{ user.email }}'
    job: query
    data: '{{ user.email }}'
2 Likes

with new v2.13.0 you can now use janitor in CLI mode to

  • janitor --tinker will start a very simple REPL session.
  • janitor --down will put your frontend in maintenance mode. Panel will still be usable.
  • janitor --up will stop the maintenance mode.

Hi Bnomei,

If I want to add a button in pane which blueprint should I edit?

I tried adding this:

janitor:
  type: janitor
  label: Clean Cache
  progress: Cleaning Cache...
  job: cleanCache

to site/blueprints/default.yml but nothing changes in the panel.

What am I doing wrong?

THanks!

That depends on your setup and on which page type you want to use the button

1 Like

@texnixe thanks for your reply!

I thought cleaning the cache would clean the whole website’s cache – ideally it’d be a button/section in the main panel navigation, like I saw other plugins do. It’s not really tied to a page. But I think the owner of the website wouldn’t mind if it would be a button just when editing a certain page, that’s fine.

I tried adding the YAML above it to all 3 blueprints that are actually used on this website and none of the forms changed when trying to edit the pages.

It would probably make sense to include this button on the Dashboard, i.e. site.yml, not a page blueprint.

Since the Janitor button is provided as a field plugins, it needs to be inside a fields section-

Yes, that is correct.

That’s not possible with a field/section plugin.

1 Like

Perfect!

First I wasn’t putting it under the fields entry, second I didn’t even realise about the site/blueprints/site.yml. While upgrading from Kirby 2 to 3 I put all blueprints into blueprints/pages and site.yml wasn’t at all respected, so I thought it was an old unused blueprint. I added it to site.yml fields and the button is there, thanks so much!

But now that I have added it, the previous dashboard where new pages could be added is gone – how could I allow both site.yml fields and editing pages too?

Thanks!

There are several ways to set this up, but the minimum requirement for this would be a fields section and a pages section.

title: Site

sections:
  pages:
    type: pages
    # other page section props.
  fields:
    type: fields
    fields:
      # your fields here

Of course, you can have multiple sections (e.g. pages separated by templates), and also put them into columns and tabs.

2 Likes

Per-fect! Works like a charm, thanks so much!

hello everyone.

i am considering dropping the custom CLI and converting my command to the new kirby CLI. so instead of janitor yourjob you would call kirby yourjob.

i am also planning to add a very easy way to run any of the kirby CLI commands using a janitor button.

whats your take on this?

2 Likes

:mega: version 3 released today. I refactored Janitor plugin to work with kirbys new cli commands. You can define and trigger your own command easily but you can keep using callbacks if you prefer that (see janitor:job).

  test_ping:
    type: janitor
    command: 'ping' # see tests/site/commands/ping.php
    label: Ping
    progress: ....
    success: Pong
    error: BAMM

  janitor_open:
    type: janitor
    command: 'janitor:open --data {{ user.panel.url }}'
    intab: true
    label: Open current user URL in new tab
    icon: open
    # the open command will forward the `data` arg to `open` and open that URL

  janitor_clipboarddata:
    type: janitor
    command: 'janitor:clipboard --data {{ page.title }}'
    label: 'Copy "{{ page.title }}" to Clipboard'
    progress: Copied!
    icon: copy
    # the clipboard command will forward the `data` arg to `clipboard` and copy that

  janitor_download:
    type: janitor
    command: 'janitor:download --data {{ site.index.files.first.url }}'
    label: Download File Example
    icon: download
    # the download command will forward the `data` arg to `download` and start downloading that

  janitor_backupzip:
    type: janitor
    command: 'janitor:backupzip'
    cooldown: 5000
    label: Generate Backup ZIP
    icon: archive

  janitor_render:
    type: janitor
    command: 'janitor:render'
    label: Render pages to create missing thumb jobs

  janitor_thumbssite:
    type: janitor
    command: 'janitor:thumbs --site'
    label: Generate thumbs from existing thumb jobs (full site)

site/blueprints/page/default.yml

title: Default Page
fields:
  call_my_command:
    type: janitor
    command: 'example --data test'
    label: Call `Example` Command

site/commands/example.php

<?php

use Bnomei\Janitor;
use Kirby\CLI\CLI;

return [
    'description' => 'Example',
    'args' => [] + Janitor::ARGS, // page, file, user, site, data
    'command' => static function (CLI $cli): void {
        $page = page($cli->arg('page'));

        // output for the command line
        defined('STDOUT') && $cli->success(
            $model->title() . ' ' . $cli->arg('data')
        );

        // output for janitor
        janitor()->data($cli->arg('command'), [
            'status' => 200,
            'message' => $model->title() . ' ' . $cli->arg('data'),
        ]);
    }
];

janitor-v3

Got: Undefined constant “STDOUT”. Was trying to remove defined('STDOUT') but without success. Running on PHP@8.0.26

janitor v3 needs the kirby cli installed. while you can run some core cli commands with the —quiet option others might fail due to the cli being optimized to run in terminal only. i created an issue but got no response yet.

in my commands i combine all calls to the cli output with a defined('STDOUT') && which essentially prevents that issue from happening.

1 Like

@bnomei I also have the STDOUT problem with my first implementation of the new janitor.
Not to test I created a fully new and empty plainkit project and followed your readme for the example command and even there I get the STDOUT :thinking: Kirby CLI is installed btw…

As a work arround I tried working with the callbacks but as soon as I add a button to my page I always get an error when adding a new page.

Example button:

processRegistrations:
  type: janitor
  icon: cog
  progress: 'Processing...'
  command: 'janitor:job --key processRegistrations'
  label: Process registrations

So when I add that and I want to create a new page I receive this:

When I remove the button everything works as expected :thinking:

Cheers @bnomei ! In regards to the other problem I’m having with the DuplicatePage exception… (I’ll keep it on the forum because i’m not sure if it’s a bug yet… I’m having a hard time to get my head around it)

I’ve tracked it down that this piece is causing it:

'command' => function ($command = null) {
    // resolve queries
    $command = \Bnomei\Janitor::query($command, $this->model());
    // append model
    if ($this->model() instanceof \Kirby\Cms\Page) {
        $command .= ' --page ' . $this->model()->uuid()->toString() ?? $this->model()->id();
    } elseif ($this->model() instanceof \Kirby\Cms\File) {
        $command .= ' --file ' . $this->model()->uuid()->toString() ?? $this->model()->id();
    } elseif ($this->model() instanceof \Kirby\Cms\User) {
        $command .= ' --user ' . $this->model()->uuid()->toString() ?? $this->model()->id();
    } elseif ($this->model() instanceof \Kirby\Cms\Site) {
        $command .= ' --site'; // boolean argument
    }
    $command .= ' --model '. $this->model()->uuid()->toString() ??
        ($this->model() instanceof \Kirby\Cms\Site ? 'site://' : $this->model()->id());
    return $command;
},

If I remove the $this->model()->uuid()->toString() references then everything works as excepted. If I keep them I get the DuplicatePage exception as mentioned in my screenshot above.

I wonder if it’s somewhat related to “a-call-to-uuid-causes-a-write-on-a-virtual-page” but I’m really flabbergasted on what’s happening here… Can’t really seem to replicate it in plainkit either.

If you have any ideas … at a loss here :sweat_smile:

the last few versions added a few new features

Delaying query resolution - if you want to send JSON etc

janitor_smart:
    label: 'Resolve Arg smart/lazy using {( instead of {{'
    type: janitor
    command: 'janitor:pipe --data {( model.text.kirbytext )} --to message'
    # pipe will show lazy/smart resolved data on button (aka the message)

colors from blueprint and api

  janitor_color:
    label: Janitor buttons can now be colorful
    type: janitor
    command: 'janitor:job --key random.colors'
    color: 'var(--color-blue-600)'
    backgroundColor: 'var(--color-blue-200)'

calling methods on a model

if you do not want to create a command file or job definition in config for everything.

  janitor_callNoData:
    label: Call method on current model
    type: janitor
    command: 'janitor:call --method whoAmI'

  janitor_callWithData:
    label: Call method on current model with Data
    type: janitor
    command: 'janitor:call --method repeatAfterMe --data {{ user.id }}'

Create a Backup of pages (and subpages) easily with undertaker()-helper

site/config/config.php or in a plugin

<?php

return [
    'hooks' => [
        'page.delete:before' => function (\Kirby\Cms\Page $page, bool $force) {
            // do something before a page gets deleted
            undertaker($page);
            // will create a file with filename DATETIME-ID-USERID.zip
            // site/graveyard/202303231729-myparent+my-slug-HYE7dsx.zip
        },
    ],
    // ... other options
];

Create Backup ZIP of content and accounts folder

If you do not just need to archive a few pages on change but want to archive the full content and/or user accounts then Janitor also has solution for that. You can adjust which folders to archive.

Toggle Maintenance Mode

janitor_maintenance:
    type: janitor
    command: 'janitor:maintenance --user {{ user.uuid }}'
    cooldown: 5000
    label: 'Maintenance: {{ site.isUnderMaintenance.ecco("DOWN","UP") }}'
    icon: '{{ site.isUnderMaintenance.ecco("cancel","circle") }}'

RTFM

Read more about these features in the README

1 Like

version 4.2.0