gview_config.go 7.7 KB

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