forgejo/modules/templates/mailer.go
mirko f015846c11 Add slogan config (#3752)
This is a PR for #3616

Currently added a new optional config `SLOGAN`  in ini file. When this config is set title page is modified in APP_NAME [ - SLOGAN]

Example in image below

![Selezione_075.png](/attachments/7a72171e-e730-4e57-8c97-ffc94258e00f)

Add the new config value in the admin settings page (readonly)

![Screenshot 2024-05-13 at 18-04-13 My Forgejo.png](/attachments/dad00fc2-29fa-4371-a7b9-5233eadeac13)

## TODO

* [x] Add the possibility to add the `SLOGAN` config from the installation form
* [ ] Update https://forgejo.org/docs/next/admin/config-cheat-sheet

Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/3752
Reviewed-by: 0ko <0ko@noreply.codeberg.org>
Reviewed-by: Earl Warren <earl-warren@noreply.codeberg.org>
Co-authored-by: mirko <mirko.perillo@gmail.com>
Co-committed-by: mirko <mirko.perillo@gmail.com>
2024-06-07 17:12:48 +00:00

111 lines
3.1 KiB
Go

// Copyright 2022 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package templates
import (
"context"
"fmt"
"html/template"
"regexp"
"strings"
texttmpl "text/template"
"code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
)
var mailSubjectSplit = regexp.MustCompile(`(?m)^-{3,}\s*$`)
// mailSubjectTextFuncMap returns functions for injecting to text templates, it's only used for mail subject
func mailSubjectTextFuncMap() texttmpl.FuncMap {
return texttmpl.FuncMap{
"dict": dict,
"Eval": Eval,
"EllipsisString": base.EllipsisString,
"AppName": func() string {
return setting.AppName
},
"AppSlogan": func() string {
return setting.AppSlogan
},
"AppDisplayName": func() string {
return setting.AppDisplayName
},
"AppDomain": func() string { // documented in mail-templates.md
return setting.Domain
},
}
}
func buildSubjectBodyTemplate(stpl *texttmpl.Template, btpl *template.Template, name string, content []byte) error {
// Split template into subject and body
var subjectContent []byte
bodyContent := content
loc := mailSubjectSplit.FindIndex(content)
if loc != nil {
subjectContent = content[0:loc[0]]
bodyContent = content[loc[1]:]
}
if _, err := stpl.New(name).Parse(string(subjectContent)); err != nil {
return fmt.Errorf("failed to parse template [%s/subject]: %w", name, err)
}
if _, err := btpl.New(name).Parse(string(bodyContent)); err != nil {
return fmt.Errorf("failed to parse template [%s/body]: %w", name, err)
}
return nil
}
// Mailer provides the templates required for sending notification mails.
func Mailer(ctx context.Context) (*texttmpl.Template, *template.Template) {
subjectTemplates := texttmpl.New("")
bodyTemplates := template.New("")
subjectTemplates.Funcs(mailSubjectTextFuncMap())
bodyTemplates.Funcs(NewFuncMap())
assetFS := AssetFS()
refreshTemplates := func(firstRun bool) {
if !firstRun {
log.Trace("Reloading mail templates")
}
assetPaths, err := ListMailTemplateAssetNames(assetFS)
if err != nil {
log.Error("Failed to list mail templates: %v", err)
return
}
for _, assetPath := range assetPaths {
content, layerName, err := assetFS.ReadLayeredFile(assetPath)
if err != nil {
log.Warn("Failed to read mail template %s by %s: %v", assetPath, layerName, err)
continue
}
tmplName := strings.TrimPrefix(strings.TrimSuffix(assetPath, ".tmpl"), "mail/")
if firstRun {
log.Trace("Adding mail template %s: %s by %s", tmplName, assetPath, layerName)
}
if err = buildSubjectBodyTemplate(subjectTemplates, bodyTemplates, tmplName, content); err != nil {
if firstRun {
log.Fatal("Failed to parse mail template, err: %v", err)
}
log.Error("Failed to parse mail template, err: %v", err)
}
}
}
refreshTemplates(true)
if !setting.IsProd {
// Now subjectTemplates and bodyTemplates are both synchronized
// thus it is safe to call refresh from a different goroutine
go assetFS.WatchLocalChanges(ctx, func() {
refreshTemplates(false)
})
}
return subjectTemplates, bodyTemplates
}