diff options
author | Thomas Boerger <thomas@webhippie.de> | 2016-12-06 18:58:31 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2016-12-06 18:58:31 +0100 |
commit | 83ed234472c85057100db5cc537049812c3a288c (patch) | |
tree | d6bb6623eb36dce5586c6f43495d99bb94b35827 /modules/templates | |
parent | 1b5b297c398a547b506029ac5a527ba9a5891ffb (diff) | |
download | gitea-83ed234472c85057100db5cc537049812c3a288c.tar.gz gitea-83ed234472c85057100db5cc537049812c3a288c.zip |
Integrate templates into bindata optionally (#314)
Integrated optional bindata for the templates
Diffstat (limited to 'modules/templates')
-rw-r--r-- | modules/templates/dynamic.go | 103 | ||||
-rw-r--r-- | modules/templates/helper.go | 299 | ||||
-rw-r--r-- | modules/templates/static.go | 109 | ||||
-rw-r--r-- | modules/templates/templates.go | 10 |
4 files changed, 521 insertions, 0 deletions
diff --git a/modules/templates/dynamic.go b/modules/templates/dynamic.go new file mode 100644 index 0000000000..c127b69470 --- /dev/null +++ b/modules/templates/dynamic.go @@ -0,0 +1,103 @@ +// +build !bindata + +// Copyright 2016 The Gitea Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package templates + +import ( + "html/template" + "io/ioutil" + "path" + "strings" + + "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/setting" + "github.com/Unknwon/com" + "gopkg.in/macaron.v1" +) + +var ( + templates = template.New("") +) + +// Renderer implements the macaron handler for serving the templates. +func Renderer() macaron.Handler { + return macaron.Renderer(macaron.RenderOptions{ + Funcs: NewFuncMap(), + Directory: path.Join(setting.StaticRootPath, "templates"), + AppendDirectories: []string{ + path.Join(setting.CustomPath, "templates"), + }, + }) +} + +// Mailer provides the templates required for sending notification mails. +func Mailer() *template.Template { + for _, funcs := range NewFuncMap() { + templates.Funcs(funcs) + } + + staticDir := path.Join(setting.StaticRootPath, "templates", "mail") + + if com.IsDir(staticDir) { + files, err := com.StatDir(staticDir) + + if err != nil { + log.Warn("Failed to read %s templates dir. %v", staticDir, err) + } else { + for _, filePath := range files { + if !strings.HasSuffix(filePath, ".tmpl") { + continue + } + + content, err := ioutil.ReadFile(path.Join(staticDir, filePath)) + + if err != nil { + log.Warn("Failed to read static %s template. %v", filePath, err) + continue + } + + templates.New( + strings.TrimSuffix( + filePath, + ".tmpl", + ), + ).Parse(string(content)) + } + } + } + + customDir := path.Join(setting.CustomPath, "templates", "mail") + + if com.IsDir(customDir) { + files, err := com.StatDir(customDir) + + if err != nil { + log.Warn("Failed to read %s templates dir. %v", customDir, err) + } else { + for _, filePath := range files { + if !strings.HasSuffix(filePath, ".tmpl") { + continue + } + + content, err := ioutil.ReadFile(path.Join(customDir, filePath)) + + if err != nil { + log.Warn("Failed to read custom %s template. %v", filePath, err) + continue + } + + templates.New( + strings.TrimSuffix( + filePath, + ".tmpl", + ), + ).Parse(string(content)) + } + } + } + + return templates +} diff --git a/modules/templates/helper.go b/modules/templates/helper.go new file mode 100644 index 0000000000..c256455399 --- /dev/null +++ b/modules/templates/helper.go @@ -0,0 +1,299 @@ +// Copyright 2014 The Gogs Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package templates + +import ( + "container/list" + "encoding/json" + "fmt" + "html/template" + "mime" + "path/filepath" + "runtime" + "strings" + "time" + + "golang.org/x/net/html/charset" + "golang.org/x/text/transform" + "gopkg.in/editorconfig/editorconfig-core-go.v1" + + "code.gitea.io/gitea/models" + "code.gitea.io/gitea/modules/base" + "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/markdown" + "code.gitea.io/gitea/modules/setting" +) + +// NewFuncMap returns functions for injecting to templates +func NewFuncMap() []template.FuncMap { + return []template.FuncMap{map[string]interface{}{ + "GoVer": func() string { + return strings.Title(runtime.Version()) + }, + "UseHTTPS": func() bool { + return strings.HasPrefix(setting.AppURL, "https") + }, + "AppName": func() string { + return setting.AppName + }, + "AppSubUrl": func() string { + return setting.AppSubURL + }, + "AppUrl": func() string { + return setting.AppURL + }, + "AppVer": func() string { + return setting.AppVer + }, + "AppDomain": func() string { + return setting.Domain + }, + "DisableGravatar": func() bool { + return setting.DisableGravatar + }, + "ShowFooterTemplateLoadTime": func() bool { + return setting.ShowFooterTemplateLoadTime + }, + "LoadTimes": func(startTime time.Time) string { + return fmt.Sprint(time.Since(startTime).Nanoseconds()/1e6) + "ms" + }, + "AvatarLink": base.AvatarLink, + "Safe": Safe, + "Str2html": Str2html, + "TimeSince": base.TimeSince, + "RawTimeSince": base.RawTimeSince, + "FileSize": base.FileSize, + "Subtract": base.Subtract, + "Add": func(a, b int) int { + return a + b + }, + "ActionIcon": ActionIcon, + "DateFmtLong": func(t time.Time) string { + return t.Format(time.RFC1123Z) + }, + "DateFmtShort": func(t time.Time) string { + return t.Format("Jan 02, 2006") + }, + "List": List, + "SubStr": func(str string, start, length int) string { + if len(str) == 0 { + return "" + } + end := start + length + if length == -1 { + end = len(str) + } + if len(str) < end { + return str + } + return str[start:end] + }, + "EllipsisString": base.EllipsisString, + "DiffTypeToStr": DiffTypeToStr, + "DiffLineTypeToStr": DiffLineTypeToStr, + "Sha1": Sha1, + "ShortSha": base.ShortSha, + "MD5": base.EncodeMD5, + "ActionContent2Commits": ActionContent2Commits, + "EscapePound": func(str string) string { + return strings.NewReplacer("%", "%25", "#", "%23", " ", "%20", "?", "%3F").Replace(str) + }, + "RenderCommitMessage": RenderCommitMessage, + "ThemeColorMetaTag": func() string { + return setting.UI.ThemeColorMetaTag + }, + "FilenameIsImage": func(filename string) bool { + mimeType := mime.TypeByExtension(filepath.Ext(filename)) + return strings.HasPrefix(mimeType, "image/") + }, + "TabSizeClass": func(ec *editorconfig.Editorconfig, filename string) string { + if ec != nil { + def := ec.GetDefinitionForFilename(filename) + if def.TabWidth > 0 { + return fmt.Sprintf("tab-size-%d", def.TabWidth) + } + } + return "tab-size-8" + }, + }} +} + +// Safe render raw as HTML +func Safe(raw string) template.HTML { + return template.HTML(raw) +} + +// Str2html render Markdown text to HTML +func Str2html(raw string) template.HTML { + return template.HTML(markdown.Sanitizer.Sanitize(raw)) +} + +// List traversings the list +func List(l *list.List) chan interface{} { + e := l.Front() + c := make(chan interface{}) + go func() { + for e != nil { + c <- e.Value + e = e.Next() + } + close(c) + }() + return c +} + +// Sha1 returns sha1 sum of string +func Sha1(str string) string { + return base.EncodeSha1(str) +} + +// ToUTF8WithErr converts content to UTF8 encoding +func ToUTF8WithErr(content []byte) (string, error) { + charsetLabel, err := base.DetectEncoding(content) + if err != nil { + return "", err + } else if charsetLabel == "UTF-8" { + return string(content), nil + } + + encoding, _ := charset.Lookup(charsetLabel) + if encoding == nil { + return string(content), fmt.Errorf("Unknown encoding: %s", charsetLabel) + } + + // If there is an error, we concatenate the nicely decoded part and the + // original left over. This way we won't loose data. + result, n, err := transform.String(encoding.NewDecoder(), string(content)) + if err != nil { + result = result + string(content[n:]) + } + + return result, err +} + +// ToUTF8 converts content to UTF8 encoding and ignore error +func ToUTF8(content string) string { + res, _ := ToUTF8WithErr([]byte(content)) + return res +} + +// ReplaceLeft replaces all prefixes 'old' in 's' with 'new'. +func ReplaceLeft(s, old, new string) string { + oldLen, newLen, i, n := len(old), len(new), 0, 0 + for ; i < len(s) && strings.HasPrefix(s[i:], old); n++ { + i += oldLen + } + + // simple optimization + if n == 0 { + return s + } + + // allocating space for the new string + curLen := n*newLen + len(s[i:]) + replacement := make([]byte, curLen, curLen) + + j := 0 + for ; j < n*newLen; j += newLen { + copy(replacement[j:j+newLen], new) + } + + copy(replacement[j:], s[i:]) + return string(replacement) +} + +// RenderCommitMessage renders commit message with XSS-safe and special links. +func RenderCommitMessage(full bool, msg, urlPrefix string, metas map[string]string) template.HTML { + cleanMsg := template.HTMLEscapeString(msg) + fullMessage := string(markdown.RenderIssueIndexPattern([]byte(cleanMsg), urlPrefix, metas)) + msgLines := strings.Split(strings.TrimSpace(fullMessage), "\n") + numLines := len(msgLines) + if numLines == 0 { + return template.HTML("") + } else if !full { + return template.HTML(msgLines[0]) + } else if numLines == 1 || (numLines >= 2 && len(msgLines[1]) == 0) { + // First line is a header, standalone or followed by empty line + header := fmt.Sprintf("<h3>%s</h3>", msgLines[0]) + if numLines >= 2 { + fullMessage = header + fmt.Sprintf("\n<pre>%s</pre>", strings.Join(msgLines[2:], "\n")) + } else { + fullMessage = header + } + } else { + // Non-standard git message, there is no header line + fullMessage = fmt.Sprintf("<h4>%s</h4>", strings.Join(msgLines, "<br>")) + } + return template.HTML(fullMessage) +} + +// Actioner describes an action +type Actioner interface { + GetOpType() int + GetActUserName() string + GetRepoUserName() string + GetRepoName() string + GetRepoPath() string + GetRepoLink() string + GetBranch() string + GetContent() string + GetCreate() time.Time + GetIssueInfos() []string +} + +// ActionIcon accepts a int that represents action operation type +// and returns a icon class name. +func ActionIcon(opType int) string { + switch opType { + case 1, 8: // Create and transfer repository + return "repo" + case 5, 9: // Commit repository + return "git-commit" + case 6: // Create issue + return "issue-opened" + case 7: // New pull request + return "git-pull-request" + case 10: // Comment issue + return "comment-discussion" + case 11: // Merge pull request + return "git-merge" + case 12, 14: // Close issue or pull request + return "issue-closed" + case 13, 15: // Reopen issue or pull request + return "issue-reopened" + default: + return "invalid type" + } +} + +// ActionContent2Commits converts action content to push commits +func ActionContent2Commits(act Actioner) *models.PushCommits { + push := models.NewPushCommits() + if err := json.Unmarshal([]byte(act.GetContent()), push); err != nil { + log.Error(4, "json.Unmarshal:\n%s\nERROR: %v", act.GetContent(), err) + } + return push +} + +// DiffTypeToStr returns diff type name +func DiffTypeToStr(diffType int) string { + diffTypes := map[int]string{ + 1: "add", 2: "modify", 3: "del", 4: "rename", + } + return diffTypes[diffType] +} + +// DiffLineTypeToStr returns diff line type name +func DiffLineTypeToStr(diffType int) string { + switch diffType { + case 2: + return "add" + case 3: + return "del" + case 4: + return "tag" + } + return "same" +} diff --git a/modules/templates/static.go b/modules/templates/static.go new file mode 100644 index 0000000000..5c9903cde6 --- /dev/null +++ b/modules/templates/static.go @@ -0,0 +1,109 @@ +// +build bindata + +// Copyright 2016 The Gitea Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package templates + +import ( + "html/template" + "io/ioutil" + "path" + "strings" + + "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/setting" + "github.com/Unknwon/com" + "github.com/go-macaron/bindata" + "gopkg.in/macaron.v1" +) + +var ( + templates = template.New("") +) + +// Renderer implements the macaron handler for serving the templates. +func Renderer() macaron.Handler { + return macaron.Renderer(macaron.RenderOptions{ + Funcs: NewFuncMap(), + AppendDirectories: []string{ + path.Join(setting.CustomPath, "templates"), + }, + TemplateFileSystem: bindata.Templates( + bindata.Options{ + Asset: Asset, + AssetDir: AssetDir, + AssetInfo: AssetInfo, + AssetNames: AssetNames, + Prefix: "", + }, + ), + }) +} + +// Mailer provides the templates required for sending notification mails. +func Mailer() *template.Template { + for _, funcs := range NewFuncMap() { + templates.Funcs(funcs) + } + + for _, assetPath := range AssetNames() { + if !strings.HasPrefix(assetPath, "mail/") { + continue + } + + if !strings.HasSuffix(assetPath, ".tmpl") { + continue + } + + content, err := Asset(assetPath) + + if err != nil { + log.Warn("Failed to read embedded %s template. %v", assetPath, err) + continue + } + + templates.New( + strings.TrimPrefix( + strings.TrimSuffix( + assetPath, + ".tmpl", + ), + "mail/", + ), + ).Parse(string(content)) + } + + customDir := path.Join(setting.CustomPath, "templates", "mail") + + if com.IsDir(customDir) { + files, err := com.StatDir(customDir) + + if err != nil { + log.Warn("Failed to read %s templates dir. %v", customDir, err) + } else { + for _, filePath := range files { + if !strings.HasSuffix(filePath, ".tmpl") { + continue + } + + content, err := ioutil.ReadFile(path.Join(customDir, filePath)) + + if err != nil { + log.Warn("Failed to read custom %s template. %v", filePath, err) + continue + } + + templates.New( + strings.TrimSuffix( + filePath, + ".tmpl", + ), + ).Parse(string(content)) + } + } + } + + return templates +} diff --git a/modules/templates/templates.go b/modules/templates/templates.go new file mode 100644 index 0000000000..91c8db5228 --- /dev/null +++ b/modules/templates/templates.go @@ -0,0 +1,10 @@ +// Copyright 2016 The Gitea Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package templates + +//go:generate go-bindata -tags "bindata" -ignore "\\.go" -pkg "templates" -o "bindata.go" ../../templates/... +//go:generate go fmt bindata.go +//go:generate sed -i.bak s/..\/..\/templates\/// bindata.go +//go:generate rm -f bindata.go.bak |