blog/core/internal/render/templates.go
Noah Petherbridge 6d3de7da69 Let me use interface{} for template vars
Since most of the `render.Vars{}` fields were hardcoded/not really
editable for the templates, apart from .Data, this struct is now locked
away in the render subpackage.

End http.HandlerFunc's can then make any arbitrary template data
structure they want to, available inside the templates as `.Data`.
2018-02-10 14:05:41 -08:00

149 lines
3.9 KiB
Go

package render
import (
"html/template"
"io"
"net/http"
"strings"
"time"
"github.com/kirsle/blog/core/internal/log"
"github.com/kirsle/blog/core/internal/middleware"
"github.com/kirsle/blog/core/internal/middleware/auth"
"github.com/kirsle/blog/core/internal/models/settings"
"github.com/kirsle/blog/core/internal/models/users"
"github.com/kirsle/blog/core/internal/sessions"
"github.com/kirsle/blog/core/internal/types"
)
// Vars is an interface to implement by the templates to pass their own custom
// variables in. It auto-loads global template variables (site name, etc.)
// when the template is rendered.
type vars struct {
// Global, "constant" template variables.
SetupNeeded bool
Title string
Path string
TemplatePath string // actual template file on disk
LoggedIn bool
CurrentUser *users.User
CSRF string
Editable bool // page is editable
Request *http.Request
RequestTime time.Time
RequestDuration time.Duration
// Common template variables.
Message string
Flashes []string
Error error
Data interface{}
}
// Template responds with an HTML template.
//
// The vars will be massaged a bit to load the global defaults (such as the
// website title and user login status), the user's session may be updated with
// new CSRF token, and other such things. If you just want to render a template
// without all that nonsense, use RenderPartialTemplate.
func Template(w io.Writer, r *http.Request, path string, data interface{}) error {
isPartial := strings.Contains(path, ".partial")
// Get the site settings.
s, err := settings.Load()
if err != nil {
s = settings.Defaults()
}
// Inject globally available variables.
v := vars{
SetupNeeded: s.Initialized == false && !strings.HasPrefix(r.URL.Path, "/initial-setup"),
Request: r,
RequestTime: r.Context().Value(types.StartTimeKey).(time.Time),
Title: s.Site.Title,
Path: r.URL.Path,
Data: data,
}
user, err := auth.CurrentUser(r)
v.CurrentUser = user
v.LoggedIn = err == nil
// If this is the HTTP response, handle session-related things.
if rw, ok := w.(http.ResponseWriter); ok {
rw.Header().Set("Content-Type", "text/html; encoding=UTF-8")
session := sessions.Get(r)
// Flashed messages.
if flashes := session.Flashes(); len(flashes) > 0 {
for _, flash := range flashes {
_ = flash
v.Flashes = append(v.Flashes, flash.(string))
}
session.Save(r, rw)
}
// CSRF token for forms.
v.CSRF = middleware.GenerateCSRFToken(rw, r, session)
}
v.RequestDuration = time.Now().Sub(v.RequestTime)
v.Editable = !strings.HasPrefix(path, "admin/")
var (
layout Filepath
templateName string
)
// Find the file path to the template.
filepath, err := ResolvePath(path)
if err != nil {
log.Error("RenderTemplate(%s): file not found", path)
return err
}
v.TemplatePath = filepath.URI
// Get the layout template.
if !isPartial {
templateName = "layout"
layout, err = ResolvePath(".layout")
if err != nil {
log.Error("RenderTemplate(%s): layout template not found", path)
return err
}
} else {
templateName = filepath.Basename
}
// The comment entry partial.
commentEntry, err := ResolvePath("comments/entry.partial")
if err != nil {
log.Error("RenderTemplate(%s): comments/entry.partial not found")
return err
}
t := template.New(filepath.Absolute).Funcs(Funcs)
// Parse the template files. The layout comes first because it's the wrapper
// and allows the filepath template to set the page title.
var templates []string
if !isPartial {
templates = append(templates, layout.Absolute)
}
t, err = t.ParseFiles(append(templates, commentEntry.Absolute, filepath.Absolute)...)
if err != nil {
log.Error(err.Error())
return err
}
err = t.ExecuteTemplate(w, templateName, v)
if err != nil {
log.Error("Template parsing error: %s", err)
return err
}
return nil
}