ACE Code Editor and Blog Random-Page Endpoint

This commit is contained in:
Noah 2020-02-17 21:21:09 -08:00
parent b642562792
commit 4d97fec246
4 changed files with 127 additions and 2 deletions

View File

@ -41,6 +41,11 @@ func init() {
Methods: []string{"GET"},
Handler: BlogArchive,
})
glue.Register(glue.Endpoint{
Path: "/blog/random",
Methods: []string{"GET"},
Handler: BlogRandom,
})
glue.Register(glue.Endpoint{
Path: "/blog/drafts",
Middleware: []mux.MiddlewareFunc{
@ -196,6 +201,17 @@ func BlogArchive(w http.ResponseWriter, r *http.Request) {
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"
func EditPost(w http.ResponseWriter, r *http.Request) {
v := responses.NewTemplateVars(w, r)

View File

@ -12,6 +12,7 @@ import (
"git.kirsle.net/apps/gophertype/pkg/console"
"git.kirsle.net/apps/gophertype/pkg/markdown"
"git.kirsle.net/apps/gophertype/pkg/rng"
"github.com/albrow/forms"
)
@ -85,6 +86,21 @@ func (m postMan) LoadFragment(fragment string) (Post, 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.
func (m postMan) GetIndexPosts(privacy string, page, perPage int) (PagedPosts, error) {
var pp = PagedPosts{

16
pkg/rng/rng.go Normal file
View File

@ -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)
}

View File

@ -15,6 +15,22 @@
{{ $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">
{{ CSRF }}
<input type="hidden" name="id" value="{{ $Post.ID }}">
@ -44,17 +60,38 @@
<div class="form-group">
<div class="float-right">
<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
</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
</label>
</div>
<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>
<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 class="form-group">
@ -141,7 +178,47 @@
</div>
</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>
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() {
// Image uploader.
let $link = document.querySelector("#attach-img-link");