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 }