diff --git a/routers/web/admin/branding.go b/routers/web/admin/branding.go index 59acfc3c6b..b4d5affb1c 100644 --- a/routers/web/admin/branding.go +++ b/routers/web/admin/branding.go @@ -32,9 +32,66 @@ 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["HelpURL"] = setting.HelpURL + 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") + helpURL := ctx.FormString("help_url") + 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.HelpURL = helpURL + 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("HELP_URL").SetValue(helpURL) + 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 +123,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 +137,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 +154,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..d1010ca31d 100644 --- a/templates/admin/branding.tmpl +++ b/templates/admin/branding.tmpl @@ -4,67 +4,110 @@ {{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
Help URL
Knowledge base or documentation link shown in help menus
Support URL
Ticket system, email, or contact page for user support requests
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" .}}