feat: SEO change history

This commit is contained in:
2026-10-27 10:45:30 +01:00
parent 9f1082d62d
commit e8a6b9fc77
21 changed files with 1070 additions and 26 deletions

View File

@@ -108,18 +108,23 @@ func typeSettingsDocument(types []pact.MenuItemType) ([]byte, error) {
})
}
// typeSettingsTabs is the form extension on the SEO settings controller
// that adds the per-type tabs; nil when no content type is registered.
// typeSettingsTabs is the plugin's form extension on its own settings
// controller: it adds the per-type tabs (none when no content type is
// registered) and records every settings save in history.
func (p *Plugin) typeSettingsTabs(types []pact.MenuItemType) *pact.AdminFormExtension {
if len(types) == 0 {
return nil
}
doc, err := typeSettingsDocument(types)
if err != nil {
pluginLog.Error("golem15.seo: per-type settings tabs", "error", err)
return nil
var doc []byte
if len(types) > 0 {
var err error
if doc, err = typeSettingsDocument(types); err != nil {
pluginLog.Error("golem15.seo: per-type settings tabs", "error", err)
return nil
}
}
fields := typeSettingsFields(types)
labels := map[string]string{}
for _, typ := range types {
labels[typ.Key] = typ.Label
}
return &pact.AdminFormExtension{
FieldsYAML: doc,
Permissions: []string{controllers.PermissionManage},
@@ -139,7 +144,21 @@ func (p *Plugin) typeSettingsTabs(types []pact.MenuItemType) *pact.AdminFormExte
return out, nil
},
Save: func(ctx context.Context, in pact.AdminFormSave) (pact.AdminFormSaveResult, error) {
return pact.AdminFormSaveResult{}, p.saveTypeSettings(ctx, in, fields)
svc := p.Service()
if svc == nil {
return pact.AdminFormSaveResult{}, classes.ErrNotBooted
}
before, after := classes.ChangesValues(in.Changes, in.Created)
if err := svc.Record(ctx, in.DB, classes.Change{
Kind: models.SubjectSettings,
SubjectID: in.ID,
What: svc.Phrase("golem15.seo::lang.history.what_settings", nil),
Before: before,
After: after,
}); err != nil {
return pact.AdminFormSaveResult{}, err
}
return pact.AdminFormSaveResult{}, p.saveTypeSettings(ctx, in, fields, labels)
},
}
}
@@ -161,7 +180,7 @@ func typeSettingValue(row models.TypeSetting, column string) any {
// saveTypeSettings stores the submitted per-type values through the
// service; a template with an unknown placeholder refuses the save.
func (p *Plugin) saveTypeSettings(ctx context.Context, in pact.AdminFormSave, fields []typeField) error {
func (p *Plugin) saveTypeSettings(ctx context.Context, in pact.AdminFormSave, fields []typeField, labels map[string]string) error {
svc := p.Service()
if svc == nil {
return classes.ErrNotBooted
@@ -208,10 +227,23 @@ func (p *Plugin) saveTypeSettings(ctx context.Context, in pact.AdminFormSave, fi
if len(refusals) > 0 {
return refusals
}
for _, row := range changed {
for key, row := range changed {
old, ok := rows[key]
if !ok {
old = models.DefaultTypeSetting(key)
}
if err := svc.SaveTypeSetting(ctx, in.DB, row); err != nil {
return err
}
if err := svc.Record(ctx, in.DB, classes.Change{
Kind: models.SubjectSettings,
SubjectID: "type:" + key,
What: svc.Phrase("golem15.seo::lang.history.what_settings", nil) + " – " + svc.Phrase(labels[key], nil),
Before: classes.TypeSettingValues(old),
After: classes.TypeSettingValues(row),
}); err != nil {
return err
}
}
return nil
}