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