Plugins

Plugin registration

Declare a plugin: its ID, the party.Plugin lifecycle, the pact capability interfaces it opts into, its embedded files and the scaffolded layout.

On this page

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:

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 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.
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 <plugin id>.<key>, and any other config/<name>.yaml becomes <plugin id>.<name>.<key>. The application's own config/ directory and SUMMER_ variables override them.
  • pact.HasLang.LangFS returns lang/<locale>/<group>.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:

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.