updates
parent
d8c619dd3c
commit
9773f5908b
|
@ -1,4 +1,4 @@
|
|||
assets.go
|
||||
node_modules
|
||||
.sass-cache
|
||||
main.css
|
||||
temp
|
||||
|
|
30
Gruntfile.js
30
Gruntfile.js
|
@ -1,5 +1,4 @@
|
|||
module.exports = function(grunt) {
|
||||
|
||||
grunt.loadNpmTasks('grunt-contrib-watch');
|
||||
grunt.loadNpmTasks('grunt-contrib-copy');
|
||||
grunt.loadNpmTasks('grunt-contrib-concat');
|
||||
|
@ -10,12 +9,12 @@ module.exports = function(grunt) {
|
|||
grunt.initConfig({
|
||||
watch: {
|
||||
sass: {
|
||||
files: ['assets/css/src/sass/**/*.scss'],
|
||||
files: ['assets/src/sass/**/*.scss'],
|
||||
tasks: ['sass', 'concat', 'cssmin']
|
||||
},
|
||||
js: {
|
||||
files: ['assets/js/src/**/*.js'],
|
||||
tasks: ['uglify']
|
||||
files: ['assets/src/js/**/*.js'],
|
||||
tasks: ['uglify:main']
|
||||
},
|
||||
},
|
||||
sass: {
|
||||
|
@ -26,9 +25,9 @@ module.exports = function(grunt) {
|
|||
},
|
||||
files: [{
|
||||
expand: true,
|
||||
cwd: 'assets/css/src/sass',
|
||||
cwd: 'assets/src/sass',
|
||||
src: ['**/*.scss'],
|
||||
dest: 'assets/css/src',
|
||||
dest: 'temp/css',
|
||||
ext: '.css'
|
||||
}]
|
||||
}
|
||||
|
@ -40,9 +39,9 @@ module.exports = function(grunt) {
|
|||
'node_modules/animate.css/animate.min.css',
|
||||
'node_modules/codemirror/lib/codemirror.css',
|
||||
'node_modules/codemirror/theme/mdn-like.css',
|
||||
'assets/css/src/main.css'
|
||||
'temp/css/**/*.css'
|
||||
],
|
||||
dest: 'assets/css/src/main.css',
|
||||
dest: 'temp/css/main.css',
|
||||
},
|
||||
},
|
||||
copy: {
|
||||
|
@ -59,7 +58,7 @@ module.exports = function(grunt) {
|
|||
target: {
|
||||
files: [{
|
||||
expand: true,
|
||||
cwd: 'assets/css/src',
|
||||
cwd: 'temp/css/',
|
||||
src: ['*.css', '!*.min.css'],
|
||||
dest: 'assets/css/',
|
||||
ext: '.min.css'
|
||||
|
@ -67,20 +66,23 @@ module.exports = function(grunt) {
|
|||
}
|
||||
},
|
||||
uglify: {
|
||||
target: {
|
||||
plugins: {
|
||||
files: {
|
||||
'assets/js/app.min.js': ['node_modules/jquery/dist/jquery.min.js',
|
||||
'assets/js/plugins.min.js': ['node_modules/jquery/dist/jquery.min.js',
|
||||
'node_modules/perfect-scrollbar/dist/js/min/perfect-scrollbar.jquery.min.js',
|
||||
'node_modules/showdown/dist/showdown.min.js',
|
||||
'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',
|
||||
'node_modules/codemirror/mode/markdown/markdown.js',
|
||||
'node_modules/textarea-autosize/dist/jquery.textarea_autosize.js',
|
||||
'assets/js/src/**/*.js'
|
||||
'node_modules/codemirror/mode/markdown/markdown.js'
|
||||
]
|
||||
}
|
||||
},
|
||||
main: {
|
||||
files: {
|
||||
'assets/js/app.min.js': ['assets/src/js/**/*.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
|
@ -1,124 +0,0 @@
|
|||
$(document).ready(function() {
|
||||
$(document).pjax('a', '#main');
|
||||
});
|
||||
|
||||
$(document).on('ready pjax:success', function() {
|
||||
$('textarea.auto-size').textareaAutoSize();
|
||||
// Starts the perfect scroolbar plugin
|
||||
$('.scroll').perfectScrollbar();
|
||||
|
||||
// Toggles between preview and editing mode
|
||||
$("#preview").click(function(event) {
|
||||
event.preventDefault();
|
||||
|
||||
var preview = $("#preview-area"),
|
||||
editor = $('.editor textarea');
|
||||
|
||||
if ($(this).data("previewing") == "true") {
|
||||
preview.hide();
|
||||
editor.fadeIn();
|
||||
$(this).data("previewing", "false");
|
||||
|
||||
notification({
|
||||
text: "Think, relax and do the better you can!",
|
||||
type: 'information',
|
||||
timeout: 2000
|
||||
});
|
||||
} else {
|
||||
var converter = new showdown.Converter(),
|
||||
text = editor.val(),
|
||||
html = converter.makeHtml(text);
|
||||
|
||||
editor.hide();
|
||||
preview.html(html).fadeIn();
|
||||
$(this).data("previewing", "true");
|
||||
|
||||
notification({
|
||||
text: "This is how your post looks like.",
|
||||
type: 'information',
|
||||
timeout: 2000
|
||||
});
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
if ($('#content-area')[0]) {
|
||||
var myCodeMirror = CodeMirror.fromTextArea($('#content-area')[0], {
|
||||
mode: 'markdown',
|
||||
theme: 'mdn-like',
|
||||
lineWrapping: true,
|
||||
lineNumbers: false,
|
||||
scrollbarStyle: null
|
||||
});
|
||||
}
|
||||
|
||||
// Submites any form in the page in JSON format
|
||||
$('form').submit(function(event) {
|
||||
event.preventDefault();
|
||||
|
||||
var data = JSON.stringify($(this).serializeJSON()),
|
||||
button = $(this).find("input[type=submit]:focus");
|
||||
|
||||
console.log(data)
|
||||
|
||||
$.ajax({
|
||||
type: 'POST',
|
||||
url: window.location,
|
||||
data: data,
|
||||
headers: {
|
||||
'X-Regenerate': button.data("regenerate"),
|
||||
'X-Content-Type': button.data("type")
|
||||
},
|
||||
dataType: 'json',
|
||||
encode: true,
|
||||
}).done(function(data) {
|
||||
notification({
|
||||
text: button.data("message"),
|
||||
type: 'success',
|
||||
timeout: 5000
|
||||
});
|
||||
}).fail(function(data) {
|
||||
notification({
|
||||
text: 'Something went wrong.',
|
||||
type: 'error'
|
||||
});
|
||||
console.log(data);
|
||||
});
|
||||
});
|
||||
|
||||
// Log out the user sending bad credentials to the server
|
||||
$("#logout").click(function(e) {
|
||||
e.preventDefault();
|
||||
$.ajax({
|
||||
type: "GET",
|
||||
url: "/admin",
|
||||
async: false,
|
||||
username: "username",
|
||||
password: "password",
|
||||
headers: {
|
||||
"Authorization": "Basic xxx"
|
||||
}
|
||||
}).fail(function() {
|
||||
window.location = "/";
|
||||
});
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
// Adds one more field to the current group
|
||||
// TODO: improve this function. add group/field/array/obj
|
||||
$(".add").click(function(e) {
|
||||
e.preventDefault();
|
||||
fieldset = $(this).closest("fieldset");
|
||||
fieldset.append("<input name=\"" + fieldset.attr("name") + "\" id=\"" + fieldset.attr("name") + "\" value=\"\"></input><br>");
|
||||
return false;
|
||||
});
|
||||
});
|
||||
|
||||
$(document).on('pjax:send', function() {
|
||||
$('#loading').fadeIn()
|
||||
})
|
||||
$(document).on('pjax:complete', function() {
|
||||
$('#loading').fadeOut()
|
||||
})
|
|
@ -0,0 +1,143 @@
|
|||
$(document).ready(function() {
|
||||
$(document).pjax('a', '#main');
|
||||
});
|
||||
|
||||
$(document).on('ready pjax:success', function() {
|
||||
// Starts the perfect scroolbar plugin
|
||||
$('.scroll').perfectScrollbar();
|
||||
|
||||
// Log out the user sending bad credentials to the server
|
||||
$("#logout").click(function(e) {
|
||||
e.preventDefault();
|
||||
$.ajax({
|
||||
type: "GET",
|
||||
url: "/admin",
|
||||
async: false,
|
||||
username: "username",
|
||||
password: "password",
|
||||
headers: {
|
||||
"Authorization": "Basic xxx"
|
||||
}
|
||||
}).fail(function() {
|
||||
window.location = "/";
|
||||
});
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
// If it's editor page
|
||||
if ($(".editor")[0]) {
|
||||
editor = false;
|
||||
preview = $("#preview-area");
|
||||
textarea = $("#content-area");
|
||||
|
||||
if (textarea[0]) {
|
||||
options = {
|
||||
mode: 'markdown',
|
||||
theme: 'mdn-like',
|
||||
lineWrapping: true,
|
||||
lineNumbers: true,
|
||||
scrollbarStyle: null
|
||||
}
|
||||
|
||||
if (textarea.data("extension") == "markdown") {
|
||||
options.lineNumbers = false
|
||||
}
|
||||
|
||||
editor = CodeMirror.fromTextArea(textarea[0], options);
|
||||
}
|
||||
|
||||
codemirror = $('.CodeMirror');
|
||||
|
||||
// Toggles between preview and editing mode
|
||||
$("#preview").click(function(event) {
|
||||
event.preventDefault();
|
||||
|
||||
// If it currently in the preview mode, hide the preview
|
||||
// and show the editor
|
||||
if ($(this).data("previewing") == "true") {
|
||||
preview.hide();
|
||||
codemirror.fadeIn();
|
||||
$(this).data("previewing", "false");
|
||||
notification({
|
||||
text: "Think, relax and do the better you can!",
|
||||
type: 'information',
|
||||
timeout: 2000
|
||||
});
|
||||
} else {
|
||||
// Copy the editor content to texteare
|
||||
editor.save()
|
||||
|
||||
// If it's in editing mode, convert the markdown to html
|
||||
// and show it
|
||||
var converter = new showdown.Converter(),
|
||||
text = textarea.val(),
|
||||
html = converter.makeHtml(text);
|
||||
|
||||
// Hide the editor and show the preview
|
||||
codemirror.hide();
|
||||
preview.html(html).fadeIn();
|
||||
$(this).data("previewing", "true");
|
||||
notification({
|
||||
text: "This is how your post looks like.",
|
||||
type: 'information',
|
||||
timeout: 2000
|
||||
});
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
// Submites any form in the page in JSON format
|
||||
$('form').submit(function(event) {
|
||||
event.preventDefault();
|
||||
|
||||
var data = JSON.stringify($(this).serializeJSON()),
|
||||
button = $(this).find("input[type=submit]:focus");
|
||||
|
||||
console.log(data)
|
||||
|
||||
$.ajax({
|
||||
type: 'POST',
|
||||
url: window.location,
|
||||
data: data,
|
||||
headers: {
|
||||
'X-Regenerate': button.data("regenerate"),
|
||||
'X-Content-Type': button.data("type")
|
||||
},
|
||||
dataType: 'json',
|
||||
encode: true,
|
||||
}).done(function(data) {
|
||||
notification({
|
||||
text: button.data("message"),
|
||||
type: 'success',
|
||||
timeout: 5000
|
||||
});
|
||||
}).fail(function(data) {
|
||||
notification({
|
||||
text: 'Something went wrong.',
|
||||
type: 'error'
|
||||
});
|
||||
console.log(data);
|
||||
});
|
||||
});
|
||||
|
||||
// Adds one more field to the current group
|
||||
// TODO: improve this function. add group/field/array/obj
|
||||
$(".add").click(function(e) {
|
||||
e.preventDefault();
|
||||
fieldset = $(this).closest("fieldset");
|
||||
fieldset.append("<input name=\"" + fieldset.attr("name") + "\" id=\"" + fieldset.attr("name") + "\" value=\"\"></input><br>");
|
||||
return false;
|
||||
});
|
||||
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
$(document).on('pjax:send', function() {
|
||||
$('#loading').fadeIn()
|
||||
})
|
||||
$(document).on('pjax:complete', function() {
|
||||
$('#loading').fadeOut()
|
||||
})
|
|
@ -41,6 +41,10 @@
|
|||
padding: 0;
|
||||
}
|
||||
|
||||
.cm-s-mdn-like.CodeMirror {
|
||||
background: none;
|
||||
}
|
||||
|
||||
.editor #preview-area *:first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
@ -150,7 +154,7 @@
|
|||
}
|
||||
|
||||
.full .cm-s-mdn-like.CodeMirror {
|
||||
background: none;
|
||||
width : auto;
|
||||
margin : 1.5em 10%;
|
||||
width : auto;
|
||||
margin: 3em 10%;
|
||||
height: calc(100% - 6em);
|
||||
}
|
|
@ -12,8 +12,8 @@ import (
|
|||
"github.com/mholt/caddy/middleware/browse"
|
||||
)
|
||||
|
||||
// Execute sth
|
||||
func Execute(w http.ResponseWriter, r *http.Request) (int, error) {
|
||||
// ServeHTTP is...
|
||||
func ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) {
|
||||
if r.URL.Path[len(r.URL.Path)-1] != '/' {
|
||||
http.Redirect(w, r, r.URL.Path+"/", http.StatusTemporaryRedirect)
|
||||
return 0, nil
|
||||
|
@ -27,6 +27,7 @@ func Execute(w http.ResponseWriter, r *http.Request) (int, error) {
|
|||
|
||||
functions := template.FuncMap{
|
||||
"canBeEdited": editor.CanBeEdited,
|
||||
"Defined": utils.Defined,
|
||||
}
|
||||
|
||||
tpl, err := utils.GetTemplate(r, functions, "browse")
|
||||
|
|
363
editor/editor.go
363
editor/editor.go
|
@ -25,186 +25,15 @@ type editor struct {
|
|||
FrontMatter interface{}
|
||||
}
|
||||
|
||||
// Execute sth
|
||||
func Execute(w http.ResponseWriter, r *http.Request) (int, error) {
|
||||
// ServeHTTP is...
|
||||
func ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) {
|
||||
filename := strings.Replace(r.URL.Path, "/admin/edit/", "", 1)
|
||||
|
||||
if r.Method == "POST" {
|
||||
// Get the JSON information sent using a buffer
|
||||
rawBuffer := new(bytes.Buffer)
|
||||
rawBuffer.ReadFrom(r.Body)
|
||||
|
||||
// Creates the raw file "map" using the JSON
|
||||
var rawFile map[string]interface{}
|
||||
json.Unmarshal(rawBuffer.Bytes(), &rawFile)
|
||||
|
||||
// Initializes the file content to write
|
||||
var file []byte
|
||||
|
||||
switch r.Header.Get("X-Content-Type") {
|
||||
case "frontmatter-only":
|
||||
frontmatter := strings.TrimPrefix(filepath.Ext(filename), ".")
|
||||
var mark rune
|
||||
|
||||
switch frontmatter {
|
||||
case "toml":
|
||||
mark = rune('+')
|
||||
case "json":
|
||||
mark = rune('{')
|
||||
case "yaml":
|
||||
mark = rune('-')
|
||||
default:
|
||||
return 400, nil
|
||||
}
|
||||
|
||||
f, err := parser.InterfaceToFrontMatter(rawFile, mark)
|
||||
fString := string(f)
|
||||
|
||||
// If it's toml or yaml, strip frontmatter identifier
|
||||
if frontmatter == "toml" {
|
||||
fString = strings.TrimSuffix(fString, "+++\n")
|
||||
fString = strings.TrimPrefix(fString, "+++\n")
|
||||
}
|
||||
|
||||
if frontmatter == "yaml" {
|
||||
fString = strings.TrimSuffix(fString, "---\n")
|
||||
fString = strings.TrimPrefix(fString, "---\n")
|
||||
}
|
||||
|
||||
f = []byte(fString)
|
||||
|
||||
if err != nil {
|
||||
log.Print(err)
|
||||
return 500, err
|
||||
}
|
||||
|
||||
file = f
|
||||
case "content-only":
|
||||
// The main content of the file
|
||||
mainContent := rawFile["content"].(string)
|
||||
mainContent = "\n\n" + strings.TrimSpace(mainContent)
|
||||
|
||||
file = []byte(mainContent)
|
||||
case "full":
|
||||
// The main content of the file
|
||||
mainContent := rawFile["content"].(string)
|
||||
mainContent = "\n\n" + strings.TrimSpace(mainContent)
|
||||
|
||||
// Removes the main content from the rest of the frontmatter
|
||||
delete(rawFile, "content")
|
||||
|
||||
// Converts the frontmatter in JSON
|
||||
jsonFrontmatter, err := json.Marshal(rawFile)
|
||||
|
||||
if err != nil {
|
||||
log.Print(err)
|
||||
return 500, err
|
||||
}
|
||||
|
||||
// Indents the json
|
||||
frontMatterBuffer := new(bytes.Buffer)
|
||||
json.Indent(frontMatterBuffer, jsonFrontmatter, "", " ")
|
||||
|
||||
// Generates the final file
|
||||
f := new(bytes.Buffer)
|
||||
f.Write(frontMatterBuffer.Bytes())
|
||||
f.Write([]byte(mainContent))
|
||||
file = f.Bytes()
|
||||
default:
|
||||
return 400, nil
|
||||
}
|
||||
|
||||
// Write the file
|
||||
err := ioutil.WriteFile(filename, file, 0666)
|
||||
|
||||
if err != nil {
|
||||
log.Print(err)
|
||||
return 500, err
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.Write([]byte("{}"))
|
||||
} else {
|
||||
// Check if the file format is supported. If not, send a "Not Acceptable"
|
||||
// header and an error
|
||||
if !CanBeEdited(filename) {
|
||||
return 406, errors.New("File format not supported.")
|
||||
}
|
||||
|
||||
// Check if the file exists. If it doesn't, send a "Not Found" message
|
||||
if _, err := os.Stat(filename); os.IsNotExist(err) {
|
||||
log.Print(err)
|
||||
return 404, nil
|
||||
}
|
||||
|
||||
// Open the file and check if there was some error while opening
|
||||
file, err := ioutil.ReadFile(filename)
|
||||
if err != nil {
|
||||
log.Print(err)
|
||||
return 500, err
|
||||
}
|
||||
|
||||
// Create a new editor variable and set the extension
|
||||
page := new(editor)
|
||||
page.Extension = strings.TrimPrefix(filepath.Ext(filename), ".")
|
||||
page.Name = filename
|
||||
|
||||
// Handle the content depending on the file extension
|
||||
switch page.Extension {
|
||||
case "markdown", "md":
|
||||
if hasFrontMatterRune(file) {
|
||||
// Starts a new buffer and parses the file using Hugo's functions
|
||||
buffer := bytes.NewBuffer(file)
|
||||
file, err := parser.ReadFrom(buffer)
|
||||
if err != nil {
|
||||
log.Print(err)
|
||||
return 500, err
|
||||
}
|
||||
|
||||
// Parses the page content and the frontmatter
|
||||
page.Content = strings.TrimSpace(string(file.Content()))
|
||||
page.FrontMatter, err = frontmatter.Pretty(file.FrontMatter())
|
||||
page.Class = "full"
|
||||
} else {
|
||||
// The editor will handle only content
|
||||
page.Class = "content-only"
|
||||
page.Content = string(file)
|
||||
}
|
||||
case "json", "toml", "yaml":
|
||||
// Defines the class and declares an error
|
||||
page.Class = "frontmatter-only"
|
||||
var err error
|
||||
|
||||
// Checks if the file already has the frontmatter rune and parses it
|
||||
if hasFrontMatterRune(file) {
|
||||
page.FrontMatter, err = frontmatter.Pretty(file)
|
||||
} else {
|
||||
page.FrontMatter, err = frontmatter.Pretty(appendFrontMatterRune(file, page.Extension))
|
||||
}
|
||||
|
||||
// Check if there were any errors
|
||||
if err != nil {
|
||||
log.Print(err)
|
||||
return 500, err
|
||||
}
|
||||
default:
|
||||
// The editor will handle only content
|
||||
page.Class = "content-only"
|
||||
page.Content = string(file)
|
||||
}
|
||||
|
||||
// Create the functions map, then the template, check for erros and
|
||||
// execute the template if there aren't errors
|
||||
functions := template.FuncMap{"splitCapitalize": utils.SplitCapitalize}
|
||||
tpl, err := utils.GetTemplate(r, functions, "editor", "frontmatter")
|
||||
if err != nil {
|
||||
log.Print(err)
|
||||
return 500, err
|
||||
}
|
||||
tpl.Execute(w, page)
|
||||
return post(w, r, filename)
|
||||
}
|
||||
|
||||
return 200, nil
|
||||
return get(w, r, filename)
|
||||
}
|
||||
|
||||
// CanBeEdited checks if a file has a supported extension
|
||||
|
@ -225,6 +54,190 @@ func CanBeEdited(filename string) bool {
|
|||
return false
|
||||
}
|
||||
|
||||
func post(w http.ResponseWriter, r *http.Request, filename string) (int, error) {
|
||||
// Get the JSON information sent using a buffer
|
||||
rawBuffer := new(bytes.Buffer)
|
||||
rawBuffer.ReadFrom(r.Body)
|
||||
|
||||
// Creates the raw file "map" using the JSON
|
||||
var rawFile map[string]interface{}
|
||||
json.Unmarshal(rawBuffer.Bytes(), &rawFile)
|
||||
|
||||
// Initializes the file content to write
|
||||
var file []byte
|
||||
|
||||
switch r.Header.Get("X-Content-Type") {
|
||||
case "frontmatter-only":
|
||||
frontmatter := strings.TrimPrefix(filepath.Ext(filename), ".")
|
||||
var mark rune
|
||||
|
||||
switch frontmatter {
|
||||
case "toml":
|
||||
mark = rune('+')
|
||||
case "json":
|
||||
mark = rune('{')
|
||||
case "yaml":
|
||||
mark = rune('-')
|
||||
default:
|
||||
return 400, nil
|
||||
}
|
||||
|
||||
f, err := parser.InterfaceToFrontMatter(rawFile, mark)
|
||||
fString := string(f)
|
||||
|
||||
// If it's toml or yaml, strip frontmatter identifier
|
||||
if frontmatter == "toml" {
|
||||
fString = strings.TrimSuffix(fString, "+++\n")
|
||||
fString = strings.TrimPrefix(fString, "+++\n")
|
||||
}
|
||||
|
||||
if frontmatter == "yaml" {
|
||||
fString = strings.TrimSuffix(fString, "---\n")
|
||||
fString = strings.TrimPrefix(fString, "---\n")
|
||||
}
|
||||
|
||||
f = []byte(fString)
|
||||
|
||||
if err != nil {
|
||||
log.Print(err)
|
||||
return 500, err
|
||||
}
|
||||
|
||||
file = f
|
||||
case "content-only":
|
||||
// The main content of the file
|
||||
mainContent := rawFile["content"].(string)
|
||||
mainContent = "\n\n" + strings.TrimSpace(mainContent)
|
||||
|
||||
file = []byte(mainContent)
|
||||
case "full":
|
||||
// The main content of the file
|
||||
mainContent := rawFile["content"].(string)
|
||||
mainContent = "\n\n" + strings.TrimSpace(mainContent)
|
||||
|
||||
// Removes the main content from the rest of the frontmatter
|
||||
delete(rawFile, "content")
|
||||
|
||||
// Converts the frontmatter in JSON
|
||||
jsonFrontmatter, err := json.Marshal(rawFile)
|
||||
|
||||
if err != nil {
|
||||
log.Print(err)
|
||||
return 500, err
|
||||
}
|
||||
|
||||
// Indents the json
|
||||
frontMatterBuffer := new(bytes.Buffer)
|
||||
json.Indent(frontMatterBuffer, jsonFrontmatter, "", " ")
|
||||
|
||||
// Generates the final file
|
||||
f := new(bytes.Buffer)
|
||||
f.Write(frontMatterBuffer.Bytes())
|
||||
f.Write([]byte(mainContent))
|
||||
file = f.Bytes()
|
||||
default:
|
||||
return 400, nil
|
||||
}
|
||||
|
||||
// Write the file
|
||||
err := ioutil.WriteFile(filename, file, 0666)
|
||||
|
||||
if err != nil {
|
||||
log.Print(err)
|
||||
return 500, err
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.Write([]byte("{}"))
|
||||
return 200, nil
|
||||
}
|
||||
|
||||
func get(w http.ResponseWriter, r *http.Request, filename string) (int, error) {
|
||||
// Check if the file format is supported. If not, send a "Not Acceptable"
|
||||
// header and an error
|
||||
if !CanBeEdited(filename) {
|
||||
return 406, errors.New("File format not supported.")
|
||||
}
|
||||
|
||||
// Check if the file exists. If it doesn't, send a "Not Found" message
|
||||
if _, err := os.Stat(filename); os.IsNotExist(err) {
|
||||
log.Print(err)
|
||||
return 404, nil
|
||||
}
|
||||
|
||||
// Open the file and check if there was some error while opening
|
||||
file, err := ioutil.ReadFile(filename)
|
||||
if err != nil {
|
||||
log.Print(err)
|
||||
return 500, err
|
||||
}
|
||||
|
||||
// Create a new editor variable and set the extension
|
||||
page := new(editor)
|
||||
page.Extension = strings.TrimPrefix(filepath.Ext(filename), ".")
|
||||
page.Name = filename
|
||||
|
||||
// Handle the content depending on the file extension
|
||||
switch page.Extension {
|
||||
case "markdown", "md":
|
||||
page.Extension = "markdown"
|
||||
if hasFrontMatterRune(file) {
|
||||
// Starts a new buffer and parses the file using Hugo's functions
|
||||
buffer := bytes.NewBuffer(file)
|
||||
file, err := parser.ReadFrom(buffer)
|
||||
if err != nil {
|
||||
log.Print(err)
|
||||
return 500, err
|
||||
}
|
||||
|
||||
// Parses the page content and the frontmatter
|
||||
page.Content = strings.TrimSpace(string(file.Content()))
|
||||
page.FrontMatter, err = frontmatter.Pretty(file.FrontMatter())
|
||||
page.Class = "full"
|
||||
} else {
|
||||
// The editor will handle only content
|
||||
page.Class = "content-only"
|
||||
page.Content = string(file)
|
||||
}
|
||||
case "json", "toml", "yaml":
|
||||
// Defines the class and declares an error
|
||||
page.Class = "frontmatter-only"
|
||||
var err error
|
||||
|
||||
// Checks if the file already has the frontmatter rune and parses it
|
||||
if hasFrontMatterRune(file) {
|
||||
page.FrontMatter, err = frontmatter.Pretty(file)
|
||||
} else {
|
||||
page.FrontMatter, err = frontmatter.Pretty(appendFrontMatterRune(file, page.Extension))
|
||||
}
|
||||
|
||||
// Check if there were any errors
|
||||
if err != nil {
|
||||
log.Print(err)
|
||||
return 500, err
|
||||
}
|
||||
default:
|
||||
// The editor will handle only content
|
||||
page.Class = "content-only"
|
||||
page.Content = string(file)
|
||||
}
|
||||
|
||||
// Create the functions map, then the template, check for erros and
|
||||
// execute the template if there aren't errors
|
||||
functions := template.FuncMap{
|
||||
"splitCapitalize": utils.SplitCapitalize,
|
||||
"Defined": utils.Defined,
|
||||
}
|
||||
|
||||
tpl, err := utils.GetTemplate(r, functions, "editor", "frontmatter")
|
||||
if err != nil {
|
||||
log.Print(err)
|
||||
return 500, err
|
||||
}
|
||||
|
||||
return 200, tpl.Execute(w, page)
|
||||
}
|
||||
|
||||
func hasFrontMatterRune(file []byte) bool {
|
||||
return strings.HasPrefix(string(file), "---") ||
|
||||
strings.HasPrefix(string(file), "+++") ||
|
||||
|
|
5
hugo.go
5
hugo.go
|
@ -18,6 +18,7 @@ import (
|
|||
"github.com/spf13/hugo/commands"
|
||||
)
|
||||
|
||||
// Setup configures the middleware
|
||||
func Setup(c *setup.Controller) (middleware.Middleware, error) {
|
||||
commands.Execute()
|
||||
|
||||
|
@ -102,12 +103,12 @@ func (h handler) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error)
|
|||
|
||||
// Browse page
|
||||
if page == "browse" {
|
||||
code, err = browse.Execute(w, r)
|
||||
code, err = browse.ServeHTTP(w, r)
|
||||
}
|
||||
|
||||
// Edit page
|
||||
if page == "edit" {
|
||||
code, err = editor.Execute(w, r)
|
||||
code, err = editor.ServeHTTP(w, r)
|
||||
}
|
||||
|
||||
// Whenever the header "X-Refenerate" is true, the website should be
|
||||
|
|
|
@ -25,8 +25,7 @@
|
|||
"normalize.css": "^3.0.3",
|
||||
"noty": "^2.3.6",
|
||||
"perfect-scrollbar": "^0.6.4",
|
||||
"showdown": "^1.2.3",
|
||||
"textarea-autosize": "^0.4.1"
|
||||
"showdown": "^1.2.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"grunt": "^0.4.5",
|
||||
|
@ -38,4 +37,4 @@
|
|||
"grunt-contrib-watch": "^0.6.1",
|
||||
"napa": "^1.2.0"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -5,10 +5,11 @@
|
|||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="theme-color" content="#fff">
|
||||
<title>{{ .Name }}</title>
|
||||
<title>{{ if Defined . "Name" }}{{ .Name }}{{ end }}</title>
|
||||
|
||||
<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">
|
||||
<script src="/admin/assets/js/plugins.min.js"></script>
|
||||
<script src="/admin/assets/js/app.min.js"></script>
|
||||
</head>
|
||||
|
||||
|
|
|
@ -21,13 +21,13 @@
|
|||
{{ else if eq .Class "content-only" }}
|
||||
<div class="container">
|
||||
<div class="content">
|
||||
<textarea id="content-area" name="content" class="scroll auto-size">{{ .Content }}</textarea>
|
||||
<textarea id="content-area" name="content" class="scroll" data-extension="{{ .Extension }}">{{ .Content }}</textarea>
|
||||
<div id="preview-area" class="scroll hidden"></div>
|
||||
</div>
|
||||
</div>
|
||||
{{ else }}
|
||||
<div class="container">
|
||||
<textarea id="content-area" name="content" class="scroll">{{ .Content }}</textarea>
|
||||
<textarea id="content-area" name="content" class="scroll" data-extension="{{ .Extension }}">{{ .Content }}</textarea>
|
||||
<div id="preview-area" class="scroll hidden"></div>
|
||||
</div>
|
||||
<div class="sidebar scroll">
|
||||
|
|
|
@ -69,6 +69,19 @@ func Dict(values ...interface{}) (map[string]interface{}, error) {
|
|||
return dict, nil
|
||||
}
|
||||
|
||||
// Defined checks if variable is defined in a struct
|
||||
func Defined(data interface{}, field string) bool {
|
||||
t := reflect.Indirect(reflect.ValueOf(data)).Type()
|
||||
|
||||
if t.Kind() != reflect.Struct {
|
||||
log.Print("Non-struct type not allowed.")
|
||||
return false
|
||||
}
|
||||
|
||||
_, b := t.FieldByName(field)
|
||||
return b
|
||||
}
|
||||
|
||||
// IsMap checks if some variable is a map
|
||||
func IsMap(sth interface{}) bool {
|
||||
return reflect.ValueOf(sth).Kind() == reflect.Map
|
||||
|
|
Loading…
Reference in New Issue