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  }