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