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