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