View .md

Plugin structure

Create a FeatherPanel plugin with the built-in plugin manager, the files and folders a plugin can have, how they are loaded and how to work on one locally.

On this page

A plugin is a folder in backend/storage/addons/ named after its identifier. When the backend boots (once per worker in the Docker image, once per request in classic mode) it loads each plugin whose conf.yml is valid and whose entry class matches. The plugin overview covers installing plugins as an admin; this page is for writing one.

Create a plugin from a template

Turn on developer mode in the admin settings, then open Admin > Dev > Plugins and choose Create Plugin. You pick a name, an identifier, authors, flags, dependencies and one of five templates:

TemplateWhat you get
Emptyconf.yml and the basic directories.
FreshA clean structure with small examples.
StarterEvery example file: route, controller, model, migration, cron job, CLI command, event handler, frontend assets.
Theme packFrontend/theme.json and theme.css, see Themes and UI packs.
UI packFrontend/ui.json with a slot action, see Themes and UI packs.

The identifier must be unique and may only contain letters, digits and _. The hasEvents flag is always set, because the panel registers a plugin’s hooks through it.

Folder layout

storage/addons/myplugin/
├── conf.yml                  manifest, see the manifest page
├── MyPlugin.php              entry class (name = plugin.name)
├── Routes/                   PHP route files, loaded automatically
├── Controllers/              your controllers (App\Addons\myplugin\Controllers)
├── Chat/                     database models, by convention
├── Events/                   your event handler classes
├── Migrations/               .sql files, run on install and by the migrate command
├── Cron/                     jobs run by the cron runner
├── Commands/                 commands added to the panel CLI
├── Public/                   static files, exposed at /addons/myplugin/
└── Frontend/
    ├── sidebar.json          pages in the client, admin, server, VDS and WebSpace sidebars
    ├── widgets.json          widgets on panel pages
    ├── index.js, index.css   loaded on every panel page
    └── Components/           HTML for pages and widgets, served at /components/myplugin/

Only conf.yml and the entry class are required. Everything else is picked up when it exists.

How each part is loaded

PartLoaded by
Entry classThe plugin manager, which calls processEvents() once when the backend boots. See worker mode.
Routes/*.phpThe router, together with the core route files. See Routes and permissions.
Migrations/*.sqlThe installer when the plugin is installed, and php cli migrate. See Migrations and storage.
Cron/*.phpThe cron runner on each run. Each file must hold the class App\Addons\<identifier>\Cron\<FileName> with a run() method.
Commands/*.phpThe panel CLI. Each class implements App\Cli\CommandBuilder.
Frontend/The frontend, through /api/system/plugin-js, /api/system/plugin-css and the sidebar and widget endpoints. See Frontend.
Public/, Frontend/Components/Linked into public/addons/<identifier> and public/components/<identifier> on install. Resync on the plugin page recreates the links.

Develop locally

  1. Run the panel from source or with Docker, see Installation.
  2. Create the plugin from a template, or copy a folder into backend/storage/addons/. In the Docker setup that path is /var/www/html/storage/addons inside the backend container.
  3. If you copied files by hand, run php cli migrate for the migrations, and use Resync on the plugin page so Public/ and Frontend/Components/ are linked.
  4. Reload the panel. There is no build step on the panel side. In the Docker image the API runs in worker mode, so use Resync or restart the backend after changing PHP files, see worker mode.

Build your first plugin walks through these steps with a small working plugin.

Next steps

Frequently asked questions

Do I need developer mode to write plugins?

The Plugin Manager under Admin > Dev and plugin export both need developer mode, which you turn on in the admin settings. A plugin you copy into storage/addons by hand loads without it.

Which namespace do plugin classes use?

App\Addons\<identifier>. Composer maps App\Addons\ to backend/storage/addons/, so App\Addons\myplugin\Controllers\Foo lives in storage/addons/myplugin/Controllers/Foo.php.