2019-02-02 16:18:25 +08:00
|
|
|
|
// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved.
|
2017-12-29 16:03:30 +08:00
|
|
|
|
//
|
|
|
|
|
// 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,
|
2019-02-02 16:18:25 +08:00
|
|
|
|
// You can obtain one at https://github.com/gogf/gf.
|
2017-12-29 16:03:30 +08:00
|
|
|
|
|
2019-01-15 23:27:47 +08:00
|
|
|
|
// Package gview implements a template engine based on text/template.
|
2017-12-13 16:45:00 +08:00
|
|
|
|
package gview
|
|
|
|
|
|
|
|
|
|
import (
|
2018-11-18 19:37:42 +08:00
|
|
|
|
"bytes"
|
|
|
|
|
"errors"
|
2018-10-31 22:39:58 +08:00
|
|
|
|
"fmt"
|
2019-02-02 16:18:25 +08:00
|
|
|
|
"github.com/gogf/gf"
|
|
|
|
|
"github.com/gogf/gf/g/container/garray"
|
|
|
|
|
"github.com/gogf/gf/g/encoding/ghash"
|
|
|
|
|
"github.com/gogf/gf/g/encoding/ghtml"
|
|
|
|
|
"github.com/gogf/gf/g/encoding/gurl"
|
2019-04-03 23:39:31 +08:00
|
|
|
|
"github.com/gogf/gf/g/internal/cmdenv"
|
2019-02-02 16:18:25 +08:00
|
|
|
|
"github.com/gogf/gf/g/os/gfcache"
|
|
|
|
|
"github.com/gogf/gf/g/os/gfile"
|
|
|
|
|
"github.com/gogf/gf/g/os/glog"
|
|
|
|
|
"github.com/gogf/gf/g/os/gspath"
|
|
|
|
|
"github.com/gogf/gf/g/os/gtime"
|
|
|
|
|
"github.com/gogf/gf/g/os/gview/internal/text/template"
|
|
|
|
|
"github.com/gogf/gf/g/text/gstr"
|
2019-03-27 11:48:53 +08:00
|
|
|
|
"github.com/gogf/gf/g/util/gconv"
|
2018-10-29 22:58:30 +08:00
|
|
|
|
"strings"
|
2017-12-13 16:45:00 +08:00
|
|
|
|
"sync"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// 视图对象
|
|
|
|
|
type View struct {
|
2018-08-06 12:39:55 +08:00
|
|
|
|
mu sync.RWMutex
|
2018-11-30 09:48:57 +08:00
|
|
|
|
paths *garray.StringArray // 模板查找目录(绝对路径)
|
2018-10-15 22:39:56 +08:00
|
|
|
|
data map[string]interface{} // 模板变量
|
2018-08-06 12:39:55 +08:00
|
|
|
|
funcmap map[string]interface{} // FuncMap
|
|
|
|
|
delimiters []string // 模板变量分隔符号
|
2017-12-13 16:45:00 +08:00
|
|
|
|
}
|
|
|
|
|
|
2018-08-23 21:55:27 +08:00
|
|
|
|
// 模板变量
|
|
|
|
|
type Params = map[string]interface{}
|
|
|
|
|
|
|
|
|
|
// 函数映射表
|
|
|
|
|
type FuncMap = map[string]interface{}
|
|
|
|
|
|
2018-04-22 22:17:20 +08:00
|
|
|
|
// 默认的视图对象
|
2019-04-03 23:39:31 +08:00
|
|
|
|
var defaultViewObj *View
|
2018-09-26 09:58:49 +08:00
|
|
|
|
|
2019-03-27 11:48:53 +08:00
|
|
|
|
// 初始化默认的视图对象, 默认加载包不会初始化,使用包方法才会初始化模板引擎对象。
|
2018-09-26 09:58:49 +08:00
|
|
|
|
func checkAndInitDefaultView() {
|
2019-04-03 23:39:31 +08:00
|
|
|
|
if defaultViewObj == nil {
|
|
|
|
|
defaultViewObj = New(gfile.Pwd())
|
2018-09-26 09:58:49 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
2018-04-22 22:17:20 +08:00
|
|
|
|
|
|
|
|
|
// 直接解析模板内容,返回解析后的内容
|
2018-10-15 22:58:54 +08:00
|
|
|
|
func ParseContent(content string, params Params) ([]byte, error) {
|
2018-09-26 09:58:49 +08:00
|
|
|
|
checkAndInitDefaultView()
|
2019-04-03 23:39:31 +08:00
|
|
|
|
return defaultViewObj.ParseContent(content, params)
|
2018-04-22 22:17:20 +08:00
|
|
|
|
}
|
|
|
|
|
|
2017-12-13 16:45:00 +08:00
|
|
|
|
// 生成一个视图对象
|
2018-11-18 19:37:42 +08:00
|
|
|
|
func New(path...string) *View {
|
2018-01-08 17:07:05 +08:00
|
|
|
|
view := &View {
|
2019-02-01 22:00:58 +08:00
|
|
|
|
paths : garray.NewStringArray(),
|
2018-10-15 22:39:56 +08:00
|
|
|
|
data : make(map[string]interface{}),
|
2018-08-06 12:39:55 +08:00
|
|
|
|
funcmap : make(map[string]interface{}),
|
|
|
|
|
delimiters : make([]string, 2),
|
2017-12-13 16:45:00 +08:00
|
|
|
|
}
|
2018-11-18 19:37:42 +08:00
|
|
|
|
if len(path) > 0 && len(path[0]) > 0 {
|
|
|
|
|
view.SetPath(path[0])
|
2019-04-03 23:39:31 +08:00
|
|
|
|
} else {
|
|
|
|
|
// Customized dir path from env/cmd.
|
|
|
|
|
if envPath := cmdenv.Get("gf.gview.path").String(); envPath != "" {
|
|
|
|
|
if gfile.Exists(envPath) {
|
|
|
|
|
view.SetPath(envPath)
|
|
|
|
|
} else {
|
|
|
|
|
glog.Errorfln("Template directory path does not exist: %s", envPath)
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
// Dir path of working dir.
|
|
|
|
|
view.SetPath(gfile.Pwd())
|
|
|
|
|
// Dir path of binary.
|
|
|
|
|
if selfPath := gfile.SelfDir(); selfPath != "" && gfile.Exists(selfPath) {
|
|
|
|
|
view.AddPath(selfPath)
|
|
|
|
|
}
|
|
|
|
|
// Dir path of main package.
|
|
|
|
|
if mainPath := gfile.MainPkgPath(); mainPath != "" && gfile.Exists(mainPath) {
|
|
|
|
|
view.AddPath(mainPath)
|
|
|
|
|
}
|
|
|
|
|
}
|
2018-11-18 19:37:42 +08:00
|
|
|
|
}
|
2018-08-06 12:39:55 +08:00
|
|
|
|
view.SetDelimiters("{{", "}}")
|
2018-12-08 18:18:05 +08:00
|
|
|
|
// 内置变量
|
|
|
|
|
view.data["GF"] = map[string]interface{} {
|
|
|
|
|
"version" : gf.VERSION,
|
|
|
|
|
}
|
2018-08-23 21:55:27 +08:00
|
|
|
|
// 内置方法
|
2018-10-29 22:58:30 +08:00
|
|
|
|
view.BindFunc("text", view.funcText)
|
|
|
|
|
view.BindFunc("html", view.funcHtmlEncode)
|
|
|
|
|
view.BindFunc("htmlencode", view.funcHtmlEncode)
|
|
|
|
|
view.BindFunc("htmldecode", view.funcHtmlDecode)
|
|
|
|
|
view.BindFunc("url", view.funcUrlEncode)
|
|
|
|
|
view.BindFunc("urlencode", view.funcUrlEncode)
|
|
|
|
|
view.BindFunc("urldecode", view.funcUrlDecode)
|
|
|
|
|
view.BindFunc("date", view.funcDate)
|
|
|
|
|
view.BindFunc("substr", view.funcSubStr)
|
2018-10-31 22:39:58 +08:00
|
|
|
|
view.BindFunc("strlimit", view.funcStrLimit)
|
2018-10-29 22:58:30 +08:00
|
|
|
|
view.BindFunc("compare", view.funcCompare)
|
2018-10-31 22:39:58 +08:00
|
|
|
|
view.BindFunc("hidestr", view.funcHideStr)
|
|
|
|
|
view.BindFunc("highlight", view.funcHighlight)
|
|
|
|
|
view.BindFunc("toupper", view.funcToUpper)
|
|
|
|
|
view.BindFunc("tolower", view.funcToLower)
|
|
|
|
|
view.BindFunc("nl2br", view.funcNl2Br)
|
2018-10-29 22:58:30 +08:00
|
|
|
|
view.BindFunc("include", view.funcInclude)
|
2018-01-08 17:07:05 +08:00
|
|
|
|
return view
|
2017-12-13 16:45:00 +08:00
|
|
|
|
}
|
|
|
|
|
|
2018-01-04 18:04:11 +08:00
|
|
|
|
// 设置模板目录绝对路径
|
2018-05-03 16:12:01 +08:00
|
|
|
|
func (view *View) SetPath(path string) error {
|
2019-03-27 11:48:53 +08:00
|
|
|
|
// 判断绝对路径(或者工作目录下目录)
|
2018-11-30 09:48:57 +08:00
|
|
|
|
realPath := gfile.RealPath(path)
|
2019-02-14 13:38:52 +08:00
|
|
|
|
if realPath == "" {
|
2019-03-27 11:48:53 +08:00
|
|
|
|
// 判断相对路径
|
|
|
|
|
view.paths.RLockFunc(func(array []string) {
|
|
|
|
|
for _, v := range array {
|
|
|
|
|
if path, _ := gspath.Search(v, path); path != "" {
|
|
|
|
|
realPath = path
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
})
|
2019-02-14 13:38:52 +08:00
|
|
|
|
}
|
2019-03-27 11:48:53 +08:00
|
|
|
|
// 目录不存在错误处理
|
2018-11-30 09:48:57 +08:00
|
|
|
|
if realPath == "" {
|
2019-03-27 11:48:53 +08:00
|
|
|
|
buffer := bytes.NewBuffer(nil)
|
|
|
|
|
if view.paths.Len() > 0 {
|
|
|
|
|
buffer.WriteString(fmt.Sprintf("[gview] SetPath failed: cannot find directory \"%s\" in following paths:", path))
|
|
|
|
|
view.paths.RLockFunc(func(array []string) {
|
|
|
|
|
for k, v := range array {
|
|
|
|
|
buffer.WriteString(fmt.Sprintf("\n%d. %s",k + 1, v))
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
} else {
|
|
|
|
|
buffer.WriteString(fmt.Sprintf(`[gview] SetPath failed: path "%s" does not exist`, path))
|
|
|
|
|
}
|
|
|
|
|
err := errors.New(buffer.String())
|
|
|
|
|
glog.Error(err)
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
// 路径必须为目录类型
|
|
|
|
|
if !gfile.IsDir(realPath) {
|
|
|
|
|
err := errors.New(fmt.Sprintf(`[gview] SetPath failed: path "%s" should be directory type`, path))
|
|
|
|
|
glog.Error(err)
|
2018-10-30 23:58:10 +08:00
|
|
|
|
return err
|
|
|
|
|
}
|
2019-02-27 22:53:39 +08:00
|
|
|
|
// 重复判断
|
|
|
|
|
if view.paths.Search(realPath) != -1 {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
2018-11-30 09:48:57 +08:00
|
|
|
|
view.paths.Clear()
|
|
|
|
|
view.paths.Append(realPath)
|
2019-03-01 23:45:55 +08:00
|
|
|
|
//glog.Debug("[gview] SetPath:", realPath)
|
2018-10-30 23:58:10 +08:00
|
|
|
|
return nil
|
2018-01-04 18:04:11 +08:00
|
|
|
|
}
|
2017-12-13 16:45:00 +08:00
|
|
|
|
|
2018-05-03 16:12:01 +08:00
|
|
|
|
// 添加模板目录搜索路径
|
|
|
|
|
func (view *View) AddPath(path string) error {
|
2019-03-27 11:48:53 +08:00
|
|
|
|
// 判断绝对路径(或者工作目录下目录)
|
2018-11-30 09:48:57 +08:00
|
|
|
|
realPath := gfile.RealPath(path)
|
2019-02-14 13:38:52 +08:00
|
|
|
|
if realPath == "" {
|
2019-03-27 11:48:53 +08:00
|
|
|
|
// 判断相对路径
|
|
|
|
|
view.paths.RLockFunc(func(array []string) {
|
|
|
|
|
for _, v := range array {
|
|
|
|
|
if path, _ := gspath.Search(v, path); path != "" {
|
|
|
|
|
realPath = path
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
})
|
2019-02-14 13:38:52 +08:00
|
|
|
|
}
|
2019-03-27 11:48:53 +08:00
|
|
|
|
// 目录不存在错误处理
|
2018-11-30 09:48:57 +08:00
|
|
|
|
if realPath == "" {
|
2019-03-27 11:48:53 +08:00
|
|
|
|
buffer := bytes.NewBuffer(nil)
|
|
|
|
|
if view.paths.Len() > 0 {
|
|
|
|
|
buffer.WriteString(fmt.Sprintf("[gview] AddPath failed: cannot find directory \"%s\" in following paths:", path))
|
|
|
|
|
view.paths.RLockFunc(func(array []string) {
|
|
|
|
|
for k, v := range array {
|
|
|
|
|
buffer.WriteString(fmt.Sprintf("\n%d. %s",k + 1, v))
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
} else {
|
|
|
|
|
buffer.WriteString(fmt.Sprintf(`[gview] AddPath failed: path "%s" does not exist`, path))
|
|
|
|
|
}
|
|
|
|
|
err := errors.New(buffer.String())
|
|
|
|
|
glog.Error(err)
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
// 路径必须为目录类型
|
|
|
|
|
if !gfile.IsDir(realPath) {
|
|
|
|
|
err := errors.New(fmt.Sprintf(`[gview] AddPath failed: path "%s" should be directory type`, path))
|
|
|
|
|
glog.Error(err)
|
2018-10-30 23:58:10 +08:00
|
|
|
|
return err
|
|
|
|
|
}
|
2019-02-27 22:53:39 +08:00
|
|
|
|
// 重复判断
|
|
|
|
|
if view.paths.Search(realPath) != -1 {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
2018-11-30 09:48:57 +08:00
|
|
|
|
view.paths.Append(realPath)
|
2019-03-01 23:45:55 +08:00
|
|
|
|
//glog.Debug("[gview] AddPath:", realPath)
|
2018-10-30 23:58:10 +08:00
|
|
|
|
return nil
|
2017-12-13 16:45:00 +08:00
|
|
|
|
}
|
|
|
|
|
|
2018-10-15 22:39:56 +08:00
|
|
|
|
// 批量绑定模板变量,即调用之后每个线程都会生效,因此有并发安全控制
|
|
|
|
|
func (view *View) Assigns(data Params) {
|
|
|
|
|
view.mu.Lock()
|
|
|
|
|
for k, v := range data {
|
|
|
|
|
view.data[k] = v
|
|
|
|
|
}
|
|
|
|
|
view.mu.Unlock()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 绑定模板变量,即调用之后每个线程都会生效,因此有并发安全控制
|
|
|
|
|
func (view *View) Assign(key string, value interface{}) {
|
|
|
|
|
view.mu.Lock()
|
|
|
|
|
view.data[key] = value
|
|
|
|
|
view.mu.Unlock()
|
|
|
|
|
}
|
|
|
|
|
|
2018-04-17 13:54:33 +08:00
|
|
|
|
// 解析模板,返回解析后的内容
|
2018-10-15 22:45:04 +08:00
|
|
|
|
func (view *View) Parse(file string, params Params, funcmap...map[string]interface{}) ([]byte, error) {
|
2018-11-30 09:48:57 +08:00
|
|
|
|
path := ""
|
|
|
|
|
view.paths.RLockFunc(func(array []string) {
|
|
|
|
|
for _, v := range array {
|
|
|
|
|
if path, _ = gspath.Search(v, file); path != "" {
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
})
|
2018-08-21 21:18:56 +08:00
|
|
|
|
if path == "" {
|
2018-11-30 09:48:57 +08:00
|
|
|
|
buffer := bytes.NewBuffer(nil)
|
2019-03-01 23:45:55 +08:00
|
|
|
|
if view.paths.Len() > 0 {
|
|
|
|
|
buffer.WriteString(fmt.Sprintf("[gview] cannot find template file \"%s\" in following paths:", file))
|
|
|
|
|
view.paths.RLockFunc(func(array []string) {
|
|
|
|
|
for k, v := range array {
|
|
|
|
|
buffer.WriteString(fmt.Sprintf("\n%d. %s",k + 1, v))
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
} else {
|
|
|
|
|
buffer.WriteString(fmt.Sprintf("[gview] cannot find template file \"%s\" with no path set/add", file))
|
|
|
|
|
}
|
2018-11-30 09:48:57 +08:00
|
|
|
|
glog.Error(buffer.String())
|
|
|
|
|
return nil, errors.New(fmt.Sprintf(`tpl "%s" not found`, file))
|
2017-12-13 16:45:00 +08:00
|
|
|
|
}
|
2018-08-21 21:18:56 +08:00
|
|
|
|
content := gfcache.GetContents(path)
|
2018-05-03 16:12:01 +08:00
|
|
|
|
// 执行模板解析,互斥锁主要是用于funcmap
|
2018-04-18 16:16:09 +08:00
|
|
|
|
view.mu.RLock()
|
|
|
|
|
defer view.mu.RUnlock()
|
2017-12-13 16:45:00 +08:00
|
|
|
|
buffer := bytes.NewBuffer(nil)
|
2018-08-23 21:55:27 +08:00
|
|
|
|
tplobj := template.New(path).Delims(view.delimiters[0], view.delimiters[1]).Funcs(view.funcmap)
|
|
|
|
|
if len(funcmap) > 0 {
|
|
|
|
|
tplobj = tplobj.Funcs(funcmap[0])
|
|
|
|
|
}
|
|
|
|
|
if tpl, err := tplobj.Parse(content); err != nil {
|
2017-12-28 15:21:25 +08:00
|
|
|
|
return nil, err
|
2018-04-17 13:54:33 +08:00
|
|
|
|
} else {
|
2018-10-15 22:39:56 +08:00
|
|
|
|
// 注意模板变量赋值不能改变已有的params或者view.data的值,因为这两个变量都是指针
|
|
|
|
|
// 因此在必要条件下,需要合并两个map的值到一个新的map
|
|
|
|
|
vars := (map[string]interface{})(nil)
|
|
|
|
|
if len(view.data) > 0 {
|
|
|
|
|
if len(params) > 0 {
|
|
|
|
|
vars = make(map[string]interface{}, len(view.data) + len(params))
|
|
|
|
|
for k, v := range params {
|
|
|
|
|
vars[k] = v
|
|
|
|
|
}
|
|
|
|
|
for k, v := range view.data {
|
|
|
|
|
vars[k] = v
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
vars = view.data
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
vars = params
|
|
|
|
|
}
|
|
|
|
|
if err := tpl.Execute(buffer, vars); err != nil {
|
2018-04-17 13:54:33 +08:00
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return buffer.Bytes(), nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 直接解析模板内容,返回解析后的内容
|
2018-10-15 22:45:04 +08:00
|
|
|
|
func (view *View) ParseContent(content string, params Params, funcmap...map[string]interface{}) ([]byte, error) {
|
2018-04-18 10:15:07 +08:00
|
|
|
|
view.mu.RLock()
|
|
|
|
|
defer view.mu.RUnlock()
|
2018-04-17 17:36:59 +08:00
|
|
|
|
name := gconv.String(ghash.BKDRHash64([]byte(content)))
|
2018-04-17 13:54:33 +08:00
|
|
|
|
buffer := bytes.NewBuffer(nil)
|
2018-08-23 21:55:27 +08:00
|
|
|
|
tplobj := template.New(name).Delims(view.delimiters[0], view.delimiters[1]).Funcs(view.funcmap)
|
|
|
|
|
if len(funcmap) > 0 {
|
|
|
|
|
tplobj = tplobj.Funcs(funcmap[0])
|
|
|
|
|
}
|
|
|
|
|
if tpl, err := tplobj.Parse(content); err != nil {
|
2018-04-17 13:54:33 +08:00
|
|
|
|
return nil, err
|
2017-12-13 16:45:00 +08:00
|
|
|
|
} else {
|
2018-10-15 22:39:56 +08:00
|
|
|
|
// 注意模板变量赋值不能改变已有的params或者view.data的值,因为这两个变量都是指针
|
|
|
|
|
// 因此在必要条件下,需要合并两个map的值到一个新的map
|
|
|
|
|
vars := (map[string]interface{})(nil)
|
|
|
|
|
if len(view.data) > 0 {
|
|
|
|
|
if len(params) > 0 {
|
|
|
|
|
vars = make(map[string]interface{}, len(view.data) + len(params))
|
|
|
|
|
for k, v := range params {
|
|
|
|
|
vars[k] = v
|
|
|
|
|
}
|
|
|
|
|
for k, v := range view.data {
|
|
|
|
|
vars[k] = v
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
vars = view.data
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
vars = params
|
|
|
|
|
}
|
|
|
|
|
if err := tpl.Execute(buffer, vars); 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-08-06 12:39:55 +08:00
|
|
|
|
// 设置模板变量解析分隔符号
|
|
|
|
|
func (view *View) SetDelimiters(left, right string) {
|
|
|
|
|
view.delimiters[0] = left
|
|
|
|
|
view.delimiters[1] = right
|
|
|
|
|
}
|
|
|
|
|
|
2018-01-08 17:07:05 +08:00
|
|
|
|
// 绑定自定义函数,该函数是全局有效,即调用之后每个线程都会生效,因此有并发安全控制
|
|
|
|
|
func (view *View) BindFunc(name string, function interface{}) {
|
|
|
|
|
view.mu.Lock()
|
|
|
|
|
view.funcmap[name] = function
|
2018-04-18 10:15:07 +08:00
|
|
|
|
view.mu.Unlock()
|
2018-01-08 17:07:05 +08:00
|
|
|
|
}
|
|
|
|
|
|
2017-12-30 18:35:24 +08:00
|
|
|
|
// 模板内置方法:include
|
2018-10-29 22:58:30 +08:00
|
|
|
|
func (view *View) funcInclude(file string, data...map[string]interface{}) string {
|
2018-04-18 10:15:07 +08:00
|
|
|
|
var m map[string]interface{} = nil
|
|
|
|
|
if len(data) > 0 {
|
|
|
|
|
m = data[0]
|
2018-01-08 17:07:05 +08:00
|
|
|
|
}
|
2018-04-18 10:15:07 +08:00
|
|
|
|
content, err := view.Parse(file, m)
|
2017-12-30 18:35:24 +08:00
|
|
|
|
if err != nil {
|
2018-10-29 22:58:30 +08:00
|
|
|
|
return err.Error()
|
2017-12-30 18:35:24 +08:00
|
|
|
|
}
|
2018-10-29 22:58:30 +08:00
|
|
|
|
return string(content)
|
2017-12-30 18:35:24 +08:00
|
|
|
|
}
|
2018-09-03 13:05:41 +08:00
|
|
|
|
|
2018-09-15 16:40:13 +08:00
|
|
|
|
// 模板内置方法:text
|
|
|
|
|
func (view *View) funcText(html interface{}) string {
|
|
|
|
|
return ghtml.StripTags(gconv.String(html))
|
|
|
|
|
}
|
|
|
|
|
|
2018-10-15 13:46:51 +08:00
|
|
|
|
// 模板内置方法:html
|
2018-10-29 22:58:30 +08:00
|
|
|
|
func (view *View) funcHtmlEncode(html interface{}) string {
|
|
|
|
|
return ghtml.Entities(gconv.String(html))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 模板内置方法:htmldecode
|
|
|
|
|
func (view *View) funcHtmlDecode(html interface{}) string {
|
|
|
|
|
return ghtml.EntitiesDecode(gconv.String(html))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 模板内置方法:url
|
|
|
|
|
func (view *View) funcUrlEncode(url interface{}) string {
|
|
|
|
|
return gurl.Encode(gconv.String(url))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 模板内置方法:urldecode
|
|
|
|
|
func (view *View) funcUrlDecode(url interface{}) string {
|
|
|
|
|
if content, err := gurl.Decode(gconv.String(url)); err == nil {
|
|
|
|
|
return content
|
|
|
|
|
} else {
|
|
|
|
|
return err.Error()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 模板内置方法:date
|
2018-12-05 15:48:15 +08:00
|
|
|
|
func (view *View) funcDate(format string, timestamp...interface{}) string {
|
|
|
|
|
t := int64(0)
|
|
|
|
|
if len(timestamp) > 0 {
|
|
|
|
|
t = gconv.Int64(timestamp[0])
|
2018-12-05 15:52:38 +08:00
|
|
|
|
}
|
|
|
|
|
if t == 0 {
|
2018-12-05 15:48:15 +08:00
|
|
|
|
t = gtime.Millisecond()
|
|
|
|
|
}
|
|
|
|
|
return gtime.NewFromTimeStamp(t).Format(format)
|
2018-10-29 22:58:30 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 模板内置方法:compare
|
|
|
|
|
func (view *View) funcCompare(value1, value2 interface{}) int {
|
|
|
|
|
return strings.Compare(gconv.String(value1), gconv.String(value2))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 模板内置方法:substr
|
|
|
|
|
func (view *View) funcSubStr(start, end int, str interface{}) string {
|
|
|
|
|
return gstr.SubStr(gconv.String(str), start, end)
|
2018-10-15 13:46:51 +08:00
|
|
|
|
}
|
|
|
|
|
|
2018-10-31 22:39:58 +08:00
|
|
|
|
// 模板内置方法:strlimit
|
|
|
|
|
func (view *View) funcStrLimit(length int, suffix string, str interface{}) string {
|
|
|
|
|
return gstr.StrLimit(gconv.String(str), length, suffix)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 模板内置方法:highlight
|
|
|
|
|
func (view *View) funcHighlight(key string, color string, str interface{}) string {
|
|
|
|
|
return gstr.Replace(gconv.String(str), key, fmt.Sprintf(`<span style="color:%s;">%s</span>`, color, key))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 模板内置方法:hidestr
|
|
|
|
|
func (view *View) funcHideStr(percent int, hide string, str interface{}) string {
|
|
|
|
|
return gstr.HideStr(gconv.String(str), percent, hide)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 模板内置方法:toupper
|
|
|
|
|
func (view *View) funcToUpper(str interface{}) string {
|
|
|
|
|
return gstr.ToUpper(gconv.String(str))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 模板内置方法:toupper
|
|
|
|
|
func (view *View) funcToLower(str interface{}) string {
|
|
|
|
|
return gstr.ToLower(gconv.String(str))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 模板内置方法:nl2br
|
|
|
|
|
func (view *View) funcNl2Br(str interface{}) string {
|
|
|
|
|
return gstr.Nl2Br(gconv.String(str))
|
|
|
|
|
}
|
|
|
|
|
|
2018-10-15 13:46:51 +08:00
|
|
|
|
|