Compare commits
1 Commits
Author | SHA1 | Date | |
---|---|---|---|
6a36c0ed76 |
8
Makefile
8
Makefile
|
@ -18,6 +18,14 @@ build:
|
||||||
gofmt -w .
|
gofmt -w .
|
||||||
go build $(LDFLAGS) -i -o bin/blog cmd/blog/main.go
|
go build $(LDFLAGS) -i -o bin/blog cmd/blog/main.go
|
||||||
|
|
||||||
|
# `make bindata` to make the bindata module.
|
||||||
|
# `make bindata-dev` for debug mode module for editing files locally.
|
||||||
|
.PHONY: bindata bindata-dev
|
||||||
|
bindata:
|
||||||
|
go-bindata -pkg root -prefix root/ -o src/root/bundle.go root/...
|
||||||
|
bindata-dev:
|
||||||
|
go-bindata -debug -pkg root -prefix root/ -o src/root/bundle.go root/...
|
||||||
|
|
||||||
# `make run` to run it in debug mode.
|
# `make run` to run it in debug mode.
|
||||||
.PHONY: run
|
.PHONY: run
|
||||||
run:
|
run:
|
||||||
|
|
48
pages.go
48
pages.go
|
@ -3,7 +3,9 @@ package blog
|
||||||
import (
|
import (
|
||||||
"html/template"
|
"html/template"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
"mime"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/kirsle/blog/src/controllers/posts"
|
"github.com/kirsle/blog/src/controllers/posts"
|
||||||
|
@ -11,6 +13,7 @@ import (
|
||||||
"github.com/kirsle/blog/src/markdown"
|
"github.com/kirsle/blog/src/markdown"
|
||||||
"github.com/kirsle/blog/src/render"
|
"github.com/kirsle/blog/src/render"
|
||||||
"github.com/kirsle/blog/src/responses"
|
"github.com/kirsle/blog/src/responses"
|
||||||
|
"github.com/kirsle/blog/src/root"
|
||||||
)
|
)
|
||||||
|
|
||||||
// PageHandler is the catch-all route handler, for serving static web pages.
|
// PageHandler is the catch-all route handler, for serving static web pages.
|
||||||
|
@ -31,7 +34,7 @@ func (b *Blog) PageHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Search for a file that matches their URL.
|
// Search for a file that matches their URL.
|
||||||
filepath, err := render.ResolvePath(path)
|
fp, err := render.ResolvePath(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// See if it resolves as a blog entry.
|
// See if it resolves as a blog entry.
|
||||||
err = postctl.ViewPost(w, r, strings.TrimLeft(path, "/"))
|
err = postctl.ViewPost(w, r, strings.TrimLeft(path, "/"))
|
||||||
|
@ -43,17 +46,30 @@ 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(fp.URI, ".gohtml") {
|
||||||
render.Template(w, r, filepath.URI, nil)
|
render.Template(w, r, fp.URI, nil)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Is it a Markdown file?
|
// Is it a Markdown file?
|
||||||
if strings.HasSuffix(filepath.URI, ".md") || strings.HasSuffix(filepath.URI, ".markdown") {
|
if strings.HasSuffix(fp.URI, ".md") || strings.HasSuffix(fp.URI, ".markdown") {
|
||||||
source, err := ioutil.ReadFile(filepath.Absolute)
|
var source []byte
|
||||||
if err != nil {
|
if len(fp.BindataKey) > 0 {
|
||||||
responses.Error(w, r, "Couldn't read Markdown source!")
|
data, err := root.Asset(fp.BindataKey)
|
||||||
return
|
if err != nil {
|
||||||
|
responses.Error(w, r, "Couldn't read bindata key: "+fp.BindataKey)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
source = data
|
||||||
|
} else {
|
||||||
|
data, err := ioutil.ReadFile(fp.Absolute)
|
||||||
|
if err != nil {
|
||||||
|
responses.Error(w, r, "Couldn't read Markdown source!")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
source = data
|
||||||
}
|
}
|
||||||
|
|
||||||
// Render it to HTML and find out its title.
|
// Render it to HTML and find out its title.
|
||||||
|
@ -64,10 +80,22 @@ func (b *Blog) PageHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
render.Template(w, r, ".markdown", map[string]interface{}{
|
render.Template(w, r, ".markdown", map[string]interface{}{
|
||||||
"Title": title,
|
"Title": title,
|
||||||
"HTML": template.HTML(html),
|
"HTML": template.HTML(html),
|
||||||
"MarkdownPath": filepath.URI,
|
"MarkdownPath": fp.URI,
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
http.ServeFile(w, r, filepath.Absolute)
|
// It's a regular static file we can serve directly.
|
||||||
|
{
|
||||||
|
// Check if we have bindata for it.
|
||||||
|
if fp.BindataKey != "" {
|
||||||
|
data, _ := root.Asset(fp.BindataKey)
|
||||||
|
w.Header().Set("Content-Type", mime.TypeByExtension(filepath.Ext(fp.URI)))
|
||||||
|
w.Write(data)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try the filesystem.
|
||||||
|
http.ServeFile(w, r, fp.Absolute)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,11 +8,12 @@ import (
|
||||||
"net/url"
|
"net/url"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/kirsle/blog/models/comments"
|
||||||
|
"github.com/kirsle/blog/models/settings"
|
||||||
"github.com/kirsle/blog/src/log"
|
"github.com/kirsle/blog/src/log"
|
||||||
"github.com/kirsle/blog/src/markdown"
|
"github.com/kirsle/blog/src/markdown"
|
||||||
"github.com/kirsle/blog/src/render"
|
"github.com/kirsle/blog/src/render"
|
||||||
"github.com/kirsle/blog/models/comments"
|
"github.com/kirsle/blog/src/root"
|
||||||
"github.com/kirsle/blog/models/settings"
|
|
||||||
"github.com/microcosm-cc/bluemonday"
|
"github.com/microcosm-cc/bluemonday"
|
||||||
gomail "gopkg.in/gomail.v2"
|
gomail "gopkg.in/gomail.v2"
|
||||||
)
|
)
|
||||||
|
@ -51,10 +52,21 @@ func SendEmail(email Email) {
|
||||||
// Render the template to HTML.
|
// Render the template to HTML.
|
||||||
var html bytes.Buffer
|
var html bytes.Buffer
|
||||||
t := template.New(tmpl.Basename)
|
t := template.New(tmpl.Basename)
|
||||||
t, err = template.ParseFiles(tmpl.Absolute)
|
|
||||||
|
// Load it from bindata or filesystem.
|
||||||
|
if tmpl.BindataKey != "" {
|
||||||
|
log.Debug("Parse %s from bindata", tmpl.BindataKey)
|
||||||
|
asset, _ := root.Asset(tmpl.BindataKey)
|
||||||
|
t, err = t.Parse(string(asset))
|
||||||
|
} else {
|
||||||
|
log.Debug("Parse %s from file", tmpl.Absolute)
|
||||||
|
t, err = t.ParseFiles(tmpl.Absolute)
|
||||||
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("SendEmail: template parsing error: %s", err.Error())
|
log.Error("SendEmail: template parsing error: %s", err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
err = t.ExecuteTemplate(&html, tmpl.Basename, email)
|
err = t.ExecuteTemplate(&html, tmpl.Basename, email)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("SendEmail: template execution error: %s", err.Error())
|
log.Error("SendEmail: template execution error: %s", err.Error())
|
||||||
|
|
|
@ -8,6 +8,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/kirsle/blog/src/log"
|
"github.com/kirsle/blog/src/log"
|
||||||
|
"github.com/kirsle/blog/src/root"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Blog configuration bindings.
|
// Blog configuration bindings.
|
||||||
|
@ -37,6 +38,10 @@ type Filepath struct {
|
||||||
Basename string
|
Basename string
|
||||||
Relative string // Relative path including document root (i.e. "root/about.html")
|
Relative string // Relative path including document root (i.e. "root/about.html")
|
||||||
Absolute string // Absolute path on disk (i.e. "/opt/blog/root/about.html")
|
Absolute string // Absolute path on disk (i.e. "/opt/blog/root/about.html")
|
||||||
|
|
||||||
|
// If file was resolved to embedded bindata, this is the bindata key name.
|
||||||
|
// Zero value means it resolved to a file on filesystem.
|
||||||
|
BindataKey string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f Filepath) String() string {
|
func (f Filepath) String() string {
|
||||||
|
@ -55,39 +60,78 @@ func ResolvePath(path string) (Filepath, error) {
|
||||||
|
|
||||||
// If you need to debug this function, edit this block.
|
// If you need to debug this function, edit this block.
|
||||||
debug := func(tmpl string, args ...interface{}) {
|
debug := func(tmpl string, args ...interface{}) {
|
||||||
if false {
|
if true { // edit this to enable
|
||||||
log.Debug(tmpl, args...)
|
log.Debug(tmpl, args...)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
debug("Resolving filepath for URI: %s", path)
|
debug("ResolvePath(%s) called", path)
|
||||||
for _, root := range []string{*UserRoot, *DocumentRoot} {
|
|
||||||
if len(root) == 0 {
|
if len(*UserRoot) > 0 {
|
||||||
continue
|
debug("1. Resolving filepath for URI in user root: %s", path)
|
||||||
}
|
|
||||||
|
|
||||||
// Resolve the file path.
|
// Resolve the file path.
|
||||||
relPath := filepath.Join(root, path)
|
relPath := filepath.Join(*UserRoot, path)
|
||||||
absPath, err := filepath.Abs(relPath)
|
absPath, err := filepath.Abs(relPath)
|
||||||
basename := filepath.Base(relPath)
|
basename := filepath.Base(relPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("%v", err)
|
log.Error("%v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
debug("Expected filepath: %s", absPath)
|
debug(" Expected filepath: %s", absPath)
|
||||||
|
|
||||||
// Found an exact hit?
|
// Found an exact hit?
|
||||||
if stat, err := os.Stat(absPath); !os.IsNotExist(err) && !stat.IsDir() {
|
if stat, err := os.Stat(absPath); !os.IsNotExist(err) && !stat.IsDir() {
|
||||||
debug("Exact filepath found: %s", absPath)
|
debug(" + Exact filepath found: %s", absPath)
|
||||||
return Filepath{path, basename, relPath, absPath}, nil
|
return Filepath{
|
||||||
|
URI: path,
|
||||||
|
Basename: basename,
|
||||||
|
Relative: relPath,
|
||||||
|
Absolute: absPath,
|
||||||
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try some supported suffixes.
|
// Try some supported suffixes.
|
||||||
for _, suffix := range hiddenSuffixes {
|
for _, suffix := range hiddenSuffixes {
|
||||||
test := absPath + suffix
|
test := absPath + suffix
|
||||||
if stat, err := os.Stat(test); !os.IsNotExist(err) && !stat.IsDir() {
|
if stat, err := os.Stat(test); !os.IsNotExist(err) && !stat.IsDir() {
|
||||||
debug("Filepath found via suffix %s: %s", suffix, test)
|
debug(" + Filepath found via suffix %s: %s", suffix, test)
|
||||||
return Filepath{path + suffix, basename + suffix, relPath + suffix, test}, nil
|
return Filepath{
|
||||||
|
URI: path + suffix,
|
||||||
|
Basename: basename + suffix,
|
||||||
|
Relative: relPath + suffix,
|
||||||
|
Absolute: test,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
debug("2. Not found in filesystem, checking bindata for: %s", path)
|
||||||
|
{
|
||||||
|
// Exact hit?
|
||||||
|
if _, err := root.Asset(path); err == nil {
|
||||||
|
debug(" Found in bindata as: %s", path)
|
||||||
|
return Filepath{
|
||||||
|
URI: path,
|
||||||
|
Basename: filepath.Base(path),
|
||||||
|
Relative: path,
|
||||||
|
Absolute: path,
|
||||||
|
BindataKey: path,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try some supported suffixes.
|
||||||
|
for _, suffix := range hiddenSuffixes {
|
||||||
|
test := path + suffix
|
||||||
|
if _, err := root.Asset(test); err == nil {
|
||||||
|
debug(" Filepath found via suffix %s: %s", suffix, test)
|
||||||
|
return Filepath{
|
||||||
|
URI: test,
|
||||||
|
Basename: filepath.Base(test),
|
||||||
|
Relative: test,
|
||||||
|
Absolute: test,
|
||||||
|
BindataKey: test,
|
||||||
|
}, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,13 +8,14 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
gorilla "github.com/gorilla/sessions"
|
gorilla "github.com/gorilla/sessions"
|
||||||
|
"github.com/kirsle/blog/models/settings"
|
||||||
|
"github.com/kirsle/blog/models/users"
|
||||||
"github.com/kirsle/blog/src/log"
|
"github.com/kirsle/blog/src/log"
|
||||||
"github.com/kirsle/blog/src/middleware"
|
"github.com/kirsle/blog/src/middleware"
|
||||||
"github.com/kirsle/blog/src/middleware/auth"
|
"github.com/kirsle/blog/src/middleware/auth"
|
||||||
|
"github.com/kirsle/blog/src/root"
|
||||||
"github.com/kirsle/blog/src/sessions"
|
"github.com/kirsle/blog/src/sessions"
|
||||||
"github.com/kirsle/blog/src/types"
|
"github.com/kirsle/blog/src/types"
|
||||||
"github.com/kirsle/blog/models/settings"
|
|
||||||
"github.com/kirsle/blog/models/users"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// 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
|
||||||
|
@ -113,19 +114,19 @@ func Template(w io.Writer, r *http.Request, path string, data interface{}) error
|
||||||
// Get the layout template.
|
// Get the layout template.
|
||||||
if !isPartial {
|
if !isPartial {
|
||||||
templateName = "layout"
|
templateName = "layout"
|
||||||
layout, err = ResolvePath(".layout")
|
layout, err = ResolvePath(".layout.gohtml")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("RenderTemplate(%s): layout template not found", path)
|
log.Error("RenderTemplate(%s): layout template not found", path)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
templateName = filepath.Basename
|
templateName = filepath.Absolute
|
||||||
}
|
}
|
||||||
|
|
||||||
// The comment entry partial.
|
// The comment entry partial.
|
||||||
commentEntry, err := ResolvePath("comments/entry.partial")
|
commentEntry, err := ResolvePath("comments/entry.partial.gohtml")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("RenderTemplate(%s): comments/entry.partial not found")
|
log.Error("RenderTemplate: comments/entry.partial not found")
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -135,17 +136,28 @@ func Template(w io.Writer, r *http.Request, path string, data interface{}) error
|
||||||
// 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 !isPartial {
|
if !isPartial {
|
||||||
templates = append(templates, layout.Absolute)
|
templates = append(templates, layout.Absolute, commentEntry.Absolute, filepath.Absolute)
|
||||||
}
|
}
|
||||||
t, err = t.ParseFiles(append(templates, commentEntry.Absolute, filepath.Absolute)...)
|
|
||||||
if err != nil {
|
for _, filename := range templates {
|
||||||
log.Error(err.Error())
|
|
||||||
return err
|
if asset, err2 := root.Asset(filename); err2 == nil {
|
||||||
|
log.Debug("Parse %s from bindata", filename)
|
||||||
|
t, err = t.Parse(string(asset))
|
||||||
|
} else {
|
||||||
|
log.Debug("Parse %s from file", filename)
|
||||||
|
t, err = t.ParseFiles(filename)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Error(err.Error())
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
err = t.ExecuteTemplate(w, templateName, v)
|
err = t.ExecuteTemplate(w, templateName, v)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("Template parsing error: %s", err)
|
log.Error("Template parsing error(tmpl name: %s; ): %s", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user