# 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.

Source: https://featherpanel.com/docs/plugins/getting-started/

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](https://featherpanel.com/docs/plugins/) 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:

| Template | What you get |
| --- | --- |
| Empty | `conf.yml` and the basic directories. |
| Fresh | A clean structure with small examples. |
| Starter | Every example file: route, controller, model, migration, cron job, CLI command, event handler, frontend assets. |
| Theme pack | `Frontend/theme.json` and `theme.css`, see [Themes and UI packs](https://featherpanel.com/docs/plugins/themes/). |
| UI pack | `Frontend/ui.json` with a slot action, see [Themes and UI packs](https://featherpanel.com/docs/plugins/themes/). |

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

```text
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

| Part | Loaded by |
| --- | --- |
| Entry class | The plugin manager, which calls `processEvents()` once when the backend boots. See [worker mode](https://featherpanel.com/docs/plugins/middleware-and-hooks/#worker-mode). |
| `Routes/*.php` | The router, together with the core route files. See [Routes and permissions](https://featherpanel.com/docs/plugins/routes-and-permissions/). |
| `Migrations/*.sql` | The installer when the plugin is installed, and `php cli migrate`. See [Migrations and storage](https://featherpanel.com/docs/plugins/database/). |
| `Cron/*.php` | The cron runner on each run. Each file must hold the class `App\Addons\<identifier>\Cron\<FileName>` with a `run()` method. |
| `Commands/*.php` | The 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](https://featherpanel.com/docs/plugins/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](https://featherpanel.com/docs/panel/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](https://featherpanel.com/docs/plugins/middleware-and-hooks/#worker-mode).

[Build your first plugin](https://featherpanel.com/docs/plugins/quick-start/) walks through these steps with a small working plugin.

## Next steps

- [Manifest (conf.yml)](https://featherpanel.com/docs/plugins/manifest/)
- [Lifecycle and events](https://featherpanel.com/docs/plugins/lifecycle-and-events/)
- [Routes and permissions](https://featherpanel.com/docs/plugins/routes-and-permissions/)
- [Examples](https://featherpanel.com/docs/plugins/examples/)

## 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.
