Various small nice fixes
This commit is contained in:
parent
7fa422e42f
commit
3b4bd4e978
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -1,3 +1,4 @@
|
|||
bin/
|
||||
dist/
|
||||
root/.private
|
||||
user-root/
|
||||
user-root/.private
|
||||
|
|
2
Makefile
2
Makefile
|
@ -21,7 +21,7 @@ build:
|
|||
# `make run` to run it in debug mode.
|
||||
.PHONY: run
|
||||
run:
|
||||
./go-reload cmd/blog/main.go -debug root
|
||||
./go-reload cmd/blog/main.go -debug user-root
|
||||
|
||||
# `make test` to run unit tests.
|
||||
.PHONY: test
|
||||
|
|
|
@ -33,12 +33,25 @@ func editorHandler(w http.ResponseWriter, r *http.Request) {
|
|||
// Are they saving?
|
||||
if saving {
|
||||
fp = filepath.Join(*render.UserRoot, file)
|
||||
body = []byte(r.FormValue("body"))
|
||||
err := ioutil.WriteFile(fp, body, 0644)
|
||||
body = []byte(strings.Replace(r.FormValue("body"), "\r\n", "\n", -1))
|
||||
|
||||
// Ensure the folders exist.
|
||||
dir, _ := filepath.Split(fp)
|
||||
err := os.MkdirAll(dir, 0755)
|
||||
if err != nil {
|
||||
responses.Flash(w, r, "Error saving: can't create folder %s: %s", dir, err)
|
||||
}
|
||||
|
||||
// Write the file.
|
||||
err = ioutil.WriteFile(fp, body, 0644)
|
||||
if err != nil {
|
||||
responses.Flash(w, r, "Error saving: %s", err)
|
||||
} else {
|
||||
if render.HasHTMLSuffix(file) {
|
||||
responses.FlashAndRedirect(w, r, render.URLFromPath(file), "Page saved successfully!")
|
||||
} else {
|
||||
responses.FlashAndRedirect(w, r, "/admin/editor?file="+url.QueryEscape(file), "Page saved successfully!")
|
||||
}
|
||||
return
|
||||
}
|
||||
} else if deleting {
|
||||
|
@ -62,8 +75,11 @@ func editorHandler(w http.ResponseWriter, r *http.Request) {
|
|||
f, err := os.Stat(fp)
|
||||
if os.IsNotExist(err) {
|
||||
fp = filepath.Join(*render.DocumentRoot, file)
|
||||
fromCore = true
|
||||
f, err = os.Stat(fp)
|
||||
if !os.IsNotExist(err) {
|
||||
// The file was found in the core.
|
||||
fromCore = true
|
||||
}
|
||||
}
|
||||
|
||||
// If it exists, load it.
|
||||
|
@ -114,6 +130,11 @@ func editorFileList(w http.ResponseWriter, r *http.Request) {
|
|||
return nil
|
||||
}
|
||||
|
||||
// Hide vendored files.
|
||||
if i == 1 && strings.HasPrefix(rel, "js/ace-editor") {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Only text files.
|
||||
ext := strings.ToLower(filepath.Ext(path))
|
||||
okTypes := []string{
|
||||
|
|
|
@ -9,10 +9,10 @@ import (
|
|||
|
||||
"github.com/kirsle/blog/internal/markdown"
|
||||
"github.com/kirsle/blog/internal/middleware/auth"
|
||||
"github.com/kirsle/blog/models/posts"
|
||||
"github.com/kirsle/blog/internal/render"
|
||||
"github.com/kirsle/blog/internal/responses"
|
||||
"github.com/kirsle/blog/internal/types"
|
||||
"github.com/kirsle/blog/models/posts"
|
||||
)
|
||||
|
||||
// editHandler is the blog writing and editing page.
|
||||
|
@ -21,6 +21,7 @@ func editHandler(w http.ResponseWriter, r *http.Request) {
|
|||
"preview": "",
|
||||
}
|
||||
var post *posts.Post
|
||||
var isNew bool
|
||||
|
||||
// Are we editing an existing post?
|
||||
if idStr := r.FormValue("id"); idStr != "" {
|
||||
|
@ -30,10 +31,12 @@ func editHandler(w http.ResponseWriter, r *http.Request) {
|
|||
if err != nil {
|
||||
v["Error"] = errors.New("that post ID was not found")
|
||||
post = posts.New()
|
||||
isNew = true
|
||||
}
|
||||
}
|
||||
} else {
|
||||
post = posts.New()
|
||||
isNew = true
|
||||
}
|
||||
|
||||
if r.Method == http.MethodPost {
|
||||
|
@ -55,8 +58,14 @@ func editHandler(w http.ResponseWriter, r *http.Request) {
|
|||
author, _ := auth.CurrentUser(r)
|
||||
post.AuthorID = author.ID
|
||||
|
||||
// When editing, allow to not touch the last updated time.
|
||||
if !isNew && r.FormValue("no-update") == "true" {
|
||||
post.Updated = post.Created
|
||||
} else {
|
||||
post.Updated = time.Now().UTC()
|
||||
}
|
||||
err = post.Save()
|
||||
|
||||
if err != nil {
|
||||
v["Error"] = err
|
||||
} else {
|
||||
|
|
|
@ -2,6 +2,7 @@ package render
|
|||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
@ -15,6 +16,17 @@ var (
|
|||
DocumentRoot *string
|
||||
)
|
||||
|
||||
// File extensions and URL suffixes that map to real files on disk, but which
|
||||
// have suffixes hidden from the URL.
|
||||
var hiddenSuffixes = []string{
|
||||
".gohtml",
|
||||
".html",
|
||||
"/index.gohtml",
|
||||
"/index.html",
|
||||
".md",
|
||||
"/index.md",
|
||||
}
|
||||
|
||||
// Filepath represents a file discovered in the document roots, and maintains
|
||||
// both its relative and absolute components.
|
||||
type Filepath struct {
|
||||
|
@ -71,15 +83,7 @@ func ResolvePath(path string) (Filepath, error) {
|
|||
}
|
||||
|
||||
// Try some supported suffixes.
|
||||
suffixes := []string{
|
||||
".gohtml",
|
||||
".html",
|
||||
"/index.gohtml",
|
||||
"/index.html",
|
||||
".md",
|
||||
"/index.md",
|
||||
}
|
||||
for _, suffix := range suffixes {
|
||||
for _, suffix := range hiddenSuffixes {
|
||||
test := absPath + suffix
|
||||
if stat, err := os.Stat(test); !os.IsNotExist(err) && !stat.IsDir() {
|
||||
debug("Filepath found via suffix %s: %s", suffix, test)
|
||||
|
@ -90,3 +94,36 @@ func ResolvePath(path string) (Filepath, error) {
|
|||
|
||||
return Filepath{}, errors.New("not found")
|
||||
}
|
||||
|
||||
// HasHTMLSuffix returns whether the file path will be renderable as HTML
|
||||
// for the front-end. Basically, whether it ends with a .gohtml, .html or .md
|
||||
// suffix and/or is an index page.
|
||||
func HasHTMLSuffix(path string) bool {
|
||||
for _, suffix := range hiddenSuffixes {
|
||||
if strings.HasSuffix(path, suffix) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// URLFromPath returns an HTTP path that matches the file path on disk.
|
||||
//
|
||||
// For example, given the file path "folder/page.md" it would return the string
|
||||
// "/folder/page"
|
||||
func URLFromPath(path string) string {
|
||||
// Strip leading slashes.
|
||||
if path[0] == '/' {
|
||||
path = strings.TrimPrefix(path, "/")
|
||||
}
|
||||
|
||||
// Hide-able suffixes.
|
||||
for _, suffix := range hiddenSuffixes {
|
||||
if strings.HasSuffix(path, suffix) {
|
||||
path = strings.TrimSuffix(path, suffix)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return fmt.Sprintf("/%s", path)
|
||||
}
|
||||
|
|
|
@ -79,7 +79,7 @@
|
|||
|
||||
{{ template "content" . }}
|
||||
|
||||
{{ if and .CurrentUser.Admin .Editable (ne .TemplatePath ".markdown") }}
|
||||
{{ if and .CurrentUser.Admin .Editable (ne .TemplatePath ".markdown.gohtml") }}
|
||||
<p class="mt-4">
|
||||
<strong>Admin:</strong> [<a href="/admin/editor?file={{ .TemplatePath }}">edit this page</a>]
|
||||
</p>
|
||||
|
|
|
@ -2,9 +2,23 @@
|
|||
{{ define "content" }}
|
||||
<h1>Page Editor</h1>
|
||||
|
||||
<form class="form-inline" method="GET" action="/admin/editor">
|
||||
Create a new page:
|
||||
<div class="input-group">
|
||||
<input type="text"
|
||||
name="file"
|
||||
class="form-control ml-2"
|
||||
placeholder="about.md">
|
||||
<div class="input-group-append">
|
||||
<button type="submit" class="btn btn-primary">Go</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
{{ range .Data.FileTrees }}
|
||||
{{ if .UserRoot }}
|
||||
<h2>User Root</h2>
|
||||
|
||||
<p>
|
||||
These are your custom web files that override those in the CoreRoot.
|
||||
</p>
|
||||
|
|
|
@ -8,6 +8,8 @@
|
|||
Preview
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<h1 class="blog-title">{{ .Data.post.Title }}</h1>
|
||||
|
||||
{{ .Data.preview }}
|
||||
</div>
|
||||
</div>
|
||||
|
@ -139,6 +141,17 @@
|
|||
> Enable comments on this post
|
||||
</label>
|
||||
</div>
|
||||
{{ if .ID }}
|
||||
<div class="form-check">
|
||||
<label class="form-check-label">
|
||||
<input type="checkbox"
|
||||
class="form-check-label"
|
||||
name="no-update"
|
||||
value="true"
|
||||
> <strong>Editing:</strong> do not show a "last updated" label.
|
||||
</label>
|
||||
</div>
|
||||
{{ end }}
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
|
|
Loading…
Reference in New Issue
Block a user