Introduce a commercial license option alongside AGPL-3.0-only, require a CLA for contributors, and document the terms in COMMERCIAL.md and NOTICE. Add a script to stamp SPDX headers on Go files and apply it across the tree.
82 lines
2.5 KiB
Go
82 lines
2.5 KiB
Go
// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Commercial
|
|
// SPDX-FileCopyrightText: 2025-2026 Christian Galo
|
|
|
|
package server
|
|
|
|
import (
|
|
"reflect"
|
|
"strings"
|
|
)
|
|
|
|
// pageTitle derives the operator shell's document title from the page the
|
|
// shell is about to render, so a browser tab and a history entry name the
|
|
// page rather than repeating "Operator overview" on every operator URL
|
|
// (maintainer, 2026-09-03, during the forms-library Lighthouse pass). The
|
|
// title is the same string the body renders through the pageHeader part,
|
|
// read from the body data's Header, WrapperHeader or LandingHeader method
|
|
// or its Header field, so the two cannot disagree; the landing page, whose
|
|
// header the shell itself renders, uses the shell's LandingHeader; a body
|
|
// whose data carries no header falls back to its sidebar capability's
|
|
// label.
|
|
func pageTitle(page OperatorPageData) string {
|
|
if t := headerTitle(page.BodyData); t != "" {
|
|
return t
|
|
}
|
|
if page.BodyTemplate == "" {
|
|
return page.LandingHeader().Title
|
|
}
|
|
return capabilityLabel(page.ActiveCapability)
|
|
}
|
|
|
|
// PageTitle is pageTitle for the template sets outside this package that
|
|
// parse the same operator shell: an integration's own operator handler
|
|
// (which renders operator.html with a server.OperatorPageData built by
|
|
// BuildOperatorPageData) and the external render tests. One function, one
|
|
// title rule, wherever the shell is parsed.
|
|
func PageTitle(page OperatorPageData) string { return pageTitle(page) }
|
|
|
|
// headerTitle returns the title of the PageHeader a body data value exposes,
|
|
// or "" when it exposes none.
|
|
func headerTitle(v any) string {
|
|
if v == nil {
|
|
return ""
|
|
}
|
|
rv := reflect.ValueOf(v)
|
|
if !rv.IsValid() {
|
|
return ""
|
|
}
|
|
for _, name := range []string{"Header", "WrapperHeader", "LandingHeader"} {
|
|
m := rv.MethodByName(name)
|
|
if !m.IsValid() || m.Type().NumIn() != 0 || m.Type().NumOut() != 1 {
|
|
continue
|
|
}
|
|
if h, ok := m.Call(nil)[0].Interface().(PageHeader); ok && h.Title != "" {
|
|
return h.Title
|
|
}
|
|
}
|
|
if rv.Kind() == reflect.Pointer {
|
|
if rv.IsNil() {
|
|
return ""
|
|
}
|
|
rv = rv.Elem()
|
|
}
|
|
if rv.Kind() == reflect.Struct {
|
|
if f := rv.FieldByName("Header"); f.IsValid() {
|
|
if h, ok := f.Interface().(PageHeader); ok {
|
|
return h.Title
|
|
}
|
|
}
|
|
}
|
|
return ""
|
|
}
|
|
|
|
// capabilityLabel turns a sidebar capability key ("entitlement-sets") into
|
|
// the label a person reads ("Entitlement sets").
|
|
func capabilityLabel(key string) string {
|
|
key = strings.TrimSpace(strings.ReplaceAll(key, "-", " "))
|
|
if key == "" {
|
|
return "Operator"
|
|
}
|
|
return strings.ToUpper(key[:1]) + key[1:]
|
|
}
|