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  }