github.com/adacta-ru/mattermost-server/v6@v6.0.0/utils/html.go (about) 1 // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. 2 // See LICENSE.txt for license information. 3 4 package utils 5 6 import ( 7 "bytes" 8 "errors" 9 "html/template" 10 "io" 11 "path/filepath" 12 "reflect" 13 "strings" 14 "sync/atomic" 15 16 "github.com/fsnotify/fsnotify" 17 "github.com/mattermost/go-i18n/i18n" 18 "github.com/adacta-ru/mattermost-server/v6/mlog" 19 "github.com/adacta-ru/mattermost-server/v6/utils/fileutils" 20 ) 21 22 type HTMLTemplateWatcher struct { 23 templates atomic.Value 24 stop chan struct{} 25 stopped chan struct{} 26 } 27 28 func NewHTMLTemplateWatcher(directory string) (*HTMLTemplateWatcher, error) { 29 templatesDir, _ := fileutils.FindDir(directory) 30 mlog.Debug("Parsing server templates", mlog.String("templates_directory", templatesDir)) 31 32 ret := &HTMLTemplateWatcher{ 33 stop: make(chan struct{}), 34 stopped: make(chan struct{}), 35 } 36 37 watcher, err := fsnotify.NewWatcher() 38 if err != nil { 39 return nil, err 40 } 41 42 if err = watcher.Add(templatesDir); err != nil { 43 return nil, err 44 } 45 46 htmlTemplates, err := template.ParseGlob(filepath.Join(templatesDir, "*.html")) 47 if err != nil { 48 return nil, err 49 } 50 ret.templates.Store(htmlTemplates) 51 52 go func() { 53 defer close(ret.stopped) 54 defer watcher.Close() 55 56 for { 57 select { 58 case <-ret.stop: 59 return 60 case event := <-watcher.Events: 61 if event.Op&fsnotify.Write == fsnotify.Write { 62 mlog.Info("Re-parsing templates because of modified file", mlog.String("file_name", event.Name)) 63 if htmlTemplates, err := template.ParseGlob(filepath.Join(templatesDir, "*.html")); err != nil { 64 mlog.Error("Failed to parse templates.", mlog.Err(err)) 65 } else { 66 ret.templates.Store(htmlTemplates) 67 } 68 } 69 case err := <-watcher.Errors: 70 mlog.Error("Failed in directory watcher", mlog.Err(err)) 71 } 72 } 73 }() 74 75 return ret, nil 76 } 77 78 func (w *HTMLTemplateWatcher) Templates() *template.Template { 79 return w.templates.Load().(*template.Template) 80 } 81 82 func (w *HTMLTemplateWatcher) Close() { 83 close(w.stop) 84 <-w.stopped 85 } 86 87 type HTMLTemplate struct { 88 Templates *template.Template 89 TemplateName string 90 Props map[string]interface{} 91 Html map[string]template.HTML 92 } 93 94 func NewHTMLTemplate(templates *template.Template, templateName string) *HTMLTemplate { 95 return &HTMLTemplate{ 96 Templates: templates, 97 TemplateName: templateName, 98 Props: make(map[string]interface{}), 99 Html: make(map[string]template.HTML), 100 } 101 } 102 103 func (t *HTMLTemplate) Render() string { 104 var text bytes.Buffer 105 t.RenderToWriter(&text) 106 return text.String() 107 } 108 109 func (t *HTMLTemplate) RenderToWriter(w io.Writer) error { 110 if t.Templates == nil { 111 return errors.New("no html templates") 112 } 113 114 if err := t.Templates.ExecuteTemplate(w, t.TemplateName, t); err != nil { 115 mlog.Error("Error rendering template", mlog.String("template_name", t.TemplateName), mlog.Err(err)) 116 return err 117 } 118 119 return nil 120 } 121 122 func TranslateAsHtml(t i18n.TranslateFunc, translationID string, args map[string]interface{}) template.HTML { 123 message := t(translationID, escapeForHtml(args)) 124 message = strings.Replace(message, "[[", "<strong>", -1) 125 message = strings.Replace(message, "]]", "</strong>", -1) 126 return template.HTML(message) 127 } 128 129 func escapeForHtml(arg interface{}) interface{} { 130 switch typedArg := arg.(type) { 131 case string: 132 return template.HTMLEscapeString(typedArg) 133 case *string: 134 return template.HTMLEscapeString(*typedArg) 135 case map[string]interface{}: 136 safeArg := make(map[string]interface{}, len(typedArg)) 137 for key, value := range typedArg { 138 safeArg[key] = escapeForHtml(value) 139 } 140 return safeArg 141 default: 142 mlog.Warn( 143 "Unable to escape value for HTML template", 144 mlog.Any("html_template", arg), 145 mlog.String("template_type", reflect.ValueOf(arg).Type().String()), 146 ) 147 return "" 148 } 149 }