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  }