ACE Code Editor for Pages and Blogs

pull/4/head
Noah 2017-12-23 15:29:38 -08:00
parent d8254826c2
commit 9e58456aaa
6 changed files with 150 additions and 17 deletions

View File

@ -20,11 +20,11 @@ import (
// AdminRoutes attaches the admin routes to the app. // AdminRoutes attaches the admin routes to the app.
func (b *Blog) AdminRoutes(r *mux.Router) { func (b *Blog) AdminRoutes(r *mux.Router) {
adminRouter := mux.NewRouter().PathPrefix("/admin").Subrouter().StrictSlash(false) adminRouter := mux.NewRouter().PathPrefix("/admin").Subrouter().StrictSlash(true)
r.HandleFunc("/admin", b.AdminHandler) // so as to not be "/admin/" adminRouter.HandleFunc("/", b.AdminHandler)
adminRouter.HandleFunc("/settings", b.SettingsHandler) adminRouter.HandleFunc("/settings", b.SettingsHandler)
adminRouter.HandleFunc("/editor", b.EditorHandler) adminRouter.HandleFunc("/editor", b.EditorHandler)
adminRouter.PathPrefix("/").HandlerFunc(b.PageHandler) // r.HandleFunc("/admin", b.AdminHandler)
r.PathPrefix("/admin").Handler(negroni.New( r.PathPrefix("/admin").Handler(negroni.New(
negroni.HandlerFunc(b.LoginRequired), negroni.HandlerFunc(b.LoginRequired),
negroni.Wrap(adminRouter), negroni.Wrap(adminRouter),

View File

@ -58,8 +58,9 @@ func (b *Blog) PageHandler(w http.ResponseWriter, r *http.Request) {
title, _ := TitleFromMarkdown(body) title, _ := TitleFromMarkdown(body)
b.RenderTemplate(w, r, ".markdown", NewVars(map[interface{}]interface{}{ b.RenderTemplate(w, r, ".markdown", NewVars(map[interface{}]interface{}{
"Title": title, "Title": title,
"HTML": template.HTML(html), "HTML": template.HTML(html),
"MarkdownFile": filepath.URI,
})) }))
return return
} }

View File

@ -81,7 +81,7 @@
{{ if and .CurrentUser.Admin .Editable }} {{ if and .CurrentUser.Admin .Editable }}
<p class="mt-4"> <p class="mt-4">
<strong>Admin:</strong> [<a href="/admin/editor?file={{ TemplateName }}">edit this page</a>] <strong>Admin:</strong> [<a href="/admin/editor?file={{ or .Data.MarkdownFile TemplateName }}">edit this page</a>]
</p> </p>
{{ end }} {{ end }}
</div> </div>
@ -197,7 +197,6 @@
</div> </div>
</footer> </footer>
<script type="text/javascript" src="/js/bootstrap.min.js"></script>
{{ template "scripts" or "" }} {{ template "scripts" or "" }}
</body> </body>

View File

@ -1,10 +1,10 @@
# About Blog # About Blog
This is a simple web blog and content management system written in Go. This is a simple web blog and content management system written in Go.
## Features ## Features
* Web blog * Web blog
* Draft, Private Posts * Draft, Private Posts
* Page editor * Page editor
* You can edit any page from the front-end. * You can edit any page from the front-end.

View File

@ -9,6 +9,25 @@
</p> </p>
{{ end }} {{ end }}
<style type="text/css" media="screen">
#editor-box {
position: relative;
display: none;
width: 100%;
height: 500px;
}
#ace-editor {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
}
#ace-buttons {
display: none;
}
</style>
<form action="/admin/editor" method="POST"> <form action="/admin/editor" method="POST">
<input type="hidden" name="_csrf" value="{{ .CSRF }}"> <input type="hidden" name="_csrf" value="{{ .CSRF }}">
<input type="hidden" name="save" value="true"> <input type="hidden" name="save" value="true">
@ -31,6 +50,18 @@
<label for="body"> <label for="body">
Content: Content:
</label> </label>
<div id="editor-box">
<div id="ace-editor">{{ .Data.Body }}</div>
</div>
<div id="ace-buttons">
Syntax:
<a href="#" onClick="return setSyntax('html')" class="btn btn-secondary btn-sm">HTML</a>
<a href="#" onClick="return setSyntax('markdown')" class="btn btn-secondary btn-sm">Markdown</a>
<a href="#" onClick="return setSyntax('javascript')" class="btn btn-secondary btn-sm">JS</a>
<a href="#" onClick="return setSyntax('css')" class="btn btn-secondary btn-sm">CSS</a>
<a href="#" onClick="return setSyntax('text')" class="btn btn-secondary btn-sm">None</a>
</div>
<textarea <textarea
cols="40" cols="40"
rows="12" rows="12"
@ -45,4 +76,49 @@
<button type="submit" name="action" value="delete" class="btn btn-danger" onClick="return window.confirm('Are you sure?')">Delete Page</button> <button type="submit" name="action" value="delete" class="btn btn-danger" onClick="return window.confirm('Are you sure?')">Delete Page</button>
</p> </p>
</form> </form>
<script src="/js/ace-editor/src-min-noconflict/ace.js" type="text/javascript" charset="utf-8"></script>
<script>
var ACE;
(function() {
var editor = ace.edit("ace-editor");
ACE = editor;
document.querySelector("#editor-box").style.display = "block";
document.querySelector("#ace-buttons").style.display = "block";
document.querySelector("#body").style.display = "none";
// Default editor settings
editor.setTheme("ace/theme/monokai");
var ses = editor.getSession();
ses.setTabSize(4);
ses.setUseSoftTabs(true);
// On save.
ses.on("change", function() {
document.getElementById("body").value = editor.getValue();
});
var filename = "{{ .Data.File }}";
var parts = filename.split(".");
var ext = parts.pop().toLowerCase();
if (ext === "js") {
ses.setMode("ace/mode/javascript");
} else if (ext === "gohtml" || ext === "html") {
ses.setMode("ace/mode/html");
} else if (ext === "md" || ext === "markdown") {
ses.setMode("ace/mode/markdown");
ses.setTabSize(2);
} else if (ext === "css") {
ses.setMode("ace/mode/css");
}
})();
function setSyntax(lang) {
if (typeof(ACE) !== undefined) {
ACE.getSession().setMode("ace/mode/"+lang);
}
return false;
}
</script>
{{ end }} {{ end }}

View File

@ -13,6 +13,22 @@
</div> </div>
{{ end }} {{ end }}
<style type="text/css" media="screen">
#editor-box {
position: relative;
display: none;
width: 100%;
height: 500px;
}
#ace-editor {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
}
</style>
{{ with .Data.post }} {{ with .Data.post }}
<input type="hidden" name="id" value="{{ .ID }}"> <input type="hidden" name="id" value="{{ .ID }}">
<div class="card"> <div class="card">
@ -52,6 +68,7 @@
class="form-check-input" class="form-check-input"
name="content-type" name="content-type"
value="markdown" value="markdown"
onChange="setSyntax(this.value)"
{{ if eq .ContentType "markdown" }}checked{{ end }} {{ if eq .ContentType "markdown" }}checked{{ end }}
> Markdown > Markdown
</label> </label>
@ -62,15 +79,21 @@
class="form-check-input" class="form-check-input"
name="content-type" name="content-type"
value="html" value="html"
onChange="setSyntax(this.value)"
{{ if eq .ContentType "html" }}checked{{ end }} {{ if eq .ContentType "html" }}checked{{ end }}
> Raw HTML > Raw HTML
</label> </label>
</div> </div>
<div id="editor-box">
<div id="ace-editor">{{ .Body }}</div>
</div>
<textarea class="form-control" <textarea class="form-control"
cols="80" cols="80"
rows="12" rows="12"
name="body" name="body"
id="body"
placeholder="Post body goes here">{{ .Body }}</textarea> placeholder="Post body goes here">{{ .Body }}</textarea>
</div> </div>
@ -137,4 +160,38 @@
{{ end }} {{ end }}
</form> </form>
<script src="/js/ace-editor/src-min-noconflict/ace.js" type="text/javascript" charset="utf-8"></script>
<script>
var ACE;
(function() {
var editor = ace.edit("ace-editor");
ACE = editor;
document.querySelector("#editor-box").style.display = "block";
document.querySelector("#body").style.display = "none";
// Default editor settings
editor.setTheme("ace/theme/monokai");
var ses = editor.getSession();
ses.setTabSize(4);
ses.setUseSoftTabs(true);
// On save.
ses.on("change", function() {
document.getElementById("body").value = editor.getValue();
});
setSyntax("markdown");
})();
function setSyntax(lang) {
if (typeof(ACE) !== undefined) {
ACE.getSession().setMode("ace/mode/"+lang);
if (lang === "markdown") {
ACE.getSession().setTabSize(2);
}
}
return false;
}
</script>
{{ end }} {{ end }}