160 lines
4.2 KiB
Go
160 lines
4.2 KiB
Go
|
package render
|
||
|
|
||
|
import (
|
||
|
"html/template"
|
||
|
"io"
|
||
|
"net/http"
|
||
|
"strings"
|
||
|
"time"
|
||
|
|
||
|
"github.com/kirsle/blog/core/internal/forms"
|
||
|
"github.com/kirsle/blog/core/internal/log"
|
||
|
"github.com/kirsle/blog/core/internal/models/users"
|
||
|
)
|
||
|
|
||
|
// Config provides the settings and injectables for rendering templates.
|
||
|
type Config struct {
|
||
|
// Refined and raw variables for the templates.
|
||
|
Vars *Vars // Normal RenderTemplate's
|
||
|
|
||
|
// Wrap the template with the `.layout.gohtml`
|
||
|
WithLayout bool
|
||
|
|
||
|
// Inject your own functions for the Go templates.
|
||
|
Functions map[string]interface{}
|
||
|
|
||
|
Request *http.Request
|
||
|
}
|
||
|
|
||
|
// 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
|
||
|
LoggedIn bool
|
||
|
CurrentUser *users.User
|
||
|
CSRF string
|
||
|
Editable bool // page is editable
|
||
|
Request *http.Request
|
||
|
RequestTime time.Time
|
||
|
RequestDuration time.Duration
|
||
|
|
||
|
// Configuration variables
|
||
|
NoLayout bool // don't wrap in .layout.html, just render the template
|
||
|
|
||
|
// Common template variables.
|
||
|
Message string
|
||
|
Flashes []string
|
||
|
Error error
|
||
|
Data map[interface{}]interface{}
|
||
|
Form forms.Form
|
||
|
}
|
||
|
|
||
|
// PartialTemplate handles rendering a Go template to a writer, without
|
||
|
// doing anything extra to the vars or dealing with net/http. This is ideal for
|
||
|
// rendering partials, such as comment partials.
|
||
|
//
|
||
|
// This will wrap the template in `.layout.gohtml` by default. To render just
|
||
|
// a bare template on its own, i.e. for partial templates, create a Vars struct
|
||
|
// with `Vars{NoIndex: true}`
|
||
|
func PartialTemplate(w io.Writer, path string, C Config) error {
|
||
|
if C.Request == nil {
|
||
|
panic("render.RenderPartialTemplate(): The *http.Request is nil!?")
|
||
|
}
|
||
|
|
||
|
// v interface{}, withLayout bool, functions map[string]interface{}) error {
|
||
|
var (
|
||
|
layout Filepath
|
||
|
templateName string
|
||
|
err error
|
||
|
)
|
||
|
|
||
|
// Find the file path to the template.
|
||
|
filepath, err := ResolvePath(path)
|
||
|
if err != nil {
|
||
|
log.Error("RenderTemplate(%s): file not found", path)
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
// Get the layout template.
|
||
|
if C.WithLayout {
|
||
|
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
|
||
|
}
|
||
|
|
||
|
// Template functions.
|
||
|
funcmap := template.FuncMap{
|
||
|
"StringsJoin": strings.Join,
|
||
|
"Now": time.Now,
|
||
|
"TemplateName": func() string {
|
||
|
return filepath.URI
|
||
|
},
|
||
|
}
|
||
|
if C.Functions != nil {
|
||
|
for name, fn := range C.Functions {
|
||
|
funcmap[name] = fn
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Useful template functions.
|
||
|
t := template.New(filepath.Absolute).Funcs(funcmap)
|
||
|
|
||
|
// 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 C.WithLayout {
|
||
|
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, C.Vars)
|
||
|
if err != nil {
|
||
|
log.Error("Template parsing error: %s", err)
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// 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 http.ResponseWriter, path string, C Config) error {
|
||
|
if C.Request == nil {
|
||
|
panic("render.RenderTemplate(): The *http.Request is nil!?")
|
||
|
}
|
||
|
|
||
|
w.Header().Set("Content-Type", "text/html; encoding=UTF-8")
|
||
|
PartialTemplate(w, path, Config{
|
||
|
Request: C.Request,
|
||
|
Vars: C.Vars,
|
||
|
WithLayout: true,
|
||
|
Functions: C.Functions,
|
||
|
})
|
||
|
|
||
|
return nil
|
||
|
}
|