updates, sort interface

pull/20/head
Henrique Dias 2015-09-18 09:47:02 +01:00
parent d847909da8
commit abb9c4efe1
16 changed files with 434 additions and 52 deletions

View File

@ -38,6 +38,7 @@ module.exports = function(grunt) {
src: ['node_modules/normalize.css/normalize.css', src: ['node_modules/normalize.css/normalize.css',
'node_modules/font-awesome/css/font-awesome.css', 'node_modules/font-awesome/css/font-awesome.css',
'node_modules/animate.css/animate.min.css', 'node_modules/animate.css/animate.min.css',
'node_modules/codemirror/lib/codemirror.css',
'assets/css/src/main.css' 'assets/css/src/main.css'
], ],
dest: '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/noty/js/noty/packaged/jquery.noty.packaged.min.js',
'node_modules/jquery-pjax/jquery.pjax.js', 'node_modules/jquery-pjax/jquery.pjax.js',
'node_modules/jquery-serializejson/jquery.serializejson.min.js', 'node_modules/jquery-serializejson/jquery.serializejson.min.js',
'node_modules/codemirror/lib/codemirror.js',
'assets/js/src/**/*.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

View File

@ -41,6 +41,7 @@ func Execute(w http.ResponseWriter, r *http.Request) (int, error) {
Template: tpl, Template: tpl,
}, },
}, },
IgnoreIndexes: true,
} }
return b.ServeHTTP(w, r) return b.ServeHTTP(w, r)

View File

@ -91,7 +91,8 @@ func Execute(w http.ResponseWriter, r *http.Request) (int, error) {
} }
page := new(page.Page) page := new(page.Page)
page.Name = "Edit" page.Name = "Editor"
page.Class = "editor"
page.Body = inf page.Body = inf
return page.Render(w, r, "edit", "frontmatter") return page.Render(w, r, "edit", "frontmatter")
} }

View File

