From 71e0af4196f1f21e4430ebab00d5ca65bdefcd98 Mon Sep 17 00:00:00 2001 From: Jonathan Miller Date: Mon, 25 May 2026 21:43:55 -0500 Subject: [PATCH 1/2] feat: three-column branding layout with identity settings Branding page now has: - Identity section: App Name, Description, Support URL, Author (saved to app.ini, applied in-memory immediately) - Images section: three-column table (Setting | Upload | Preview) for Nav Icon, Login Logo, and Favicon Co-Authored-By: Claude Opus 4.6 (1M context) --- routers/web/admin/branding.go | 59 +++++++++++-- routers/web/web.go | 1 + templates/admin/branding.tmpl | 155 +++++++++++++++++++++------------- 3 files changed, 152 insertions(+), 63 deletions(-) diff --git a/routers/web/admin/branding.go b/routers/web/admin/branding.go index 59acfc3c6b..e47b59d581 100644 --- a/routers/web/admin/branding.go +++ b/routers/web/admin/branding.go @@ -32,9 +32,62 @@ func Branding(ctx *context.Context) { ctx.Data["HasLogo"] = fileExists(filepath.Join(imgDir, "logo.png")) ctx.Data["HasFavicon"] = fileExists(filepath.Join(imgDir, "favicon.png")) + ctx.Data["MetaDescription"] = setting.UI.Meta.Description + ctx.Data["MetaAuthor"] = setting.UI.Meta.Author + ctx.Data["SupportURL"] = setting.SupportURL + ctx.HTML(http.StatusOK, tplBranding) } +// BrandingSettings handles the text branding form submission. +func BrandingSettings(ctx *context.Context) { + appName := ctx.FormString("app_name") + description := ctx.FormString("description") + supportURL := ctx.FormString("support_url") + author := ctx.FormString("author") + + // Update in-memory settings + if appName != "" { + setting.AppName = appName + } + if description != "" { + setting.UI.Meta.Description = description + } + setting.SupportURL = supportURL + if author != "" { + setting.UI.Meta.Author = author + } + + // Persist to app.ini + cfg, err := setting.NewConfigProviderFromFile(setting.CustomConf) + if err != nil { + ctx.Flash.Error("Failed to load config: " + err.Error()) + ctx.Redirect(setting.AppSubURL + "/-/admin/branding") + return + } + + if appName != "" { + cfg.Section("").Key("APP_NAME").SetValue(appName) + } + if description != "" { + cfg.Section("ui.meta").Key("DESCRIPTION").SetValue(description) + } + cfg.Section("").Key("SUPPORT_URL").SetValue(supportURL) + if author != "" { + cfg.Section("ui.meta").Key("AUTHOR").SetValue(author) + } + + if err := cfg.SaveTo(setting.CustomConf); err != nil { + ctx.Flash.Error("Failed to save config: " + err.Error()) + log.Error("SaveTo %s: %v", setting.CustomConf, err) + } else { + ctx.Flash.Success("Branding settings saved") + log.Info("Branding settings updated: AppName=%s, Author=%s", appName, author) + } + + ctx.Redirect(setting.AppSubURL + "/-/admin/branding") +} + // BrandingUpload handles branding image uploads. func BrandingUpload(ctx *context.Context) { imageType := ctx.FormString("type") @@ -66,14 +119,12 @@ func BrandingUpload(ctx *context.Context) { } defer file.Close() - // Validate file size (max 2MB) if header.Size > 2*1024*1024 { ctx.Flash.Error("File too large (max 2MB)") ctx.Redirect(setting.AppSubURL + "/-/admin/branding") return } - // Ensure the custom image directory exists imgDir := brandingImageDir() if err := os.MkdirAll(imgDir, 0o755); err != nil { ctx.Flash.Error("Failed to create image directory") @@ -82,7 +133,6 @@ func BrandingUpload(ctx *context.Context) { return } - // Write the file destPath := filepath.Join(imgDir, filename) dest, err := os.Create(destPath) if err != nil { @@ -100,11 +150,10 @@ func BrandingUpload(ctx *context.Context) { return } - // Also remove SVG override if present (PNG should take priority) + // Remove SVG override if present svgPath := filepath.Join(imgDir, filename[:len(filename)-4]+".svg") if fileExists(svgPath) { os.Remove(svgPath) - log.Info("Removed SVG override: %s", svgPath) } ctx.Flash.Success("Branding image updated: " + imageType) diff --git a/routers/web/web.go b/routers/web/web.go index 4b24034a1c..ff6f18a99a 100644 --- a/routers/web/web.go +++ b/routers/web/web.go @@ -768,6 +768,7 @@ func registerWebRoutes(m *web.Router, webAuth *AuthMiddleware) { m.Group("/branding", func() { m.Get("", admin.Branding) m.Post("/upload", admin.BrandingUpload) + m.Post("/settings", admin.BrandingSettings) }) m.Group("/monitor", func() { diff --git a/templates/admin/branding.tmpl b/templates/admin/branding.tmpl index 10f3f2b707..cafc42d3ef 100644 --- a/templates/admin/branding.tmpl +++ b/templates/admin/branding.tmpl @@ -4,67 +4,106 @@ {{svg "octicon-paintbrush" 16}} Branding
-

