Files
member-console/internal/server/page_title.go
T
cgalo5758 88db730fcc Add dual licensing and SPDX headers
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.
2026-09-06 02:29:42 -05:00

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:]
}