github.com/zhongdalu/gf@v1.0.0/g/os/gview/gview.go (about) 1 // Copyright 2017 gf Author(https://github.com/zhongdalu/gf). All Rights Reserved. 2 // 3 // This Source Code Form is subject to the terms of the MIT License. 4 // If a copy of the MIT was not distributed with this file, 5 // You can obtain one at https://github.com/zhongdalu/gf. 6 7 // Package gview implements a template engine based on text/template. 8 package gview 9 10 import ( 11 "bytes" 12 "errors" 13 "fmt" 14 "github.com/zhongdalu/gf" 15 "github.com/zhongdalu/gf/g/container/garray" 16 "github.com/zhongdalu/gf/g/internal/cmdenv" 17 "github.com/zhongdalu/gf/g/os/gfile" 18 "github.com/zhongdalu/gf/g/os/glog" 19 "github.com/zhongdalu/gf/g/os/gspath" 20 "sync" 21 ) 22 23 // View object for template engine. 24 type View struct { 25 mu sync.RWMutex 26 paths *garray.StringArray // Searching path array. 27 data map[string]interface{} // Global template variables. 28 funcMap map[string]interface{} // Global template function map. 29 delimiters []string // Customized template delimiters. 30 } 31 32 // Params is type for template params. 33 type Params = map[string]interface{} 34 35 // FuncMap is type for custom template functions. 36 type FuncMap = map[string]interface{} 37 38 // Default view object. 39 var defaultViewObj *View 40 41 // checkAndInitDefaultView checks and initializes the default view object. 42 // The default view object will be initialized just once. 43 func checkAndInitDefaultView() { 44 if defaultViewObj == nil { 45 defaultViewObj = New() 46 } 47 } 48 49 // ParseContent parses the template content directly using the default view object 50 // and returns the parsed content. 51 func ParseContent(content string, params Params) (string, error) { 52 checkAndInitDefaultView() 53 return defaultViewObj.ParseContent(content, params) 54 } 55 56 // New returns a new view object. 57 // The parameter <path> specifies the template directory path to load template files. 58 func New(path ...string) *View { 59 view := &View{ 60 paths: garray.NewStringArray(), 61 data: make(map[string]interface{}), 62 funcMap: make(map[string]interface{}), 63 delimiters: make([]string, 2), 64 } 65 if len(path) > 0 && len(path[0]) > 0 { 66 view.SetPath(path[0]) 67 } else { 68 // Customized dir path from env/cmd. 69 if envPath := cmdenv.Get("gf.gview.path").String(); envPath != "" { 70 if gfile.Exists(envPath) { 71 view.SetPath(envPath) 72 } else { 73 if errorPrint() { 74 glog.Errorf("Template directory path does not exist: %s", envPath) 75 } 76 } 77 } else { 78 // Dir path of working dir. 79 view.SetPath(gfile.Pwd()) 80 // Dir path of binary. 81 if selfPath := gfile.SelfDir(); selfPath != "" && gfile.Exists(selfPath) { 82 view.AddPath(selfPath) 83 } 84 // Dir path of main package. 85 if mainPath := gfile.MainPkgPath(); mainPath != "" && gfile.Exists(mainPath) { 86 view.AddPath(mainPath) 87 } 88 } 89 } 90 view.SetDelimiters("{{", "}}") 91 // default build-in variables. 92 view.data["GF"] = map[string]interface{}{ 93 "version": gf.VERSION, 94 } 95 // default build-in functions. 96 view.BindFunc("eq", view.funcEq) 97 view.BindFunc("ne", view.funcNe) 98 view.BindFunc("lt", view.funcLt) 99 view.BindFunc("le", view.funcLe) 100 view.BindFunc("gt", view.funcGt) 101 view.BindFunc("ge", view.funcGe) 102 view.BindFunc("text", view.funcText) 103 view.BindFunc("html", view.funcHtmlEncode) 104 view.BindFunc("htmlencode", view.funcHtmlEncode) 105 view.BindFunc("htmldecode", view.funcHtmlDecode) 106 view.BindFunc("url", view.funcUrlEncode) 107 view.BindFunc("urlencode", view.funcUrlEncode) 108 view.BindFunc("urldecode", view.funcUrlDecode) 109 view.BindFunc("date", view.funcDate) 110 view.BindFunc("substr", view.funcSubStr) 111 view.BindFunc("strlimit", view.funcStrLimit) 112 view.BindFunc("compare", view.funcCompare) 113 view.BindFunc("hidestr", view.funcHideStr) 114 view.BindFunc("highlight", view.funcHighlight) 115 view.BindFunc("toupper", view.funcToUpper) 116 view.BindFunc("tolower", view.funcToLower) 117 view.BindFunc("nl2br", view.funcNl2Br) 118 view.BindFunc("include", view.funcInclude) 119 return view 120 } 121 122 // SetPath sets the template directory path for template file search. 123 // The parameter <path> can be absolute or relative path, but absolute path is suggested. 124 func (view *View) SetPath(path string) error { 125 // Absolute path. 126 realPath := gfile.RealPath(path) 127 if realPath == "" { 128 // Relative path. 129 view.paths.RLockFunc(func(array []string) { 130 for _, v := range array { 131 if path, _ := gspath.Search(v, path); path != "" { 132 realPath = path 133 break 134 } 135 } 136 }) 137 } 138 // Path not exist. 139 if realPath == "" { 140 buffer := bytes.NewBuffer(nil) 141 if view.paths.Len() > 0 { 142 buffer.WriteString(fmt.Sprintf("[gview] SetPath failed: cannot find directory \"%s\" in following paths:", path)) 143 view.paths.RLockFunc(func(array []string) { 144 for k, v := range array { 145 buffer.WriteString(fmt.Sprintf("\n%d. %s", k+1, v)) 146 } 147 }) 148 } else { 149 buffer.WriteString(fmt.Sprintf(`[gview] SetPath failed: path "%s" does not exist`, path)) 150 } 151 err := errors.New(buffer.String()) 152 if errorPrint() { 153 glog.Error(err) 154 } 155 return err 156 } 157 // Should be a directory. 158 if !gfile.IsDir(realPath) { 159 err := errors.New(fmt.Sprintf(`[gview] SetPath failed: path "%s" should be directory type`, path)) 160 if errorPrint() { 161 glog.Error(err) 162 } 163 return err 164 } 165 // Repeated path check. 166 if view.paths.Search(realPath) != -1 { 167 return nil 168 } 169 view.paths.Clear() 170 view.paths.Append(realPath) 171 //glog.Debug("[gview] SetPath:", realPath) 172 return nil 173 } 174 175 // AddPath adds a absolute or relative path to the search paths. 176 func (view *View) AddPath(path string) error { 177 // Absolute path. 178 realPath := gfile.RealPath(path) 179 if realPath == "" { 180 // Relative path. 181 view.paths.RLockFunc(func(array []string) { 182 for _, v := range array { 183 if path, _ := gspath.Search(v, path); path != "" { 184 realPath = path 185 break 186 } 187 } 188 }) 189 } 190 // Path not exist. 191 if realPath == "" { 192 buffer := bytes.NewBuffer(nil) 193 if view.paths.Len() > 0 { 194 buffer.WriteString(fmt.Sprintf("[gview] AddPath failed: cannot find directory \"%s\" in following paths:", path)) 195 view.paths.RLockFunc(func(array []string) { 196 for k, v := range array { 197 buffer.WriteString(fmt.Sprintf("\n%d. %s", k+1, v)) 198 } 199 }) 200 } else { 201 buffer.WriteString(fmt.Sprintf(`[gview] AddPath failed: path "%s" does not exist`, path)) 202 } 203 err := errors.New(buffer.String()) 204 if errorPrint() { 205 glog.Error(err) 206 } 207 return err 208 } 209 // realPath should be type of folder. 210 if !gfile.IsDir(realPath) { 211 err := errors.New(fmt.Sprintf(`[gview] AddPath failed: path "%s" should be directory type`, path)) 212 if errorPrint() { 213 glog.Error(err) 214 } 215 return err 216 } 217 // Repeated path check. 218 if view.paths.Search(realPath) != -1 { 219 return nil 220 } 221 view.paths.Append(realPath) 222 return nil 223 }