161 lines
5.0 KiB
Go
161 lines
5.0 KiB
Go
package controllers
|
|
|
|
import (
|
|
"context"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"git.golem15.com/golem15/sm-seo-plugin/classes"
|
|
"git.golem15.com/golem15/sm-seo-plugin/models"
|
|
"git.golem15.com/golem15/summercms/modules/backpack"
|
|
"git.golem15.com/golem15/summercms/modules/pact"
|
|
"gorm.io/gorm"
|
|
)
|
|
|
|
// rulesAdminController lists and edits URL rules (D-02, UI-SPEC C7).
|
|
type rulesAdminController struct {
|
|
app func() *backpack.App
|
|
}
|
|
|
|
func (rulesAdminController) ID() string { return RulesControllerID }
|
|
func (rulesAdminController) ModelName() string { return `Golem15\SEO\Models\Rule` }
|
|
func (rulesAdminController) ConfigDir() string { return "controllers/rules" }
|
|
func (rulesAdminController) NewRecord() any { return &models.Rule{} }
|
|
func (rulesAdminController) RequiredPermissions() []string {
|
|
return []string{PermissionManage}
|
|
}
|
|
|
|
// FormBeforeCreate trims the address, normalises the match and refuses an
|
|
// invalid or taken address.
|
|
func (c rulesAdminController) FormBeforeCreate(ctx context.Context, model any) error {
|
|
return c.check(ctx, model)
|
|
}
|
|
|
|
// FormBeforeUpdate checks an update like a create.
|
|
func (c rulesAdminController) FormBeforeUpdate(ctx context.Context, model any) error {
|
|
return c.check(ctx, model)
|
|
}
|
|
|
|
func (c rulesAdminController) check(ctx context.Context, model any) error {
|
|
r, ok := model.(*models.Rule)
|
|
if !ok || r == nil {
|
|
return nil
|
|
}
|
|
r.URL = strings.TrimSpace(r.URL)
|
|
r.Match = models.NormalizeMatch(r.Match)
|
|
db, err := database(ctx, c.app)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return problemsError(ctx, translator(c.app), r.Problems(db))
|
|
}
|
|
|
|
// FormAfterDelete records the deleted rule in history inside the delete's
|
|
// transaction, so its values can be restored.
|
|
func (c rulesAdminController) FormAfterDelete(ctx context.Context, model any) error {
|
|
r, ok := model.(*models.Rule)
|
|
if !ok || r == nil {
|
|
return nil
|
|
}
|
|
svc, err := classes.NewService(appOf(c.app))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
db, err := database(ctx, c.app)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return svc.Record(ctx, db, classes.Change{
|
|
Kind: models.SubjectRule,
|
|
SubjectID: strconv.FormatUint(uint64(r.ID), 10),
|
|
What: svc.Phrase("golem15.seo::lang.history.what_rule", map[string]string{"url": r.URL}),
|
|
Before: classes.RuleValues(*r),
|
|
})
|
|
}
|
|
|
|
// ListExtendRecords fills the overrides column: the translated labels of
|
|
// the properties each rule sets, comma-joined.
|
|
func (c rulesAdminController) ListExtendRecords(ctx context.Context, _ *gorm.DB, records []any) error {
|
|
tr := translator(c.app)
|
|
for _, record := range records {
|
|
r, ok := record.(*models.Rule)
|
|
if !ok || r == nil {
|
|
continue
|
|
}
|
|
var labels []string
|
|
for _, p := range models.RuleProperties {
|
|
if strings.TrimSpace(r.Value(p.Column)) != "" {
|
|
labels = append(labels, Phrase(ctx, tr, p.Label, nil))
|
|
}
|
|
}
|
|
r.Overrides = strings.Join(labels, ", ")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// service returns the SEO service of the booted app, nil before boot.
|
|
func (c rulesAdminController) service() *classes.Service {
|
|
svc, err := classes.NewService(appOf(c.app))
|
|
if err != nil {
|
|
return nil
|
|
}
|
|
return svc
|
|
}
|
|
|
|
// AdminJS loads the SEO element bundle on the rules screens (the result
|
|
// preview widget).
|
|
func (rulesAdminController) AdminJS() []string { return []string{"assets/dist/seo.js"} }
|
|
|
|
// AdminCSS loads the SEO element styles on the rules screens.
|
|
func (rulesAdminController) AdminCSS() []string { return []string{"assets/dist/seo.css"} }
|
|
|
|
// AdminActions registers the result preview's widget action (E3).
|
|
func (c rulesAdminController) AdminActions() []pact.AdminAction {
|
|
return []pact.AdminAction{classes.RuleSnippetAction(c.service, "golem15.seo::lang.snippet.action", PermissionManage)}
|
|
}
|
|
|
|
// PartialData serves P1 seo_status (canonical warnings) and P2
|
|
// seo_effective of a rule.
|
|
func (c rulesAdminController) PartialData(ctx context.Context, name string, record any) (any, error) {
|
|
svc := c.service()
|
|
r, ok := record.(*models.Rule)
|
|
if svc == nil || !ok || r == nil {
|
|
if name == "seo_effective" {
|
|
return classes.EffectiveView{}, nil
|
|
}
|
|
return classes.StatusView{}, nil
|
|
}
|
|
switch name {
|
|
case "seo_status":
|
|
return svc.RuleStatus(ctx, *r), nil
|
|
case "seo_effective":
|
|
return svc.RuleEffective(ctx, *r)
|
|
}
|
|
return nil, nil
|
|
}
|
|
|
|
// FilterOptions serves the match filter's two choices.
|
|
func (rulesAdminController) FilterOptions(scope string) []pact.Option {
|
|
if scope != models.FilterMatch {
|
|
return nil
|
|
}
|
|
return []pact.Option{
|
|
{Value: models.MatchExact, Label: "golem15.seo::lang.rules.match_exact"},
|
|
{Value: models.MatchPattern, Label: "golem15.seo::lang.rules.match_pattern"},
|
|
}
|
|
}
|
|
|
|
var (
|
|
_ pact.AdminController = rulesAdminController{}
|
|
_ pact.AdminPermissioned = rulesAdminController{}
|
|
_ pact.AdminRecordSource = rulesAdminController{}
|
|
_ pact.FormBeforeCreate = rulesAdminController{}
|
|
_ pact.FormBeforeUpdate = rulesAdminController{}
|
|
_ pact.ListExtendRecords = rulesAdminController{}
|
|
_ pact.FilterOptions = rulesAdminController{}
|
|
_ pact.FormAfterDelete = rulesAdminController{}
|
|
_ pact.HasAdminActions = rulesAdminController{}
|
|
_ pact.AdminPartialData = rulesAdminController{}
|
|
_ pact.AdminClientAssets = rulesAdminController{}
|
|
)
|