Further simplify template rendering
This commit is contained in:
parent
f0045ae2cf
commit
eab7dae75b
|
@ -33,7 +33,7 @@ func (b *Blog) AdminRoutes(r *mux.Router) {
|
||||||
|
|
||||||
// AdminHandler is the admin landing page.
|
// AdminHandler is the admin landing page.
|
||||||
func (b *Blog) AdminHandler(w http.ResponseWriter, r *http.Request) {
|
func (b *Blog) AdminHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
b.RenderTemplate(w, r, "admin/index", render.Vars{})
|
render.Template(w, r, "admin/index", NewVars())
|
||||||
}
|
}
|
||||||
|
|
||||||
// FileTree holds information about files in the document roots.
|
// FileTree holds information about files in the document roots.
|
||||||
|
|
|
@ -23,7 +23,6 @@ func (b *Blog) AuthRoutes(r *mux.Router) {
|
||||||
// MustLogin handles errors from the LoginRequired middleware by redirecting
|
// MustLogin handles errors from the LoginRequired middleware by redirecting
|
||||||
// the user to the login page.
|
// the user to the login page.
|
||||||
func (b *Blog) MustLogin(w http.ResponseWriter, r *http.Request) {
|
func (b *Blog) MustLogin(w http.ResponseWriter, r *http.Request) {
|
||||||
log.Info("MustLogin for %s", r.URL.Path)
|
|
||||||
responses.Redirect(w, "/login?next="+r.URL.Path)
|
responses.Redirect(w, "/login?next="+r.URL.Path)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
26
core/blog.go
26
core/blog.go
|
@ -44,6 +44,10 @@ type Archive struct {
|
||||||
|
|
||||||
// BlogRoutes attaches the blog routes to the app.
|
// BlogRoutes attaches the blog routes to the app.
|
||||||
func (b *Blog) BlogRoutes(r *mux.Router) {
|
func (b *Blog) BlogRoutes(r *mux.Router) {
|
||||||
|
render.Funcs["RenderIndex"] = b.RenderIndex
|
||||||
|
render.Funcs["RenderPost"] = b.RenderPost
|
||||||
|
render.Funcs["RenderTags"] = b.RenderTags
|
||||||
|
|
||||||
// Public routes
|
// Public routes
|
||||||
r.HandleFunc("/blog", b.IndexHandler)
|
r.HandleFunc("/blog", b.IndexHandler)
|
||||||
r.HandleFunc("/blog.rss", b.RSSHandler)
|
r.HandleFunc("/blog.rss", b.RSSHandler)
|
||||||
|
@ -302,19 +306,14 @@ func (b *Blog) RenderIndex(r *http.Request, tag, privacy string) template.HTML {
|
||||||
// Render the blog index partial.
|
// Render the blog index partial.
|
||||||
var output bytes.Buffer
|
var output bytes.Buffer
|
||||||
v := render.Vars{
|
v := render.Vars{
|
||||||
|
NoLayout: true,
|
||||||
Data: map[interface{}]interface{}{
|
Data: map[interface{}]interface{}{
|
||||||
"PreviousPage": previousPage,
|
"PreviousPage": previousPage,
|
||||||
"NextPage": nextPage,
|
"NextPage": nextPage,
|
||||||
"View": view,
|
"View": view,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
v = b.LoadDefaults(v, r)
|
b.RenderTemplate(&output, r, "blog/index.partial", v)
|
||||||
render.PartialTemplate(&output, "blog/index.partial", render.Config{
|
|
||||||
Request: r,
|
|
||||||
Vars: &v,
|
|
||||||
WithLayout: false,
|
|
||||||
Functions: b.TemplateFuncs(nil, r, nil),
|
|
||||||
})
|
|
||||||
|
|
||||||
return template.HTML(output.String())
|
return template.HTML(output.String())
|
||||||
}
|
}
|
||||||
|
@ -333,19 +332,13 @@ func (b *Blog) RenderTags(r *http.Request, indexView bool) template.HTML {
|
||||||
|
|
||||||
var output bytes.Buffer
|
var output bytes.Buffer
|
||||||
v := render.Vars{
|
v := render.Vars{
|
||||||
|
NoLayout: true,
|
||||||
Data: map[interface{}]interface{}{
|
Data: map[interface{}]interface{}{
|
||||||
"IndexView": indexView,
|
"IndexView": indexView,
|
||||||
"Tags": tags,
|
"Tags": tags,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
v = b.LoadDefaults(v, r)
|
b.RenderTemplate(&output, r, "blog/tags.partial", v)
|
||||||
render.PartialTemplate(&output, "blog/tags.partial", render.Config{
|
|
||||||
Request: r,
|
|
||||||
Vars: &v,
|
|
||||||
WithLayout: false,
|
|
||||||
Functions: b.TemplateFuncs(nil, nil, nil),
|
|
||||||
})
|
|
||||||
// b.RenderPartialTemplate(&output, r, "blog/tags.partial", v, false, nil)
|
|
||||||
|
|
||||||
return template.HTML(output.String())
|
return template.HTML(output.String())
|
||||||
}
|
}
|
||||||
|
@ -455,6 +448,7 @@ func (b *Blog) RenderPost(r *http.Request, p *posts.Post, indexView bool, numCom
|
||||||
}
|
}
|
||||||
|
|
||||||
meta := render.Vars{
|
meta := render.Vars{
|
||||||
|
NoLayout: true,
|
||||||
Data: map[interface{}]interface{}{
|
Data: map[interface{}]interface{}{
|
||||||
"Post": p,
|
"Post": p,
|
||||||
"Rendered": rendered,
|
"Rendered": rendered,
|
||||||
|
@ -465,7 +459,7 @@ func (b *Blog) RenderPost(r *http.Request, p *posts.Post, indexView bool, numCom
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
output := bytes.Buffer{}
|
output := bytes.Buffer{}
|
||||||
err = b.RenderPartialTemplate(&output, r, "blog/entry.partial", meta, false, nil)
|
err = b.RenderTemplate(&output, r, "blog/entry.partial", meta)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return template.HTML(fmt.Sprintf("[template error in blog/entry.partial: %s]", err.Error()))
|
return template.HTML(fmt.Sprintf("[template error in blog/entry.partial: %s]", err.Error()))
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,6 @@ import (
|
||||||
|
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
gorilla "github.com/gorilla/sessions"
|
|
||||||
"github.com/kirsle/blog/core/internal/log"
|
"github.com/kirsle/blog/core/internal/log"
|
||||||
"github.com/kirsle/blog/core/internal/markdown"
|
"github.com/kirsle/blog/core/internal/markdown"
|
||||||
"github.com/kirsle/blog/core/internal/middleware/auth"
|
"github.com/kirsle/blog/core/internal/middleware/auth"
|
||||||
|
@ -23,6 +22,8 @@ import (
|
||||||
|
|
||||||
// CommentRoutes attaches the comment routes to the app.
|
// CommentRoutes attaches the comment routes to the app.
|
||||||
func (b *Blog) CommentRoutes(r *mux.Router) {
|
func (b *Blog) CommentRoutes(r *mux.Router) {
|
||||||
|
render.Funcs["RenderComments"] = b.RenderComments
|
||||||
|
|
||||||
r.HandleFunc("/comments", b.CommentHandler)
|
r.HandleFunc("/comments", b.CommentHandler)
|
||||||
r.HandleFunc("/comments/subscription", b.SubscriptionHandler)
|
r.HandleFunc("/comments/subscription", b.SubscriptionHandler)
|
||||||
r.HandleFunc("/comments/quick-delete", b.QuickDeleteHandler)
|
r.HandleFunc("/comments/quick-delete", b.QuickDeleteHandler)
|
||||||
|
@ -40,13 +41,16 @@ type CommentMeta struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// RenderComments renders a comment form partial and returns the HTML.
|
// RenderComments renders a comment form partial and returns the HTML.
|
||||||
func (b *Blog) RenderComments(session *gorilla.Session, csrfToken, url, subject string, ids ...string) template.HTML {
|
func (b *Blog) RenderComments(r *http.Request, subject string, ids ...string) template.HTML {
|
||||||
id := strings.Join(ids, "-")
|
id := strings.Join(ids, "-")
|
||||||
|
session := sessions.Get(r)
|
||||||
|
url := r.URL.Path
|
||||||
|
|
||||||
// Load their cached name and email if they posted a comment before.
|
// Load their cached name and email if they posted a comment before.
|
||||||
name, _ := session.Values["c.name"].(string)
|
name, _ := session.Values["c.name"].(string)
|
||||||
email, _ := session.Values["c.email"].(string)
|
email, _ := session.Values["c.email"].(string)
|
||||||
editToken, _ := session.Values["c.token"].(string)
|
editToken, _ := session.Values["c.token"].(string)
|
||||||
|
csrf, _ := session.Values["csrf"].(string)
|
||||||
|
|
||||||
// Check if the user is a logged-in admin, to make all comments editable.
|
// Check if the user is a logged-in admin, to make all comments editable.
|
||||||
var isAdmin bool
|
var isAdmin bool
|
||||||
|
@ -71,7 +75,7 @@ func (b *Blog) RenderComments(session *gorilla.Session, csrfToken, url, subject
|
||||||
c.HTML = template.HTML(markdown.RenderMarkdown(c.Body))
|
c.HTML = template.HTML(markdown.RenderMarkdown(c.Body))
|
||||||
c.ThreadID = thread.ID
|
c.ThreadID = thread.ID
|
||||||
c.OriginURL = url
|
c.OriginURL = url
|
||||||
c.CSRF = csrfToken
|
c.CSRF = csrf
|
||||||
|
|
||||||
// Look up the author username.
|
// Look up the author username.
|
||||||
if c.UserID > 0 {
|
if c.UserID > 0 {
|
||||||
|
@ -120,7 +124,7 @@ func (b *Blog) RenderComments(session *gorilla.Session, csrfToken, url, subject
|
||||||
ID: thread.ID,
|
ID: thread.ID,
|
||||||
OriginURL: url,
|
OriginURL: url,
|
||||||
Subject: subject,
|
Subject: subject,
|
||||||
CSRF: csrfToken,
|
CSRF: csrf,
|
||||||
Thread: &thread,
|
Thread: &thread,
|
||||||
NewComment: comments.Comment{
|
NewComment: comments.Comment{
|
||||||
Name: name,
|
Name: name,
|
||||||
|
|
1
core/internal/controllers/setup/setup.go
Normal file
1
core/internal/controllers/setup/setup.go
Normal file
|
@ -0,0 +1 @@
|
||||||
|
package setup
|
|
@ -5,7 +5,6 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/kirsle/blog/core/internal/log"
|
|
||||||
"github.com/kirsle/blog/core/internal/models/users"
|
"github.com/kirsle/blog/core/internal/models/users"
|
||||||
"github.com/kirsle/blog/core/internal/sessions"
|
"github.com/kirsle/blog/core/internal/sessions"
|
||||||
"github.com/kirsle/blog/core/internal/types"
|
"github.com/kirsle/blog/core/internal/types"
|
||||||
|
@ -46,8 +45,6 @@ func LoginRequired(onError http.HandlerFunc) negroni.HandlerFunc {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Info("Redirect away!")
|
|
||||||
onError(w, r)
|
onError(w, r)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
14
core/internal/render/functions.go
Normal file
14
core/internal/render/functions.go
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
package render
|
||||||
|
|
||||||
|
import (
|
||||||
|
"html/template"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Funcs is a global funcmap that the blog can hook its internal
|
||||||
|
// methods onto.
|
||||||
|
var Funcs = template.FuncMap{
|
||||||
|
"StringsJoin": strings.Join,
|
||||||
|
"Now": time.Now,
|
||||||
|
}
|
|
@ -9,23 +9,14 @@ import (
|
||||||
|
|
||||||
"github.com/kirsle/blog/core/internal/forms"
|
"github.com/kirsle/blog/core/internal/forms"
|
||||||
"github.com/kirsle/blog/core/internal/log"
|
"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/models/users"
|
||||||
|
"github.com/kirsle/blog/core/internal/sessions"
|
||||||
|
"github.com/kirsle/blog/core/internal/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
// 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
|
// 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.)
|
// variables in. It auto-loads global template variables (site name, etc.)
|
||||||
// when the template is rendered.
|
// when the template is rendered.
|
||||||
|
@ -34,6 +25,7 @@ type Vars struct {
|
||||||
SetupNeeded bool
|
SetupNeeded bool
|
||||||
Title string
|
Title string
|
||||||
Path string
|
Path string
|
||||||
|
TemplatePath string
|
||||||
LoggedIn bool
|
LoggedIn bool
|
||||||
CurrentUser *users.User
|
CurrentUser *users.User
|
||||||
CSRF string
|
CSRF string
|
||||||
|
@ -53,18 +45,58 @@ type Vars struct {
|
||||||
Form forms.Form
|
Form forms.Form
|
||||||
}
|
}
|
||||||
|
|
||||||
// PartialTemplate handles rendering a Go template to a writer, without
|
// loadDefaults combines template variables with default, globally available vars.
|
||||||
// doing anything extra to the vars or dealing with net/http. This is ideal for
|
func (v *Vars) loadDefaults(r *http.Request) {
|
||||||
// rendering partials, such as comment partials.
|
// Get the site settings.
|
||||||
//
|
s, err := settings.Load()
|
||||||
// This will wrap the template in `.layout.gohtml` by default. To render just
|
if err != nil {
|
||||||
// a bare template on its own, i.e. for partial templates, create a Vars struct
|
s = settings.Defaults()
|
||||||
// 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!?")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if s.Initialized == false && !strings.HasPrefix(r.URL.Path, "/initial-setup") {
|
||||||
|
v.SetupNeeded = true
|
||||||
|
}
|
||||||
|
v.Request = r
|
||||||
|
v.RequestTime = r.Context().Value(types.StartTimeKey).(time.Time)
|
||||||
|
v.Title = s.Site.Title
|
||||||
|
v.Path = r.URL.Path
|
||||||
|
|
||||||
|
user, err := auth.CurrentUser(r)
|
||||||
|
v.CurrentUser = user
|
||||||
|
v.LoggedIn = err == 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 io.Writer, r *http.Request, path string, v Vars) error {
|
||||||
|
// Inject globally available variables.
|
||||||
|
v.loadDefaults(r)
|
||||||
|
|
||||||
|
// 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/")
|
||||||
|
|
||||||
// v interface{}, withLayout bool, functions map[string]interface{}) error {
|
// v interface{}, withLayout bool, functions map[string]interface{}) error {
|
||||||
var (
|
var (
|
||||||
layout Filepath
|
layout Filepath
|
||||||
|
@ -80,7 +112,7 @@ func PartialTemplate(w io.Writer, path string, C Config) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the layout template.
|
// Get the layout template.
|
||||||
if C.WithLayout {
|
if !v.NoLayout {
|
||||||
templateName = "layout"
|
templateName = "layout"
|
||||||
layout, err = ResolvePath(".layout")
|
layout, err = ResolvePath(".layout")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -98,27 +130,12 @@ func PartialTemplate(w io.Writer, path string, C Config) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Template functions.
|
t := template.New(filepath.Absolute).Funcs(Funcs)
|
||||||
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
|
// Parse the template files. The layout comes first because it's the wrapper
|
||||||
// and allows the filepath template to set the page title.
|
// and allows the filepath template to set the page title.
|
||||||
var templates []string
|
var templates []string
|
||||||
if C.WithLayout {
|
if !v.NoLayout {
|
||||||
templates = append(templates, layout.Absolute)
|
templates = append(templates, layout.Absolute)
|
||||||
}
|
}
|
||||||
t, err = t.ParseFiles(append(templates, commentEntry.Absolute, filepath.Absolute)...)
|
t, err = t.ParseFiles(append(templates, commentEntry.Absolute, filepath.Absolute)...)
|
||||||
|
@ -127,7 +144,7 @@ func PartialTemplate(w io.Writer, path string, C Config) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = t.ExecuteTemplate(w, templateName, C.Vars)
|
err = t.ExecuteTemplate(w, templateName, v)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("Template parsing error: %s", err)
|
log.Error("Template parsing error: %s", err)
|
||||||
return err
|
return err
|
||||||
|
@ -135,25 +152,3 @@ func PartialTemplate(w io.Writer, path string, C Config) error {
|
||||||
|
|
||||||
return nil
|
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
|
|
||||||
}
|
|
||||||
|
|
|
@ -41,7 +41,7 @@ func (b *Blog) PageHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
// Is it a template file?
|
// Is it a template file?
|
||||||
if strings.HasSuffix(filepath.URI, ".gohtml") {
|
if strings.HasSuffix(filepath.URI, ".gohtml") {
|
||||||
b.RenderTemplate(w, r, filepath.URI, render.Vars{})
|
b.RenderTemplate(w, r, filepath.URI, NewVars())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,49 +1,12 @@
|
||||||
package core
|
package core
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"html/template"
|
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/kirsle/blog/core/internal/forms"
|
|
||||||
"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/render"
|
"github.com/kirsle/blog/core/internal/render"
|
||||||
"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
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewVars initializes a Vars struct with the custom Data map initialized.
|
// NewVars initializes a Vars struct with the custom Data map initialized.
|
||||||
// You may pass in an initial value for this map if you want.
|
// You may pass in an initial value for this map if you want.
|
||||||
func NewVars(data ...map[interface{}]interface{}) render.Vars {
|
func NewVars(data ...map[interface{}]interface{}) render.Vars {
|
||||||
|
@ -58,102 +21,20 @@ func NewVars(data ...map[interface{}]interface{}) render.Vars {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// LoadDefaults combines template variables with default, globally available vars.
|
|
||||||
func (b *Blog) LoadDefaults(v render.Vars, r *http.Request) render.Vars {
|
|
||||||
// Get the site settings.
|
|
||||||
s, err := settings.Load()
|
|
||||||
if err != nil {
|
|
||||||
s = settings.Defaults()
|
|
||||||
}
|
|
||||||
|
|
||||||
if s.Initialized == false && !strings.HasPrefix(r.URL.Path, "/initial-setup") {
|
|
||||||
v.SetupNeeded = true
|
|
||||||
}
|
|
||||||
v.Request = r
|
|
||||||
v.RequestTime = r.Context().Value(types.StartTimeKey).(time.Time)
|
|
||||||
v.Title = s.Site.Title
|
|
||||||
v.Path = r.URL.Path
|
|
||||||
|
|
||||||
user, err := auth.CurrentUser(r)
|
|
||||||
v.CurrentUser = user
|
|
||||||
v.LoggedIn = err == nil
|
|
||||||
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
|
|
||||||
// RenderPartialTemplate 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 (b *Blog) RenderPartialTemplate(w io.Writer, r *http.Request, path string, v render.Vars, withLayout bool, functions map[string]interface{}) error {
|
|
||||||
v = b.LoadDefaults(v, r)
|
|
||||||
return render.PartialTemplate(w, path, render.Config{
|
|
||||||
Request: r,
|
|
||||||
Vars: &v,
|
|
||||||
WithLayout: withLayout,
|
|
||||||
Functions: b.TemplateFuncs(nil, nil, functions),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// RenderTemplate responds with an HTML template.
|
// RenderTemplate responds with an HTML template.
|
||||||
//
|
//
|
||||||
// The vars will be massaged a bit to load the global defaults (such as the
|
// 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
|
// 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
|
// new CSRF token, and other such things.
|
||||||
// without all that nonsense, use RenderPartialTemplate.
|
//
|
||||||
func (b *Blog) RenderTemplate(w http.ResponseWriter, r *http.Request, path string, vars render.Vars) error {
|
// For server-rendered templates given directly to the user (i.e., in controllers),
|
||||||
|
// give it the http.ResponseWriter; for partial templates you can give it a
|
||||||
|
// bytes.Buffer to write to instead. The subtle difference is whether or not the
|
||||||
|
// template will have access to the request's session.
|
||||||
|
func (b *Blog) RenderTemplate(w io.Writer, r *http.Request, path string, vars render.Vars) error {
|
||||||
if r == nil {
|
if r == nil {
|
||||||
panic("core.RenderTemplate(): the *http.Request is nil!?")
|
panic("core.RenderTemplate(): the *http.Request is nil!?")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Inject globally available variables.
|
return render.Template(w, r, path, vars)
|
||||||
vars = b.LoadDefaults(vars, r)
|
|
||||||
|
|
||||||
// Add any flashed messages from the endpoint controllers.
|
|
||||||
session := sessions.Get(r)
|
|
||||||
if flashes := session.Flashes(); len(flashes) > 0 {
|
|
||||||
for _, flash := range flashes {
|
|
||||||
_ = flash
|
|
||||||
vars.Flashes = append(vars.Flashes, flash.(string))
|
|
||||||
}
|
|
||||||
session.Save(r, w)
|
|
||||||
}
|
|
||||||
|
|
||||||
vars.RequestDuration = time.Now().Sub(vars.RequestTime)
|
|
||||||
vars.CSRF = middleware.GenerateCSRFToken(w, r, session)
|
|
||||||
vars.Editable = !strings.HasPrefix(path, "admin/")
|
|
||||||
|
|
||||||
return render.Template(w, path, render.Config{
|
|
||||||
Request: r,
|
|
||||||
Vars: &vars,
|
|
||||||
Functions: b.TemplateFuncs(w, r, nil),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// TemplateFuncs returns the common template function map.
|
|
||||||
func (b *Blog) TemplateFuncs(w http.ResponseWriter, r *http.Request, inject map[string]interface{}) map[string]interface{} {
|
|
||||||
fn := map[string]interface{}{
|
|
||||||
"RenderIndex": b.RenderIndex,
|
|
||||||
"RenderPost": b.RenderPost,
|
|
||||||
"RenderTags": b.RenderTags,
|
|
||||||
"RenderComments": func(subject string, ids ...string) template.HTML {
|
|
||||||
if w == nil || r == nil {
|
|
||||||
return template.HTML("[RenderComments Error: need both http.ResponseWriter and http.Request]")
|
|
||||||
}
|
|
||||||
|
|
||||||
session := sessions.Get(r)
|
|
||||||
csrf := middleware.GenerateCSRFToken(w, r, session)
|
|
||||||
return b.RenderComments(session, csrf, r.URL.Path, subject, ids...)
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
if inject != nil {
|
|
||||||
for k, v := range inject {
|
|
||||||
fn[k] = v
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return fn
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -81,7 +81,7 @@
|
||||||
|
|
||||||
{{ if and .CurrentUser.Admin .Editable }}
|
{{ if and .CurrentUser.Admin .Editable }}
|
||||||
<p class="mt-4">
|
<p class="mt-4">
|
||||||
<strong>Admin:</strong> [<a href="/admin/editor?file={{ or .Data.MarkdownFile TemplateName }}">edit this page</a>]
|
<strong>Admin:</strong> [<a href="/admin/editor?file={{ or .Data.MarkdownFile .Path }}">edit this page</a>]
|
||||||
</p>
|
</p>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -18,7 +18,7 @@
|
||||||
<h2 id="comments" class="mt-4">Comments</h2>
|
<h2 id="comments" class="mt-4">Comments</h2>
|
||||||
|
|
||||||
{{ $idStr := printf "%d" $p.ID}}
|
{{ $idStr := printf "%d" $p.ID}}
|
||||||
{{ RenderComments $p.Title "post" $idStr }}
|
{{ RenderComments .Request $p.Title "post" $idStr }}
|
||||||
{{ else }}
|
{{ else }}
|
||||||
<hr>
|
<hr>
|
||||||
<em>Comments are disabled on this post.</em>
|
<em>Comments are disabled on this post.</em>
|
||||||
|
|
Loading…
Reference in New Issue
Block a user