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`.
This commit is contained in:
parent
eab7dae75b
commit
6d3de7da69
|
@ -33,7 +33,7 @@ func (b *Blog) AdminRoutes(r *mux.Router) {
|
|||
|
||||
// AdminHandler is the admin landing page.
|
||||
func (b *Blog) AdminHandler(w http.ResponseWriter, r *http.Request) {
|
||||
render.Template(w, r, "admin/index", NewVars())
|
||||
render.Template(w, r, "admin/index", nil)
|
||||
}
|
||||
|
||||
// FileTree holds information about files in the document roots.
|
||||
|
@ -106,13 +106,13 @@ func (b *Blog) EditorHandler(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
}
|
||||
|
||||
v := NewVars(map[interface{}]interface{}{
|
||||
v := map[string]interface{}{
|
||||
"File": file,
|
||||
"Path": fp,
|
||||
"Body": string(body),
|
||||
"FromCore": fromCore,
|
||||
})
|
||||
b.RenderTemplate(w, r, "admin/editor", v)
|
||||
}
|
||||
render.Template(w, r, "admin/editor", v)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -165,19 +165,19 @@ func (b *Blog) editorFileList(w http.ResponseWriter, r *http.Request) {
|
|||
|
||||
trees = append(trees, tree)
|
||||
}
|
||||
v := NewVars(map[interface{}]interface{}{
|
||||
v := map[string]interface{}{
|
||||
"FileTrees": trees,
|
||||
})
|
||||
b.RenderTemplate(w, r, "admin/filelist", v)
|
||||
}
|
||||
render.Template(w, r, "admin/filelist", v)
|
||||
}
|
||||
|
||||
// SettingsHandler lets you configure the app from the frontend.
|
||||
func (b *Blog) SettingsHandler(w http.ResponseWriter, r *http.Request) {
|
||||
v := NewVars()
|
||||
|
||||
// Get the current settings.
|
||||
settings, _ := settings.Load()
|
||||
v.Data["s"] = settings
|
||||
v := map[string]interface{}{
|
||||
"s": settings,
|
||||
}
|
||||
|
||||
if r.Method == http.MethodPost {
|
||||
redisPort, _ := strconv.Atoi(r.FormValue("redis-port"))
|
||||
|
@ -220,7 +220,7 @@ func (b *Blog) SettingsHandler(w http.ResponseWriter, r *http.Request) {
|
|||
settings.Mail.Password = form.MailPassword
|
||||
err := form.Validate()
|
||||
if err != nil {
|
||||
v.Error = err
|
||||
v["Error"] = err
|
||||
} else {
|
||||
// Save the settings.
|
||||
settings.Save()
|
||||
|
@ -230,5 +230,5 @@ func (b *Blog) SettingsHandler(w http.ResponseWriter, r *http.Request) {
|
|||
return
|
||||
}
|
||||
}
|
||||
b.RenderTemplate(w, r, "admin/settings", v)
|
||||
render.Template(w, r, "admin/settings", v)
|
||||
}
|
||||
|
|
23
core/auth.go
23
core/auth.go
|
@ -9,6 +9,7 @@ import (
|
|||
"github.com/kirsle/blog/core/internal/log"
|
||||
"github.com/kirsle/blog/core/internal/middleware/auth"
|
||||
"github.com/kirsle/blog/core/internal/models/users"
|
||||
"github.com/kirsle/blog/core/internal/render"
|
||||
"github.com/kirsle/blog/core/internal/responses"
|
||||
"github.com/kirsle/blog/core/internal/sessions"
|
||||
)
|
||||
|
@ -40,8 +41,9 @@ func (b *Blog) Login(w http.ResponseWriter, r *http.Request, u *users.User) erro
|
|||
|
||||
// LoginHandler shows and handles the login page.
|
||||
func (b *Blog) LoginHandler(w http.ResponseWriter, r *http.Request) {
|
||||
vars := NewVars()
|
||||
vars.Form = forms.Setup{}
|
||||
vars := map[string]interface{}{
|
||||
"Form": forms.Setup{},
|
||||
}
|
||||
|
||||
var nextURL string
|
||||
if r.Method == http.MethodPost {
|
||||
|
@ -49,22 +51,22 @@ func (b *Blog) LoginHandler(w http.ResponseWriter, r *http.Request) {
|
|||
} else {
|
||||
nextURL = r.URL.Query().Get("next")
|
||||
}
|
||||
vars.Data["NextURL"] = nextURL
|
||||
vars["NextURL"] = nextURL
|
||||
|
||||
if r.Method == http.MethodPost {
|
||||
form := &forms.Login{
|
||||
Username: r.FormValue("username"),
|
||||
Password: r.FormValue("password"),
|
||||
}
|
||||
vars.Form = form
|
||||
vars["Form"] = form
|
||||
err := form.Validate()
|
||||
if err != nil {
|
||||
vars.Error = err
|
||||
vars["Error"] = err
|
||||
} else {
|
||||
// Test the login.
|
||||
user, err := users.CheckAuth(form.Username, form.Password)
|
||||
if err != nil {
|
||||
vars.Error = errors.New("bad username or password")
|
||||
vars["Error"] = errors.New("bad username or password")
|
||||
} else {
|
||||
// Login OK!
|
||||
responses.Flash(w, r, "Login OK!")
|
||||
|
@ -82,7 +84,7 @@ func (b *Blog) LoginHandler(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
}
|
||||
|
||||
b.RenderTemplate(w, r, "login", vars)
|
||||
render.Template(w, r, "login", vars)
|
||||
}
|
||||
|
||||
// LogoutHandler logs the user out and redirects to the home page.
|
||||
|
@ -113,13 +115,14 @@ func (b *Blog) AccountHandler(w http.ResponseWriter, r *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
v := NewVars()
|
||||
form := &forms.Account{
|
||||
Username: user.Username,
|
||||
Email: user.Email,
|
||||
Name: user.Name,
|
||||
}
|
||||
v.Form = form
|
||||
v := map[string]interface{}{
|
||||
"Form": form,
|
||||
}
|
||||
|
||||
if r.Method == http.MethodPost {
|
||||
form.Username = users.Normalize(r.FormValue("username"))
|
||||
|
@ -172,5 +175,5 @@ func (b *Blog) AccountHandler(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
}
|
||||
|
||||
b.RenderTemplate(w, r, "account", v)
|
||||
render.Template(w, r, "account", v)
|
||||
}
|
||||
|
|
87
core/blog.go
87
core/blog.go
|
@ -151,7 +151,7 @@ func (b *Blog) Tagged(w http.ResponseWriter, r *http.Request) {
|
|||
tag, ok := params["tag"]
|
||||
if !ok {
|
||||
// They're listing all the tags.
|
||||
b.RenderTemplate(w, r, "blog/tags.gohtml", NewVars())
|
||||
render.Template(w, r, "blog/tags.gohtml", nil)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -182,11 +182,11 @@ func (b *Blog) CommonIndexHandler(w http.ResponseWriter, r *http.Request, tag, p
|
|||
title = "Blog"
|
||||
}
|
||||
|
||||
b.RenderTemplate(w, r, "blog/index", NewVars(map[interface{}]interface{}{
|
||||
render.Template(w, r, "blog/index", map[string]interface{}{
|
||||
"Title": title,
|
||||
"Tag": tag,
|
||||
"Privacy": privacy,
|
||||
}))
|
||||
})
|
||||
}
|
||||
|
||||
// RecentPosts gets and filters the blog entries and orders them by most recent.
|
||||
|
@ -305,15 +305,12 @@ func (b *Blog) RenderIndex(r *http.Request, tag, privacy string) template.HTML {
|
|||
|
||||
// Render the blog index partial.
|
||||
var output bytes.Buffer
|
||||
v := render.Vars{
|
||||
NoLayout: true,
|
||||
Data: map[interface{}]interface{}{
|
||||
"PreviousPage": previousPage,
|
||||
"NextPage": nextPage,
|
||||
"View": view,
|
||||
},
|
||||
v := map[string]interface{}{
|
||||
"PreviousPage": previousPage,
|
||||
"NextPage": nextPage,
|
||||
"View": view,
|
||||
}
|
||||
b.RenderTemplate(&output, r, "blog/index.partial", v)
|
||||
render.Template(&output, r, "blog/index.partial", v)
|
||||
|
||||
return template.HTML(output.String())
|
||||
}
|
||||
|
@ -331,14 +328,11 @@ func (b *Blog) RenderTags(r *http.Request, indexView bool) template.HTML {
|
|||
}
|
||||
|
||||
var output bytes.Buffer
|
||||
v := render.Vars{
|
||||
NoLayout: true,
|
||||
Data: map[interface{}]interface{}{
|
||||
"IndexView": indexView,
|
||||
"Tags": tags,
|
||||
},
|
||||
v := map[string]interface{}{
|
||||
"IndexView": indexView,
|
||||
"Tags": tags,
|
||||
}
|
||||
b.RenderTemplate(&output, r, "blog/tags.partial", v)
|
||||
render.Template(&output, r, "blog/tags.partial", v)
|
||||
|
||||
return template.HTML(output.String())
|
||||
}
|
||||
|
@ -384,10 +378,10 @@ func (b *Blog) BlogArchive(w http.ResponseWriter, r *http.Request) {
|
|||
result = append(result, byMonth[label])
|
||||
}
|
||||
|
||||
v := NewVars(map[interface{}]interface{}{
|
||||
v := map[string]interface{}{
|
||||
"Archive": result,
|
||||
})
|
||||
b.RenderTemplate(w, r, "blog/archive", v)
|
||||
}
|
||||
render.Template(w, r, "blog/archive", v)
|
||||
}
|
||||
|
||||
// viewPost is the underlying implementation of the handler to view a blog
|
||||
|
@ -408,10 +402,10 @@ func (b *Blog) viewPost(w http.ResponseWriter, r *http.Request, fragment string)
|
|||
}
|
||||
}
|
||||
|
||||
v := NewVars(map[interface{}]interface{}{
|
||||
v := map[string]interface{}{
|
||||
"Post": post,
|
||||
})
|
||||
b.RenderTemplate(w, r, "blog/entry", v)
|
||||
}
|
||||
render.Template(w, r, "blog/entry", v)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
@ -447,19 +441,16 @@ func (b *Blog) RenderPost(r *http.Request, p *posts.Post, indexView bool, numCom
|
|||
rendered = template.HTML(p.Body)
|
||||
}
|
||||
|
||||
meta := render.Vars{
|
||||
NoLayout: true,
|
||||
Data: map[interface{}]interface{}{
|
||||
"Post": p,
|
||||
"Rendered": rendered,
|
||||
"Author": author,
|
||||
"IndexView": indexView,
|
||||
"Snipped": snipped,
|
||||
"NumComments": numComments,
|
||||
},
|
||||
meta := map[string]interface{}{
|
||||
"Post": p,
|
||||
"Rendered": rendered,
|
||||
"Author": author,
|
||||
"IndexView": indexView,
|
||||
"Snipped": snipped,
|
||||
"NumComments": numComments,
|
||||
}
|
||||
output := bytes.Buffer{}
|
||||
err = b.RenderTemplate(&output, r, "blog/entry.partial", meta)
|
||||
err = render.Template(&output, r, "blog/entry.partial", meta)
|
||||
if err != nil {
|
||||
return template.HTML(fmt.Sprintf("[template error in blog/entry.partial: %s]", err.Error()))
|
||||
}
|
||||
|
@ -469,9 +460,9 @@ func (b *Blog) RenderPost(r *http.Request, p *posts.Post, indexView bool, numCom
|
|||
|
||||
// EditBlog is the blog writing and editing page.
|
||||
func (b *Blog) EditBlog(w http.ResponseWriter, r *http.Request) {
|
||||
v := NewVars(map[interface{}]interface{}{
|
||||
v := map[string]interface{}{
|
||||
"preview": "",
|
||||
})
|
||||
}
|
||||
var post *posts.Post
|
||||
|
||||
// Are we editing an existing post?
|
||||
|
@ -480,7 +471,7 @@ func (b *Blog) EditBlog(w http.ResponseWriter, r *http.Request) {
|
|||
if err == nil {
|
||||
post, err = posts.Load(id)
|
||||
if err != nil {
|
||||
v.Error = errors.New("that post ID was not found")
|
||||
v["Error"] = errors.New("that post ID was not found")
|
||||
post = posts.New()
|
||||
}
|
||||
}
|
||||
|
@ -496,13 +487,13 @@ func (b *Blog) EditBlog(w http.ResponseWriter, r *http.Request) {
|
|||
switch r.FormValue("submit") {
|
||||
case "preview":
|
||||
if post.ContentType == string(MARKDOWN) {
|
||||
v.Data["preview"] = template.HTML(markdown.RenderTrustedMarkdown(post.Body))
|
||||
v["preview"] = template.HTML(markdown.RenderTrustedMarkdown(post.Body))
|
||||
} else {
|
||||
v.Data["preview"] = template.HTML(post.Body)
|
||||
v["preview"] = template.HTML(post.Body)
|
||||
}
|
||||
case "post":
|
||||
if err := post.Validate(); err != nil {
|
||||
v.Error = err
|
||||
v["Error"] = err
|
||||
} else {
|
||||
author, _ := auth.CurrentUser(r)
|
||||
post.AuthorID = author.ID
|
||||
|
@ -510,7 +501,7 @@ func (b *Blog) EditBlog(w http.ResponseWriter, r *http.Request) {
|
|||
post.Updated = time.Now().UTC()
|
||||
err = post.Save()
|
||||
if err != nil {
|
||||
v.Error = err
|
||||
v["Error"] = err
|
||||
} else {
|
||||
responses.Flash(w, r, "Post created!")
|
||||
responses.Redirect(w, "/"+post.Fragment)
|
||||
|
@ -519,16 +510,16 @@ func (b *Blog) EditBlog(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
}
|
||||
|
||||
v.Data["post"] = post
|
||||
b.RenderTemplate(w, r, "blog/edit", v)
|
||||
v["post"] = post
|
||||
render.Template(w, r, "blog/edit", v)
|
||||
}
|
||||
|
||||
// DeletePost to delete a blog entry.
|
||||
func (b *Blog) DeletePost(w http.ResponseWriter, r *http.Request) {
|
||||
var post *posts.Post
|
||||
v := NewVars(map[interface{}]interface{}{
|
||||
v := map[string]interface{}{
|
||||
"Post": nil,
|
||||
})
|
||||
}
|
||||
|
||||
var idStr string
|
||||
if r.Method == http.MethodPost {
|
||||
|
@ -557,6 +548,6 @@ func (b *Blog) DeletePost(w http.ResponseWriter, r *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
v.Data["Post"] = post
|
||||
b.RenderTemplate(w, r, "blog/delete", v)
|
||||
v["Post"] = post
|
||||
render.Template(w, r, "blog/delete", v)
|
||||
}
|
||||
|
|
|
@ -148,7 +148,6 @@ func (b *Blog) CommentHandler(w http.ResponseWriter, r *http.Request) {
|
|||
b.BadRequest(w, r, "That method is not allowed.")
|
||||
return
|
||||
}
|
||||
v := NewVars()
|
||||
currentUser, _ := auth.CurrentUser(r)
|
||||
editToken := b.GetEditToken(w, r)
|
||||
submit := r.FormValue("submit")
|
||||
|
@ -205,6 +204,8 @@ func (b *Blog) CommentHandler(w http.ResponseWriter, r *http.Request) {
|
|||
session.Values["c.email"] = c.Email
|
||||
session.Save(r, w)
|
||||
|
||||
v := map[string]interface{}{}
|
||||
|
||||
// Previewing, deleting, or posting?
|
||||
switch submit {
|
||||
case ActionPreview, ActionDelete:
|
||||
|
@ -216,7 +217,7 @@ func (b *Blog) CommentHandler(w http.ResponseWriter, r *http.Request) {
|
|||
c.HTML = template.HTML(markdown.RenderMarkdown(c.Body))
|
||||
case ActionPost:
|
||||
if err := c.Validate(); err != nil {
|
||||
v.Error = err
|
||||
v["Error"] = err
|
||||
} else {
|
||||
// Store our edit token, if we don't have one. For example, admins
|
||||
// can edit others' comments but should not replace their edit token.
|
||||
|
@ -255,25 +256,24 @@ func (b *Blog) CommentHandler(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
}
|
||||
|
||||
v.Data["Thread"] = t
|
||||
v.Data["Comment"] = c
|
||||
v.Data["Editing"] = c.Editing
|
||||
v.Data["Deleting"] = submit == ActionDelete
|
||||
v["Thread"] = t
|
||||
v["Comment"] = c
|
||||
v["Editing"] = c.Editing
|
||||
v["Deleting"] = submit == ActionDelete
|
||||
|
||||
b.RenderTemplate(w, r, "comments/index.gohtml", v)
|
||||
render.Template(w, r, "comments/index.gohtml", v)
|
||||
}
|
||||
|
||||
// SubscriptionHandler to opt out of subscriptions.
|
||||
func (b *Blog) SubscriptionHandler(w http.ResponseWriter, r *http.Request) {
|
||||
v := NewVars()
|
||||
|
||||
var err error
|
||||
// POST to unsubscribe from all threads.
|
||||
if r.Method == http.MethodPost {
|
||||
email := r.FormValue("email")
|
||||
if email == "" {
|
||||
v.Error = errors.New("email address is required to unsubscribe from comment threads")
|
||||
err = errors.New("email address is required to unsubscribe from comment threads")
|
||||
} else if _, err := mail.ParseAddress(email); err != nil {
|
||||
v.Error = errors.New("invalid email address")
|
||||
err = errors.New("invalid email address")
|
||||
}
|
||||
|
||||
m := comments.LoadMailingList()
|
||||
|
@ -294,7 +294,9 @@ func (b *Blog) SubscriptionHandler(w http.ResponseWriter, r *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
b.RenderTemplate(w, r, "comments/subscription.gohtml", v)
|
||||
render.Template(w, r, "comments/subscription.gohtml", map[string]error{
|
||||
"Error": err,
|
||||
})
|
||||
}
|
||||
|
||||
// QuickDeleteHandler allows the admin to quickly delete spam without logging in.
|
||||
|
|
|
@ -12,15 +12,17 @@ import (
|
|||
"github.com/kirsle/blog/core/internal/forms"
|
||||
"github.com/kirsle/blog/core/internal/markdown"
|
||||
"github.com/kirsle/blog/core/internal/models/settings"
|
||||
"github.com/kirsle/blog/core/internal/render"
|
||||
"github.com/kirsle/blog/core/internal/responses"
|
||||
)
|
||||
|
||||
// ContactRoutes attaches the contact URL to the app.
|
||||
func (b *Blog) ContactRoutes(r *mux.Router) {
|
||||
r.HandleFunc("/contact", func(w http.ResponseWriter, r *http.Request) {
|
||||
v := NewVars()
|
||||
form := forms.Contact{}
|
||||
v.Form = &form
|
||||
form := &forms.Contact{}
|
||||
v := map[string]interface{}{
|
||||
"Form": form,
|
||||
}
|
||||
|
||||
// If there is no site admin, show an error.
|
||||
cfg, err := settings.Load()
|
||||
|
@ -73,6 +75,6 @@ func (b *Blog) ContactRoutes(r *mux.Router) {
|
|||
}
|
||||
}
|
||||
|
||||
b.RenderTemplate(w, r, "contact", v)
|
||||
render.Template(w, r, "contact", v)
|
||||
})
|
||||
}
|
||||
|
|
|
@ -14,8 +14,8 @@ func (b *Blog) NotFound(w http.ResponseWriter, r *http.Request, message string)
|
|||
}
|
||||
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
err := b.RenderTemplate(w, r, ".errors/404", render.Vars{
|
||||
Message: message,
|
||||
err := render.Template(w, r, ".errors/404", map[string]string{
|
||||
"Message": message,
|
||||
})
|
||||
if err != nil {
|
||||
log.Error(err.Error())
|
||||
|
@ -26,8 +26,8 @@ func (b *Blog) NotFound(w http.ResponseWriter, r *http.Request, message string)
|
|||
// Forbidden sends an HTTP 403 Forbidden response.
|
||||
func (b *Blog) Forbidden(w http.ResponseWriter, r *http.Request, message string) {
|
||||
w.WriteHeader(http.StatusForbidden)
|
||||
err := b.RenderTemplate(w, r, ".errors/403", render.Vars{
|
||||
Message: message,
|
||||
err := render.Template(w, r, ".errors/403", map[string]string{
|
||||
"Message": message,
|
||||
})
|
||||
if err != nil {
|
||||
log.Error(err.Error())
|
||||
|
@ -38,8 +38,8 @@ func (b *Blog) Forbidden(w http.ResponseWriter, r *http.Request, message string)
|
|||
// Error sends an HTTP 500 Internal Server Error response.
|
||||
func (b *Blog) Error(w http.ResponseWriter, r *http.Request, message string) {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
err := b.RenderTemplate(w, r, ".errors/500", render.Vars{
|
||||
Message: message,
|
||||
err := render.Template(w, r, ".errors/500", map[string]string{
|
||||
"Message": message,
|
||||
})
|
||||
if err != nil {
|
||||
log.Error(err.Error())
|
||||
|
@ -50,8 +50,8 @@ func (b *Blog) Error(w http.ResponseWriter, r *http.Request, message string) {
|
|||
// BadRequest sends an HTTP 400 Bad Request.
|
||||
func (b *Blog) BadRequest(w http.ResponseWriter, r *http.Request, message string) {
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
err := b.RenderTemplate(w, r, ".errors/400", render.Vars{
|
||||
Message: message,
|
||||
err := render.Template(w, r, ".errors/400", map[string]string{
|
||||
"Message": message,
|
||||
})
|
||||
if err != nil {
|
||||
log.Error(err.Error())
|
||||
|
|
|
@ -14,8 +14,9 @@ import (
|
|||
|
||||
// SetupHandler is the initial blog setup route.
|
||||
func (b *Blog) SetupHandler(w http.ResponseWriter, r *http.Request) {
|
||||
vars := render.Vars{
|
||||
Form: forms.Setup{},
|
||||
form := &forms.Setup{}
|
||||
vars := map[string]interface{}{
|
||||
"Form": form,
|
||||
}
|
||||
|
||||
// Reject if we're already set up.
|
||||
|
@ -26,15 +27,10 @@ func (b *Blog) SetupHandler(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
|
||||
if r.Method == http.MethodPost {
|
||||
form := forms.Setup{
|
||||
Username: r.FormValue("username"),
|
||||
Password: r.FormValue("password"),
|
||||
Confirm: r.FormValue("confirm"),
|
||||
}
|
||||
vars.Form = form
|
||||
form.ParseForm(r)
|
||||
err := form.Validate()
|
||||
if err != nil {
|
||||
vars.Error = err
|
||||
vars["Error"] = err
|
||||
} else {
|
||||
// Save the site config.
|
||||
log.Info("Creating default website config file")
|
||||
|
@ -54,7 +50,7 @@ func (b *Blog) SetupHandler(w http.ResponseWriter, r *http.Request) {
|
|||
err := users.Create(user)
|
||||
if err != nil {
|
||||
log.Error("Error: %v", err)
|
||||
vars.Error = err
|
||||
vars["Error"] = err
|
||||
}
|
||||
|
||||
// All set!
|
||||
|
@ -64,5 +60,5 @@ func (b *Blog) SetupHandler(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
}
|
||||
|
||||
b.RenderTemplate(w, r, "initial-setup", vars)
|
||||
render.Template(w, r, "initial-setup", vars)
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
package forms
|
||||
|
||||
// Form is an interface for forms that can validate themselves.
|
||||
type Form interface {
|
||||
Validate() error
|
||||
}
|
||||
// type Form interface {
|
||||
// Validate() error
|
||||
// }
|
||||
|
|
|
@ -2,6 +2,7 @@ package forms
|
|||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// Setup is for the initial blog setup page at /initial-setup.
|
||||
|
@ -11,6 +12,13 @@ type Setup struct {
|
|||
Confirm string
|
||||
}
|
||||
|
||||
// Parse form values.
|
||||
func (f *Setup) ParseForm(r *http.Request) {
|
||||
f.Username = r.FormValue("username")
|
||||
f.Password = r.FormValue("password")
|
||||
f.Confirm = r.FormValue("confirm")
|
||||
}
|
||||
|
||||
// Validate the form.
|
||||
func (f Setup) Validate() error {
|
||||
if len(f.Username) == 0 {
|
||||
|
|
|
@ -125,8 +125,10 @@ func Pygmentize(language, source string) (string, error) {
|
|||
cacheKey := "pygmentize:" + hash
|
||||
|
||||
// Do we have it cached?
|
||||
if cached, err := Cache.Get(cacheKey); err == nil && len(cached) > 0 {
|
||||
return string(cached), nil
|
||||
if Cache != nil {
|
||||
if cached, err := Cache.Get(cacheKey); err == nil && len(cached) > 0 {
|
||||
return string(cached), nil
|
||||
}
|
||||
}
|
||||
|
||||
// Defer to the `pygmentize` command
|
||||
|
@ -150,9 +152,11 @@ func Pygmentize(language, source string) (string, error) {
|
|||
}
|
||||
|
||||
result = out.String()
|
||||
err := Cache.Set(cacheKey, []byte(result), 60*60*24) // cool md5's don't change
|
||||
if err != nil {
|
||||
log.Error("Couldn't cache Pygmentize output: %s", err)
|
||||
if Cache != nil {
|
||||
err := Cache.Set(cacheKey, []byte(result), 60*60*24) // cool md5's don't change
|
||||
if err != nil {
|
||||
log.Error("Couldn't cache Pygmentize output: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
return result, nil
|
||||
|
|
|
@ -7,7 +7,6 @@ import (
|
|||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/kirsle/blog/core/internal/forms"
|
||||
"github.com/kirsle/blog/core/internal/log"
|
||||
"github.com/kirsle/blog/core/internal/middleware"
|
||||
"github.com/kirsle/blog/core/internal/middleware/auth"
|
||||
|
@ -20,12 +19,12 @@ import (
|
|||
// 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 {
|
||||
type vars struct {
|
||||
// Global, "constant" template variables.
|
||||
SetupNeeded bool
|
||||
Title string
|
||||
Path string
|
||||
TemplatePath string
|
||||
TemplatePath string // actual template file on disk
|
||||
LoggedIn bool
|
||||
CurrentUser *users.User
|
||||
CSRF string
|
||||
|
@ -34,36 +33,11 @@ type Vars struct {
|
|||
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
|
||||
}
|
||||
|
||||
// loadDefaults combines template variables with default, globally available vars.
|
||||
func (v *Vars) loadDefaults(r *http.Request) {
|
||||
// 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
|
||||
Data interface{}
|
||||
}
|
||||
|
||||
// Template responds with an HTML template.
|
||||
|
@ -72,9 +46,30 @@ func (v *Vars) loadDefaults(r *http.Request) {
|
|||
// 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 {
|
||||
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.loadDefaults(r)
|
||||
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 {
|
||||
|
@ -97,11 +92,9 @@ func Template(w io.Writer, r *http.Request, path string, v Vars) error {
|
|||
v.RequestDuration = time.Now().Sub(v.RequestTime)
|
||||
v.Editable = !strings.HasPrefix(path, "admin/")
|
||||
|
||||
// v interface{}, withLayout bool, functions map[string]interface{}) error {
|
||||
var (
|
||||
layout Filepath
|
||||
templateName string
|
||||
err error
|
||||
)
|
||||
|
||||
// Find the file path to the template.
|
||||
|
@ -110,9 +103,10 @@ func Template(w io.Writer, r *http.Request, path string, v Vars) error {
|
|||
log.Error("RenderTemplate(%s): file not found", path)
|
||||
return err
|
||||
}
|
||||
v.TemplatePath = filepath.URI
|
||||
|
||||
// Get the layout template.
|
||||
if !v.NoLayout {
|
||||
if !isPartial {
|
||||
templateName = "layout"
|
||||
layout, err = ResolvePath(".layout")
|
||||
if err != nil {
|
||||
|
@ -135,7 +129,7 @@ func Template(w io.Writer, r *http.Request, path string, v Vars) error {
|
|||
// 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 !v.NoLayout {
|
||||
if !isPartial {
|
||||
templates = append(templates, layout.Absolute)
|
||||
}
|
||||
t, err = t.ParseFiles(append(templates, commentEntry.Absolute, filepath.Absolute)...)
|
||||
|
|
|
@ -41,7 +41,7 @@ func (b *Blog) PageHandler(w http.ResponseWriter, r *http.Request) {
|
|||
|
||||
// Is it a template file?
|
||||
if strings.HasSuffix(filepath.URI, ".gohtml") {
|
||||
b.RenderTemplate(w, r, filepath.URI, NewVars())
|
||||
render.Template(w, r, filepath.URI, nil)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -58,11 +58,11 @@ func (b *Blog) PageHandler(w http.ResponseWriter, r *http.Request) {
|
|||
html := markdown.RenderTrustedMarkdown(body)
|
||||
title, _ := markdown.TitleFromMarkdown(body)
|
||||
|
||||
b.RenderTemplate(w, r, ".markdown", NewVars(map[interface{}]interface{}{
|
||||
render.Template(w, r, ".markdown", map[string]interface{}{
|
||||
"Title": title,
|
||||
"HTML": template.HTML(html),
|
||||
"MarkdownFile": filepath.URI,
|
||||
}))
|
||||
"MarkdownPath": filepath.URI,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -1,40 +0,0 @@
|
|||
package core
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net/http"
|
||||
|
||||
"github.com/kirsle/blog/core/internal/render"
|
||||
)
|
||||
|
||||
// NewVars initializes a Vars struct with the custom Data map initialized.
|
||||
// You may pass in an initial value for this map if you want.
|
||||
func NewVars(data ...map[interface{}]interface{}) render.Vars {
|
||||
var value map[interface{}]interface{}
|
||||
if len(data) > 0 {
|
||||
value = data[0]
|
||||
} else {
|
||||
value = make(map[interface{}]interface{})
|
||||
}
|
||||
return render.Vars{
|
||||
Data: value,
|
||||
}
|
||||
}
|
||||
|
||||
// RenderTemplate 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.
|
||||
//
|
||||
// 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 {
|
||||
panic("core.RenderTemplate(): the *http.Request is nil!?")
|
||||
}
|
||||
|
||||
return render.Template(w, r, path, vars)
|
||||
}
|
|
@ -2,5 +2,5 @@
|
|||
{{ define "content" }}
|
||||
<h1>400 Bad Request</h1>
|
||||
|
||||
{{ .Message }}
|
||||
{{ .Data.Message }}
|
||||
{{ end }}
|
||||
|
|
|
@ -2,5 +2,5 @@
|
|||
{{ define "content" }}
|
||||
<h1>403 Forbidden</h1>
|
||||
|
||||
{{ .Message }}
|
||||
{{ .Data.Message }}
|
||||
{{ end }}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
{{ define "content" }}
|
||||
<h1>404 Not Found</h1>
|
||||
|
||||
{{ .Message }}
|
||||
{{ .Data.Message }}
|
||||
|
||||
{{ if .CurrentUser.Admin }}
|
||||
<p>
|
||||
|
|
|
@ -2,5 +2,5 @@
|
|||
{{ define "content" }}
|
||||
<h1>500 Internal Server Error</h1>
|
||||
|
||||
{{ .Message }}
|
||||
{{ .Data.Message }}
|
||||
{{ end }}
|
||||
|
|
|
@ -79,9 +79,9 @@
|
|||
|
||||
{{ template "content" . }}
|
||||
|
||||
{{ if and .CurrentUser.Admin .Editable }}
|
||||
{{ if and .CurrentUser.Admin .Editable (ne .TemplatePath ".markdown") }}
|
||||
<p class="mt-4">
|
||||
<strong>Admin:</strong> [<a href="/admin/editor?file={{ or .Data.MarkdownFile .Path }}">edit this page</a>]
|
||||
<strong>Admin:</strong> [<a href="/admin/editor?file={{ .TemplatePath }}">edit this page</a>]
|
||||
</p>
|
||||
{{ end }}
|
||||
</div>
|
||||
|
|
|
@ -3,4 +3,10 @@
|
|||
|
||||
{{ .Data.HTML }}
|
||||
|
||||
{{ if and .CurrentUser.Admin .Editable }}
|
||||
<p class="mt-4">
|
||||
<strong>Admin:</strong> [<a href="/admin/editor?file={{ .Data.MarkdownPath }}">edit this page</a>]
|
||||
</p>
|
||||
{{ end }}
|
||||
|
||||
{{ end }}
|
||||
|
|
|
@ -7,6 +7,9 @@
|
|||
administrator.
|
||||
</p>
|
||||
|
||||
data={{ .Data }}
|
||||
|
||||
{{ $form := .Data.Form }}
|
||||
<form method="POST" action="/contact">
|
||||
<input type="hidden" name="_csrf" value="{{ .CSRF }}">
|
||||
<div class="form-group">
|
||||
|
@ -19,7 +22,7 @@
|
|||
class="form-control"
|
||||
id="name"
|
||||
placeholder="Anonymous"
|
||||
value="{{ .Form.Name }}">
|
||||
value="{{ $form.Name }}">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="email">Your email:</label>
|
||||
|
@ -28,7 +31,7 @@
|
|||
class="form-control"
|
||||
id="email"
|
||||
placeholder="(if you want a response)"
|
||||
value="{{ .Form.Email }}">
|
||||
value="{{ $form.Email }}">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="subject">
|
||||
|
@ -40,7 +43,7 @@
|
|||
class="form-control"
|
||||
id="subject"
|
||||
placeholder="No Subject"
|
||||
value="{{ .Form.Subject }}">
|
||||
value="{{ $form.Subject }}">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="message">Message:</label>
|
||||
|
@ -50,7 +53,7 @@
|
|||
name="message"
|
||||
id="message"
|
||||
placeholder="Message"
|
||||
required>{{ .Form.Message }}</textarea>
|
||||
required>{{ $form.Message }}</textarea>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn btn-primary">Send Message</button>
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
predictable for an attacker to guess.
|
||||
</p>
|
||||
|
||||
{{ $form := .Data.Form }}
|
||||
<form method="POST" action="/initial-setup">
|
||||
<input type="hidden" name="_csrf" value="{{ .CSRF }}">
|
||||
<div class="form-group">
|
||||
|
@ -22,7 +23,7 @@
|
|||
class="form-control"
|
||||
id="setup-admin-username"
|
||||
placeholder="Enter username"
|
||||
value="{{ .Form.Username }}">
|
||||
value="{{ $form.Username }}">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="setup-admin-password1">Passphrase:</label>
|
||||
|
|
Loading…
Reference in New Issue
Block a user