feat: auto-links screen, keyphrase feed and MCP tools

- SEO › Auto-links (golem15.seo.autolinks, after Pages): list by phrase
  with locale, origin and active filters, the disabled row state, the
  form with its refusals, history through a field-less extension
- toolbar Add focus keyphrases (from_keyphrases) and
  POST /autolinks/from-keyphrases
- GET/POST /autolinks, PUT /autolinks/{id}, DELETE /autolinks and the
  tools seo_list_autolinks, seo_create_autolink, seo_update_autolink,
  seo_delete_autolinks, seo_autolinks_from_keyphrases
- the settings tile description and the permission comment name
  auto-links and the site audit
This commit is contained in:
Jakub Zych
2026-11-04 16:30:56 +01:00
parent 6203aec2bc
commit ab4e83416d
19 changed files with 852 additions and 13 deletions

View File

@@ -43,6 +43,7 @@ func (p *Plugin) AdminActionExtensions() []pact.AdminActionExtension {
}
out = append(out, pact.AdminActionExtension{Controller: controllers.RulesControllerID, Form: p.ruleHistory()})
out = append(out, pact.AdminActionExtension{Controller: controllers.RedirectsControllerID, Form: p.redirectChecks()})
out = append(out, pact.AdminActionExtension{Controller: controllers.AutolinksControllerID, Form: p.autolinkHistory()})
out = append(out, pact.AdminActionExtension{Controller: controllers.RobotsControllerID, Form: p.robotsForm()})
return out
}
@@ -116,6 +117,34 @@ func (p *Plugin) ruleHistory() *pact.AdminFormExtension {
}
}
// autolinkHistory is a field-less extension on the plugin's own Auto-links
// controller: its Save sees the save's changes inside the save
// transaction and records them in history, which purges the site's cached
// pages (deletes are recorded by the controller).
func (p *Plugin) autolinkHistory() *pact.AdminFormExtension {
return &pact.AdminFormExtension{
Permissions: []string{controllers.PermissionManage},
Save: func(ctx context.Context, in pact.AdminFormSave) (pact.AdminFormSaveResult, error) {
svc := p.Service()
if svc == nil {
return pact.AdminFormSaveResult{}, classes.ErrNotBooted
}
a, ok := in.Record.(*models.Autolink)
if !ok || a == nil {
return pact.AdminFormSaveResult{}, nil
}
before, after := classes.ChangesValues(in.Changes, in.Created)
return pact.AdminFormSaveResult{}, svc.Record(ctx, in.DB, classes.Change{
Kind: models.SubjectAutolink,
SubjectID: in.ID,
What: svc.Phrase("golem15.seo::lang.history.what_autolink", map[string]string{"phrase": a.Phrase}),
Before: before,
After: after,
})
},
}
}
func (p *Plugin) registry() *signpost.Registry {
if p == nil || p.app == nil {
return nil