github.com/aarzilli/tools@v0.0.0-20151123112009-0d27094f75e0/net/http/tplx/tpl.go (about)

     1  // Package tplx makes composing templates more comfortable.
     2  package tplx
     3  
     4  import (
     5  	"bytes"
     6  	"fmt"
     7  	"html"
     8  	tt "html/template"
     9  	"io/ioutil"
    10  	"net/http"
    11  	"time"
    12  
    13  	"github.com/pbberlin/tools/net/http/loghttp"
    14  )
    15  
    16  /*
    17  We a assume a fixed html page
    18  with embedded detail templates.
    19  
    20  
    21  We give http handler functions a tiny interface
    22  to construct and execute html templates.
    23  
    24  
    25  Three and a half prefixes:
    26     n_ ... is the string *name*    of a template
    27     c_ ... is the string *content* of a template
    28  		PrefixLff prompts loading of the template content
    29  		from directory static/[x].html
    30     t_ ... is a *parsed template*
    31  
    32  
    33  
    34  Usage:
    35  
    36  type GBEntry struct {
    37      Author  string
    38      Content string
    39  }
    40  
    41  const c_tpl_gbentry = `
    42  		{{range .}}
    43  			<b>{{.Author}}</b> wrote:
    44  			{{.Content}} <br>
    45  		{{end}}
    46  `
    47  
    48  	gbe1 := GBEntry{
    49  		Content: "It crawls into a man's bowels ...",
    50  		Author:   "John Dos Passos",
    51  	}
    52  	gbe2 := GBEntry{
    53  		Content: "Praised be the man ...",
    54  		Author:   "T.S. Elliot",
    55  	}
    56  	vGbe := []GBEntry{gbe1,gbe2}
    57  
    58  	myTemplateAdder, myTplExec := FuncTplBuilder(w,r)
    59  
    60  	myTemplateAdder("n_html_title","What authors think","")  // no dyn data
    61  	myTemplateAdder("n_cont_0","<i>{{.}}</i><br><br>","Deep thought:")
    62  	myTemplateAdder("n_cont_0", PrefixLff + "reloaded_template"," dyn data for file ")
    63  	myTemplateAdder("n_cont_1",c_tpl_gbentry ,vGbe)
    64  	myTemplateAdder("n_cont_2","<br><br>end of thoughts","")
    65  
    66  	myTplExec(w,r)
    67  
    68  
    69  
    70  */
    71  
    72  const FurtherDefinition = "Further_Definition_"
    73  
    74  var dbg bool = false
    75  
    76  const Head = `<!DOCTYPE html>
    77  <html>
    78    <head>
    79      <meta charset="utf-8">
    80      <link rel="shortcut icon" href="data:;base64,=">
    81      <link rel="icon"          href="data:;base64,=">
    82      <title>{{ .HtmlTitle }}</title>
    83    </head>
    84    <body>
    85      <style> pre {line-height:14px;}
    86        body {font-family:tahoma; font-size: 15px;}
    87      </style>
    88      <span class='body'></span>
    89        `
    90  
    91  // <span class='body'></span> is to parse out the body by splitting it.
    92  const Foot = `
    93      <span class='body'></span>
    94    </body>
    95  </html>`
    96  
    97  const c_page_scaffold_01 = `<!DOCTYPE html>
    98  <html>
    99    <head>
   100      <meta charset="utf-8">
   101      <link rel="shortcut icon" href="data:;base64,=">
   102  	<link rel="icon"          href="data:;base64,=">
   103  	<title>{{template "n_html_title"  }}</title>
   104  	<link rel="stylesheet" type="text/css" href="http://fonts.googleapis.com/css?family=Open Sans">
   105    </head>
   106    <link rel="stylesheet" href="/static/basic.css" media="screen" type="text/css" />
   107    <body>
   108  	<div class="wmax" style='margin: 0px auto; '>
   109  		{{template "n_cont_0" .n_cont_0}}
   110  		{{template "n_cont_1" .n_cont_1 }}
   111  		{{template "n_cont_2" .n_cont_2 }}
   112  		{{template "n_cont_3" .n_cont_3 }}
   113  		<p><a href='/'>Back to root</a></p>
   114  	</div>
   115    </body>
   116  </html>`
   117  
   118  // the defautls map must contain all subtemplates
   119  // demanded by c_page_scaffold_01
   120  var map_default map[string]string = map[string]string{
   121  	"n_html_title": "",
   122  	"n_cont_0":     "",
   123  	"n_cont_1":     "",
   124  	"n_cont_2":     "",
   125  	"n_cont_3":     "",
   126  }
   127  
   128  const c_contentpl_extended = `
   129  	--{{.}}--
   130  `
   131  
   132  const PrefixLff string = "load_file_" // prefix load from file
   133  var lenLff int = len(PrefixLff)
   134  
   135  //
   136  
   137  var t_base *tt.Template = nil
   138  
   139  // a clone factory
   140  //  as t_base is a "app scope" variable
   141  //  it will live as long as the application runs
   142  func cloneFromBase(w http.ResponseWriter, r *http.Request) *tt.Template {
   143  
   144  	// use INDEX to access certain elements
   145  	funcMap := tt.FuncMap{
   146  		"unescape":   html.UnescapeString,
   147  		"escape":     html.EscapeString,
   148  		"fMult":      fMult,
   149  		"fAdd":       fAdd,
   150  		"fChop":      fChop,
   151  		"fMakeRange": fMakeRange,
   152  		"fNumCols":   fNumCols,
   153  		"df": func(g time.Time) string {
   154  			return g.Format("2006-01-02 (Jan 02)")
   155  		},
   156  	}
   157  
   158  	if t_base == nil {
   159  		t_base = tt.Must(tt.New("n_page_scaffold_01").Funcs(funcMap).Parse(c_page_scaffold_01))
   160  	}
   161  
   162  	t_derived, err := t_base.Clone()
   163  	loghttp.E(w, r, err, false)
   164  
   165  	return t_derived
   166  }
   167  
   168  // m contains defaults or overwritten template contents
   169  func templatesExtend(w http.ResponseWriter, r *http.Request, subtemplates map[string]string) *tt.Template {
   170  
   171  	var err error = nil
   172  	tder := cloneFromBase(w, r)
   173  
   174  	//for k,v := range furtherDefinitions{
   175  	//	tder, err = tder.Parse( `{{define "` + k  +`"}}`   + v + `{{end}}` )
   176  	//	loghttp.E(w,r,err,false)
   177  	//}
   178  
   179  	for k, v := range subtemplates {
   180  
   181  		if len(v) > lenLff && v[0:lenLff] == PrefixLff {
   182  			fileName := v[lenLff:]
   183  			fcontent, err := ioutil.ReadFile("templates/" + fileName + ".html")
   184  			loghttp.E(w, r, err, false, "could not open static template file")
   185  			v = string(fcontent)
   186  		}
   187  
   188  		tder, err = tder.Parse(`{{define "` + k + `"}}` + v + `{{end}}`)
   189  		loghttp.E(w, r, err, false)
   190  
   191  	}
   192  
   193  	return tder
   194  }
   195  
   196  // returns two functions
   197  //   first  function   collects detail templates and detail content data
   198  //   second function   combines all those and executes them
   199  // example usage
   200  // myTemplateAdder, myTplExec := FuncTplBuilder()
   201  // myTemplateAdder("n_content","--{{.}}--","some dyn data string")
   202  // myTplExec(w,r)
   203  
   204  func FuncTplBuilder(w http.ResponseWriter, r *http.Request) (f1 func(string, string, interface{}),
   205  	f2 func(http.ResponseWriter, *http.Request)) {
   206  
   207  	// prepare collections
   208  	mtc := map[string]string{}      // map template content
   209  	mtd := map[string]interface{}{} // map template data
   210  
   211  	// template key - template content, template data
   212  	f1 = func(tk string, tc string, td interface{}) {
   213  		mtc[tk] = tc
   214  		mtd[tk] = td
   215  	}
   216  
   217  	f2 = func(w http.ResponseWriter, r *http.Request) {
   218  
   219  		// merge arguments with defaults
   220  		map_result := map[string]string{}
   221  		for k, v := range map_default {
   222  			if _, ok := mtc[k]; ok {
   223  				map_result[k] = mtc[k]
   224  				delete(mtc, k)
   225  			} else {
   226  				map_result[k] = v
   227  			}
   228  			if dbg {
   229  				w.Write([]byte(fmt.Sprintf("  %q  %q \n", map_result[k], v)))
   230  			}
   231  		}
   232  
   233  		// additional templates beyond the default
   234  		for k, v := range mtc {
   235  			map_result[k] = v
   236  			if dbg {
   237  				w.Write([]byte(fmt.Sprintf("  %q  %q \n", map_result[k], v)))
   238  			}
   239  		}
   240  
   241  		tpl_extended := templatesExtend(w, r, map_result)
   242  
   243  		err := tpl_extended.ExecuteTemplate(w, "n_page_scaffold_01", mtd)
   244  		loghttp.E(w, r, err, false)
   245  
   246  	}
   247  
   248  	return f1, f2
   249  }
   250  
   251  // Simply combines one given template string
   252  // with one given map of params
   253  //  {{ .ParamX }} in template string
   254  // must match key in data map[string]string{"ParamX":"Val"}
   255  // No panic
   256  // Returns the unexecuted template on error
   257  func ExecTplHelper(templateString string, data map[string]interface{}) string {
   258  	tplBase, err := tt.New("tplName").Parse(templateString) // tplName is irrelevant, only for inner reference if multiple templates were compiled into one
   259  	if err != nil {
   260  		return fmt.Sprintf("%v - tpl compilation failed: %v", templateString, err)
   261  	}
   262  
   263  	b := new(bytes.Buffer)
   264  
   265  	err = tplBase.ExecuteTemplate(b, "tplName", data)
   266  	if err != nil {
   267  		return fmt.Sprintf("%v - tpl execution failed: %v", templateString, err)
   268  	}
   269  
   270  	return b.String()
   271  
   272  }