1
0
mirror of https://github.com/astaxie/beego.git synced 2024-11-22 12:30:54 +00:00

Merge pull request #2417 from eyalpost/develop

support for multiple view paths
This commit is contained in:
astaxie 2017-02-13 12:37:17 +08:00 committed by GitHub
commit 1d49a4bcbd
5 changed files with 124 additions and 19 deletions

View File

@ -69,6 +69,7 @@ type Controller struct {
// template data
TplName string
ViewPath string
Layout string
LayoutSections map[string]string // the key is the section name and the value is the template name
TplPrefix string
@ -213,7 +214,7 @@ func (c *Controller) RenderBytes() ([]byte, error) {
continue
}
buf.Reset()
err = ExecuteTemplate(&buf, sectionTpl, c.Data)
err = ExecuteViewPathTemplate(&buf, sectionTpl, c.viewPath(), c.Data)
if err != nil {
return nil, err
}
@ -222,7 +223,7 @@ func (c *Controller) RenderBytes() ([]byte, error) {
}
buf.Reset()
ExecuteTemplate(&buf, c.Layout, c.Data)
ExecuteViewPathTemplate(&buf, c.Layout, c.viewPath() ,c.Data)
}
return buf.Bytes(), err
}
@ -248,9 +249,16 @@ func (c *Controller) renderTemplate() (bytes.Buffer, error) {
}
}
}
BuildTemplate(BConfig.WebConfig.ViewsPath, buildFiles...)
BuildTemplate(c.viewPath() , buildFiles...)
}
return buf, ExecuteTemplate(&buf, c.TplName, c.Data)
return buf, ExecuteViewPathTemplate(&buf, c.TplName, c.viewPath(), c.Data)
}
func (c *Controller) viewPath() string {
if c.ViewPath == "" {
return BConfig.WebConfig.ViewsPath
}
return c.ViewPath
}
// Redirect sends the redirection response to url with status code.

View File

@ -20,6 +20,8 @@ import (
"testing"
"github.com/astaxie/beego/context"
"os"
"path/filepath"
)
func TestGetInt(t *testing.T) {
@ -121,3 +123,59 @@ func TestGetUint64(t *testing.T) {
t.Errorf("TestGetUint64 expect %v,get %T,%v", uint64(math.MaxUint64), val, val)
}
}
func TestAdditionalViewPaths(t *testing.T) {
dir1 := "_beeTmp"
dir2 := "_beeTmp2"
defer os.RemoveAll(dir1)
defer os.RemoveAll(dir2)
dir1file := "file1.tpl"
dir2file := "file2.tpl"
genFile := func(dir string, name string, content string) {
os.MkdirAll(filepath.Dir(filepath.Join(dir, name)), 0777)
if f, err := os.Create(filepath.Join(dir, name)); err != nil {
t.Fatal(err)
} else {
defer f.Close()
f.WriteString(content)
f.Close()
}
}
genFile(dir1, dir1file, `<div>{{.Content}}</div>`)
genFile(dir2, dir2file, `<html>{{.Content}}</html>`)
AddViewPath(dir1)
AddViewPath(dir2)
ctrl := Controller{
TplName: "file1.tpl",
ViewPath: dir1,
}
ctrl.Data = map[interface{}]interface{}{
"Content": "value2",
}
if result, err := ctrl.RenderString(); err != nil {
t.Fatal(err)
} else {
if result != "<div>value2</div>" {
t.Fatalf("TestAdditionalViewPaths expect %s got %s", "<div>value2</div>", result)
}
}
func() {
ctrl.TplName = "file2.tpl"
defer func() {
if r := recover(); r == nil {
t.Fatal("TestAdditionalViewPaths expected error")
}
}()
ctrl.RenderString();
}()
ctrl.TplName = "file2.tpl"
ctrl.ViewPath = dir2
ctrl.RenderString();
}

View File

