fix #2
This commit is contained in:
parent
3c0383d500
commit
0ac1f84f60
10
README.md
10
README.md
|
@ -2,6 +2,16 @@
|
||||||
|
|
||||||
This is an add-on for Caddy which wants to deliver a good UI to edit the content of the website.
|
This is an add-on for Caddy which wants to deliver a good UI to edit the content of the website.
|
||||||
|
|
||||||
|
## Add-on configuration
|
||||||
|
|
||||||
|
You can define, or not, the admin UI styles. It will **not** replace the default ones, it will be included after it. The path must be relative to ```public``` folder.
|
||||||
|
|
||||||
|
```
|
||||||
|
hugo {
|
||||||
|
styles [file]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
## Try it
|
## Try it
|
||||||
|
|
||||||
### Prepare your machine
|
### Prepare your machine
|
||||||
|
|
|
@ -6,27 +6,25 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"text/template"
|
"text/template"
|
||||||
|
|
||||||
"github.com/hacdias/caddy-hugo/editor"
|
"github.com/hacdias/caddy-hugo/config"
|
||||||
"github.com/hacdias/caddy-hugo/utils"
|
"github.com/hacdias/caddy-hugo/utils"
|
||||||
"github.com/mholt/caddy/middleware"
|
"github.com/mholt/caddy/middleware"
|
||||||
"github.com/mholt/caddy/middleware/browse"
|
"github.com/mholt/caddy/middleware/browse"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ServeHTTP is...
|
// ServeHTTP is used to serve the content of Browse page
|
||||||
func ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) {
|
// using Browse middleware from Caddy
|
||||||
if r.URL.Path[len(r.URL.Path)-1] != '/' {
|
func ServeHTTP(w http.ResponseWriter, r *http.Request, c *config.Config) (int, error) {
|
||||||
http.Redirect(w, r, r.URL.Path+"/", http.StatusTemporaryRedirect)
|
// Removes the page main path from the URL
|
||||||
return 0, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
r.URL.Path = strings.Replace(r.URL.Path, "/admin/browse", "", 1)
|
r.URL.Path = strings.Replace(r.URL.Path, "/admin/browse", "", 1)
|
||||||
|
|
||||||
|
// If the URL is blank now, replace it with a trailing slash
|
||||||
if r.URL.Path == "" {
|
if r.URL.Path == "" {
|
||||||
r.URL.Path = "/"
|
r.URL.Path = "/"
|
||||||
}
|
}
|
||||||
|
|
||||||
functions := template.FuncMap{
|
functions := template.FuncMap{
|
||||||
"canBeEdited": editor.CanBeEdited,
|
"CanBeEdited": utils.CanBeEdited,
|
||||||
"Defined": utils.Defined,
|
"Defined": utils.Defined,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -45,6 +43,7 @@ func ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) {
|
||||||
Configs: []browse.Config{
|
Configs: []browse.Config{
|
||||||
browse.Config{
|
browse.Config{
|
||||||
PathScope: "/",
|
PathScope: "/",
|
||||||
|
Variables: c,
|
||||||
Template: tpl,
|
Template: tpl,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
@ -0,0 +1,35 @@
|
||||||
|
package config
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/mholt/caddy/config/setup"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Config is the add-on configuration set on Caddyfile
|
||||||
|
type Config struct {
|
||||||
|
Styles string
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseHugo parses the configuration file
|
||||||
|
func ParseHugo(c *setup.Controller) (*Config, error) {
|
||||||
|
conf := &Config{}
|
||||||
|
|
||||||
|
for c.Next() {
|
||||||
|
for c.NextBlock() {
|
||||||
|
switch c.Val() {
|
||||||
|
case "styles":
|
||||||
|
if !c.NextArg() {
|
||||||
|
return nil, c.ArgErr()
|
||||||
|
}
|
||||||
|
conf.Styles = c.Val()
|
||||||
|
// Remove the beginning slash if it exists or not
|
||||||
|
conf.Styles = strings.TrimPrefix(conf.Styles, "/")
|
||||||
|
// Add a beginning slash to make a
|
||||||
|
conf.Styles = "/" + conf.Styles
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return conf, nil
|
||||||
|
}
|
|
@ -12,6 +12,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"text/template"
|
"text/template"
|
||||||
|
|
||||||
|
"github.com/hacdias/caddy-hugo/config"
|
||||||
"github.com/hacdias/caddy-hugo/frontmatter"
|
"github.com/hacdias/caddy-hugo/frontmatter"
|
||||||
"github.com/hacdias/caddy-hugo/utils"
|
"github.com/hacdias/caddy-hugo/utils"
|
||||||
"github.com/spf13/hugo/parser"
|
"github.com/spf13/hugo/parser"
|
||||||
|
@ -23,38 +24,21 @@ type editor struct {
|
||||||
Mode string
|
Mode string
|
||||||
Content string
|
Content string
|
||||||
FrontMatter interface{}
|
FrontMatter interface{}
|
||||||
|
Config *config.Config
|
||||||
}
|
}
|
||||||
|
|
||||||
// ServeHTTP is...
|
// ServeHTTP serves the editor page
|
||||||
func ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) {
|
func ServeHTTP(w http.ResponseWriter, r *http.Request, c *config.Config) (int, error) {
|
||||||
filename := strings.Replace(r.URL.Path, "/admin/edit/", "", 1)
|
filename := strings.Replace(r.URL.Path, "/admin/edit/", "", 1)
|
||||||
|
|
||||||
if r.Method == "POST" {
|
if r.Method == "POST" {
|
||||||
return post(w, r, filename)
|
return servePost(w, r, filename)
|
||||||
}
|
}
|
||||||
|
|
||||||
return get(w, r, filename)
|
return serveGet(w, r, c, filename)
|
||||||
}
|
}
|
||||||
|
|
||||||
// CanBeEdited checks if a file has a supported extension
|
func servePost(w http.ResponseWriter, r *http.Request, filename string) (int, error) {
|
||||||
func CanBeEdited(filename string) bool {
|
|
||||||
extensions := [...]string{".markdown", ".md",
|
|
||||||
".json", ".toml", ".yaml",
|
|
||||||
".css", ".sass", ".scss",
|
|
||||||
".js",
|
|
||||||
".html",
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, extension := range extensions {
|
|
||||||
if strings.HasSuffix(filename, extension) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func post(w http.ResponseWriter, r *http.Request, filename string) (int, error) {
|
|
||||||
// Get the JSON information sent using a buffer
|
// Get the JSON information sent using a buffer
|
||||||
rawBuffer := new(bytes.Buffer)
|
rawBuffer := new(bytes.Buffer)
|
||||||
rawBuffer.ReadFrom(r.Body)
|
rawBuffer.ReadFrom(r.Body)
|
||||||
|
@ -152,10 +136,10 @@ func post(w http.ResponseWriter, r *http.Request, filename string) (int, error)
|
||||||
return 200, nil
|
return 200, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func get(w http.ResponseWriter, r *http.Request, filename string) (int, error) {
|
func serveGet(w http.ResponseWriter, r *http.Request, c *config.Config, filename string) (int, error) {
|
||||||
// Check if the file format is supported. If not, send a "Not Acceptable"
|
// Check if the file format is supported. If not, send a "Not Acceptable"
|
||||||
// header and an error
|
// header and an error
|
||||||
if !CanBeEdited(filename) {
|
if !utils.CanBeEdited(filename) {
|
||||||
return 406, errors.New("File format not supported.")
|
return 406, errors.New("File format not supported.")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -176,6 +160,7 @@ func get(w http.ResponseWriter, r *http.Request, filename string) (int, error) {
|
||||||
page := new(editor)
|
page := new(editor)
|
||||||
page.Mode = strings.TrimPrefix(filepath.Ext(filename), ".")
|
page.Mode = strings.TrimPrefix(filepath.Ext(filename), ".")
|
||||||
page.Name = filename
|
page.Name = filename
|
||||||
|
page.Config = c
|
||||||
|
|
||||||
// Sanitize the extension
|
// Sanitize the extension
|
||||||
page.Mode = sanitizeMode(page.Mode)
|
page.Mode = sanitizeMode(page.Mode)
|
||||||
|
@ -227,7 +212,7 @@ func get(w http.ResponseWriter, r *http.Request, filename string) (int, error) {
|
||||||
// Create the functions map, then the template, check for erros and
|
// Create the functions map, then the template, check for erros and
|
||||||
// execute the template if there aren't errors
|
// execute the template if there aren't errors
|
||||||
functions := template.FuncMap{
|
functions := template.FuncMap{
|
||||||
"splitCapitalize": utils.SplitCapitalize,
|
"SplitCapitalize": utils.SplitCapitalize,
|
||||||
"Defined": utils.Defined,
|
"Defined": utils.Defined,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
16
hugo.go
16
hugo.go
|
@ -11,6 +11,7 @@ import (
|
||||||
|
|
||||||
"github.com/hacdias/caddy-hugo/assets"
|
"github.com/hacdias/caddy-hugo/assets"
|
||||||
"github.com/hacdias/caddy-hugo/browse"
|
"github.com/hacdias/caddy-hugo/browse"
|
||||||
|
"github.com/hacdias/caddy-hugo/config"
|
||||||
"github.com/hacdias/caddy-hugo/editor"
|
"github.com/hacdias/caddy-hugo/editor"
|
||||||
"github.com/hacdias/caddy-hugo/utils"
|
"github.com/hacdias/caddy-hugo/utils"
|
||||||
"github.com/mholt/caddy/config/setup"
|
"github.com/mholt/caddy/config/setup"
|
||||||
|
@ -20,16 +21,21 @@ import (
|
||||||
|
|
||||||
// Setup configures the middleware
|
// Setup configures the middleware
|
||||||
func Setup(c *setup.Controller) (middleware.Middleware, error) {
|
func Setup(c *setup.Controller) (middleware.Middleware, error) {
|
||||||
|
config, _ := config.ParseHugo(c)
|
||||||
commands.Execute()
|
commands.Execute()
|
||||||
|
|
||||||
return func(next middleware.Handler) middleware.Handler {
|
return func(next middleware.Handler) middleware.Handler {
|
||||||
return &handler{Next: next}
|
return &CaddyHugo{Next: next, Config: config}
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type handler struct{ Next middleware.Handler }
|
// CaddyHugo main type
|
||||||
|
type CaddyHugo struct {
|
||||||
|
Next middleware.Handler
|
||||||
|
Config *config.Config
|
||||||
|
}
|
||||||
|
|
||||||
func (h handler) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) {
|
func (h CaddyHugo) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) {
|
||||||
// Only handle /admin path
|
// Only handle /admin path
|
||||||
if middleware.Path(r.URL.Path).Matches("/admin") {
|
if middleware.Path(r.URL.Path).Matches("/admin") {
|
||||||
var err error
|
var err error
|
||||||
|
@ -103,12 +109,12 @@ func (h handler) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error)
|
||||||
|
|
||||||
// Browse page
|
// Browse page
|
||||||
if page == "browse" {
|
if page == "browse" {
|
||||||
code, err = browse.ServeHTTP(w, r)
|
code, err = browse.ServeHTTP(w, r, h.Config)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Edit page
|
// Edit page
|
||||||
if page == "edit" {
|
if page == "edit" {
|
||||||
code, err = editor.ServeHTTP(w, r)
|
code, err = editor.ServeHTTP(w, r, h.Config)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Whenever the header "X-Refenerate" is true, the website should be
|
// Whenever the header "X-Refenerate" is true, the website should be
|
||||||
|
|
|
@ -10,6 +10,8 @@
|
||||||
|
|
||||||
<link href='https://fonts.googleapis.com/css?family=Roboto:400,700,400italic,700italic' rel='stylesheet' type='text/css'>
|
<link href='https://fonts.googleapis.com/css?family=Roboto:400,700,400italic,700italic' rel='stylesheet' type='text/css'>
|
||||||
<link rel="stylesheet" href="/admin/assets/css/main.min.css">
|
<link rel="stylesheet" href="/admin/assets/css/main.min.css">
|
||||||
|
{{ if and (Defined . "Config")}}{{ if not (eq .Config.Styles "") }}<link rel="stylesheet" href="{{ .Config.Styles }}">{{ end }}{{ end }}
|
||||||
|
{{ if and (Defined . "User") }}{{ if not (eq .User.Styles "") }}<link rel="stylesheet" href="{{ .User.Styles }}">{{ end }}{{ end }}
|
||||||
<script src="/admin/assets/js/plugins.min.js"></script>
|
<script src="/admin/assets/js/plugins.min.js"></script>
|
||||||
<script src="/admin/assets/js/app.min.js"></script>
|
<script src="/admin/assets/js/app.min.js"></script>
|
||||||
</head>
|
</head>
|
||||||
|
|
|
@ -46,7 +46,7 @@
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
{{if .IsDir}}
|
{{if .IsDir}}
|
||||||
<i class="fa fa-folder"></i> <a href="{{.URL}}">{{.Name}}</a> {{else}} {{ if canBeEdited .URL }}
|
<i class="fa fa-folder"></i> <a href="{{.URL}}">{{.Name}}</a> {{else}} {{ if CanBeEdited .URL }}
|
||||||
<i class="fa fa-file"></i> <a class="file" href="/admin/edit{{ $path }}{{.URL}}">{{.Name}}</a> {{ else }}
|
<i class="fa fa-file"></i> <a class="file" href="/admin/edit{{ $path }}{{.URL}}">{{.Name}}</a> {{ else }}
|
||||||
<i class="fa fa-file"></i> {{.Name}} {{ end }} {{ end }}
|
<i class="fa fa-file"></i> {{.Name}} {{ end }} {{ end }}
|
||||||
</td>
|
</td>
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{{ define "frontmatter" }} {{ range $key, $value := . }} {{ if or (eq $value.Type "object") (eq $value.Type "array") }}
|
{{ define "frontmatter" }} {{ range $key, $value := . }} {{ if or (eq $value.Type "object") (eq $value.Type "array") }}
|
||||||
<fieldset id="{{ $value.Name }}" data-name="{{ $value.Name }}" data-type="{{ $value.Type }}">
|
<fieldset id="{{ $value.Name }}" data-name="{{ $value.Name }}" data-type="{{ $value.Type }}">
|
||||||
<h3>{{ splitCapitalize $value.Title }}
|
<h3>{{ SplitCapitalize $value.Title }}
|
||||||
<span class="actions">
|
<span class="actions">
|
||||||
<button class="delete"><i class="fa fa-minus"></i></button>
|
<button class="delete"><i class="fa fa-minus"></i></button>
|
||||||
<button class="add"><i class="fa fa-plus"></i></button>
|
<button class="add"><i class="fa fa-plus"></i></button>
|
||||||
|
@ -9,7 +9,7 @@
|
||||||
{{ template "frontmatter" $value.Content }}
|
{{ template "frontmatter" $value.Content }}
|
||||||
</fieldset>
|
</fieldset>
|
||||||
{{ else }} {{ if not (eq $value.Parent.Type "array") }}
|
{{ else }} {{ if not (eq $value.Parent.Type "array") }}
|
||||||
<label for="{{ $value.Name }}">{{ splitCapitalize $value.Title }}
|
<label for="{{ $value.Name }}">{{ SplitCapitalize $value.Title }}
|
||||||
<span class="actions">
|
<span class="actions">
|
||||||
<button class="delete"><i class="fa fa-minus"></i></button>
|
<button class="delete"><i class="fa fa-minus"></i></button>
|
||||||
</span>
|
</span>
|
||||||
|
|
|
@ -12,6 +12,24 @@ import (
|
||||||
"github.com/hacdias/caddy-hugo/assets"
|
"github.com/hacdias/caddy-hugo/assets"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// CanBeEdited checks if a filename has a supported extension
|
||||||
|
func CanBeEdited(filename string) bool {
|
||||||
|
extensions := [...]string{".markdown", ".md",
|
||||||
|
".json", ".toml", ".yaml",
|
||||||
|
".css", ".sass", ".scss",
|
||||||
|
".js",
|
||||||
|
".html",
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, extension := range extensions {
|
||||||
|
if strings.HasSuffix(filename, extension) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
// GetTemplate is used to get a ready to use template based on the url and on
|
// GetTemplate is used to get a ready to use template based on the url and on
|
||||||
// other sent templates
|
// other sent templates
|
||||||
func GetTemplate(r *http.Request, functions template.FuncMap, templates ...string) (*template.Template, error) {
|
func GetTemplate(r *http.Request, functions template.FuncMap, templates ...string) (*template.Template, error) {
|
||||||
|
|
Loading…
Reference in New Issue