ACE Code Editor and Blog Random-Page Endpoint
parent
b642562792
commit
4d97fec246
|
@ -41,6 +41,11 @@ func init() {
|
||||||
Methods: []string{"GET"},
|
Methods: []string{"GET"},
|
||||||
Handler: BlogArchive,
|
Handler: BlogArchive,
|
||||||
})
|
})
|
||||||
|
glue.Register(glue.Endpoint{
|
||||||
|
Path: "/blog/random",
|
||||||
|
Methods: []string{"GET"},
|
||||||
|
Handler: BlogRandom,
|
||||||
|
})
|
||||||
glue.Register(glue.Endpoint{
|
glue.Register(glue.Endpoint{
|
||||||
Path: "/blog/drafts",
|
Path: "/blog/drafts",
|
||||||
Middleware: []mux.MiddlewareFunc{
|
Middleware: []mux.MiddlewareFunc{
|
||||||
|
@ -196,6 +201,17 @@ func BlogArchive(w http.ResponseWriter, r *http.Request) {
|
||||||
responses.RenderTemplate(w, r, "_builtin/blog/archive.gohtml", v)
|
responses.RenderTemplate(w, r, "_builtin/blog/archive.gohtml", v)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// BlogRandom handles the /blog/random route and picks a random post.
|
||||||
|
func BlogRandom(w http.ResponseWriter, r *http.Request) {
|
||||||
|
post, err := models.Posts.LoadRandom(models.Public)
|
||||||
|
if err != nil {
|
||||||
|
responses.Error(w, r, http.StatusInternalServerError, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
responses.Redirect(w, r, "/"+post.Fragment)
|
||||||
|
}
|
||||||
|
|
||||||
// EditPost at "/blog/edit"
|
// EditPost at "/blog/edit"
|
||||||
func EditPost(w http.ResponseWriter, r *http.Request) {
|
func EditPost(w http.ResponseWriter, r *http.Request) {
|
||||||
v := responses.NewTemplateVars(w, r)
|
v := responses.NewTemplateVars(w, r)
|
||||||
|
|
|
@ -12,6 +12,7 @@ import (
|
||||||
|
|
||||||
"git.kirsle.net/apps/gophertype/pkg/console"
|
"git.kirsle.net/apps/gophertype/pkg/console"
|
||||||
"git.kirsle.net/apps/gophertype/pkg/markdown"
|
"git.kirsle.net/apps/gophertype/pkg/markdown"
|
||||||
|
"git.kirsle.net/apps/gophertype/pkg/rng"
|
||||||
"github.com/albrow/forms"
|
"github.com/albrow/forms"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -85,6 +86,21 @@ func (m postMan) LoadFragment(fragment string) (Post, error) {
|
||||||
return post, r.Error
|
return post, r.Error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// LoadRandom gets a random post for a given privacy setting.
|
||||||
|
func (m postMan) LoadRandom(privacy string) (Post, error) {
|
||||||
|
// Find all the post IDs.
|
||||||
|
var pp []Post
|
||||||
|
r := DB.Debug().Select("id").Where("privacy = ?", privacy).Find(&pp)
|
||||||
|
if r.Error != nil || len(pp) == 0 {
|
||||||
|
return Post{}, r.Error
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pick one at random.
|
||||||
|
randPost := pp[rng.Intn(len(pp))]
|
||||||
|
post, err := Posts.Load(randPost.ID)
|
||||||
|
return post, err
|
||||||
|
}
|
||||||
|
|
||||||
// GetIndex returns the index page of blog posts.
|
// GetIndex returns the index page of blog posts.
|
||||||
func (m postMan) GetIndexPosts(privacy string, page, perPage int) (PagedPosts, error) {
|
func (m postMan) GetIndexPosts(privacy string, page, perPage int) (PagedPosts, error) {
|
||||||
var pp = PagedPosts{
|
var pp = PagedPosts{
|
||||||
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
// Package rng provides the random number generator for the app.
|
||||||
|
package rng
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math/rand"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
rand.Seed(time.Now().UnixNano())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Intn returns a random number between 0 and n-1.
|
||||||
|
func Intn(v int) int {
|
||||||
|
return rand.Intn(v)
|
||||||
|
}
|
|
@ -15,6 +15,22 @@
|
||||||
|
|
||||||
{{ $Post := .V.post }}
|
{{ $Post := .V.post }}
|
||||||
|
|
||||||
|
<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>
|
||||||
|
|
||||||
<form method="POST" action="/blog/edit">
|
<form method="POST" action="/blog/edit">
|
||||||
{{ CSRF }}
|
{{ CSRF }}
|
||||||
<input type="hidden" name="id" value="{{ $Post.ID }}">
|
<input type="hidden" name="id" value="{{ $Post.ID }}">
|
||||||
|
@ -44,17 +60,38 @@
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="float-right">
|
<div class="float-right">
|
||||||
<label>
|
<label>
|
||||||
<input type="radio" name="content-type" value="markdown"{{ if ne $Post.ContentType "html" }} checked{{ end }}>
|
<input type="radio"
|
||||||
|
name="content-type"
|
||||||
|
value="markdown"
|
||||||
|
onChange="setSyntax(this.value)"
|
||||||
|
{{ if eq $Post.ContentType "markdown" }}checked{{ end }}
|
||||||
|
>
|
||||||
Markdown
|
Markdown
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
<label>
|
<label>
|
||||||
<input type="radio" name="content-type" value="html"{{ if eq $Post.ContentType "html" }} checked{{ end }}>
|
<input type="radio"
|
||||||
|
name="content-type"
|
||||||
|
value="html"
|
||||||
|
onChange="setSyntax(this.value)"
|
||||||
|
{{ if eq $Post.ContentType "html" }}checked{{ end }}
|
||||||
|
>
|
||||||
HTML
|
HTML
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<label for="body">Body</label>
|
<label for="body">Body</label>
|
||||||
|
|
||||||
|
<div id="editor-box">
|
||||||
|
<div id="ace-editor">{{ $Post.Body }}</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<textarea class="form-control" cols="40" rows="12" name="body" id="body">{{ $Post.Body }}</textarea>
|
<textarea class="form-control" cols="40" rows="12" name="body" id="body">{{ $Post.Body }}</textarea>
|
||||||
|
|
||||||
|
<div class="mt-2">
|
||||||
|
<button id="ace-toggle-button" type="button" class="btn btn-sm btn-secondary">
|
||||||
|
Toggle Rich Code Editor
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
|
@ -141,7 +178,47 @@
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
|
<!-- ACE Code Editor and helper code -->
|
||||||
|
<script src="/_builtin/js/ace-toggle.js"></script>
|
||||||
|
<script src="/_builtin/js/ace-1.4.8/src-min-noconflict/ace.js" charset="utf-8"></script>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
var ACE;
|
||||||
|
(function() {
|
||||||
|
console.log("BEGIN: %s", DISABLE_ACE_EDITOR);
|
||||||
|
if (DISABLE_ACE_EDITOR) return;
|
||||||
|
|
||||||
|
let editor = ace.edit("ace-editor");
|
||||||
|
ACE = editor;
|
||||||
|
document.querySelector("#editor-box").style.display = "block";
|
||||||
|
document.querySelector("#body").style.display = "none";
|
||||||
|
|
||||||
|
console.log("HERE");
|
||||||
|
|
||||||
|
// Default editor settings.
|
||||||
|
editor.setTheme("ace/theme/monokai");
|
||||||
|
let ses = editor.getSession();
|
||||||
|
ses.setTabSize(4);
|
||||||
|
ses.setUseSoftTabs(false);
|
||||||
|
ses.setUseWrapMode(true);
|
||||||
|
|
||||||
|
// On save.
|
||||||
|
ses.on("change", () => {
|
||||||
|
document.querySelector("#body").value = editor.getValue();
|
||||||
|
});
|
||||||
|
setSyntax("markdown");
|
||||||
|
})();
|
||||||
|
|
||||||
|
// Set syntax for ACE.
|
||||||
|
function setSyntax(lang) {
|
||||||
|
if (typeof(ACE) !== undefined) {
|
||||||
|
let ses = ACE.getSession();
|
||||||
|
ses.setMode("ace/mode/"+lang);
|
||||||
|
ses.setTabSize(lang === "markdown" ? 2 : 4);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
(function() {
|
(function() {
|
||||||
// Image uploader.
|
// Image uploader.
|
||||||
let $link = document.querySelector("#attach-img-link");
|
let $link = document.querySelector("#attach-img-link");
|
||||||
|
|
Loading…
Reference in New Issue