]> source.dussan.org Git - gitea.git/commitdiff
Integrate templates into bindata optionally (#314)
authorThomas Boerger <thomas@webhippie.de>
Tue, 6 Dec 2016 17:58:31 +0000 (18:58 +0100)
committerGitHub <noreply@github.com>
Tue, 6 Dec 2016 17:58:31 +0000 (18:58 +0100)
Integrated optional bindata for the templates

17 files changed:
.gitignore
cmd/web.go
models/git_diff.go
models/mail.go
modules/highlight/highlight.go [new file with mode: 0644]
modules/public/public.go
modules/public/static.go
modules/template/highlight/highlight.go [deleted file]
modules/template/template.go [deleted file]
modules/templates/dynamic.go [new file with mode: 0644]
modules/templates/helper.go [new file with mode: 0644]
modules/templates/static.go [new file with mode: 0644]
modules/templates/templates.go [new file with mode: 0644]
routers/init.go
routers/repo/editor.go
routers/repo/view.go
templates/.VERSION [deleted file]

index 9825b96ee3d9d3fe7e2e95ea1f221e455c759623..0bca5df1dfded71397836a77e30695b374fe5712 100644 (file)
@@ -29,6 +29,7 @@ _testmain.go
 coverage.out
 
 /modules/public/bindata.go
+/modules/templates/bindata.go
 
 *.db
 *.log
index 5442850561379b27a1a1810fae5eaf19d9045887..dfb3ac23854612f20c13e163f0c075154f082e9e 100644 (file)
@@ -7,7 +7,6 @@ package cmd
 import (
        "crypto/tls"
        "fmt"
-       "io/ioutil"
        "net"
        "net/http"
        "net/http/fcgi"
@@ -15,7 +14,6 @@ import (
        "path"
        "strings"
 
-       "code.gitea.io/git"
        "code.gitea.io/gitea/models"
        "code.gitea.io/gitea/modules/auth"
        "code.gitea.io/gitea/modules/bindata"
@@ -23,7 +21,7 @@ import (
        "code.gitea.io/gitea/modules/log"
        "code.gitea.io/gitea/modules/public"
        "code.gitea.io/gitea/modules/setting"
-       "code.gitea.io/gitea/modules/template"
+       "code.gitea.io/gitea/modules/templates"
        "code.gitea.io/gitea/routers"
        "code.gitea.io/gitea/routers/admin"
        apiv1 "code.gitea.io/gitea/routers/api/v1"
@@ -39,10 +37,7 @@ import (
        "github.com/go-macaron/i18n"
        "github.com/go-macaron/session"
        "github.com/go-macaron/toolbox"
-       "github.com/go-xorm/xorm"
-       version "github.com/mcuadros/go-version"
        "github.com/urfave/cli"
-       ini "gopkg.in/ini.v1"
        macaron "gopkg.in/macaron.v1"
 )
 
@@ -74,45 +69,6 @@ type VerChecker struct {
        Expected   string
 }
 
-// checkVersion checks if binary matches the version of templates files.
-func checkVersion() {
-       // Templates.
-       data, err := ioutil.ReadFile(setting.StaticRootPath + "/templates/.VERSION")
-       if err != nil {
-               log.Fatal(4, "Fail to read 'templates/.VERSION': %v", err)
-       }
-       tplVer := string(data)
-       if tplVer != setting.AppVer {
-               if version.Compare(tplVer, setting.AppVer, ">") {
-                       log.Fatal(4, "Binary version is lower than template file version, did you forget to recompile Gogs?")
-               } else {
-                       log.Fatal(4, "Binary version is higher than template file version, did you forget to update template files?")
-               }
-       }
-
-       // Check dependency version.
-       checkers := []VerChecker{
-               {"github.com/go-xorm/xorm", func() string { return xorm.Version }, "0.5.5"},
-               {"github.com/go-macaron/binding", binding.Version, "0.3.2"},
-               {"github.com/go-macaron/cache", cache.Version, "0.1.2"},
-               {"github.com/go-macaron/csrf", csrf.Version, "0.1.0"},
-               {"github.com/go-macaron/i18n", i18n.Version, "0.3.0"},
-               {"github.com/go-macaron/session", session.Version, "0.1.6"},
-               {"github.com/go-macaron/toolbox", toolbox.Version, "0.1.0"},
-               {"gopkg.in/ini.v1", ini.Version, "1.8.4"},
-               {"gopkg.in/macaron.v1", macaron.Version, "1.1.7"},
-               {"code.gitea.io/git", git.Version, "0.4.1"},
-       }
-       for _, c := range checkers {
-               if !version.Compare(c.Version(), c.Expected, ">=") {
-                       log.Fatal(4, `Dependency outdated!
-Package '%s' current version (%s) is below requirement (%s),
-please use following command to update this package and recompile Gogs:
-go get -u %[1]s`, c.ImportPath, c.Version(), c.Expected)
-               }
-       }
-}
-
 // newMacaron initializes Macaron instance.
 func newMacaron() *macaron.Macaron {
        m := macaron.New()
@@ -140,15 +96,8 @@ func newMacaron() *macaron.Macaron {
                },
        ))
 
-       funcMap := template.NewFuncMap()
-       m.Use(macaron.Renderer(macaron.RenderOptions{
-               Directory:         path.Join(setting.StaticRootPath, "templates"),
-               AppendDirectories: []string{path.Join(setting.CustomPath, "templates")},
-               Funcs:             funcMap,
-               IndentJSON:        macaron.Env != macaron.PROD,
-       }))
-       models.InitMailRender(path.Join(setting.StaticRootPath, "templates/mail"),
-               path.Join(setting.CustomPath, "templates/mail"), funcMap)
+       m.Use(templates.Renderer())
+       models.InitMailRender(templates.Mailer())
 
        localeNames, err := bindata.AssetDir("conf/locale")
        if err != nil {
@@ -200,7 +149,6 @@ func runWeb(ctx *cli.Context) error {
                setting.CustomConf = ctx.String("config")
        }
        routers.GlobalInit()
-       checkVersion()
 
        m := newMacaron()
 
index b8f9cacec9f66fe3f55ed3717a913c8f91020660..fe99f61bfea188bb54082d409e9a9b02f73c03d2 100644 (file)
@@ -18,10 +18,10 @@ import (
 
        "code.gitea.io/git"
        "code.gitea.io/gitea/modules/base"
+       "code.gitea.io/gitea/modules/highlight"
        "code.gitea.io/gitea/modules/log"
        "code.gitea.io/gitea/modules/process"
        "code.gitea.io/gitea/modules/setting"
-       "code.gitea.io/gitea/modules/template/highlight"
        "github.com/Unknwon/com"
        "github.com/sergi/go-diff/diffmatchpatch"
        "golang.org/x/net/html/charset"
index f89e38e6252944c6370e868aff58cd9345fe5973..a5e9bd551feb66eff6194547088caee23c5a8422 100644 (file)
@@ -5,18 +5,18 @@
 package models
 
 import (
+       "bytes"
        "fmt"
        "html/template"
        "path"
 
-       "gopkg.in/gomail.v2"
-       "gopkg.in/macaron.v1"
-
        "code.gitea.io/gitea/modules/base"
        "code.gitea.io/gitea/modules/log"
        "code.gitea.io/gitea/modules/mailer"
        "code.gitea.io/gitea/modules/markdown"
        "code.gitea.io/gitea/modules/setting"
+       "gopkg.in/gomail.v2"
+       "gopkg.in/macaron.v1"
 )
 
 const (
@@ -31,27 +31,11 @@ const (
        mailNotifyCollaborator base.TplName = "notify/collaborator"
 )
 
-type mailRenderInterface interface {
-       HTMLString(string, interface{}, ...macaron.HTMLOptions) (string, error)
-}
-
-var mailRender mailRenderInterface
+var templates *template.Template
 
 // InitMailRender initializes the macaron mail renderer
-func InitMailRender(dir, appendDir string, funcMap []template.FuncMap) {
-       opt := &macaron.RenderOptions{
-               Directory:         dir,
-               AppendDirectories: []string{appendDir},
-               Funcs:             funcMap,
-               Extensions:        []string{".tmpl", ".html"},
-       }
-       ts := macaron.NewTemplateSet()
-       ts.Set(macaron.DEFAULT_TPL_SET_NAME, opt)
-
-       mailRender = &macaron.TplRender{
-               TemplateSet: ts,
-               Opt:         opt,
-       }
+func InitMailRender(tmpls *template.Template) {
+       templates = tmpls
 }
 
 // SendTestMail sends a test mail
@@ -67,13 +51,15 @@ func SendUserMail(c *macaron.Context, u *User, tpl base.TplName, code, subject,
                "ResetPwdCodeLives": setting.Service.ResetPwdCodeLives / 60,
                "Code":              code,
        }
-       body, err := mailRender.HTMLString(string(tpl), data)
-       if err != nil {
-               log.Error(3, "HTMLString: %v", err)
+
+       var content bytes.Buffer
+
+       if err := templates.ExecuteTemplate(&content, string(tpl), data); err != nil {
+               log.Error(3, "Template: %v", err)
                return
        }
 
-       msg := mailer.NewMessage([]string{u.Email}, subject, body)
+       msg := mailer.NewMessage([]string{u.Email}, subject, content.String())
        msg.Info = fmt.Sprintf("UID: %d, %s", u.ID, info)
 
        mailer.SendAsync(msg)
@@ -97,13 +83,15 @@ func SendActivateEmailMail(c *macaron.Context, u *User, email *EmailAddress) {
                "Code":            u.GenerateEmailActivateCode(email.Email),
                "Email":           email.Email,
        }
-       body, err := mailRender.HTMLString(string(mailAuthActivateEmail), data)
-       if err != nil {
-               log.Error(3, "HTMLString: %v", err)
+
+       var content bytes.Buffer
+
+       if err := templates.ExecuteTemplate(&content, string(mailAuthActivateEmail), data); err != nil {
+               log.Error(3, "Template: %v", err)
                return
        }
 
-       msg := mailer.NewMessage([]string{email.Email}, c.Tr("mail.activate_email"), body)
+       msg := mailer.NewMessage([]string{email.Email}, c.Tr("mail.activate_email"), content.String())
        msg.Info = fmt.Sprintf("UID: %d, activate email", u.ID)
 
        mailer.SendAsync(msg)
@@ -114,13 +102,15 @@ func SendRegisterNotifyMail(c *macaron.Context, u *User) {
        data := map[string]interface{}{
                "Username": u.DisplayName(),
        }
-       body, err := mailRender.HTMLString(string(mailAuthRegisterNotify), data)
-       if err != nil {
-               log.Error(3, "HTMLString: %v", err)
+
+       var content bytes.Buffer
+
+       if err := templates.ExecuteTemplate(&content, string(mailAuthRegisterNotify), data); err != nil {
+               log.Error(3, "Template: %v", err)
                return
        }
 
-       msg := mailer.NewMessage([]string{u.Email}, c.Tr("mail.register_notify"), body)
+       msg := mailer.NewMessage([]string{u.Email}, c.Tr("mail.register_notify"), content.String())
        msg.Info = fmt.Sprintf("UID: %d, registration notify", u.ID)
 
        mailer.SendAsync(msg)
@@ -136,13 +126,15 @@ func SendCollaboratorMail(u, doer *User, repo *Repository) {
                "RepoName": repoName,
                "Link":     repo.HTMLURL(),
        }
-       body, err := mailRender.HTMLString(string(mailNotifyCollaborator), data)
-       if err != nil {
-               log.Error(3, "HTMLString: %v", err)
+
+       var content bytes.Buffer
+
+       if err := templates.ExecuteTemplate(&content, string(mailNotifyCollaborator), data); err != nil {
+               log.Error(3, "Template: %v", err)
                return
        }
 
-       msg := mailer.NewMessage([]string{u.Email}, subject, body)
+       msg := mailer.NewMessage([]string{u.Email}, subject, content.String())
        msg.Info = fmt.Sprintf("UID: %d, add collaborator", u.ID)
 
        mailer.SendAsync(msg)
@@ -161,11 +153,14 @@ func composeIssueMessage(issue *Issue, doer *User, tplName base.TplName, tos []s
        body := string(markdown.RenderSpecialLink([]byte(issue.Content), issue.Repo.HTMLURL(), issue.Repo.ComposeMetas()))
        data := composeTplData(subject, body, issue.HTMLURL())
        data["Doer"] = doer
-       content, err := mailRender.HTMLString(string(tplName), data)
-       if err != nil {
-               log.Error(3, "HTMLString (%s): %v", tplName, err)
+
+       var content bytes.Buffer
+
+       if err := templates.ExecuteTemplate(&content, string(tplName), data); err != nil {
+               log.Error(3, "Template: %v", err)
        }
-       msg := mailer.NewMessageFrom(tos, fmt.Sprintf(`"%s" <%s>`, doer.DisplayName(), setting.MailService.FromEmail), subject, content)
+
+       msg := mailer.NewMessageFrom(tos, fmt.Sprintf(`"%s" <%s>`, doer.DisplayName(), setting.MailService.FromEmail), subject, content.String())
        msg.Info = fmt.Sprintf("Subject: %s, %s", subject, info)
        return msg
 }
diff --git a/modules/highlight/highlight.go b/modules/highlight/highlight.go
new file mode 100644 (file)
index 0000000..39b5d6d
--- /dev/null
@@ -0,0 +1,98 @@
+// Copyright 2015 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 highlight
+
+import (
+       "path"
+       "strings"
+
+       "code.gitea.io/gitea/modules/setting"
+)
+
+var (
+       // File name should ignore highlight.
+       ignoreFileNames = map[string]bool{
+               "license": true,
+               "copying": true,
+       }
+
+       // File names that are representing highlight classes.
+       highlightFileNames = map[string]bool{
+               "dockerfile": true,
+               "makefile":   true,
+       }
+
+       // Extensions that are same as highlight classes.
+       highlightExts = map[string]bool{
+               ".arm":    true,
+               ".as":     true,
+               ".sh":     true,
+               ".cs":     true,
+               ".cpp":    true,
+               ".c":      true,
+               ".css":    true,
+               ".cmake":  true,
+               ".bat":    true,
+               ".dart":   true,
+               ".patch":  true,
+               ".elixir": true,
+               ".erlang": true,
+               ".go":     true,
+               ".html":   true,
+               ".xml":    true,
+               ".hs":     true,
+               ".ini":    true,
+               ".json":   true,
+               ".java":   true,
+               ".js":     true,
+               ".less":   true,
+               ".lua":    true,
+               ".php":    true,
+               ".py":     true,
+               ".rb":     true,
+               ".scss":   true,
+               ".sql":    true,
+               ".scala":  true,
+               ".swift":  true,
+               ".ts":     true,
+               ".vb":     true,
+       }
+
+       // Extensions that are not same as highlight classes.
+       highlightMapping = map[string]string{}
+)
+
+// NewContext loads highlight map
+func NewContext() {
+       keys := setting.Cfg.Section("highlight.mapping").Keys()
+       for i := range keys {
+               highlightMapping[keys[i].Name()] = keys[i].Value()
+       }
+}
+
+// FileNameToHighlightClass returns the best match for highlight class name
+// based on the rule of highlight.js.
+func FileNameToHighlightClass(fname string) string {
+       fname = strings.ToLower(fname)
+       if ignoreFileNames[fname] {
+               return "nohighlight"
+       }
+
+       if highlightFileNames[fname] {
+               return fname
+       }
+
+       ext := path.Ext(fname)
+       if highlightExts[ext] {
+               return ext[1:]
+       }
+
+       name, ok := highlightMapping[ext]
+       if ok {
+               return name
+       }
+
+       return ""
+}
index 261ed88874c0857d3f7ae201f76ccabe9da133a8..9c9e9d533dadc69d85f22e04be961da3d0678611 100644 (file)
@@ -6,6 +6,8 @@ package public
 
 //go:generate go-bindata -tags "bindata" -ignore "\\.go|\\.less" -pkg "public" -o "bindata.go" ../../public/...
 //go:generate go fmt bindata.go
+//go:generate sed -i.bak s/..\/..\/public\/// bindata.go
+//go:generate rm -f bindata.go.bak
 
 // Options represents the available options to configure the macaron handler.
 type Options struct {
index 8184bc214978b27cfb29f582800186c5a2d107dd..f68400d329c1edc7f409723f4ec1f4e36616ec2b 100644 (file)
@@ -22,7 +22,7 @@ func Static(opts *Options) macaron.Handler {
                                AssetDir:   AssetDir,
                                AssetInfo:  AssetInfo,
                                AssetNames: AssetNames,
-                               Prefix:     "../../public",
+                               Prefix:     "",
                        }),
                },
        )
diff --git a/modules/template/highlight/highlight.go b/modules/template/highlight/highlight.go
deleted file mode 100644 (file)
index 39b5d6d..0000000
+++ /dev/null
@@ -1,98 +0,0 @@
-// Copyright 2015 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 highlight
-
-import (
-       "path"
-       "strings"
-
-       "code.gitea.io/gitea/modules/setting"
-)
-
-var (
-       // File name should ignore highlight.
-       ignoreFileNames = map[string]bool{
-               "license": true,
-               "copying": true,
-       }
-
-       // File names that are representing highlight classes.
-       highlightFileNames = map[string]bool{
-               "dockerfile": true,
-               "makefile":   true,
-       }
-
-       // Extensions that are same as highlight classes.
-       highlightExts = map[string]bool{
-               ".arm":    true,
-               ".as":     true,
-               ".sh":     true,
-               ".cs":     true,
-               ".cpp":    true,
-               ".c":      true,
-               ".css":    true,
-               ".cmake":  true,
-               ".bat":    true,
-               ".dart":   true,
-               ".patch":  true,
-               ".elixir": true,
-               ".erlang": true,
-               ".go":     true,
-               ".html":   true,
-               ".xml":    true,
-               ".hs":     true,
-               ".ini":    true,
-               ".json":   true,
-               ".java":   true,
-               ".js":     true,
-               ".less":   true,
-               ".lua":    true,
-               ".php":    true,
-               ".py":     true,
-               ".rb":     true,
-               ".scss":   true,
-               ".sql":    true,
-               ".scala":  true,
-               ".swift":  true,
-               ".ts":     true,
-               ".vb":     true,
-       }
-
-       // Extensions that are not same as highlight classes.
-       highlightMapping = map[string]string{}
-)
-
-// NewContext loads highlight map
-func NewContext() {
-       keys := setting.Cfg.Section("highlight.mapping").Keys()
-       for i := range keys {
-               highlightMapping[keys[i].Name()] = keys[i].Value()
-       }
-}
-
-// FileNameToHighlightClass returns the best match for highlight class name
-// based on the rule of highlight.js.
-func FileNameToHighlightClass(fname string) string {
-       fname = strings.ToLower(fname)
-       if ignoreFileNames[fname] {
-               return "nohighlight"
-       }
-
-       if highlightFileNames[fname] {
-               return fname
-       }
-
-       ext := path.Ext(fname)
-       if highlightExts[ext] {
-               return ext[1:]
-       }
-
-       name, ok := highlightMapping[ext]
-       if ok {
-               return name
-       }
-
-       return ""
-}
diff --git a/modules/template/template.go b/modules/template/template.go
deleted file mode 100644 (file)
index 65d78c2..0000000
+++ /dev/null
@@ -1,299 +0,0 @@
-// 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 template
-
-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/dynamic.go b/modules/templates/dynamic.go
new file mode 100644 (file)
index 0000000..c127b69
--- /dev/null
@@ -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 (file)
index 0000000..c256455
--- /dev/null
@@ -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 (file)
index 0000000..5c9903c
--- /dev/null
@@ -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 (file)
index 0000000..91c8db5
--- /dev/null
@@ -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
index b6b9911c8099622b06227c9bedef150134e3a36a..3d5235d79d200bcc371b8a2272b06190c8a85bf3 100644 (file)
@@ -11,12 +11,12 @@ import (
        "code.gitea.io/git"
        "code.gitea.io/gitea/models"
        "code.gitea.io/gitea/modules/cron"
+       "code.gitea.io/gitea/modules/highlight"
        "code.gitea.io/gitea/modules/log"
        "code.gitea.io/gitea/modules/mailer"
        "code.gitea.io/gitea/modules/markdown"
        "code.gitea.io/gitea/modules/setting"
        "code.gitea.io/gitea/modules/ssh"
-       "code.gitea.io/gitea/modules/template/highlight"
        macaron "gopkg.in/macaron.v1"
 )
 
index f037b39b21d364cb9e9f4c0ab207f019f6b5bdc4..8d45521a2ee9e02221d79f0253adb5c424a369a7 100644 (file)
@@ -18,7 +18,7 @@ import (
        "code.gitea.io/gitea/modules/context"
        "code.gitea.io/gitea/modules/log"
        "code.gitea.io/gitea/modules/setting"
-       "code.gitea.io/gitea/modules/template"
+       "code.gitea.io/gitea/modules/templates"
 )
 
 const (
@@ -74,7 +74,7 @@ func editFile(ctx *context.Context, isNewFile bool) {
 
                d, _ := ioutil.ReadAll(dataRc)
                buf = append(buf, d...)
-               if content, err := template.ToUTF8WithErr(buf); err != nil {
+               if content, err := templates.ToUTF8WithErr(buf); err != nil {
                        if err != nil {
                                log.Error(4, "ToUTF8WithErr: %v", err)
                        }
index 3ef3716f27df9bea289ecfd09e1ec56140e3449e..4417383bdc7eaf811910517b8a3d8234f4ab9bb4 100644 (file)
@@ -16,11 +16,11 @@ import (
        "code.gitea.io/gitea/models"
        "code.gitea.io/gitea/modules/base"
        "code.gitea.io/gitea/modules/context"
+       "code.gitea.io/gitea/modules/highlight"
        "code.gitea.io/gitea/modules/log"
        "code.gitea.io/gitea/modules/markdown"
        "code.gitea.io/gitea/modules/setting"
-       "code.gitea.io/gitea/modules/template"
-       "code.gitea.io/gitea/modules/template/highlight"
+       "code.gitea.io/gitea/modules/templates"
        "github.com/Unknwon/paginater"
 )
 
@@ -164,7 +164,7 @@ func renderFile(ctx *context.Context, entry *git.TreeEntry, treeLink, rawLink st
                } else {
                        // Building code view blocks with line number on server side.
                        var fileContent string
-                       if content, err := template.ToUTF8WithErr(buf); err != nil {
+                       if content, err := templates.ToUTF8WithErr(buf); err != nil {
                                if err != nil {
                                        log.Error(4, "ToUTF8WithErr: %s", err)
                                }
diff --git a/templates/.VERSION b/templates/.VERSION
deleted file mode 100644 (file)
index 927f673..0000000
+++ /dev/null
@@ -1 +0,0 @@
-0.9.99.0915
\ No newline at end of file