2017-12-29 16:03:30 +08:00
|
|
|
|
// Copyright 2017 gf Author(https://gitee.com/johng/gf). All Rights Reserved.
|
|
|
|
|
//
|
|
|
|
|
// This Source Code Form is subject to the terms of the MIT License.
|
|
|
|
|
// If a copy of the MIT was not distributed with this file,
|
|
|
|
|
// You can obtain one at https://gitee.com/johng/gf.
|
|
|
|
|
|
2018-01-03 10:38:53 +08:00
|
|
|
|
// 视图管理
|
2017-12-13 16:45:00 +08:00
|
|
|
|
package gview
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"sync"
|
|
|
|
|
"bytes"
|
|
|
|
|
"errors"
|
2018-01-08 17:07:05 +08:00
|
|
|
|
"strings"
|
|
|
|
|
"html/template"
|
|
|
|
|
"gitee.com/johng/gf/g/os/gfile"
|
|
|
|
|
"gitee.com/johng/gf/g/container/gmap"
|
2017-12-13 16:45:00 +08:00
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// 视图对象
|
|
|
|
|
type View struct {
|
2018-01-08 17:07:05 +08:00
|
|
|
|
mu sync.RWMutex
|
|
|
|
|
path string // 模板目录(绝对路径)
|
|
|
|
|
funcmap map[string]interface{} // FuncMap
|
|
|
|
|
contents *gmap.StringStringMap // 已解析的模板文件内容
|
2017-12-13 16:45:00 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 视图表
|
|
|
|
|
var viewMap = gmap.NewStringInterfaceMap()
|
|
|
|
|
|
|
|
|
|
// 获取或者创建一个视图对象
|
2018-01-03 10:38:53 +08:00
|
|
|
|
func Get(path string) *View {
|
2017-12-13 16:45:00 +08:00
|
|
|
|
if r := viewMap.Get(path); r != nil {
|
|
|
|
|
return r.(*View)
|
|
|
|
|
}
|
|
|
|
|
v := New(path)
|
|
|
|
|
viewMap.Set(path, v)
|
|
|
|
|
return v
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 生成一个视图对象
|
|
|
|
|
func New(path string) *View {
|
2018-01-08 17:07:05 +08:00
|
|
|
|
view := &View {
|
|
|
|
|
path : path,
|
|
|
|
|
funcmap : make(map[string]interface{}),
|
|
|
|
|
contents : gmap.NewStringStringMap(),
|
2017-12-13 16:45:00 +08:00
|
|
|
|
}
|
2018-01-08 17:07:05 +08:00
|
|
|
|
view.BindFunc("include", view.funcInclude)
|
|
|
|
|
return view
|
2017-12-13 16:45:00 +08:00
|
|
|
|
}
|
|
|
|
|
|
2018-01-04 18:04:11 +08:00
|
|
|
|
// 设置模板目录绝对路径
|
|
|
|
|
func (view *View) SetPath(path string) {
|
|
|
|
|
view.mu.Lock()
|
|
|
|
|
defer view.mu.Unlock()
|
|
|
|
|
view.path = path
|
|
|
|
|
}
|
2017-12-13 16:45:00 +08:00
|
|
|
|
|
2018-01-04 18:04:11 +08:00
|
|
|
|
// 获取模板目录绝对路径
|
|
|
|
|
func (view *View) GetPath() string {
|
2017-12-13 16:45:00 +08:00
|
|
|
|
view.mu.RLock()
|
|
|
|
|
defer view.mu.RUnlock()
|
2018-01-04 18:04:11 +08:00
|
|
|
|
return view.path
|
|
|
|
|
}
|
|
|
|
|
|
2018-01-08 17:07:05 +08:00
|
|
|
|
// 获取模板文件内容
|
|
|
|
|
func (view *View) GetTplContent() string {
|
|
|
|
|
view.mu.RLock()
|
|
|
|
|
defer view.mu.RUnlock()
|
|
|
|
|
return view.path
|
2017-12-13 16:45:00 +08:00
|
|
|
|
}
|
|
|
|
|
|
2018-01-08 17:07:05 +08:00
|
|
|
|
// 直接解析模板,返回解析后的内容
|
|
|
|
|
func (view *View) Parse(file string, params map[string]interface{}) ([]byte, error) {
|
|
|
|
|
// 获取模板文件路径及内容
|
|
|
|
|
path := strings.TrimRight(view.GetPath(), gfile.Separator) + gfile.Separator + file
|
|
|
|
|
content := view.contents.Get(path)
|
|
|
|
|
if content == "" {
|
|
|
|
|
content = gfile.GetContents(path)
|
|
|
|
|
if content == "" {
|
|
|
|
|
content = gfile.GetContents(file)
|
|
|
|
|
}
|
|
|
|
|
view.contents.Set(path, content)
|
2017-12-13 16:45:00 +08:00
|
|
|
|
}
|
2018-01-08 17:07:05 +08:00
|
|
|
|
if content == "" {
|
|
|
|
|
return nil, errors.New("invalid tpl \"" + file + "\"")
|
2017-12-13 16:45:00 +08:00
|
|
|
|
}
|
2018-01-08 17:07:05 +08:00
|
|
|
|
// 执行模板解析
|
2017-12-13 16:45:00 +08:00
|
|
|
|
buffer := bytes.NewBuffer(nil)
|
2018-01-08 17:07:05 +08:00
|
|
|
|
if tpl, err := template.New(path).Funcs(view.getFuncs()).Parse(content); err != nil {
|
2017-12-28 15:21:25 +08:00
|
|
|
|
return nil, err
|
2017-12-13 16:45:00 +08:00
|
|
|
|
} else {
|
2018-01-08 17:07:05 +08:00
|
|
|
|
if err := tpl.Execute(buffer, params); err != nil {
|
2017-12-28 15:21:25 +08:00
|
|
|
|
return nil, err
|
2017-12-13 16:45:00 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
2017-12-28 15:21:25 +08:00
|
|
|
|
return buffer.Bytes(), nil
|
2017-12-13 16:45:00 +08:00
|
|
|
|
}
|
|
|
|
|
|
2018-01-08 17:07:05 +08:00
|
|
|
|
// 绑定自定义函数,该函数是全局有效,即调用之后每个线程都会生效,因此有并发安全控制
|
|
|
|
|
func (view *View) BindFunc(name string, function interface{}) {
|
|
|
|
|
view.mu.Lock()
|
|
|
|
|
defer view.mu.Unlock()
|
|
|
|
|
view.funcmap[name] = function
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 获取模板自定义函数,每一次都是一份拷贝
|
|
|
|
|
func (view *View) getFuncs() map[string]interface{} {
|
|
|
|
|
m := make(map[string]interface{})
|
|
|
|
|
view.mu.RLock()
|
|
|
|
|
for k, v := range view.funcmap {
|
|
|
|
|
m[k] = v
|
|
|
|
|
}
|
|
|
|
|
view.mu.RUnlock()
|
|
|
|
|
return m
|
|
|
|
|
}
|
|
|
|
|
|
2017-12-30 18:35:24 +08:00
|
|
|
|
// 模板内置方法:include
|
2018-01-08 17:07:05 +08:00
|
|
|
|
func (view *View) funcInclude(file string, datas...map[string]interface{}) template.HTML {
|
|
|
|
|
var data map[string]interface{} = nil
|
|
|
|
|
if len(datas) > 0 {
|
|
|
|
|
data = datas[0]
|
|
|
|
|
}
|
|
|
|
|
content, err := view.Parse(file, data)
|
2017-12-30 18:35:24 +08:00
|
|
|
|
if err != nil {
|
|
|
|
|
return template.HTML(err.Error())
|
|
|
|
|
}
|
|
|
|
|
return template.HTML(content)
|
|
|
|
|
}
|