@ -72,7 +72,8 @@ func registerSession() error {
}
func registerTemplate() error {
if err := BuildTemplate(BConfig.WebConfig.ViewsPath); err != nil {
defer lockViewPaths()
if err := AddViewPath(BConfig.WebConfig.ViewsPath); err != nil {
if BConfig.RunMode == DEV {
logs.Warn(err)
}

View File

@ -32,8 +32,9 @@ import (
var (
beegoTplFuncMap = make(template.FuncMap)
// beeTemplates caching map and supported template file extensions.
beeTemplates = make(map[string]*template.Template)
beeViewPathTemplateLocked = false
// beeViewPathTemplates caching map and supported template file extensions per view
beeViewPathTemplates = make(map[string]map[string]*template.Template)
templatesLock sync.RWMutex
// beeTemplateExt stores the template extension which will build
beeTemplateExt = []string{"tpl", "html"}
@ -45,23 +46,33 @@ var (
// writing the output to wr.
// A template will be executed safely in parallel.
func ExecuteTemplate(wr io.Writer, name string, data interface{}) error {
return ExecuteViewPathTemplate(wr,name, BConfig.WebConfig.ViewsPath, data)
}
// ExecuteViewPathTemplate applies the template with name and from specific viewPath to the specified data object,
// writing the output to wr.
// A template will be executed safely in parallel.
func ExecuteViewPathTemplate(wr io.Writer, name string, viewPath string, data interface{}) error {
if BConfig.RunMode == DEV {
templatesLock.RLock()
defer templatesLock.RUnlock()
}
if t, ok := beeTemplates[name]; ok {
var err error
if t.Lookup(name) != nil {
err = t.ExecuteTemplate(wr, name, data)
} else {
err = t.Execute(wr, data)
if beeTemplates,ok := beeViewPathTemplates[viewPath]; ok {
if t, ok := beeTemplates[name]; ok {
var err error
if t.Lookup(name) != nil {
err = t.ExecuteTemplate(wr, name, data)
} else {
err = t.Execute(wr, data)
}
if err != nil {
logs.Trace("template Execute err:", err)
}
return err
}
if err != nil {
logs.Trace("template Execute err:", err)
}
return err
panic("can't find templatefile in the path:" + viewPath + "/" + name)
}
panic("can't find templatefile in the path:" + name)
panic("Uknown view path:" + viewPath)
}
func init() {
@ -149,6 +160,21 @@ func AddTemplateExt(ext string) {
beeTemplateExt = append(beeTemplateExt, ext)
}
// AddViewPath adds a new path to the supported view paths.
//Can later be used by setting a controller ViewPath to this folder
//will panic if called after beego.Run()
func AddViewPath(viewPath string) error {
if beeViewPathTemplateLocked {
panic("Can not add new view paths after beego.Run()")
}
beeViewPathTemplates[viewPath] = make(map[string]*template.Template)
return BuildTemplate(viewPath)
}
func lockViewPaths() {
beeViewPathTemplateLocked = true
}
// BuildTemplate will build all template files in a directory.
// it makes beego can render any template file in view directory.
func BuildTemplate(dir string, files ...string) error {
@ -158,6 +184,10 @@ func BuildTemplate(dir string, files ...string) error {
}
return errors.New("dir open err")
}
beeTemplates,ok := beeViewPathTemplates[dir];
if !ok {
panic("Unknown view path: " + dir)
}
self := &templateFile{
root: dir,
files: make(map[string][]string),

View File

@ -67,9 +67,10 @@ func TestTemplate(t *testing.T) {
f.Close()
}
}
if err := BuildTemplate(dir); err != nil {
if err := AddViewPath(dir); err != nil {
t.Fatal(err)
}
beeTemplates := beeViewPathTemplates[dir]
if len(beeTemplates) != 3 {
t.Fatalf("should be 3 but got %v", len(beeTemplates))
}
@ -103,6 +104,12 @@ var user = `<!DOCTYPE html>
func TestRelativeTemplate(t *testing.T) {
dir := "_beeTmp"
//Just add dir to known viewPaths
if err := AddViewPath(dir); err != nil {
t.Fatal(err)
}
files := []string{
"easyui/public/menu.tpl",
"easyui/rbac/user.tpl",
@ -126,6 +133,7 @@ func TestRelativeTemplate(t *testing.T) {
if err := BuildTemplate(dir, files[1]); err != nil {
t.Fatal(err)
}
beeTemplates := beeViewPathTemplates[dir]
if err := beeTemplates["easyui/rbac/user.tpl"].ExecuteTemplate(os.Stdout, "easyui/rbac/user.tpl", nil); err != nil {
t.Fatal(err)
}