- generator in Definition::addItemToSet order: 50000 URL cap, lastmod from the record or the updates manifest (regular file, 5 MiB cap, fail safe), BeforeAddItem (skip) and AddItem (extra XML) festival events, tier or item priority, per-alternate expansion of list type children - priority tiers from golem15.sitemap.priority_tiers, compiled at boot, defaults equal to the PHP getPriorityForUrl rules - frontend_url rewrite of loc, xhtml:link href and the stylesheet href; GET /sitemap.xsl serves the PHP stylesheet byte for byte - golem15_sitemap_settings singleton and Settings model - sitemap:import (PHP ImportCommand) and sitemap:import-winter with the read-only Winter export SQL - urlto and full goldens generated from PHP; TestSitemapImportSmoke
99 lines
3.3 KiB
Go
99 lines
3.3 KiB
Go
package classes
|
|
|
|
import (
|
|
"fmt"
|
|
"regexp"
|
|
"slices"
|
|
"strings"
|
|
|
|
"git.golem15.com/golem15/sm-sitemap-plugin/models"
|
|
"git.golem15.com/golem15/summercms/modules/compass"
|
|
)
|
|
|
|
// TiersConfigKey is the config key of the priority tier rules. The plugin's
|
|
// config/config.yaml ships the rules of the PHP getPriorityForUrl; a host
|
|
// replaces the whole list in its own config.
|
|
const TiersConfigKey = "golem15.sitemap.priority_tiers"
|
|
|
|
// TierConfig is one configured tier rule: Setting names the priority setting
|
|
// whose value applies (priority_homepage, priority_services, priority_blog,
|
|
// priority_categories or priority_legal), and exactly one of Exact (paths
|
|
// that match as they are) and Pattern (a regular expression, RE2 syntax)
|
|
// selects the URLs.
|
|
type TierConfig struct {
|
|
Setting string `koanf:"setting"`
|
|
Exact []string `koanf:"exact"`
|
|
Pattern string `koanf:"pattern"`
|
|
}
|
|
|
|
// TierRule is a compiled tier rule.
|
|
type TierRule struct {
|
|
Setting string
|
|
Exact []string
|
|
Pattern *regexp.Regexp
|
|
}
|
|
|
|
// Tiers is the ordered tier rule list; the first matching rule wins.
|
|
type Tiers []TierRule
|
|
|
|
// CompileTiers checks and compiles tier rules. Every error names the rule:
|
|
// an unknown setting, neither or both of exact and pattern, or a pattern
|
|
// that does not compile.
|
|
func CompileTiers(rules []TierConfig) (Tiers, error) {
|
|
out := make(Tiers, 0, len(rules))
|
|
for i, r := range rules {
|
|
if _, ok := models.PriorityDefaults[r.Setting]; !ok {
|
|
return nil, fmt.Errorf("%s[%d]: setting %q is not one of priority_homepage, priority_services, priority_blog, priority_categories, priority_legal", TiersConfigKey, i, r.Setting)
|
|
}
|
|
hasPattern := strings.TrimSpace(r.Pattern) != ""
|
|
if hasPattern == (len(r.Exact) > 0) {
|
|
return nil, fmt.Errorf("%s[%d]: set exactly one of exact and pattern", TiersConfigKey, i)
|
|
}
|
|
rule := TierRule{Setting: r.Setting, Exact: append([]string(nil), r.Exact...)}
|
|
if hasPattern {
|
|
re, err := regexp.Compile(r.Pattern)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("%s[%d]: pattern: %w", TiersConfigKey, i, err)
|
|
}
|
|
rule.Pattern = re
|
|
}
|
|
out = append(out, rule)
|
|
}
|
|
return out, nil
|
|
}
|
|
|
|
// LoadTiers reads and compiles the rules at TiersConfigKey; an absent key
|
|
// is no rules.
|
|
func LoadTiers(cfg *compass.Config) (Tiers, error) {
|
|
if cfg == nil || !cfg.Has(TiersConfigKey) {
|
|
return Tiers{}, nil
|
|
}
|
|
var rules []TierConfig
|
|
if err := cfg.LoadSection(TiersConfigKey, &rules); err != nil {
|
|
return nil, fmt.Errorf("%s must be a list of {setting, exact} or {setting, pattern} rules: %w", TiersConfigKey, err)
|
|
}
|
|
return CompileTiers(rules)
|
|
}
|
|
|
|
// Priority is PHP getPriorityForUrl with configured rules: when the
|
|
// settings enable tiers, the URL's path (parse_url's, "/" when it has none)
|
|
// right-trimmed of slashes is matched against the rules in order and the
|
|
// first match's setting value is returned. ok is false when tiers are off or
|
|
// no rule matches, so the item's own priority applies.
|
|
func (t Tiers) Priority(url string, s models.Settings) (string, bool) {
|
|
if !s.EnablePriorityTiers {
|
|
return "", false
|
|
}
|
|
path := URLPath(url)
|
|
if path == "" {
|
|
path = "/"
|
|
}
|
|
path = strings.TrimRight(path, "/")
|
|
for _, r := range t {
|
|
if r.Pattern != nil && r.Pattern.MatchString(path) || r.Pattern == nil && slices.Contains(r.Exact, path) {
|
|
return s.Priority(r.Setting), true
|
|
}
|
|
}
|
|
return "", false
|
|
}
|