updates, sort interface
parent
d847909da8
commit
abb9c4efe1
|
@ -38,6 +38,7 @@ module.exports = function(grunt) {
|
|||
src: ['node_modules/normalize.css/normalize.css',
|
||||
'node_modules/font-awesome/css/font-awesome.css',
|
||||
'node_modules/animate.css/animate.min.css',
|
||||
'node_modules/codemirror/lib/codemirror.css',
|
||||
'assets/css/src/main.css'
|
||||
],
|
||||
dest: 'assets/css/src/main.css',
|
||||
|
@ -73,6 +74,7 @@ module.exports = function(grunt) {
|
|||
'node_modules/noty/js/noty/packaged/jquery.noty.packaged.min.js',
|
||||
'node_modules/jquery-pjax/jquery.pjax.js',
|
||||
'node_modules/jquery-serializejson/jquery.serializejson.min.js',
|
||||
'node_modules/codemirror/lib/codemirror.js',
|
||||
'assets/js/src/**/*.js'
|
||||
]
|
||||
}
|
||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -41,6 +41,7 @@ func Execute(w http.ResponseWriter, r *http.Request) (int, error) {
|
|||
Template: tpl,
|
||||
},
|
||||
},
|
||||
IgnoreIndexes: true,
|
||||
}
|
||||
|
||||
return b.ServeHTTP(w, r)
|
||||
|
|
|
@ -91,7 +91,8 @@ func Execute(w http.ResponseWriter, r *http.Request) (int, error) {
|
|||
}
|
||||
|
||||
page := new(page.Page)
|
||||
page.Name = "Edit"
|
||||
page.Name = "Editor"
|
||||
page.Class = "editor"
|
||||
page.Body = inf
|
||||
return page.Render(w, r, "edit", "frontmatter")
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ import (
|
|||
"log"
|
||||
"reflect"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/hacdias/caddy-hugo/utils"
|
||||
"github.com/spf13/hugo/parser"
|
||||
|
@ -64,9 +65,9 @@ func rawToPretty(config interface{}, parent *frontmatter) interface{} {
|
|||
log.Panic("Parent type not allowed.")
|
||||
}
|
||||
|
||||
sortByTitle(objects)
|
||||
sortByTitle(arrays)
|
||||
sortByTitle(fields)
|
||||
sort.Sort(sortByTitle(objects))
|
||||
sort.Sort(sortByTitle(arrays))
|
||||
sort.Sort(sortByTitle(fields))
|
||||
|
||||
settings := []*frontmatter{}
|
||||
settings = append(settings, fields...)
|
||||
|
@ -75,26 +76,12 @@ func rawToPretty(config interface{}, parent *frontmatter) interface{} {
|
|||
return settings
|
||||
}
|
||||
|
||||
func sortByTitle(config []*frontmatter) {
|
||||
keys := make([]string, len(config))
|
||||
positionByTitle := make(map[string]int)
|
||||
type sortByTitle []*frontmatter
|
||||
|
||||
for index, element := range config {
|
||||
keys[index] = element.Title
|
||||
positionByTitle[element.Title] = index
|
||||
}
|
||||
|
||||
sort.Strings(keys)
|
||||
// TODO: http://golang.org/pkg/sort/#Interface
|
||||
cnf := make([]*frontmatter, len(config))
|
||||
|
||||
for index, title := range keys {
|
||||
cnf[index] = config[positionByTitle[title]]
|
||||
}
|
||||
|
||||
for index := range config {
|
||||
config[index] = cnf[index]
|
||||
}
|
||||
func (f sortByTitle) Len() int { return len(f) }
|
||||
func (f sortByTitle) Swap(i, j int) { f[i], f[j] = f[j], f[i] }
|
||||
func (f sortByTitle) Less(i, j int) bool {
|
||||
return strings.ToLower(f[i].Name) < strings.ToLower(f[j].Name)
|
||||
}
|
||||
|
||||
func handleObjects(content interface{}, parent *frontmatter, name string) *frontmatter {
|
||||
|
@ -133,6 +120,7 @@ func handleFlatValues(content interface{}, parent *frontmatter, name string) *fr
|
|||
c := new(frontmatter)
|
||||
c.Parent = parent
|
||||
|
||||
// TODO: see why isn't this working
|
||||
switch reflect.ValueOf(content).Kind() {
|
||||
case reflect.Bool:
|
||||
c.Type = "boolean"
|
||||
|
|
53
hugo.go
53
hugo.go
|
@ -18,7 +18,6 @@ import (
|
|||
"github.com/spf13/hugo/commands"
|
||||
)
|
||||
|
||||
// Setup function
|
||||
func Setup(c *setup.Controller) (middleware.Middleware, error) {
|
||||
commands.Execute()
|
||||
|
||||
|
@ -30,11 +29,22 @@ func Setup(c *setup.Controller) (middleware.Middleware, error) {
|
|||
type handler struct{ Next middleware.Handler }
|
||||
|
||||
func (h handler) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) {
|
||||
// Only handle /admin path
|
||||
if middleware.Path(r.URL.Path).Matches("/admin") {
|
||||
page := utils.ParseComponents(r)[1]
|
||||
code := 404
|
||||
var err error
|
||||
var page string
|
||||
code := 404
|
||||
|
||||
// If the length of the components string is less than one, the variable
|
||||
// page will always be "admin"
|
||||
if len(utils.ParseComponents(r)) > 1 {
|
||||
page = utils.ParseComponents(r)[1]
|
||||
} else {
|
||||
page = utils.ParseComponents(r)[0]
|
||||
}
|
||||
|
||||
// If the page isn't "assets" neither "edit", it should always put a
|
||||
// trailing slash in the path
|
||||
if page != "assets" && page != "edit" {
|
||||
if r.URL.Path[len(r.URL.Path)-1] != '/' {
|
||||
http.Redirect(w, r, r.URL.Path+"/", http.StatusTemporaryRedirect)
|
||||
|
@ -42,6 +52,13 @@ func (h handler) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error)
|
|||
}
|
||||
}
|
||||
|
||||
// If the current page is only "/admin/", redirect to "/admin/browse/contents"
|
||||
if r.URL.Path == "/admin/" {
|
||||
http.Redirect(w, r, "/admin/browse/content/", http.StatusTemporaryRedirect)
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
// Serve the static assets
|
||||
if page == "assets" {
|
||||
filename := strings.Replace(r.URL.Path, "/admin/", "", 1)
|
||||
file, err := assets.Asset(filename)
|
||||
|
@ -50,28 +67,36 @@ func (h handler) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error)
|
|||
return 404, nil
|
||||
}
|
||||
|
||||
// Get the file extension ant its mime type
|
||||
extension := filepath.Ext(filename)
|
||||
mime := mime.TypeByExtension(extension)
|
||||
|
||||
header := w.Header()
|
||||
header.Set("Content-Type", mime)
|
||||
|
||||
// Write the header with the Content-Type and write the file
|
||||
// content to the buffer
|
||||
w.Header().Set("Content-Type", mime)
|
||||
w.Write(file)
|
||||
return 200, nil
|
||||
}
|
||||
|
||||
if page == "browse" {
|
||||
code, err = browse.Execute(w, r)
|
||||
}
|
||||
|
||||
if page == "edit" {
|
||||
code, err = edit.Execute(w, r)
|
||||
}
|
||||
|
||||
// If the url matches exactly with /admin/settings/ serve that page
|
||||
// page variable isn't used here to avoid people using URLs like
|
||||
// "/admin/settings/something".
|
||||
if r.URL.Path == "/admin/settings/" {
|
||||
code, err = settings.Execute(w, r)
|
||||
}
|
||||
|
||||
// Browse page
|
||||
if page == "browse" {
|
||||
code, err = browse.Execute(w, r)
|
||||
}
|
||||
|
||||
// Edit page
|
||||
if page == "edit" {
|
||||
code, err = edit.Execute(w, r)
|
||||
}
|
||||
|
||||
// Whenever the header "X-Refenerate" is true, the website should be
|
||||
// regenerated. Used in edit and settings, for example.
|
||||
if r.Header.Get("X-Regenerate") == "true" {
|
||||
commands.Execute()
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"animate.css": "^3.4.0",
|
||||
"codemirror": "^5.6.0",
|
||||
"font-awesome": "^4.4.0",
|
||||
"jquery": "^2.1.4",
|
||||
"jquery-serializejson": "^2.5.0",
|
||||
|
|
14
page/page.go
14
page/page.go
|
@ -20,8 +20,9 @@ var funcMap = template.FuncMap{
|
|||
|
||||
// Page type
|
||||
type Page struct {
|
||||
Name string
|
||||
Body interface{}
|
||||
Name string
|
||||
Class string
|
||||
Body interface{}
|
||||
}
|
||||
|
||||
// Render the page
|
||||
|
@ -37,7 +38,11 @@ func (p *Page) Render(w http.ResponseWriter, r *http.Request, templates ...strin
|
|||
return 200, nil
|
||||
}
|
||||
|
||||
// GetTemplate is used to get a ready to use template based on the url and on
|
||||
// other sent templates
|
||||
func GetTemplate(r *http.Request, templates ...string) (*template.Template, error) {
|
||||
// If this is a pjax request, use the minimal template to send only
|
||||
// the main content
|
||||
if r.Header.Get("X-PJAX") == "true" {
|
||||
templates = append(templates, "base_minimal")
|
||||
} else {
|
||||
|
@ -46,14 +51,19 @@ func GetTemplate(r *http.Request, templates ...string) (*template.Template, erro
|
|||
|
||||
var tpl *template.Template
|
||||
|
||||
// For each template, add it to the the tpl variable
|
||||
for i, t := range templates {
|
||||
// Get the template from the assets
|
||||
page, err := assets.Asset("templates/" + t + templateExtension)
|
||||
|
||||
// Check if there is some error. If so, the template doesn't exist
|
||||
if err != nil {
|
||||
log.Print(err)
|
||||
return new(template.Template), err
|
||||
}
|
||||
|
||||
// If it's the first iteration, creates a new template and add the
|
||||
// functions map
|
||||
if i == 0 {
|
||||
tpl, err = template.New(t).Funcs(funcMap).Parse(string(page))
|
||||
} else {
|
||||
|
|
|
@ -62,6 +62,7 @@ func Execute(w http.ResponseWriter, r *http.Request) (int, error) {
|
|||
|
||||
page := new(page.Page)
|
||||
page.Name = "Settings"
|
||||
page.Class = "settings"
|
||||
page.Body = f
|
||||
return page.Render(w, r, "settings", "frontmatter")
|
||||
}
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
<!DOCTYPE html>
|
||||
<html class="no-js" lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
|
@ -17,16 +16,14 @@
|
|||
<nav>
|
||||
<ul>
|
||||
<li><a href="/"><i class="fa fa-home fa-lg"></i> Home</a></li>
|
||||
<li><a href="/admin/browse/content"><i class="fa fa-newspaper-o"></i> Content</a></li>
|
||||
<li><a href="/admin/browse"><i class="fa fa-folder-o"></i> Browse</a></li>
|
||||
<li><a href="/admin/settings"><i class="fa fa-cog"></i> Settings</a></li>
|
||||
<li><a href="/admin/browse/content/"><i class="fa fa-newspaper-o"></i> Content</a></li>
|
||||
<li><a href="/admin/browse/"><i class="fa fa-folder-o"></i> Browse</a></li>
|
||||
<li><a href="/admin/settings/"><i class="fa fa-cog"></i> Settings</a></li>
|
||||
<li><a id="logout" href="#logout"><i class="fa fa-sign-out"></i> Logout</a></li>
|
||||
</ul>
|
||||
</nav>
|
||||
<div class="main" id="container">
|
||||
{{ template "content" . }}
|
||||
</div>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
</div>
|
||||
|
||||
<div class="container data">
|
||||
<textarea name="content" class="scroll">{{ .Content }}</textarea>
|
||||
<textarea id="content-area" name="content" class="scroll">{{ .Content }}</textarea>
|
||||
<div id="preview-area" class="scroll hidden"></div>
|
||||
</div>
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
{{ if not (eq $value.Parent.Type "array") }}
|
||||
<label for="{{ $value.Name }}">{{ splitCapitalize $value.Title }}</label>
|
||||
{{ end }}
|
||||
<input name="{{ $value.Name }}" id="{{ $value.Name }}" value="{{ $value.Content }}"></input><br>
|
||||
<input name="{{ $value.Name }}:auto" id="{{ $value.Name }}" value="{{ $value.Content }}"></input><br>
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
|
|
|
@ -1,8 +1,13 @@
|
|||
{{ define "content" }}
|
||||
<header>
|
||||
<div class="content">
|
||||
<h1>Settings</h1>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main>
|
||||
{{ with .Body }}
|
||||
<div class="content">
|
||||
<h1>Settings</h1>
|
||||
<form method="POST" action="/admin/settings">
|
||||
{{ template "frontmatter" . }}
|
||||
<input type="submit" data-message="Settings updated." data-regenerate="true" value="Save">
|
||||
|
|
|
@ -24,38 +24,48 @@ func Dict(values ...interface{}) (map[string]interface{}, error) {
|
|||
return dict, nil
|
||||
}
|
||||
|
||||
// IsMap checks if some variable is a map
|
||||
func IsMap(sth interface{}) bool {
|
||||
return reflect.ValueOf(sth).Kind() == reflect.Map
|
||||
}
|
||||
|
||||
// IsSlice checks if some variable is a slice
|
||||
func IsSlice(sth interface{}) bool {
|
||||
return reflect.ValueOf(sth).Kind() == reflect.Slice
|
||||
}
|
||||
|
||||
// IsArray checks if some variable is an array
|
||||
func IsArray(sth interface{}) bool {
|
||||
return reflect.ValueOf(sth).Kind() == reflect.Array
|
||||
}
|
||||
|
||||
// IsString checks if some variable is a string
|
||||
func IsString(sth interface{}) bool {
|
||||
return reflect.ValueOf(sth).Kind() == reflect.String
|
||||
}
|
||||
|
||||
// IsInt checks if some variable is an integer
|
||||
func IsInt(sth interface{}) bool {
|
||||
return reflect.ValueOf(sth).Kind() == reflect.Int
|
||||
}
|
||||
|
||||
// IsBool checks if some variable is a boolean
|
||||
func IsBool(sth interface{}) bool {
|
||||
return reflect.ValueOf(sth).Kind() == reflect.Bool
|
||||
}
|
||||
|
||||
// IsInterface checks if some variable is an interface
|
||||
func IsInterface(sth interface{}) bool {
|
||||
return reflect.ValueOf(sth).Kind() == reflect.Interface
|
||||
}
|
||||
|
||||
// IsMarkdownFile checks if a filename belongs to a markdown file
|
||||
func IsMarkdownFile(filename string) bool {
|
||||
return strings.HasSuffix(filename, ".markdown") || strings.HasSuffix(filename, ".md")
|
||||
}
|
||||
|
||||
// SplitCapitalize splits a string by its uppercase letters and capitalize the
|
||||
// first letter of the string
|
||||
func SplitCapitalize(name string) string {
|
||||
var words []string
|
||||
l := 0
|
||||
|
@ -79,6 +89,7 @@ func SplitCapitalize(name string) string {
|
|||
return name
|
||||
}
|
||||
|
||||
// ParseComponents parses the components of an URL creating an array
|
||||
func ParseComponents(r *http.Request) []string {
|
||||
//The URL that the user queried.
|
||||
path := r.URL.Path
|
||||
|
|
Loading…
Reference in New Issue