gview_config.go 7.4 KB

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