Migliorato il sistema dei template, ora è presente la cartella "views/partials/"

In particolare è stato generalizzato leggermente il sistema di loading dei template (ok forse un po' più del necessario) però ora è possibile aggiungere facilmente più cartelle di parziali e creare gerarchie di template più complicate.
main
Antonio De Lucreziis 3 years ago
parent 847f588e87
commit 1a2e2341d3

@ -8,6 +8,7 @@ import (
"github.com/go-chi/chi/v5"
"github.com/go-chi/chi/v5/middleware"
"github.com/phc-dm/phc-server/config"
"github.com/phc-dm/phc-server/templates"
"github.com/phc-dm/phc-server/util"
)
@ -25,7 +26,11 @@ func main() {
r.Handle("/public/*", http.StripPrefix("/public", http.FileServer(http.Dir("./public"))))
// Templates & Renderer
renderer := NewTemplateRenderer("base.html")
renderer := templates.NewRenderer(
"./views/",
templates.File("./views/base.html"),
templates.Pattern("./views/partials/*.html"),
)
newsArticlesRegistry := NewArticleRenderer("./news")
// Routes

@ -1,52 +0,0 @@
package main
import (
"io"
"strings"
"text/template"
"github.com/phc-dm/phc-server/config"
"github.com/phc-dm/phc-server/util"
)
// TemplateRenderer holds cached templates for rendering
type TemplateRenderer struct {
baseFile string
baseTemplate *template.Template
templateMap map[string]*template.Template
}
// NewTemplateRenderer constructs a template renderer with a base file
func NewTemplateRenderer(baseFile string) *TemplateRenderer {
return &TemplateRenderer{
baseFile: baseFile,
baseTemplate: template.Must(template.ParseFiles("./views/" + baseFile)),
templateMap: make(map[string]*template.Template),
}
}
// Render the template
func (t *TemplateRenderer) Render(w io.Writer, name string, data util.H) error {
tmpl := t.templateMap[name]
if config.Mode == "development" || tmpl == nil {
if config.Mode == "development" {
tmpl = template.Must(template.ParseFiles("./views/" + t.baseFile))
} else {
tmpl = template.Must(t.baseTemplate.Clone())
}
tmpl.ParseFiles("./views/" + name)
t.templateMap[name] = tmpl
}
newData := util.H{}
newData.Apply(data)
newData["Page"] = util.H{
// Used to inject a page specific class on <body>
"Name": strings.TrimSuffix(name, ".html"),
}
newData["Config"] = config.Object()
return tmpl.ExecuteTemplate(w, "base", newData)
}

@ -0,0 +1,117 @@
package templates
import (
"html/template"
"io"
"path"
"strings"
"github.com/phc-dm/phc-server/config"
"github.com/phc-dm/phc-server/util"
)
type LoadTemplate interface {
Load(t *template.Template) (*template.Template, error)
}
type File string
func (file File) Load(t *template.Template) (*template.Template, error) {
return t.ParseFiles(string(file))
}
type Pattern string
func (pattern Pattern) Load(t *template.Template) (*template.Template, error) {
return t.ParseGlob(string(pattern))
}
type CachedTemplate struct {
Loaders []LoadTemplate
template *template.Template
}
func NewCacheTemplate(loaders ...LoadTemplate) *CachedTemplate {
cachedTemplate := &CachedTemplate{
Loaders: loaders,
template: nil,
}
template.Must(cachedTemplate.Load(template.New("")))
return cachedTemplate
}
func (ct *CachedTemplate) Load(t *template.Template) (*template.Template, error) {
if ct.template != nil {
return ct.template, nil
}
ct.template = t
for _, loader := range ct.Loaders {
var err error
ct.template, err = loader.Load(ct.template)
if err != nil {
return nil, err
}
}
return ct.template, nil
}
func (ct *CachedTemplate) Reload() {
ct.template = nil
template.Must(ct.Load(nil))
}
func (ct *CachedTemplate) Template() *template.Template {
return ct.template
}
// Renderer holds cached templates for rendering
type Renderer struct {
rootPath string
baseLoaders []LoadTemplate
routes map[string]*CachedTemplate
}
// NewRenderer constructs a template renderer with a base file
func NewRenderer(rootPath string, loaders ...LoadTemplate) *Renderer {
return &Renderer{
rootPath,
loaders,
map[string]*CachedTemplate{},
}
}
func (r *Renderer) Load(name string) *CachedTemplate {
cachedTemplate, present := r.routes[name]
if !present {
loaders := []LoadTemplate{}
loaders = append(loaders, r.baseLoaders...)
loaders = append(loaders, File(path.Join(r.rootPath, name)))
cachedTemplate = NewCacheTemplate(loaders...)
r.routes[name] = cachedTemplate
}
return cachedTemplate
}
// Render the template, also injects "Page" and "Config" values in the template
func (r *Renderer) Render(w io.Writer, name string, data util.H) error {
cachedTemplate := r.Load(name)
if config.Mode == "development" {
cachedTemplate.Reload()
}
newData := util.H{}
newData.Apply(data)
newData["Page"] = util.H{
// Used to inject a page specific class on <body>
"Name": strings.TrimSuffix(path.Base(name), ".html"),
}
newData["Config"] = config.Object()
return cachedTemplate.Template().ExecuteTemplate(w, "base", newData)
}

@ -62,89 +62,7 @@
</head>
<body {{ if .Page.Name }}class="page-{{ .Page.Name }}"{{ end }}>
<nav>
<!-- Site -->
<div class="nav-logo">
<a class="nav-element" href="/">
<img src="/public/images/logo-circuit-board.svg" alt="phc-logo">
</a>
</div>
<div class="nav-main">
<div class="nav-item">
<a class="nav-element" href="/utenti">Utenti</a>
</div>
<div class="nav-item dropdown">
<div class="name">
<a class="nav-element" href="/progetti">
<div class="icon">
<i class="fas fa-chevron-down"></i>
</div>
<div class="label">
Progetti
</div>
</a>
</div>
<div class="nav-items">
<div class="nav-item">
<a class="nav-element" href="{{ .Config.GitUrl }}">Gitea</a>
</div>
<div class="nav-item">
<a class="nav-element" href="{{ .Config.ForumUrl }}">Zulip</a>
</div>
<div class="nav-item">
<a class="nav-element" href="/seminari">Seminari</a>
</div>
</div>
</div>
<div class="nav-item dropdown">
<div class="name">
<a class="nav-element" href="/risorse">
<div class="icon">
<i class="fas fa-chevron-down"></i>
</div>
<div class="label">
Risorse
</div>
</a>
</div>
<div class="nav-items">
<div class="nav-item">
<a class="nav-element" href="/news">News</a>
</div>
<div class="nav-item">
<a class="nav-element" href="/guide">Guide</a>
</div>
<div class="nav-item">
<a class="nav-element" href="/link">Link Utili</a>
</div>
</div>
</div>
<div class="nav-item">
<a class="nav-element" href="/storia">Storia</a>
</div>
<div class="nav-item">
<a class="nav-element" href="/about">About</a>
</div>
<div class="nav-item filler"></div>
<!-- User Related -->
<div class="nav-item">
<div id="toggle-dark-mode" class="nav-button">
<i class="fas fa-moon"></i>
</div>
</div>
{{if .User}}
<div class="nav-item">
<a class="nav-element" href="/profile">@{{ .User }}</a>
</div>
{{else}}
<div class="nav-item">
<a class="nav-element" href="/login">Accedi</a>
</div>
{{end}}
</div>
</nav>
{{ template "navbar" . }}
<div class="main">
{{ template "body" . }}

@ -0,0 +1,85 @@
{{ define "navbar" }}
<nav>
<!-- Site -->
<div class="nav-logo">
<a class="nav-element" href="/">
<img src="/public/images/logo-circuit-board.svg" alt="phc-logo">
</a>
</div>
<div class="nav-main">
<div class="nav-item">
<a class="nav-element" href="/utenti">Utenti</a>
</div>
<div class="nav-item dropdown">
<div class="name">
<a class="nav-element" href="/progetti">
<div class="icon">
<i class="fas fa-chevron-down"></i>
</div>
<div class="label">
Progetti
</div>
</a>
</div>
<div class="nav-items">
<div class="nav-item">
<a class="nav-element" href="{{ .Config.GitUrl }}">Gitea</a>
</div>
<div class="nav-item">
<a class="nav-element" href="{{ .Config.ForumUrl }}">Zulip</a>
</div>
<div class="nav-item">
<a class="nav-element" href="/seminari">Seminari</a>
</div>
</div>
</div>
<div class="nav-item dropdown">
<div class="name">
<a class="nav-element" href="/risorse">
<div class="icon">
<i class="fas fa-chevron-down"></i>
</div>
<div class="label">
Risorse
</div>
</a>
</div>
<div class="nav-items">
<div class="nav-item">
<a class="nav-element" href="/news">News</a>
</div>
<div class="nav-item">
<a class="nav-element" href="/guide">Guide</a>
</div>
<div class="nav-item">
<a class="nav-element" href="/link">Link Utili</a>
</div>
</div>
</div>
<div class="nav-item">
<a class="nav-element" href="/storia">Storia</a>
</div>
<div class="nav-item">
<a class="nav-element" href="/about">About</a>
</div>
<div class="nav-item filler"></div>
<!-- User Related -->
<div class="nav-item">
<div id="toggle-dark-mode" class="nav-button">
<i class="fas fa-moon"></i>
</div>
</div>
{{if .User}}
<div class="nav-item">
<a class="nav-element" href="/profile">@{{ .User }}</a>
</div>
{{else}}
<div class="nav-item">
<a class="nav-element" href="/login">Accedi</a>
</div>
{{end}}
</div>
</nav>
{{ end }}
Loading…
Cancel
Save