@ -4,6 +4,7 @@ import (
"log" "log"
"reflect" "reflect"
"sort" "sort"
"strings"
"github.com/hacdias/caddy-hugo/utils" "github.com/hacdias/caddy-hugo/utils"
"github.com/spf13/hugo/parser" "github.com/spf13/hugo/parser"
@ -64,9 +65,9 @@ func rawToPretty(config interface{}, parent *frontmatter) interface{} {
log.Panic("Parent type not allowed.") log.Panic("Parent type not allowed.")
} }
sortByTitle(objects) sort.Sort(sortByTitle(objects))
sortByTitle(arrays) sort.Sort(sortByTitle(arrays))
sortByTitle(fields) sort.Sort(sortByTitle(fields))
settings := []*frontmatter{} settings := []*frontmatter{}
settings = append(settings, fields...) settings = append(settings, fields...)
@ -75,26 +76,12 @@ func rawToPretty(config interface{}, parent *frontmatter) interface{} {
return settings return settings
} }
func sortByTitle(config []*frontmatter) { type sortByTitle []*frontmatter
keys := make([]string, len(config))
positionByTitle := make(map[string]int)
for index, element := range config { func (f sortByTitle) Len() int { return len(f) }
keys[index] = element.Title func (f sortByTitle) Swap(i, j int) { f[i], f[j] = f[j], f[i] }
positionByTitle[element.Title] = index func (f sortByTitle) Less(i, j int) bool {
} return strings.ToLower(f[i].Name) < strings.ToLower(f[j].Name)
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 handleObjects(content interface{}, parent *frontmatter, name string) *frontmatter { 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 := new(frontmatter)
c.Parent = parent c.Parent = parent
// TODO: see why isn't this working
switch reflect.ValueOf(content).Kind() { switch reflect.ValueOf(content).Kind() {
case reflect.Bool: case reflect.Bool:
c.Type = "boolean" c.Type = "boolean"

53
hugo.go
View File

@ -18,7 +18,6 @@ import (
"github.com/spf13/hugo/commands" "github.com/spf13/hugo/commands"
) )
// Setup function
func Setup(c *setup.Controller) (middleware.Middleware, error) { func Setup(c *setup.Controller) (middleware.Middleware, error) {
commands.Execute() commands.Execute()
@ -30,11 +29,22 @@ func Setup(c *setup.Controller) (middleware.Middleware, error) {
type handler struct{ Next middleware.Handler } type handler struct{ Next middleware.Handler }
func (h handler) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) { func (h handler) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) {
// Only handle /admin path
if middleware.Path(r.URL.Path).Matches("/admin") { if middleware.Path(r.URL.Path).Matches("/admin") {
page := utils.ParseComponents(r)[1]
code := 404
var err error 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 page != "assets" && page != "edit" {
if r.URL.Path[len(r.URL.Path)-1] != '/' { if r.URL.Path[len(r.URL.Path)-1] != '/' {
http.Redirect(w, r, r.URL.Path+"/", http.StatusTemporaryRedirect) 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" { if page == "assets" {
filename := strings.Replace(r.URL.Path, "/admin/", "", 1) filename := strings.Replace(r.URL.Path, "/admin/", "", 1)
file, err := assets.Asset(filename) file, err := assets.Asset(filename)
@ -50,28 +67,36 @@ func (h handler) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error)
return 404, nil return 404, nil
} }
// Get the file extension ant its mime type
extension := filepath.Ext(filename) extension := filepath.Ext(filename)
mime := mime.TypeByExtension(extension) mime := mime.TypeByExtension(extension)
header := w.Header() // Write the header with the Content-Type and write the file
header.Set("Content-Type", mime) // content to the buffer
w.Header().Set("Content-Type", mime)
w.Write(file) w.Write(file)
return 200, nil return 200, nil
} }
if page == "browse" { // If the url matches exactly with /admin/settings/ serve that page
code, err = browse.Execute(w, r) // page variable isn't used here to avoid people using URLs like
} // "/admin/settings/something".
if page == "edit" {
code, err = edit.Execute(w, r)
}
if r.URL.Path == "/admin/settings/" { if r.URL.Path == "/admin/settings/" {
code, err = settings.Execute(w, r) 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" { if r.Header.Get("X-Regenerate") == "true" {
commands.Execute() commands.Execute()
} }

View File

@ -18,6 +18,7 @@
}, },
"dependencies": { "dependencies": {
"animate.css": "^3.4.0", "animate.css": "^3.4.0",
"codemirror": "^5.6.0",
"font-awesome": "^4.4.0", "font-awesome": "^4.4.0",
"jquery": "^2.1.4", "jquery": "^2.1.4",
"jquery-serializejson": "^2.5.0", "jquery-serializejson": "^2.5.0",

View File

@ -20,8 +20,9 @@ var funcMap = template.FuncMap{
// Page type // Page type
type Page struct { type Page struct {
Name string Name string
Body interface{} Class string
Body interface{}
} }
// Render the page // Render the page
@ -37,7 +38,11 @@ func (p *Page) Render(w http.ResponseWriter, r *http.Request, templates ...strin
return 200, nil 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) { 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" { if r.Header.Get("X-PJAX") == "true" {
templates = append(templates, "base_minimal") templates = append(templates, "base_minimal")
} else { } else {
@ -46,14 +51,19 @@ func GetTemplate(r *http.Request, templates ...string) (*template.Template, erro
var tpl *template.Template var tpl *template.Template
// For each template, add it to the the tpl variable
for i, t := range templates { for i, t := range templates {
// Get the template from the assets
page, err := assets.Asset("templates/" + t + templateExtension) page, err := assets.Asset("templates/" + t + templateExtension)
// Check if there is some error. If so, the template doesn't exist
if err != nil { if err != nil {
log.Print(err) log.Print(err)
return new(template.Template), err return new(template.Template), err
} }
// If it's the first iteration, creates a new template and add the
// functions map
if i == 0 { if i == 0 {
tpl, err = template.New(t).Funcs(funcMap).Parse(string(page)) tpl, err = template.New(t).Funcs(funcMap).Parse(string(page))
} else { } else {

View File

@ -62,6 +62,7 @@ func Execute(w http.ResponseWriter, r *http.Request) (int, error) {
page := new(page.Page) page := new(page.Page)
page.Name = "Settings" page.Name = "Settings"
page.Class = "settings"
page.Body = f page.Body = f
return page.Render(w, r, "settings", "frontmatter") return page.Render(w, r, "settings", "frontmatter")
} }

View File

@ -1,6 +1,5 @@
<!DOCTYPE html> <!DOCTYPE html>
<html class="no-js" lang="en"> <html class="no-js" lang="en">
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta http-equiv="X-UA-Compatible" content="IE=edge">
@ -17,16 +16,14 @@
<nav> <nav>
<ul> <ul>
<li><a href="/"><i class="fa fa-home fa-lg"></i> Home</a></li> <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/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/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/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> <li><a id="logout" href="#logout"><i class="fa fa-sign-out"></i> Logout</a></li>
</ul> </ul>
</nav> </nav>
<div class="main" id="container"> <div class="main" id="container">
{{ template "content" . }} {{ template "content" . }}
</div> </div>
</body> </body>
</html> </html>

View File

@ -8,7 +8,7 @@
</div> </div>
<div class="container data"> <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 id="preview-area" class="scroll hidden"></div>
</div> </div>

View File

@ -9,7 +9,7 @@
{{ if not (eq $value.Parent.Type "array") }} {{ if not (eq $value.Parent.Type "array") }}
<label for="{{ $value.Name }}">{{ splitCapitalize $value.Title }}</label> <label for="{{ $value.Name }}">{{ splitCapitalize $value.Title }}</label>
{{ end }} {{ 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 }} {{ end }}
{{ end }} {{ end }}

View File

@ -1,8 +1,13 @@
{{ define "content" }} {{ define "content" }}
<header>
<div class="content">
<h1>Settings</h1>
</div>
</header>
<main> <main>
{{ with .Body }} {{ with .Body }}
<div class="content"> <div class="content">
<h1>Settings</h1>
<form method="POST" action="/admin/settings"> <form method="POST" action="/admin/settings">
{{ template "frontmatter" . }} {{ template "frontmatter" . }}
<input type="submit" data-message="Settings updated." data-regenerate="true" value="Save"> <input type="submit" data-message="Settings updated." data-regenerate="true" value="Save">

View File

@ -24,38 +24,48 @@ func Dict(values ...interface{}) (map[string]interface{}, error) {
return dict, nil return dict, nil
} }
// IsMap checks if some variable is a map
func IsMap(sth interface{}) bool { func IsMap(sth interface{}) bool {
return reflect.ValueOf(sth).Kind() == reflect.Map return reflect.ValueOf(sth).Kind() == reflect.Map
} }
// IsSlice checks if some variable is a slice
func IsSlice(sth interface{}) bool { func IsSlice(sth interface{}) bool {
return reflect.ValueOf(sth).Kind() == reflect.Slice return reflect.ValueOf(sth).Kind() == reflect.Slice
} }
// IsArray checks if some variable is an array
func IsArray(sth interface{}) bool { func IsArray(sth interface{}) bool {
return reflect.ValueOf(sth).Kind() == reflect.Array return reflect.ValueOf(sth).Kind() == reflect.Array
} }
// IsString checks if some variable is a string
func IsString(sth interface{}) bool { func IsString(sth interface{}) bool {
return reflect.ValueOf(sth).Kind() == reflect.String return reflect.ValueOf(sth).Kind() == reflect.String
} }
// IsInt checks if some variable is an integer
func IsInt(sth interface{}) bool { func IsInt(sth interface{}) bool {
return reflect.ValueOf(sth).Kind() == reflect.Int return reflect.ValueOf(sth).Kind() == reflect.Int
} }
// IsBool checks if some variable is a boolean
func IsBool(sth interface{}) bool { func IsBool(sth interface{}) bool {
return reflect.ValueOf(sth).Kind() == reflect.Bool return reflect.ValueOf(sth).Kind() == reflect.Bool
} }
// IsInterface checks if some variable is an interface
func IsInterface(sth interface{}) bool { func IsInterface(sth interface{}) bool {
return reflect.ValueOf(sth).Kind() == reflect.Interface return reflect.ValueOf(sth).Kind() == reflect.Interface
} }
// IsMarkdownFile checks if a filename belongs to a markdown file
func IsMarkdownFile(filename string) bool { func IsMarkdownFile(filename string) bool {
return strings.HasSuffix(filename, ".markdown") || strings.HasSuffix(filename, ".md") 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 { func SplitCapitalize(name string) string {
var words []string var words []string
l := 0 l := 0
@ -79,6 +89,7 @@ func SplitCapitalize(name string) string {
return name return name
} }
// ParseComponents parses the components of an URL creating an array
func ParseComponents(r *http.Request) []string { func ParseComponents(r *http.Request) []string {
//The URL that the user queried. //The URL that the user queried.
path := r.URL.Path path := r.URL.Path