Files
sm-seo-plugin/plugin.go
Jakub Zych 17b810dfde feat: sitemap filter excludes noindex, canonicalised and redirected URLs (tracer)
- classes.SitemapFilter implements the sitemap plugin's contract.Filter on
  the production values: redirected, noindex and canonicalised addresses
  leave the sitemaps with their reason; staging lists the same set
- the plugin requires golem15.sitemap and publishes the filter in Register
- go.mod requires sm-sitemap-plugin, go.work mounts the sibling checkout
2026-11-04 13:02:39 +01:00

101 lines
3.2 KiB
Go

// Package seo is the golem15.seo plugin: per-record and per-URL SEO for
// every content type registered in signpost, global SEO settings, history,
// and the effective SEO of a URL for the frontend. Content plugins never
// import it: they register a content descriptor (pact.ContentDescriptor) on
// their menu item type, and this plugin adds an SEO tab to their admin form
// through a form extension and stores the values in its own tables.
package seo
import (
"embed"
"fmt"
"io/fs"
"git.golem15.com/golem15/sm-seo-plugin/classes"
"git.golem15.com/golem15/sm-seo-plugin/models"
"git.golem15.com/golem15/sm-seo-plugin/updates"
"git.golem15.com/golem15/sm-sitemap-plugin/contract"
"git.golem15.com/golem15/summercms/modules/backpack"
"git.golem15.com/golem15/summercms/modules/pact"
"git.golem15.com/golem15/summercms/modules/party"
"git.golem15.com/golem15/summercms/modules/surf"
"github.com/go-gormigrate/gormigrate/v2"
)
var (
_ party.Plugin = (*Plugin)(nil)
_ pact.HasConfig = (*Plugin)(nil)
_ pact.HasMigrations = (*Plugin)(nil)
_ pact.HasModels = (*Plugin)(nil)
_ pact.HasRoutes = (*Plugin)(nil)
_ pact.HasAdminActionExtensions = (*Plugin)(nil)
_ pact.HasLang = (*Plugin)(nil)
_ surf.BucketProvider = (*Plugin)(nil)
)
//go:embed config
var configFS embed.FS
//go:embed lang
var langFS embed.FS
// Plugin is the golem15.seo plugin.
type Plugin struct {
app *backpack.App
service *classes.Service
}
func (p *Plugin) ID() string { return "golem15.seo" }
// SitemapPluginID is the sitemap plugin the SEO plugin requires: it
// decides which addresses the sitemaps list (D-95). Content types are still
// found through signpost, never by plugin ID.
const SitemapPluginID = "golem15.sitemap"
// Requires names the sitemap plugin, whose Filter seam the SEO plugin
// implements (D-95).
func (p *Plugin) Requires() []string { return []string{SitemapPluginID} }
// Register publishes the sitemap filter (classes.SitemapFilter): noindex,
// canonicalised and redirected addresses leave the sitemaps (D-95, D-24).
// It reads the service lazily, since Boot builds the service.
func (p *Plugin) Register(app *backpack.App) error {
p.app = app
return app.Publish[contract.Filter](classes.SitemapFilter{Service: p.Service})
}
// Boot builds the service on the application's database, signpost registry
// and config, and subscribes to the record events.
func (p *Plugin) Boot(app *backpack.App) error {
p.app = app
svc, err := classes.NewService(app)
if err != nil {
return fmt.Errorf("golem15.seo: %w", err)
}
p.service = svc
p.listen(app)
return nil
}
// Service returns the booted service, nil before Boot.
func (p *Plugin) Service() *classes.Service {
if p == nil {
return nil
}
return p.service
}
// ConfigFS ships config/config.yaml.
func (p *Plugin) ConfigFS() fs.FS { return configFS }
// LangFS is the embedded English and Polish phrasebooks.
func (p *Plugin) LangFS() fs.FS { return langFS }
func (p *Plugin) Migrations() []*gormigrate.Migration { return updates.All() }
func (p *Plugin) Models() []any { return models.All() }
func init() {
party.Register(&Plugin{})
}