--- title: Plugin registration description: "Declare a plugin: its ID, the party.Plugin lifecycle, the pact capability interfaces it opts into, its embedded files and the scaffolded layout." section: plugins order: 10 --- # Plugin registration Plugins are the foundation of every SummerCMS application. A plugin adds models, routes, admin screens, console commands, jobs and translations, and it can extend other plugins. This page covers how a plugin tells the framework what it contributes. ## Plugin identifiers Every plugin has an ID in `vendor.plugin` form: two lower-case parts, each starting with a letter and containing only letters and digits, such as `acme.blog`. The ID is how the manifest lists the plugin, how other plugins require it and how its configuration is namespaced (`acme.blog.posts_per_page`). WinterCMS writes the same identifier as `Acme.Blog`; in SummerCMS it is always lower case. The scaffolder derives the package and directory name from the second part, so `summer make:plugin acme.blog` creates `plugins/blog` with `package blog`. ## The plugin type A plugin is a Go type that implements `party.Plugin`. Its package registers it from `init` with `party.Register`, so importing the package is enough to make the plugin available; the generated `plugins.gen.go` does that import for every plugin in `summer.yaml`. | Method | Purpose | |--------|---------| | `party.Plugin.ID` | Returns the plugin ID. | | `party.Plugin.Requires` | Lists the IDs of plugins that must register and boot before this one, like `$require` in WinterCMS. | | `party.Plugin.Register` | Runs before any plugin boots. Publish services on the container here. | | `party.Plugin.Boot` | Runs after every plugin registered. Listen to events and use other plugins' services here. | Here is a complete plugin that also declares a backend permission: ```go src=modules/party/example_plugin_test.go package party_test import ( "git.golem15.com/golem15/summercms/modules/backpack" "git.golem15.com/golem15/summercms/modules/pact" ) // BlogPlugin is the acme.blog plugin: the Go form of a WinterCMS Plugin.php. // A real plugin package also registers it from init with // party.Register(&BlogPlugin{}). type BlogPlugin struct{} // The optional capabilities the plugin opts into, checked at compile time. var _ pact.HasPermissions = (*BlogPlugin)(nil) // ID is the plugin identifier in vendor.plugin form. func (p *BlogPlugin) ID() string { return "acme.blog" } // Requires lists the plugins that must register and boot first ($require). func (p *BlogPlugin) Requires() []string { return []string{"acme.user"} } // Register runs before any plugin boots: publish services here. func (p *BlogPlugin) Register(app *backpack.App) error { return nil } // Boot runs after every plugin registered: listen to events and look up // services other plugins published. func (p *BlogPlugin) Boot(app *backpack.App) error { return nil } // Permissions replaces registerPermissions(). func (p *BlogPlugin) Permissions() []pact.Permission { return []pact.Permission{ {Code: "acme.blog.access_posts", Tab: "Blog", Label: "Manage posts"}, } } ``` The `var _ pact.HasPermissions = (*BlogPlugin)(nil)` line is a compile-time check: if a method is missing or has the wrong signature, the build fails instead of the capability being silently ignored. Add one such line for every capability your plugin implements. ## Capability interfaces A WinterCMS plugin overrides `register*` methods of `PluginBase`. A SummerCMS plugin implements small interfaces from [pact](../../modules/pact/README.md) instead, and the framework discovers each one with a type assertion. | Interface | Contributes | |-----------|-------------| | `pact.HasRoutes` | HTTP routes, declared on a `pact.Router`. | | `pact.HasMiddleware`, `pact.HasMiddlewareFactories` | Named and parameterized route middleware. | | `pact.HasConfig` | Default configuration, merged under the plugin ID. | | `pact.HasMigrations` | An ordered set of database migrations. | | `pact.HasCommands` | Console commands for the application binary. | | `pact.HasJobs` | Background jobs. | | `pact.HasSchedule` | Console commands that run on a schedule; see [Scheduling](scheduling.md). | | `pact.HasLang`, `pact.HasLangOverrides` | Translations, and overrides of other namespaces. | | `pact.HasMailTemplates` | Mail templates and layouts. | | `pact.HasPermissions`, `pact.HasNavigation`, `pact.HasSettings` | Backend permissions, navigation and settings screens. | | `pact.HasAdminControllers` | Admin controllers built from `fields.yaml` and `columns.yaml`. | | `pact.HasModels` | The plugin's GORM models. No framework package reads it yet. | ## Embedded files Configuration defaults, translations and mail templates ship inside the binary through Go's `embed` package. The plugin returns an `fs.FS` for each: - `pact.HasConfig.ConfigFS` returns a tree with `config/config.yaml`. Its keys become `.`, and any other `config/.yaml` becomes `..`. The application's own `config/` directory and `SUMMER_` variables override them. - `pact.HasLang.LangFS` returns `lang//.yaml` files. - `pact.HasMailTemplates.MailTemplatesFS` returns the `views/mail` templates, and `pact.HasMailTemplates.MailTemplates` lists their names. ## The scaffolded layout `summer make:plugin acme.blog` writes a plugin that compiles and follows the WinterCMS directory layout, with each directory as a Go subpackage: ```text plugins/blog/ ├── go.mod the plugin module, requiring the framework ├── plugin.go the Plugin type, its capabilities and init registration ├── routes.go the Routes method ├── registry.gen.go generated lists of models, migrations, commands, jobs and admin controllers ├── classes/ services and hooks ├── config/config.yaml default configuration ├── console/ console commands ├── controllers/ HTTP handlers and admin controllers ├── jobs/ background jobs ├── lang/en/lang.yaml translations ├── middleware/ named middleware ├── models/ GORM models ├── updates/ migrations └── views/mail/ mail templates ``` The `make:` commands, such as `summer make:model acme.blog Post`, add files to these directories and regenerate `registry.gen.go`, so you do not edit that file by hand. The capability methods in `plugin.go` return the generated lists, so a new model, migration, command, job or admin controller is picked up without editing the plugin type.