code.gitea.io/gitea@v1.19.3/modules/templates/mailer.go (about) 1 // Copyright 2022 The Gitea Authors. All rights reserved. 2 // SPDX-License-Identifier: MIT 3 4 package templates 5 6 import ( 7 "context" 8 "html/template" 9 "io/fs" 10 "os" 11 "strings" 12 texttmpl "text/template" 13 14 "code.gitea.io/gitea/modules/log" 15 "code.gitea.io/gitea/modules/setting" 16 "code.gitea.io/gitea/modules/util" 17 "code.gitea.io/gitea/modules/watcher" 18 ) 19 20 // Mailer provides the templates required for sending notification mails. 21 func Mailer(ctx context.Context) (*texttmpl.Template, *template.Template) { 22 for _, funcs := range NewTextFuncMap() { 23 subjectTemplates.Funcs(funcs) 24 } 25 for _, funcs := range NewFuncMap() { 26 bodyTemplates.Funcs(funcs) 27 } 28 29 refreshTemplates := func() { 30 for _, assetPath := range BuiltinAssetNames() { 31 if !strings.HasPrefix(assetPath, "mail/") { 32 continue 33 } 34 35 if !strings.HasSuffix(assetPath, ".tmpl") { 36 continue 37 } 38 39 content, err := BuiltinAsset(assetPath) 40 if err != nil { 41 log.Warn("Failed to read embedded %s template. %v", assetPath, err) 42 continue 43 } 44 45 assetName := strings.TrimPrefix(strings.TrimSuffix(assetPath, ".tmpl"), "mail/") 46 47 log.Trace("Adding built-in mailer template for %s", assetName) 48 buildSubjectBodyTemplate(subjectTemplates, 49 bodyTemplates, 50 assetName, 51 content) 52 } 53 54 if err := walkMailerTemplates(func(path, name string, d fs.DirEntry, err error) error { 55 if err != nil { 56 return err 57 } 58 if d.IsDir() { 59 return nil 60 } 61 62 content, err := os.ReadFile(path) 63 if err != nil { 64 log.Warn("Failed to read custom %s template. %v", path, err) 65 return nil 66 } 67 68 assetName := strings.TrimSuffix(name, ".tmpl") 69 assetName = util.PathJoinRelX(assetName) 70 log.Trace("Adding mailer template for %s from %q", assetName, path) 71 buildSubjectBodyTemplate(subjectTemplates, 72 bodyTemplates, 73 assetName, 74 content) 75 return nil 76 }); err != nil && !os.IsNotExist(err) { 77 log.Warn("Error whilst walking mailer templates directories. %v", err) 78 } 79 } 80 81 refreshTemplates() 82 83 if !setting.IsProd { 84 // Now subjectTemplates and bodyTemplates are both synchronized 85 // thus it is safe to call refresh from a different goroutine 86 watcher.CreateWatcher(ctx, "Mailer Templates", &watcher.CreateWatcherOpts{ 87 PathsCallback: walkMailerTemplates, 88 BetweenCallback: refreshTemplates, 89 }) 90 } 91 92 return subjectTemplates, bodyTemplates 93 }