github.com/zhongdalu/gf@v1.0.0/g/os/gview/gview.go (about)

     1  // Copyright 2017 gf Author(https://github.com/zhongdalu/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/zhongdalu/gf.
     6  
     7  // Package gview implements a template engine based on text/template.
     8  package gview
     9  
    10  import (
    11  	"bytes"
    12  	"errors"
    13  	"fmt"
    14  	"github.com/zhongdalu/gf"
    15  	"github.com/zhongdalu/gf/g/container/garray"
    16  	"github.com/zhongdalu/gf/g/internal/cmdenv"
    17  	"github.com/zhongdalu/gf/g/os/gfile"
    18  	"github.com/zhongdalu/gf/g/os/glog"
    19  	"github.com/zhongdalu/gf/g/os/gspath"
    20  	"sync"
    21  )
    22  
    23  // View object for template engine.
    24  type View struct {
    25  	mu         sync.RWMutex
    26  	paths      *garray.StringArray    // Searching path array.
    27  	data       map[string]interface{} // Global template variables.
    28  	funcMap    map[string]interface{} // Global template function map.
    29  	delimiters []string               // Customized template delimiters.
    30  }
    31  
    32  // Params is type for template params.
    33  type Params = map[string]interface{}
    34  
    35  // FuncMap is type for custom template functions.
    36  type FuncMap = map[string]interface{}
    37  
    38  // Default view object.
    39  var defaultViewObj *View
    40  
    41  // checkAndInitDefaultView checks and initializes the default view object.
    42  // The default view object will be initialized just once.
    43  func checkAndInitDefaultView() {
    44  	if defaultViewObj == nil {
    45  		defaultViewObj = New()
    46  	}
    47  }
    48  
    49  // ParseContent parses the template content directly using the default view object
    50  // and returns the parsed content.
    51  func ParseContent(content string, params Params) (string, error) {
    52  	checkAndInitDefaultView()
    53  	return defaultViewObj.ParseContent(content, params)
    54  }
    55  
    56  // New returns a new view object.
    57  // The parameter <path> specifies the template directory path to load template files.
    58  func New(path ...string) *View {
    59  	view := &View{
    60  		paths:      garray.NewStringArray(),
    61  		data:       make(map[string]interface{}),
    62  		funcMap:    make(map[string]interface{}),
    63  		delimiters: make([]string, 2),
    64  	}
    65  	if len(path) > 0 && len(path[0]) > 0 {
    66  		view.SetPath(path[0])
    67  	} else {
    68  		// Customized dir path from env/cmd.
    69  		if envPath := cmdenv.Get("gf.gview.path").String(); envPath != "" {
    70  			if gfile.Exists(envPath) {
    71  				view.SetPath(envPath)
    72  			} else {
    73  				if errorPrint() {
    74  					glog.Errorf("Template directory path does not exist: %s", envPath)
    75  				}
    76  			}
    77  		} else {
    78  			// Dir path of working dir.
    79  			view.SetPath(gfile.Pwd())
    80  			// Dir path of binary.
    81  			if selfPath := gfile.SelfDir(); selfPath != "" && gfile.Exists(selfPath) {
    82  				view.AddPath(selfPath)
    83  			}
    84  			// Dir path of main package.
    85  			if mainPath := gfile.MainPkgPath(); mainPath != "" && gfile.Exists(mainPath) {
    86  				view.AddPath(mainPath)
    87  			}
    88  		}
    89  	}
    90  	view.SetDelimiters("{{", "}}")
    91  	// default build-in variables.
    92  	view.data["GF"] = map[string]interface{}{
    93  		"version": gf.VERSION,
    94  	}
    95  	// default build-in functions.
    96  	view.BindFunc("eq", view.funcEq)
    97  	view.BindFunc("ne", view.funcNe)
    98  	view.BindFunc("lt", view.funcLt)
    99  	view.BindFunc("le", view.funcLe)
   100  	view.BindFunc("gt", view.funcGt)
   101  	view.BindFunc("ge", view.funcGe)
   102  	view.BindFunc("text", view.funcText)
   103  	view.BindFunc("html", view.funcHtmlEncode)
   104  	view.BindFunc("htmlencode", view.funcHtmlEncode)
   105  	view.BindFunc("htmldecode", view.funcHtmlDecode)
   106  	view.BindFunc("url", view.funcUrlEncode)
   107  	view.BindFunc("urlencode", view.funcUrlEncode)
   108  	view.BindFunc("urldecode", view.funcUrlDecode)
   109  	view.BindFunc("date", view.funcDate)
   110  	view.BindFunc("substr", view.funcSubStr)
   111  	view.BindFunc("strlimit", view.funcStrLimit)
   112  	view.BindFunc("compare", view.funcCompare)
   113  	view.BindFunc("hidestr", view.funcHideStr)
   114  	view.BindFunc("highlight", view.funcHighlight)
   115  	view.BindFunc("toupper", view.funcToUpper)
   116  	view.BindFunc("tolower", view.funcToLower)
   117  	view.BindFunc("nl2br", view.funcNl2Br)
   118  	view.BindFunc("include", view.funcInclude)
   119  	return view
   120  }
   121  
   122  // SetPath sets the template directory path for template file search.
   123  // The parameter <path> can be absolute or relative path, but absolute path is suggested.
   124  func (view *View) SetPath(path string) error {
   125  	// Absolute path.
   126  	realPath := gfile.RealPath(path)
   127  	if realPath == "" {
   128  		// Relative path.
   129  		view.paths.RLockFunc(func(array []string) {
   130  			for _, v := range array {
   131  				if path, _ := gspath.Search(v, path); path != "" {
   132  					realPath = path
   133  					break
   134  				}
   135  			}
   136  		})
   137  	}
   138  	// Path not exist.
   139  	if realPath == "" {
   140  		buffer := bytes.NewBuffer(nil)
   141  		if view.paths.Len() > 0 {
   142  			buffer.WriteString(fmt.Sprintf("[gview] SetPath failed: cannot find directory \"%s\" in following paths:", path))
   143  			view.paths.RLockFunc(func(array []string) {
   144  				for k, v := range array {
   145  					buffer.WriteString(fmt.Sprintf("\n%d. %s", k+1, v))
   146  				}
   147  			})
   148  		} else {
   149  			buffer.WriteString(fmt.Sprintf(`[gview] SetPath failed: path "%s" does not exist`, path))
   150  		}
   151  		err := errors.New(buffer.String())
   152  		if errorPrint() {
   153  			glog.Error(err)
   154  		}
   155  		return err
   156  	}
   157  	// Should be a directory.
   158  	if !gfile.IsDir(realPath) {
   159  		err := errors.New(fmt.Sprintf(`[gview] SetPath failed: path "%s" should be directory type`, path))
   160  		if errorPrint() {
   161  			glog.Error(err)
   162  		}
   163  		return err
   164  	}
   165  	// Repeated path check.
   166  	if view.paths.Search(realPath) != -1 {
   167  		return nil
   168  	}
   169  	view.paths.Clear()
   170  	view.paths.Append(realPath)
   171  	//glog.Debug("[gview] SetPath:", realPath)
   172  	return nil
   173  }
   174  
   175  // AddPath adds a absolute or relative path to the search paths.
   176  func (view *View) AddPath(path string) error {
   177  	// Absolute path.
   178  	realPath := gfile.RealPath(path)
   179  	if realPath == "" {
   180  		// Relative path.
   181  		view.paths.RLockFunc(func(array []string) {
   182  			for _, v := range array {
   183  				if path, _ := gspath.Search(v, path); path != "" {
   184  					realPath = path
   185  					break
   186  				}
   187  			}
   188  		})
   189  	}
   190  	// Path not exist.
   191  	if realPath == "" {
   192  		buffer := bytes.NewBuffer(nil)
   193  		if view.paths.Len() > 0 {
   194  			buffer.WriteString(fmt.Sprintf("[gview] AddPath failed: cannot find directory \"%s\" in following paths:", path))
   195  			view.paths.RLockFunc(func(array []string) {
   196  				for k, v := range array {
   197  					buffer.WriteString(fmt.Sprintf("\n%d. %s", k+1, v))
   198  				}
   199  			})
   200  		} else {
   201  			buffer.WriteString(fmt.Sprintf(`[gview] AddPath failed: path "%s" does not exist`, path))
   202  		}
   203  		err := errors.New(buffer.String())
   204  		if errorPrint() {
   205  			glog.Error(err)
   206  		}
   207  		return err
   208  	}
   209  	// realPath should be type of folder.
   210  	if !gfile.IsDir(realPath) {
   211  		err := errors.New(fmt.Sprintf(`[gview] AddPath failed: path "%s" should be directory type`, path))
   212  		if errorPrint() {
   213  			glog.Error(err)
   214  		}
   215  		return err
   216  	}
   217  	// Repeated path check.
   218  	if view.paths.Search(realPath) != -1 {
   219  		return nil
   220  	}
   221  	view.paths.Append(realPath)
   222  	return nil
   223  }