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