Files
sm-seo-plugin/listeners.go
Jakub Zych 327925c43e feat: address-change redirects for writes outside the admin
- the RecordSaved listener re-describes an updated record of a content
  type after commit and applies AddressChanged per changed locale, so
  Journal's API, MCP and imports get the 301 too; an admin save that
  already applied it changes nothing more
2026-10-27 11:37:20 +01:00

72 lines
2.1 KiB
Go

package seo
import (
"context"
"git.golem15.com/golem15/sm-seo-plugin/classes"
"git.golem15.com/golem15/summercms/modules/backpack"
"git.golem15.com/golem15/summercms/modules/pact"
"gorm.io/gorm"
)
// listen subscribes to the framework's record events (D-92): a deleted
// record of a content type loses its SEO entries (kept in history), a
// created one gets its address snapshot, and an updated one whose address
// changed gets its 301 after commit (D-18), whoever wrote it: Journal's
// API, MCP, an import, or an admin form whose SEO tab the administrator
// does not see. Listener errors are logged by the bus and never fail the
// write that fired them.
func (p *Plugin) listen(app *backpack.App) {
if app == nil || app.Events == nil {
return
}
app.Events.Listen("golem15.seo", func(ctx context.Context, ev *pact.RecordDeleted) error {
svc := p.Service()
if svc == nil {
return nil
}
return svc.RecordDeleted(ctx, ev)
})
app.Events.Listen("golem15.seo", func(ctx context.Context, ev *pact.RecordSaved) error {
svc := p.Service()
if svc == nil || ev == nil {
return nil
}
if ev.Created {
return svc.RecordCreated(ctx, ev)
}
return p.addressesAfterSave(ctx, svc, ev.Model, ev.ID)
})
}
// addressesAfterSave re-describes a saved record of a content type in a
// transaction of its own and applies AddressChanged to every locale whose
// address differs from its entry's URL. After an admin save whose SEO tab
// already did this, nothing differs and nothing happens.
func (p *Plugin) addressesAfterSave(ctx context.Context, svc *classes.Service, model, id string) error {
reg := svc.Registry()
if reg == nil {
return nil
}
typ, ok := reg.ContentTypeForModel(model)
if !ok || typ.Content == nil {
return nil
}
db, err := svc.DB(ctx)
if err != nil {
return err
}
return db.Transaction(func(tx *gorm.DB) error {
changes, err := svc.AddressChanges(ctx, tx, typ, id)
if err != nil {
return err
}
for _, change := range changes {
if _, err := svc.AddressChanged(ctx, tx, change); err != nil {
return err
}
}
return nil
})
}