Upload custom branding images. Changes take effect immediately.

- -
-
- -
- Nav Icon -
Top-left corner, 30x30px recommended
-
- {{if .HasNavIcon}}Custom{{else}}Default{{end}} -
-
- {{.CsrfTokenHtml}} - -
- - -
-
-
+ +
Identity
+
+ {{.CsrfTokenHtml}} + + + + + + + + + + + + + + + + + + + +
Application Name
Shown in page titles, emails, and footer
Description
Meta description for SEO and social sharing
Support URL
Link shown on error pages and help menus
Author
Meta author tag
+ +
- -
-
- -
- Login Logo -
Login page and homepage, wide format recommended
-
- {{if .HasLogo}}Custom{{else}}Default{{end}} -
-
- {{.CsrfTokenHtml}} - -
- - -
-
-
+
- -
-
- -
- Favicon -
Browser tab icon, 256x256px recommended
-
- {{if .HasFavicon}}Custom{{else}}Default{{end}} -
-
- {{.CsrfTokenHtml}} - -
- - -
-
-
+ +
Images
+

Changes take effect immediately.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
SettingUploadPreview
+ Nav Icon {{if .HasNavIcon}}Custom{{else}}Default{{end}} +
Top-left corner across all pages. Square, 30x30px.
+
+
+ {{.CsrfTokenHtml}} + + +
+
+
+ +
+ Login Logo {{if .HasLogo}}Custom{{else}}Default{{end}} +
Login page and homepage. Wide format, max 220px display.
+
+
+ {{.CsrfTokenHtml}} + + +
+
+
+ +
+ Favicon {{if .HasFavicon}}Custom{{else}}Default{{end}} +
Browser tab and PWA app icon. Square, 256x256px.
+
+
+ {{.CsrfTokenHtml}} + + +
+
+
+ +
{{template "admin/layout_footer" .}} -- 2.52.0 From b64ed1972ae4a37f517c553284d8c17bf1c1ba94 Mon Sep 17 00:00:00 2001 From: Jonathan Miller Date: Mon, 25 May 2026 21:48:05 -0500 Subject: [PATCH 2/2] feat: add Help URL and Support URL to branding settings - Help URL: knowledge base / documentation link - Support URL: ticket system / email for user support Both saved to app.ini and applied in-memory immediately. Co-Authored-By: Claude Opus 4.6 (1M context) --- routers/web/admin/branding.go | 4 ++++ templates/admin/branding.tmpl | 6 +++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/routers/web/admin/branding.go b/routers/web/admin/branding.go index e47b59d581..b4d5affb1c 100644 --- a/routers/web/admin/branding.go +++ b/routers/web/admin/branding.go @@ -34,6 +34,7 @@ func Branding(ctx *context.Context) { ctx.Data["MetaDescription"] = setting.UI.Meta.Description ctx.Data["MetaAuthor"] = setting.UI.Meta.Author + ctx.Data["HelpURL"] = setting.HelpURL ctx.Data["SupportURL"] = setting.SupportURL ctx.HTML(http.StatusOK, tplBranding) @@ -43,6 +44,7 @@ func Branding(ctx *context.Context) { func BrandingSettings(ctx *context.Context) { appName := ctx.FormString("app_name") description := ctx.FormString("description") + helpURL := ctx.FormString("help_url") supportURL := ctx.FormString("support_url") author := ctx.FormString("author") @@ -53,6 +55,7 @@ func BrandingSettings(ctx *context.Context) { if description != "" { setting.UI.Meta.Description = description } + setting.HelpURL = helpURL setting.SupportURL = supportURL if author != "" { setting.UI.Meta.Author = author @@ -72,6 +75,7 @@ func BrandingSettings(ctx *context.Context) { if description != "" { cfg.Section("ui.meta").Key("DESCRIPTION").SetValue(description) } + cfg.Section("").Key("HELP_URL").SetValue(helpURL) cfg.Section("").Key("SUPPORT_URL").SetValue(supportURL) if author != "" { cfg.Section("ui.meta").Key("AUTHOR").SetValue(author) diff --git a/templates/admin/branding.tmpl b/templates/admin/branding.tmpl index cafc42d3ef..d1010ca31d 100644 --- a/templates/admin/branding.tmpl +++ b/templates/admin/branding.tmpl @@ -20,7 +20,11 @@ - Support URL
Link shown on error pages and help menus
+ Help URL
Knowledge base or documentation link shown in help menus
+ + + + Support URL
Ticket system, email, or contact page for user support requests
-- 2.52.0