summaryrefslogtreecommitdiffstats
path: root/modules/templates
diff options
context:
space:
mode:
authorThomas Boerger <thomas@webhippie.de>2016-12-06 18:58:31 +0100
committerGitHub <noreply@github.com>2016-12-06 18:58:31 +0100
commit83ed234472c85057100db5cc537049812c3a288c (patch)
treed6bb6623eb36dce5586c6f43495d99bb94b35827 /modules/templates
parent1b5b297c398a547b506029ac5a527ba9a5891ffb (diff)
downloadgitea-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.go103
-rw-r--r--modules/templates/helper.go299
-rw-r--r--modules/templates/static.go109
-rw-r--r--modules/templates/templates.go10
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