mirror of
https://github.com/dnote/dnote
synced 2026-03-16 15:35:52 +01:00
155 lines
3.3 KiB
Go
155 lines
3.3 KiB
Go
package views
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"html/template"
|
|
"io"
|
|
"net/http"
|
|
"path/filepath"
|
|
"strings"
|
|
|
|
"github.com/dnote/dnote/pkg/server/context"
|
|
"github.com/dnote/dnote/pkg/server/log"
|
|
"github.com/gorilla/csrf"
|
|
"github.com/pkg/errors"
|
|
)
|
|
|
|
const (
|
|
// templateExt is the template extension
|
|
templateExt string = ".gohtml"
|
|
)
|
|
|
|
const (
|
|
siteTitle = "Dnote"
|
|
)
|
|
|
|
// Config is a view config
|
|
type Config struct {
|
|
Title string
|
|
Layout string
|
|
HeaderTemplate string
|
|
}
|
|
|
|
func (c Config) getLayout() string {
|
|
if c.Layout == "" {
|
|
return "base"
|
|
}
|
|
|
|
return c.Layout
|
|
}
|
|
|
|
// NewView returns a new view by parsing the given layout and files
|
|
func NewView(baseDir string, c Config, files ...string) *View {
|
|
addTemplatePath(baseDir, files)
|
|
addTemplateExt(files)
|
|
files = append(files, layoutFiles(baseDir)...)
|
|
|
|
t, err := template.New(c.Title).Funcs(template.FuncMap{
|
|
"csrfField": func() (template.HTML, error) {
|
|
return "", errors.New("csrfField is not implemented")
|
|
},
|
|
"css": func() []string {
|
|
return strings.Split("", ",")
|
|
},
|
|
"title": func() string {
|
|
if c.Title != "" {
|
|
return fmt.Sprintf("%s | %s", c.Title, siteTitle)
|
|
}
|
|
|
|
return siteTitle
|
|
},
|
|
"headerTemplate": func() string {
|
|
return c.HeaderTemplate
|
|
},
|
|
}).ParseFiles(files...)
|
|
if err != nil {
|
|
panic(errors.Wrap(err, "instantiating view."))
|
|
}
|
|
|
|
return &View{
|
|
Template: t,
|
|
Layout: c.getLayout(),
|
|
}
|
|
}
|
|
|
|
// View holds the information about a view
|
|
type View struct {
|
|
Template *template.Template
|
|
Layout string
|
|
}
|
|
|
|
func (v *View) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|
v.Render(w, r, nil)
|
|
}
|
|
|
|
// Render is used to render the view with the predefined layout.
|
|
func (v *View) Render(w http.ResponseWriter, r *http.Request, data interface{}) {
|
|
w.Header().Set("Content-Type", "text/html")
|
|
|
|
var vd Data
|
|
switch d := data.(type) {
|
|
case Data:
|
|
vd = d
|
|
// do nothing
|
|
default:
|
|
vd = Data{
|
|
Yield: data,
|
|
}
|
|
}
|
|
|
|
if alert := getAlert(r); alert != nil {
|
|
vd.Alert = alert
|
|
clearAlert(w)
|
|
}
|
|
|
|
vd.User = context.User(r.Context())
|
|
|
|
var buf bytes.Buffer
|
|
csrfField := csrf.TemplateField(r)
|
|
tpl := v.Template.Funcs(template.FuncMap{
|
|
"csrfField": func() template.HTML {
|
|
return csrfField
|
|
},
|
|
})
|
|
|
|
if err := tpl.ExecuteTemplate(&buf, v.Layout, vd); err != nil {
|
|
log.ErrorWrap(err, fmt.Sprintf("executing a template '%s'", v.Template.Name()))
|
|
http.Error(w, AlertMsgGeneric, http.StatusInternalServerError)
|
|
return
|
|
}
|
|
io.Copy(w, &buf)
|
|
}
|
|
|
|
// layoutFiles returns a slice of strings representing
|
|
// the layout files used in our application.
|
|
func layoutFiles(baseDir string) []string {
|
|
pattern := fmt.Sprintf("%s/layouts/*%s", baseDir, templateExt)
|
|
|
|
files, err := filepath.Glob(pattern)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
return files
|
|
}
|
|
|
|
// addTemplatePath takes in a slice of strings
|
|
// representing file paths for templates.
|
|
func addTemplatePath(baseDir string, files []string) {
|
|
for i, f := range files {
|
|
files[i] = fmt.Sprintf("%s/%s", baseDir, f)
|
|
}
|
|
}
|
|
|
|
// addTemplateExt takes in a slice of strings
|
|
// representing file paths for templates and it appends
|
|
// the templateExt extension to each string in the slice
|
|
//
|
|
// Eg the input {"home"} would result in the output
|
|
// {"home.gohtml"} if templateExt == ".gohtml"
|
|
func addTemplateExt(files []string) {
|
|
for i, f := range files {
|
|
files[i] = f + templateExt
|
|
}
|
|
}
|