mirror of
https://github.com/mattermost/mattermost.git
synced 2025-02-25 18:55:24 -06:00
* failing to find i18n shouldn't segfault The server was trying to handle the fact that it couldn't find the i18n directory, by emitting a translated log message... * fix utils.FindDir The attempts to find the directory in the parent or grandparent directory don't work if the current working directory was inside `enterprise`, with `enterprise` itself being a symlink as per the usual developer setup. Recurse to the root of the filesystem, cleaning the path along the way to work around this limitation (and allow tests to be run from an arbitrarily deep nesting level.) Fix corresponding usages to employ filepath.Join. * failing to find html templates shouldn't segfault * fail fast if the test user cannot be created * rework utils.FindDir to retain backwards compatibility
141 lines
3.3 KiB
Go
141 lines
3.3 KiB
Go
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
|
|
// See License.txt for license information.
|
|
|
|
package utils
|
|
|
|
import (
|
|
"bytes"
|
|
"errors"
|
|
"html/template"
|
|
"io"
|
|
"path/filepath"
|
|
"reflect"
|
|
"sync/atomic"
|
|
|
|
l4g "github.com/alecthomas/log4go"
|
|
"github.com/fsnotify/fsnotify"
|
|
"github.com/nicksnyder/go-i18n/i18n"
|
|
)
|
|
|
|
type HTMLTemplateWatcher struct {
|
|
templates atomic.Value
|
|
stop chan struct{}
|
|
stopped chan struct{}
|
|
}
|
|
|
|
func NewHTMLTemplateWatcher(directory string) (*HTMLTemplateWatcher, error) {
|
|
templatesDir, _ := FindDir(directory)
|
|
l4g.Debug("Parsing server templates at %v", templatesDir)
|
|
|
|
ret := &HTMLTemplateWatcher{
|
|
stop: make(chan struct{}),
|
|
stopped: make(chan struct{}),
|
|
}
|
|
|
|
watcher, err := fsnotify.NewWatcher()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if err = watcher.Add(templatesDir); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if htmlTemplates, err := template.ParseGlob(filepath.Join(templatesDir, "*.html")); err != nil {
|
|
return nil, err
|
|
} else {
|
|
ret.templates.Store(htmlTemplates)
|
|
}
|
|
|
|
go func() {
|
|
defer close(ret.stopped)
|
|
defer watcher.Close()
|
|
|
|
for {
|
|
select {
|
|
case <-ret.stop:
|
|
return
|
|
case event := <-watcher.Events:
|
|
if event.Op&fsnotify.Write == fsnotify.Write {
|
|
l4g.Info("Re-parsing templates because of modified file %v", event.Name)
|
|
if htmlTemplates, err := template.ParseGlob(filepath.Join(templatesDir, "*.html")); err != nil {
|
|
l4g.Error("Failed to parse templates %v", err)
|
|
} else {
|
|
ret.templates.Store(htmlTemplates)
|
|
}
|
|
}
|
|
case err := <-watcher.Errors:
|
|
l4g.Error("Failed in directory watcher %s", err)
|
|
}
|
|
}
|
|
}()
|
|
|
|
return ret, nil
|
|
}
|
|
|
|
func (w *HTMLTemplateWatcher) Templates() *template.Template {
|
|
return w.templates.Load().(*template.Template)
|
|
}
|
|
|
|
func (w *HTMLTemplateWatcher) Close() {
|
|
close(w.stop)
|
|
<-w.stopped
|
|
}
|
|
|
|
type HTMLTemplate struct {
|
|
Templates *template.Template
|
|
TemplateName string
|
|
Props map[string]interface{}
|
|
Html map[string]template.HTML
|
|
}
|
|
|
|
func NewHTMLTemplate(templates *template.Template, templateName string) *HTMLTemplate {
|
|
return &HTMLTemplate{
|
|
Templates: templates,
|
|
TemplateName: templateName,
|
|
Props: make(map[string]interface{}),
|
|
Html: make(map[string]template.HTML),
|
|
}
|
|
}
|
|
|
|
func (t *HTMLTemplate) Render() string {
|
|
var text bytes.Buffer
|
|
t.RenderToWriter(&text)
|
|
return text.String()
|
|
}
|
|
|
|
func (t *HTMLTemplate) RenderToWriter(w io.Writer) error {
|
|
if t.Templates == nil {
|
|
return errors.New("no html templates")
|
|
}
|
|
|
|
if err := t.Templates.ExecuteTemplate(w, t.TemplateName, t); err != nil {
|
|
l4g.Error(T("api.api.render.error"), t.TemplateName, err)
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func TranslateAsHtml(t i18n.TranslateFunc, translationID string, args map[string]interface{}) template.HTML {
|
|
return template.HTML(t(translationID, escapeForHtml(args)))
|
|
}
|
|
|
|
func escapeForHtml(arg interface{}) interface{} {
|
|
switch typedArg := arg.(type) {
|
|
case string:
|
|
return template.HTMLEscapeString(typedArg)
|
|
case *string:
|
|
return template.HTMLEscapeString(*typedArg)
|
|
case map[string]interface{}:
|
|
safeArg := make(map[string]interface{}, len(typedArg))
|
|
for key, value := range typedArg {
|
|
safeArg[key] = escapeForHtml(value)
|
|
}
|
|
return safeArg
|
|
default:
|
|
l4g.Warn("Unable to escape value for HTML template %v of type %v", arg, reflect.ValueOf(arg).Type())
|
|
return ""
|
|
}
|
|
}
|