github.com/wangyougui/gf/v2@v2.6.5/os/gview/gview_config.go (about) 1 // Copyright GoFrame Author(https://goframe.org). 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/wangyougui/gf. 6 7 package gview 8 9 import ( 10 "context" 11 12 "github.com/wangyougui/gf/v2/errors/gcode" 13 "github.com/wangyougui/gf/v2/errors/gerror" 14 "github.com/wangyougui/gf/v2/i18n/gi18n" 15 "github.com/wangyougui/gf/v2/internal/intlog" 16 "github.com/wangyougui/gf/v2/os/gfile" 17 "github.com/wangyougui/gf/v2/os/glog" 18 "github.com/wangyougui/gf/v2/os/gres" 19 "github.com/wangyougui/gf/v2/os/gspath" 20 "github.com/wangyougui/gf/v2/util/gconv" 21 "github.com/wangyougui/gf/v2/util/gutil" 22 ) 23 24 // Config is the configuration object for template engine. 25 type Config struct { 26 Paths []string `json:"paths"` // Searching array for path, NOT concurrent-safe for performance purpose. 27 Data map[string]interface{} `json:"data"` // Global template variables including configuration. 28 DefaultFile string `json:"defaultFile"` // Default template file for parsing. 29 Delimiters []string `json:"delimiters"` // Custom template delimiters. 30 AutoEncode bool `json:"autoEncode"` // Automatically encodes and provides safe html output, which is good for avoiding XSS. 31 I18nManager *gi18n.Manager `json:"-"` // I18n manager for the view. 32 } 33 34 const ( 35 // Default template file for parsing. 36 defaultParsingFile = "index.html" 37 ) 38 39 // DefaultConfig creates and returns a configuration object with default configurations. 40 func DefaultConfig() Config { 41 return Config{ 42 DefaultFile: defaultParsingFile, 43 I18nManager: gi18n.Instance(), 44 Delimiters: make([]string, 2), 45 } 46 } 47 48 // SetConfig sets the configuration for view. 49 func (view *View) SetConfig(config Config) error { 50 var err error 51 if len(config.Paths) > 0 { 52 for _, v := range config.Paths { 53 if err = view.AddPath(v); err != nil { 54 return err 55 } 56 } 57 } 58 if len(config.Data) > 0 { 59 view.Assigns(config.Data) 60 } 61 if config.DefaultFile != "" { 62 view.SetDefaultFile(config.DefaultFile) 63 } 64 if len(config.Delimiters) > 1 { 65 view.SetDelimiters(config.Delimiters[0], config.Delimiters[1]) 66 } 67 view.config = config 68 // Clear global template object cache. 69 // It's just cache, do not hesitate clearing it. 70 templates.Clear() 71 72 intlog.Printf(context.TODO(), "SetConfig: %+v", view.config) 73 return nil 74 } 75 76 // SetConfigWithMap set configurations with map for the view. 77 func (view *View) SetConfigWithMap(m map[string]interface{}) error { 78 if len(m) == 0 { 79 return gerror.NewCode(gcode.CodeInvalidParameter, "configuration cannot be empty") 80 } 81 // The m now is a shallow copy of m. 82 // Any changes to m does not affect the original one. 83 // A little tricky, isn't it? 84 m = gutil.MapCopy(m) 85 // Most common used configuration support for single view path. 86 _, v1 := gutil.MapPossibleItemByKey(m, "paths") 87 _, v2 := gutil.MapPossibleItemByKey(m, "path") 88 if v1 == nil && v2 != nil { 89 switch v2.(type) { 90 case string: 91 m["paths"] = []string{v2.(string)} 92 case []string: 93 m["paths"] = v2 94 } 95 } 96 err := gconv.Struct(m, &view.config) 97 if err != nil { 98 return err 99 } 100 return view.SetConfig(view.config) 101 } 102 103 // SetPath sets the template directory path for template file search. 104 // The parameter `path` can be absolute or relative path, but absolute path is suggested. 105 func (view *View) SetPath(path string) error { 106 var ( 107 ctx = context.TODO() 108 isDir = false 109 realPath = "" 110 ) 111 if file := gres.Get(path); file != nil { 112 realPath = path 113 isDir = file.FileInfo().IsDir() 114 } else { 115 // Absolute path. 116 realPath = gfile.RealPath(path) 117 if realPath == "" { 118 // Relative path. 119 view.searchPaths.RLockFunc(func(array []string) { 120 for _, v := range array { 121 if path, _ := gspath.Search(v, path); path != "" { 122 realPath = path 123 break 124 } 125 } 126 }) 127 } 128 if realPath != "" { 129 isDir = gfile.IsDir(realPath) 130 } 131 } 132 // Path not exist. 133 if realPath == "" { 134 err := gerror.NewCodef(gcode.CodeInvalidParameter, `View.SetPath failed: path "%s" does not exist`, path) 135 if errorPrint() { 136 glog.Error(ctx, err) 137 } 138 return err 139 } 140 // Should be a directory. 141 if !isDir { 142 err := gerror.NewCodef(gcode.CodeInvalidParameter, `View.SetPath failed: path "%s" should be directory type`, path) 143 if errorPrint() { 144 glog.Error(ctx, err) 145 } 146 return err 147 } 148 // Repeated path adding check. 149 if view.searchPaths.Search(realPath) != -1 { 150 return nil 151 } 152 view.searchPaths.Clear() 153 view.searchPaths.Append(realPath) 154 view.fileCacheMap.Clear() 155 return nil 156 } 157 158 // AddPath adds an absolute or relative path to the search paths. 159 func (view *View) AddPath(path string) error { 160 var ( 161 ctx = context.TODO() 162 isDir = false 163 realPath = "" 164 ) 165 if file := gres.Get(path); file != nil { 166 realPath = path 167 isDir = file.FileInfo().IsDir() 168 } else { 169 // Absolute path. 170 if realPath = gfile.RealPath(path); realPath == "" { 171 // Relative path. 172 view.searchPaths.RLockFunc(func(array []string) { 173 for _, v := range array { 174 if searchedPath, _ := gspath.Search(v, path); searchedPath != "" { 175 realPath = searchedPath 176 break 177 } 178 } 179 }) 180 } 181 if realPath != "" { 182 isDir = gfile.IsDir(realPath) 183 } 184 } 185 // Path not exist. 186 if realPath == "" { 187 err := gerror.NewCodef(gcode.CodeInvalidParameter, `View.AddPath failed: path "%s" does not exist`, path) 188 if errorPrint() { 189 glog.Error(ctx, err) 190 } 191 return err 192 } 193 // realPath should be type of folder. 194 if !isDir { 195 err := gerror.NewCodef(gcode.CodeInvalidParameter, `View.AddPath failed: path "%s" should be directory type`, path) 196 if errorPrint() { 197 glog.Error(ctx, err) 198 } 199 return err 200 } 201 // Repeated path adding check. 202 if view.searchPaths.Search(realPath) != -1 { 203 return nil 204 } 205 view.searchPaths.Append(realPath) 206 view.fileCacheMap.Clear() 207 return nil 208 } 209 210 // Assigns binds multiple global template variables to current view object. 211 // Note that it's not concurrent-safe, which means it would panic 212 // if it's called in multiple goroutines in runtime. 213 func (view *View) Assigns(data Params) { 214 for k, v := range data { 215 view.data[k] = v 216 } 217 } 218 219 // Assign binds a global template variable to current view object. 220 // Note that it's not concurrent-safe, which means it would panic 221 // if it's called in multiple goroutines in runtime. 222 func (view *View) Assign(key string, value interface{}) { 223 view.data[key] = value 224 } 225 226 // SetDefaultFile sets default template file for parsing. 227 func (view *View) SetDefaultFile(file string) { 228 view.config.DefaultFile = file 229 } 230 231 // GetDefaultFile returns default template file for parsing. 232 func (view *View) GetDefaultFile() string { 233 return view.config.DefaultFile 234 } 235 236 // SetDelimiters sets customized delimiters for template parsing. 237 func (view *View) SetDelimiters(left, right string) { 238 view.config.Delimiters = []string{left, right} 239 } 240 241 // SetAutoEncode enables/disables automatically html encoding feature. 242 // When AutoEncode feature is enables, view engine automatically encodes and provides safe html output, 243 // which is good for avoid XSS. 244 func (view *View) SetAutoEncode(enable bool) { 245 view.config.AutoEncode = enable 246 } 247 248 // BindFunc registers customized global template function named `name` 249 // with given function `function` to current view object. 250 // The `name` is the function name which can be called in template content. 251 func (view *View) BindFunc(name string, function interface{}) { 252 view.funcMap[name] = function 253 // Clear global template object cache. 254 templates.Clear() 255 } 256 257 // BindFuncMap registers customized global template functions by map to current view object. 258 // The key of map is the template function name 259 // and the value of map is the address of customized function. 260 func (view *View) BindFuncMap(funcMap FuncMap) { 261 for k, v := range funcMap { 262 view.funcMap[k] = v 263 } 264 // Clear global template object cache. 265 templates.Clear() 266 } 267 268 // SetI18n binds i18n manager to current view engine. 269 func (view *View) SetI18n(manager *gi18n.Manager) { 270 view.config.I18nManager = manager 271 }