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