Plugins

How a plugin is built, every method a plugin can use, and an example.

A plugin adds something to Wake that it does not do by itself: a card, a field type for categories, an address, a maintenance task. Plugins live in plugins/, one folder each. To install one, copy its folder there by FTP or Git. Under Plugins in the panel (people with settings.manage) you see all plugins, switch them on and off and change their settings.

Plugins run with the same rights as Wake. Only install plugins whose source you trust. Why it works this way: decision 0009.

A plugin folder

plugins/opening-hours/
  plugin.yml      name, description, version, author, settings
  plugin.php      registers what the plugin adds
  src/            your classes, loaded through "autoload" in plugin.yml
  vendor/         bundled libraries; vendor/autoload.php is loaded if present
  views/          Blade views, used as opening-hours::name
  lang/           translations, de.json and so on
  assets/         CSS, JavaScript and images for the website

The folder name is the plugin's key: lowercase letters, digits, - and _. Folders with other names, and folders without a plugin.php, are ignored.

For a step-by-step example, see Write a plugin.

plugin.yml

name: Opening hours
description: { en: Shows today's opening hours., de: Zeigt die heutigen Öffnungszeiten. }
version: 1.0.0
author: Jane Doe
url: https://example.org/opening-hours
autoload:
  OpeningHours: src            # PSR-4: OpeningHours\Hours is src/Hours.php
options:
  closed_on:
    type: select
    label: { en: Closed on, de: Geschlossen am }
    options: { sunday: Sunday, monday: Monday }
    default: sunday

autoload maps namespaces to folders inside the plugin (PSR-4). Folders outside the plugin are ignored.

Options work like theme options: text, textarea, toggle, select and color, each with label, default and optionally placeholder and hint. Values are stored in plugins.options in settings/general.yml (configuration).

plugin.php

<?php

use Illuminate\Support\Facades\Route;
use Wake\Plugins\Plugin;

return function (Plugin $plugin) {
    $plugin->card('hours', 'opening-hours::cards.hours');

    $plugin->routes(fn () => Route::get('hours.json', fn () => [
        'closed_on' => $plugin->option('closed_on'),
    ]));
};

Wake calls the function on every request that is not answered from the page cache, so keep it quick: register things here and do the work later, when they are used. The file may also return nothing; then Wake only loads its views, translations and assets.

Your own classes are loaded through autoload in plugin.yml; give them a namespace of your own. To share an object between views, routes and tasks, put it into Laravel's container: app()->instance(MyService::class, new MyService($plugin));.

What a plugin can add

Method Adds
wake.form:{name} (route middleware) protects a public form that contains @wakeForm('{name}'): signed stamp, honeypot, ten submissions per visitor in ten minutes. See forms.
card($name, $view) a card for Markdown, ::: name … :::. The view gets $name, $value, $attributes and $content. A theme's cards/{name}.blade.php still wins. A views/cards/{name}.yml describes it for the editor and assistants. See cards.
field($type, $view, $label) a field type for categories. The view renders the input in the editor and gets $key, $field, $model (for wire:model) and $disabled. $label is a text or one per language.
routes($closure) website routes, registered before Wake's own addresses, in the alphabetical order of the plugin folders. They have no session and no page cache. While the site is closed, they answer only signed-in team members, like the website. Wrap them in Route::middleware('web') only for routes used by signed-in team members (session and CSRF protection).
task($class) a maintenance task, a class implementing Wake\Maintenance\Task with run(Deadline $deadline). Wake creates it through Laravel's container, so the constructor can ask for services. Check $deadline->expired() in loops.
head($closure) HTML for the <head> of every website page: fn ($site, $entry) => '<meta …>'.
on($event, $listener) an event listener, e.g. for Wake\Content\ContentChanged.
command($class) an Artisan command for php wake/artisan.

And to help with that:

Method Returns
key() the folder name
path($file) the full path of a file in the plugin
option($key, $default) a setting from the panel, or its default from plugin.yml
asset($file) the public URL of a file in assets/, with a version for caching
info the plugin.yml data: name(), description(), version(), author(), url(), options()

Every method that adds something returns the plugin, so calls can be chained.

Views, translations and assets

Folder What Wake does with it
views/ registered as the view namespace {folder}: views/cards/hours.blade.php is opening-hours::cards.hours
lang/ JSON translations (de.json, …) added to Wake's texts, used with __('Text')
assets/ copied to public/assets/plugins/{folder}/ when a file changed; asset('file') returns its address with a version

Translations of all plugins, themes and Wake share one list, so pick texts that are unlikely to mean something else elsewhere.

Example: a field type

// plugin.php
return function (Wake\Plugins\Plugin $plugin) {
    $plugin->field('colour', 'colours::field', ['en' => 'Colour', 'de' => 'Farbe']);
};
{{-- views/field.blade.php --}}
<input type="color" id="f-{{ $key }}" wire:model="{{ $model }}" @disabled($disabled)>

The value is saved in the entry's front matter like any other field.

Useful Wake APIs for plugins

The methods of Wake\Plugins\Plugin are the promise Wake makes to plugin authors. Beyond that, these classes are the ones plugins can rely on. They are also what themes use, so they change only with a note in the changelog.

Reading content

use Wake\Content\Models\Page;
use Wake\Content\Models\Post;
use Wake\Content\Query;
use Wake\Content\Site;

$latest = Post::published()->tag('events')->latest()->limit(3)->get();
$page = Page::find('pages/about');                      // by folder, or null
$events = (new Query('post'))->published()->category('events')->upcoming()->get();
$site = app(Site::class);                                // the same as $site in templates

A query reads Wake's index from memory. It offers published(), drafts(), scheduled(), tag(), category(), upcoming(), past(), childrenOf(), listed(), where(), latest(), oldest(), orderBy(), limit(), offset(), in($language), get(), first(), find(), count(), exists() and paginate(). Entries offer id(), title(), url(), date(), get('field'), content(), excerpt(), image(), file(), category(), tags(), parent(), children() and more; see templates for the full list.

Changing content

use Wake\Content\Writer;

$writer = app(Writer::class);
$entry = $writer->create('post', ['title' => 'Open on Sunday', 'status' => 'draft'], 'We open at noon.');
$writer->update($entry->id(), 'en', ['title' => 'Open this Sunday']);
$writer->publish($entry->id(), 'en');

The writer keeps versions, the history and redirects, moves deleted entries to the trash and clears the page cache. Do not edit files in content/ directly from a plugin.

Files, paths and settings

Class Use
Wake\Support\Files::write($path, $contents) write a file atomically (never file_put_contents())
Wake\Support\Files::lock($name, $callback) run code while holding a lock, e.g. when two requests could write the same file
Wake\Support\Paths::content(), settings(), themes(), plugins(), data(), theme(), root() folders of the site; data you keep belongs in Paths::data('your-plugin/…'), caches in storage_path('your-plugin/…') (temp/)
config('wake.…') every setting, e.g. config('wake.closed')
$plugin->option($key) your plugin's settings from the panel
app(Wake\Cache\PageCache::class)->flush() throw away all cached pages, e.g. when your output changes

Events

Event Fired when
Wake\Content\ContentChanged the index noticed new, changed or deleted content (from the panel, an assistant, FTP or Git); Wake clears the page cache right after

Laravel's own events work as well, for example Illuminate\Auth\Events\Login when someone signs in to the panel.

When something goes wrong

If plugin.php throws an exception or has a syntax error, Wake skips the plugin, writes the error to temp/logs/laravel.log and shows it in the panel. The rest of the site keeps working. Switching the plugin off in the panel stops Wake from loading it at all.

Plugins that are switched off stay in their folder. To remove a plugin, delete its folder.

source: docs/reference/plugins.md