mirror of
https://github.com/schollz/cowyo.git
synced 2023-08-10 21:13:00 +03:00
Added locking. Bumped version
Former-commit-id: 09543cf9d15b734ed48d24eb2fe30ad2ecc96cbb [formerly 793f2ff37d1dea630bd254643372c25f64603ff5] [formerly b4d60e8cd53aad403d8d0d7bf6324fb95631ebbd [formerly 6b526e4327
]]
Former-commit-id: f1d0cc3a5199cfccab35243e9b6d75de3935f9fa [formerly bd9c741701aecb0a99a23e64b9c5a34e199281fb]
Former-commit-id: 56d6dcb02e92b18d69c2a1fdbb404be01bfa3ee7
This commit is contained in:
parent
17f04a167f
commit
0b2928468b
24
README.md
24
README.md
@ -2,37 +2,49 @@
|
||||
|
||||
# AwwKoala - [Demo](http://awwkoala.com/)
|
||||
## A Websocket Wiki and Kind Of A List Application
|
||||
[![Version 0.93](https://img.shields.io/badge/version-0.93-brightgreen.svg)]() [![Go Report Card](https://goreportcard.com/badge/github.com/schollz/AwwKoala)](https://goreportcard.com/report/github.com/schollz/AwwKoala) [![Join the chat at https://gitter.im/schollz/AwwKoala](https://badges.gitter.im/schollz/AwwKoala.svg)](https://gitter.im/schollz/AwwKoala?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
||||
[![Version 0.94](https://img.shields.io/badge/version-0.94-brightgreen.svg)]() [![Go Report Card](https://goreportcard.com/badge/github.com/schollz/AwwKoala)](https://goreportcard.com/report/github.com/schollz/AwwKoala) [![Join the chat at https://gitter.im/schollz/AwwKoala](https://badges.gitter.im/schollz/AwwKoala.svg)](https://gitter.im/schollz/AwwKoala?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
||||
|
||||
This is a self-contained wiki webserver that makes sharing easy and _fast_. You can make any page you want, and any page is editable by anyone. Pages load instantly for editing, and have special rendering for whether you want to view as a web page or view as list.
|
||||
This is a self-contained wiki webserver that makes sharing easy and _fast_. The most important feature here is *simplicity*. There are many other features as well including versioning, page locking, self-destructing messages, encryption, math support, and listifying. Read on to learn more about the features.
|
||||
|
||||
# Features
|
||||
**Simplicity**. The philosophy here is to *just type*. To jot a note, simply load the page at [`/`](http://AwwKoala.com/) and just start typing. No need to press edit, the browser will already be focused on the text. No need to press save - it will automatically save when you stop writing. The URL at [`/`](http://AwwKoala.com/) will redirect to an easy-to-remember name that you can use to reload the page at anytime, anywhere. But, you can also use any URL you want, e.g. [`/AnythingYouWant`](http://AwwKoala.com/AnythingYouWant).
|
||||
|
||||
**Viewing**. All pages can be rendered into HTML by adding `/view`. For example, the page [`/AnythingYouWant`](http://AwwKoala.com/AnythingYouWant) is rendered at [`/AnythingYouWant/view`](http://AwwKoala.com/AnythingYouWant/view). You can write in HTML or [Markdown](https://daringfireball.net/projects/markdown/) for page rendering. To quickly link to `/view` pages, just use `[[AnythingYouWnat]]`. Math is supported with [Katex](https://github.com/Khan/KaTeX) using `$\frac{1}{2}$` for inline equations and `$$\frac{1}{2}$$` for regular equations.
|
||||
**Simplicity**. The philosophy here is to *just type*. To jot a note, simply load the page at [`/`](http://AwwKoala.com/) and just start typing. No need to press edit, the browser will already be focused on the text. No need to press save - it will automatically save when you stop writing. The URL at [`/`](http://AwwKoala.com/) will redirect to an easy-to-remember name that you can use to reload the page at anytime, anywhere. But, you can also use any URL you want, e.g. [`/AnythingYouWant`](http://AwwKoala.com/AnythingYouWant). All pages can be rendered into HTML by adding `/view`. For example, the page [`/AnythingYouWant`](http://AwwKoala.com/AnythingYouWant) is rendered at [`/AnythingYouWant/view`](http://AwwKoala.com/AnythingYouWant/view). You can write in HTML or [Markdown](https://daringfireball.net/projects/markdown/) for page rendering. To quickly link to `/view` pages, just use `[[AnythingYouWnat]]`.
|
||||
|
||||
![Simply type to edit.](https://raw.githubusercontent.com/schollz/awwkoala/master/static/img/Main1.gif)
|
||||
|
||||
<br>
|
||||
|
||||
**Listifying**. If you are writing a list and you want to tick off things really easily, just add `/list`. For example, after editing [`/grocery`](http://AwwKoala.com/grocery), goto [`/grocery/list`](http://AwwKoala.com/grocery/list). In this page, whatever you click on will be struck through and moved to the end. This is helpful if you write a grocery list and then want to easily delete things from it.
|
||||
|
||||
![Lists are easy to make.](https://raw.githubusercontent.com/schollz/awwkoala/master/static/img/Main3.gif)
|
||||
|
||||
<br>
|
||||
|
||||
**Automatic versioning**. All previous versions of all notes are stored and can be accessed by adding `?version=X` onto `/view` or `/edit`. If you are on the `/view` or `/edit` pages the menu below will show the most substantial changes in the history. Note, only the _current_ version can be edited (no branching allowed, yet).
|
||||
|
||||
![Versioning is easy.](https://raw.githubusercontent.com/schollz/awwkoala/master/static/img/Main2.gif)
|
||||
|
||||
<br>
|
||||
|
||||
**Self-destructing messages**. You can write a message that will delete itself when a user loads it (in any view). Useful for transmitting sensitive information. To use, simply add a line somewhere that says only "`self-destruct`".
|
||||
|
||||
![Simply type to edit.](https://raw.githubusercontent.com/schollz/awwkoala/master/static/img/Main4.gif)
|
||||
|
||||
**Security**. HTTPS support is provided and everything is sanitized to prevent XSS attacks. Though all URLs are publicly accessible, you are free to obfuscate your website by using an obscure/random address (read: the site is still publicly accessible, just hard to find!). In addition to TLS support, you can PGP-encrypt your messages using a passphrase.
|
||||
|
||||
<br>
|
||||
|
||||
**Security**. HTTPS support is provided and everything is sanitized to prevent XSS attacks. Though all URLs are publicly accessible, you are free to obfuscate your website by using an obscure/random address (read: the site is still publicly accessible, just hard to find!). In addition to TLS support, you can PGP-encrypt your messages using a passphrase (_Note: This will delete the version tree_).
|
||||
|
||||
![Simply type to edit.](https://raw.githubusercontent.com/schollz/awwkoala/master/static/img/Main7.gif)
|
||||
|
||||
<br>
|
||||
|
||||
**Page locking**. You can apply a password to a page to allow further edits from being available. The whole version tree will still be available. _Note_: This is not available for list mode.
|
||||
|
||||
**Keyboard Shortcuts**. Quickly transition between Edit/View/List by using `Ctl+Shift+E` to Edit, `Ctl+Shift+Z` to View, and `Ctl+Shift+L` to Listify.
|
||||
|
||||
**Admin controls**. The Admin can view/delete all the documents by setting the `-a YourAdminKey` when starting the program. Then the admin has access to the `/ls/YourAdminKey` to view and delete any of the pages.
|
||||
|
||||
**Math support**. Math is supported with [Katex](https://github.com/Khan/KaTeX) using `$\frac{1}{2}$` for inline equations and `$$\frac{1}{2}$$` for regular equations.
|
||||
|
||||
|
||||
|
||||
|
9
db.go
9
db.go
@ -40,6 +40,7 @@ type WikiData struct {
|
||||
Diffs []string
|
||||
Timestamps []string
|
||||
Encrypted bool
|
||||
Locked string
|
||||
}
|
||||
|
||||
func hasPassword(title string) (bool, error) {
|
||||
@ -79,7 +80,7 @@ func hasPassword(title string) (bool, error) {
|
||||
return hasPassword, nil
|
||||
}
|
||||
|
||||
func getCurrentText(title string, version int) (string, []versionsInfo, bool, time.Duration, bool) {
|
||||
func getCurrentText(title string, version int) (string, []versionsInfo, bool, time.Duration, bool, string) {
|
||||
Open(RuntimeArgs.DatabaseLocation)
|
||||
defer Close()
|
||||
title = strings.ToLower(title)
|
||||
@ -88,8 +89,9 @@ func getCurrentText(title string, version int) (string, []versionsInfo, bool, ti
|
||||
isCurrent := true
|
||||
currentText := ""
|
||||
encrypted := false
|
||||
locked := ""
|
||||
if !open {
|
||||
return currentText, vi, isCurrent, totalTime, encrypted
|
||||
return currentText, vi, isCurrent, totalTime, encrypted, locked
|
||||
}
|
||||
err := db.View(func(tx *bolt.Tx) error {
|
||||
var err error
|
||||
@ -109,6 +111,7 @@ func getCurrentText(title string, version int) (string, []versionsInfo, bool, ti
|
||||
}
|
||||
currentText = p.CurrentText
|
||||
encrypted = p.Encrypted
|
||||
locked = p.Locked
|
||||
if version > -1 && version < len(p.Diffs) {
|
||||
// get that version of text instead
|
||||
currentText = rebuildTextsToDiffN(p, version)
|
||||
@ -121,7 +124,7 @@ func getCurrentText(title string, version int) (string, []versionsInfo, bool, ti
|
||||
if err != nil {
|
||||
fmt.Printf("Could not get WikiData: %s", err)
|
||||
}
|
||||
return currentText, vi, isCurrent, totalTime, encrypted
|
||||
return currentText, vi, isCurrent, totalTime, encrypted, locked
|
||||
}
|
||||
|
||||
func (p *WikiData) load(title string) error {
|
||||
|
4
main.go
4
main.go
@ -35,7 +35,7 @@ var RuntimeArgs struct {
|
||||
var VersionNum string
|
||||
|
||||
func main() {
|
||||
VersionNum = "0.93"
|
||||
VersionNum = "0.94"
|
||||
// _, executableFile, _, _ := runtime.Caller(0) // get full path of this file
|
||||
cwd, _ := os.Getwd()
|
||||
databaseFile := path.Join(cwd, "data.db")
|
||||
@ -88,7 +88,7 @@ Options:`)
|
||||
|
||||
// Default page
|
||||
aboutFile, _ := ioutil.ReadFile(path.Join(RuntimeArgs.SourcePath, "templates/aboutpage.md"))
|
||||
p := WikiData{"help", "", []string{}, []string{}, false}
|
||||
p := WikiData{"help", "", []string{}, []string{}, false, "zzz"}
|
||||
p.save(string(aboutFile))
|
||||
|
||||
// var q WikiData
|
||||
|
99
routes.go
99
routes.go
@ -31,7 +31,7 @@ func encryptionRoute(c *gin.Context) {
|
||||
if option == "/decrypt" {
|
||||
if c.BindJSON(&jsonLoad) == nil {
|
||||
var err error
|
||||
currentText, _, _, _, encrypted := getCurrentText(title, -1)
|
||||
currentText, _, _, _, encrypted, _ := getCurrentText(title, -1)
|
||||
if encrypted == true {
|
||||
currentText, err = decryptString(currentText, jsonLoad.Password)
|
||||
if err != nil {
|
||||
@ -42,7 +42,7 @@ func encryptionRoute(c *gin.Context) {
|
||||
"success": false,
|
||||
})
|
||||
} else {
|
||||
p := WikiData{strings.ToLower(title), "", []string{}, []string{}, false}
|
||||
p := WikiData{strings.ToLower(title), "", []string{}, []string{}, false, ""}
|
||||
p.save(currentText)
|
||||
c.JSON(200, gin.H{
|
||||
"status": "posted",
|
||||
@ -63,7 +63,7 @@ func encryptionRoute(c *gin.Context) {
|
||||
}
|
||||
if option == "/encrypt" {
|
||||
if c.BindJSON(&jsonLoad) == nil {
|
||||
p := WikiData{strings.ToLower(title), "", []string{}, []string{}, true}
|
||||
p := WikiData{strings.ToLower(title), "", []string{}, []string{}, true, ""}
|
||||
p.save(encryptString(jsonLoad.Text, jsonLoad.Password))
|
||||
c.JSON(200, gin.H{
|
||||
"status": "posted",
|
||||
@ -80,6 +80,63 @@ func encryptionRoute(c *gin.Context) {
|
||||
})
|
||||
}
|
||||
}
|
||||
if option == "/lock" {
|
||||
if c.BindJSON(&jsonLoad) == nil {
|
||||
var p WikiData
|
||||
err := p.load(strings.ToLower(title))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
p.Locked = jsonLoad.Password
|
||||
p.save(p.CurrentText)
|
||||
c.JSON(200, gin.H{
|
||||
"status": "posted",
|
||||
"title": title,
|
||||
"option": option,
|
||||
"success": true,
|
||||
})
|
||||
} else {
|
||||
c.JSON(200, gin.H{
|
||||
"status": "posted",
|
||||
"title": title,
|
||||
"option": option,
|
||||
"success": false,
|
||||
})
|
||||
}
|
||||
}
|
||||
if option == "/unlock" {
|
||||
if c.BindJSON(&jsonLoad) == nil {
|
||||
var p WikiData
|
||||
err := p.load(strings.ToLower(title))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if len(p.Locked) > 0 && p.Locked == jsonLoad.Password {
|
||||
p.Locked = ""
|
||||
p.save(p.CurrentText)
|
||||
c.JSON(200, gin.H{
|
||||
"status": "Unlocked!",
|
||||
"title": title,
|
||||
"option": option,
|
||||
"success": true,
|
||||
})
|
||||
} else {
|
||||
c.JSON(200, gin.H{
|
||||
"status": "Incorrect password!",
|
||||
"title": title,
|
||||
"option": option,
|
||||
"success": false,
|
||||
})
|
||||
}
|
||||
} else {
|
||||
c.JSON(200, gin.H{
|
||||
"status": "posted",
|
||||
"title": title,
|
||||
"option": option,
|
||||
"success": false,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -103,8 +160,8 @@ func editNote(c *gin.Context) {
|
||||
} else {
|
||||
version := c.DefaultQuery("version", "-1")
|
||||
versionNum, _ := strconv.Atoi(version)
|
||||
currentText, versions, currentVersion, totalTime, encrypted := getCurrentText(title, versionNum)
|
||||
if encrypted {
|
||||
currentText, versions, currentVersion, totalTime, encrypted, locked := getCurrentText(title, versionNum)
|
||||
if encrypted || len(locked) > 0 {
|
||||
c.Redirect(302, "/"+title+"/view")
|
||||
}
|
||||
if strings.Contains(currentText, "self-destruct\n") || strings.Contains(currentText, "\nself-destruct") {
|
||||
@ -141,16 +198,16 @@ func everythingElse(c *gin.Context) {
|
||||
if strings.ToLower(title) == "help" {
|
||||
versionNum = -1
|
||||
}
|
||||
currentText, versions, _, totalTime, encrypted := getCurrentText(title, versionNum)
|
||||
currentText, versions, _, totalTime, encrypted, locked := getCurrentText(title, versionNum)
|
||||
if (strings.Contains(currentText, "self-destruct\n") || strings.Contains(currentText, "\nself-destruct")) && strings.ToLower(title) != "help" {
|
||||
currentText = strings.Replace(currentText, "self-destruct\n", `> *This page has been deleted, you cannot return after closing.*`+"\n", 1)
|
||||
currentText = strings.Replace(currentText, "\nself-destruct", "\n"+`> *This page has been deleted, you cannot return after closing.*`, 1)
|
||||
p := WikiData{strings.ToLower(title), "", []string{}, []string{}, false}
|
||||
p := WikiData{strings.ToLower(title), "", []string{}, []string{}, false, ""}
|
||||
p.save("")
|
||||
}
|
||||
renderMarkdown(c, currentText, title, versions, "", totalTime, encrypted, noprompt == "-1")
|
||||
renderMarkdown(c, currentText, title, versions, "", totalTime, encrypted, noprompt == "-1", len(locked) > 0)
|
||||
} else if title == "ls" && option == "/"+RuntimeArgs.AdminKey && len(RuntimeArgs.AdminKey) > 1 {
|
||||
renderMarkdown(c, listEverything(), "ls", nil, RuntimeArgs.AdminKey, time.Now().Sub(time.Now()), false, false)
|
||||
renderMarkdown(c, listEverything(), "ls", nil, RuntimeArgs.AdminKey, time.Now().Sub(time.Now()), false, false, false)
|
||||
} else if option == "/list" {
|
||||
renderList(c, title)
|
||||
} else if title == "static" {
|
||||
@ -169,7 +226,7 @@ func serveStaticFile(c *gin.Context, option string) {
|
||||
}
|
||||
}
|
||||
|
||||
func renderMarkdown(c *gin.Context, currentText string, title string, versions []versionsInfo, AdminKey string, totalTime time.Duration, encrypted bool, noprompt bool) {
|
||||
func renderMarkdown(c *gin.Context, currentText string, title string, versions []versionsInfo, AdminKey string, totalTime time.Duration, encrypted bool, noprompt bool, locked bool) {
|
||||
r, _ := regexp.Compile("\\[\\[(.*?)\\]\\]")
|
||||
for _, s := range r.FindAllString(currentText, -1) {
|
||||
currentText = strings.Replace(currentText, s, "["+s[2:len(s)-2]+"](/"+s[2:len(s)-2]+"/view)", 1)
|
||||
@ -203,14 +260,16 @@ func renderMarkdown(c *gin.Context, currentText string, title string, versions [
|
||||
totalTimeString = "< 1 s"
|
||||
}
|
||||
c.HTML(http.StatusOK, "view.tmpl", gin.H{
|
||||
"Title": title,
|
||||
"WikiName": RuntimeArgs.WikiName,
|
||||
"Body": template.HTML([]byte(html2)),
|
||||
"Versions": versions,
|
||||
"TotalTime": totalTimeString,
|
||||
"AdminKey": AdminKey,
|
||||
"Encrypted": encrypted,
|
||||
"Prompt": noprompt,
|
||||
"Title": title,
|
||||
"WikiName": RuntimeArgs.WikiName,
|
||||
"Body": template.HTML([]byte(html2)),
|
||||
"Versions": versions,
|
||||
"TotalTime": totalTimeString,
|
||||
"AdminKey": AdminKey,
|
||||
"Encrypted": encrypted,
|
||||
"Locked": locked,
|
||||
"Prompt": noprompt,
|
||||
"LockedOrEncrypted": locked || encrypted,
|
||||
})
|
||||
|
||||
}
|
||||
@ -270,7 +329,7 @@ func renderList(c *gin.Context, title string) {
|
||||
if strings.Contains(currentText, "self-destruct\n") || strings.Contains(currentText, "\nself-destruct") {
|
||||
c.Redirect(302, "/"+title+"/view")
|
||||
}
|
||||
if p.Encrypted {
|
||||
if p.Encrypted || len(p.Locked) > 0 {
|
||||
c.Redirect(302, "/"+title+"/view")
|
||||
}
|
||||
|
||||
@ -336,7 +395,7 @@ func deletePage(c *gin.Context) {
|
||||
fmt.Println(deleteName)
|
||||
// if adminKey == RuntimeArgs.AdminKey || true == true {
|
||||
if strings.ToLower(deleteName) != "help" {
|
||||
p := WikiData{strings.ToLower(deleteName), "", []string{}, []string{}, false}
|
||||
p := WikiData{strings.ToLower(deleteName), "", []string{}, []string{}, false, ""}
|
||||
p.save("")
|
||||
}
|
||||
// // remove from program data
|
||||
|
@ -2,35 +2,50 @@
|
||||
|
||||
# AwwKoala
|
||||
## A Websocket Wiki and Kind Of A List Application
|
||||
![Version 0.93](https://img.shields.io/badge/version-0.92-brightgreen.svg)
|
||||
![Version 0.94](https://img.shields.io/badge/version-0.94-brightgreen.svg)
|
||||
|
||||
This is a self-contained wiki webserver that makes sharing easy and _fast_. You can make any page you want, and any page is editable by anyone. Pages load instantly for editing, and have special rendering for whether you want to view as a web page or view as list. **AwwKoala** is also [Open Source](https://github.com/schollz/AwwKoala).
|
||||
This is a self-contained wiki webserver that makes sharing easy and _fast_. The most important feature here is *simplicity*. There are many other features as well including versioning, page locking, self-destructing messages, encryption, math support, and listifying. Read on to learn more about the features. **AwwKoala** is also [Open Source](https://github.com/schollz/AwwKoala).
|
||||
|
||||
## Features
|
||||
**Simplicity**. The philosophy here is to *just type*. To jot a note, simply load the page at [`/`](http://AwwKoala.com/) and just start typing. No need to press edit, the browser will already be focused on the text. No need to press save - it will automatically save when you stop writing. The URL at [`/`](http://AwwKoala.com/) will redirect to an easy-to-remember name that you can use to reload the page at anytime, anywhere. But, you can also use any URL you want, e.g. [`/AnythingYouWant`](http://AwwKoala.com/AnythingYouWant).
|
||||
|
||||
**Viewing**. All pages can be rendered into HTML by adding `/view`. For example, the page [`/AnythingYouWant`](http://AwwKoala.com/AnythingYouWant) is rendered at [`/AnythingYouWant/view`](http://AwwKoala.com/AnythingYouWant/view). You can write in HTML or [Markdown](https://daringfireball.net/projects/markdown/) for page rendering. To quickly link to `/view` pages, just use `[[AnythingYouWnat]]`. Math is supported with [Katex](https://github.com/Khan/KaTeX) using `$\frac{1}{2}$` for inline equations and `$$\frac{1}{2}$$` for regular equations.
|
||||
**Simplicity**. The philosophy here is to *just type*. To jot a note, simply load the page at [`/`](http://AwwKoala.com/) and just start typing. No need to press edit, the browser will already be focused on the text. No need to press save - it will automatically save when you stop writing. The URL at [`/`](http://AwwKoala.com/) will redirect to an easy-to-remember name that you can use to reload the page at anytime, anywhere. But, you can also use any URL you want, e.g. [`/AnythingYouWant`](http://AwwKoala.com/AnythingYouWant). All pages can be rendered into HTML by adding `/view`. For example, the page [`/AnythingYouWant`](http://AwwKoala.com/AnythingYouWant) is rendered at [`/AnythingYouWant/view`](http://AwwKoala.com/AnythingYouWant/view). You can write in HTML or [Markdown](https://daringfireball.net/projects/markdown/) for page rendering. To quickly link to `/view` pages, just use `[[AnythingYouWnat]]`.
|
||||
|
||||
![Simply type to edit.](https://raw.githubusercontent.com/schollz/awwkoala/master/static/img/Main1.gif)
|
||||
|
||||
<br>
|
||||
|
||||
**Listifying**. If you are writing a list and you want to tick off things really easily, just add `/list`. For example, after editing [`/grocery`](http://AwwKoala.com/grocery), goto [`/grocery/list`](http://AwwKoala.com/grocery/list). In this page, whatever you click on will be struck through and moved to the end. This is helpful if you write a grocery list and then want to easily delete things from it.
|
||||
|
||||
![Lists are easy to make.](https://raw.githubusercontent.com/schollz/awwkoala/master/static/img/Main3.gif)
|
||||
|
||||
<br>
|
||||
|
||||
**Automatic versioning**. All previous versions of all notes are stored and can be accessed by adding `?version=X` onto `/view` or `/edit`. If you are on the `/view` or `/edit` pages the menu below will show the most substantial changes in the history. Note, only the _current_ version can be edited (no branching allowed, yet).
|
||||
|
||||
![Versioning is easy.](https://raw.githubusercontent.com/schollz/awwkoala/master/static/img/Main2.gif)
|
||||
|
||||
<br>
|
||||
|
||||
**Self-destructing messages**. You can write a message that will delete itself when a user loads it (in any view). Useful for transmitting sensitive information. To use, simply add a line somewhere that says only "`self-destruct`".
|
||||
|
||||
![Simply type to edit.](https://raw.githubusercontent.com/schollz/awwkoala/master/static/img/Main4.gif)
|
||||
|
||||
|
||||
<br>
|
||||
|
||||
**Security**. HTTPS support is provided and everything is sanitized to prevent XSS attacks. Though all URLs are publicly accessible, you are free to obfuscate your website by using an obscure/random address (read: the site is still publicly accessible, just hard to find!). In addition to TLS support, you can PGP-encrypt your messages using a passphrase.
|
||||
|
||||
![Simply type to edit.](https://raw.githubusercontent.com/schollz/awwkoala/master/static/img/Main7.gif)
|
||||
|
||||
<br>
|
||||
|
||||
**Page locking**. You can apply a password to a page to allow further edits from being available. The whole version tree will still be available. _Note_: This is not available for list mode.
|
||||
|
||||
**Keyboard Shortcuts**. Quickly transition between Edit/View/List by using `Ctl+Shift+E` to Edit, `Ctl+Shift+Z` to View, and `Ctl+Shift+L` to Listify.
|
||||
|
||||
**Admin controls**. The Admin can view/delete all the documents by setting the `-a YourAdminKey` when starting the program. Then the admin has access to the `/ls/YourAdminKey` to view and delete any of the pages.
|
||||
|
||||
**Math support**. Math is supported with [Katex](https://github.com/Khan/KaTeX) using `$\frac{1}{2}$` for inline equations and `$$\frac{1}{2}$$` for regular equations.
|
||||
|
||||
|
||||
# Contact
|
||||
Any other comments, questions or anything at all, just <a href="https://twitter.com/intent/tweet?screen_name=zack_118" class="twitter-mention-button" data-related="zack_118">tweet me @zack_118</a>
|
||||
|
@ -86,6 +86,7 @@
|
||||
<li role="separator" class="divider"></li>
|
||||
<li class="dropdown-header">Options</li>
|
||||
<li><a href="#" class="postencrypt">Encrypt</a></li>
|
||||
<li><a href="#" class="postlock">Lock</a></li>
|
||||
<li><a href="#" id="{{ .Title }}" class="deleteable">Erase</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
@ -126,28 +127,13 @@
|
||||
});
|
||||
|
||||
|
||||
$('.postencrypt').click(function(event) {
|
||||
var pass1 = "";
|
||||
var pass2 = "";
|
||||
event.preventDefault();
|
||||
swal({
|
||||
title: "Encryption",
|
||||
text: "Enter your passphrase:",
|
||||
type: "input",
|
||||
showCancelButton: true,
|
||||
closeOnConfirm: false,
|
||||
animation: "slide-from-top",
|
||||
inputPlaceholder: "Write something"
|
||||
}, function(inputValue) {
|
||||
if (inputValue === false) return false;
|
||||
if (inputValue === "") {
|
||||
swal.showInputError("You need to write something!");
|
||||
return false
|
||||
}
|
||||
pass1 = inputValue;
|
||||
$('.postencrypt').click(function(event) {
|
||||
var pass1 = "";
|
||||
var pass2 = "";
|
||||
event.preventDefault();
|
||||
swal({
|
||||
title: "Encryption",
|
||||
text: "Enter your passphrase again:",
|
||||
text: "Enter your passphrase:",
|
||||
type: "input",
|
||||
showCancelButton: true,
|
||||
closeOnConfirm: false,
|
||||
@ -159,33 +145,112 @@
|
||||
swal.showInputError("You need to write something!");
|
||||
return false
|
||||
}
|
||||
pass2 = inputValue
|
||||
if (pass1 == pass2) {
|
||||
swal("Encryption", "Passwords match!", "success");
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
//the url where you want to sent the userName and password to
|
||||
url: '/{{ .Title }}/encrypt',
|
||||
dataType: 'json',
|
||||
data: JSON.stringify({
|
||||
text: $('#emit_data').val(),
|
||||
password: pass1
|
||||
}),
|
||||
success: function (data) {
|
||||
if (data['success'] == true) {
|
||||
swal("Encryption", "Encrypted!", "success");
|
||||
window.location.href = '/{{ .Title }}/view?noprompt=1';
|
||||
} else {
|
||||
swal("Encryption", "Something went wrong.", "error");
|
||||
}
|
||||
}
|
||||
pass1 = inputValue;
|
||||
swal({
|
||||
title: "Encryption",
|
||||
text: "Enter your passphrase again:",
|
||||
type: "input",
|
||||
showCancelButton: true,
|
||||
closeOnConfirm: false,
|
||||
animation: "slide-from-top",
|
||||
inputPlaceholder: "Write something"
|
||||
}, function(inputValue) {
|
||||
if (inputValue === false) return false;
|
||||
if (inputValue === "") {
|
||||
swal.showInputError("You need to write something!");
|
||||
return false
|
||||
}
|
||||
pass2 = inputValue
|
||||
if (pass1 == pass2) {
|
||||
swal("Encryption", "Passwords match!", "success");
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
//the url where you want to sent the userName and password to
|
||||
url: '/{{ .Title }}/encrypt',
|
||||
dataType: 'json',
|
||||
data: JSON.stringify({
|
||||
text: $('#emit_data').val(),
|
||||
password: pass1
|
||||
}),
|
||||
success: function (data) {
|
||||
if (data['success'] == true) {
|
||||
swal("Encryption", "Encrypted!", "success");
|
||||
window.location.href = '/{{ .Title }}/view?noprompt=1';
|
||||
} else {
|
||||
swal("Encryption", "Something went wrong.", "error");
|
||||
}
|
||||
}
|
||||
});
|
||||
} else {
|
||||
swal("Encryption", "Passwords do not match.", "error");
|
||||
}
|
||||
});
|
||||
} else {
|
||||
swal("Encryption", "Passwords do not match.", "error");
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
$('.postlock').click(function(event) {
|
||||
var pass1 = "";
|
||||
var pass2 = "";
|
||||
event.preventDefault();
|
||||
swal({
|
||||
title: "Locking",
|
||||
text: "Enter your passphrase:",
|
||||
type: "input",
|
||||
showCancelButton: true,
|
||||
closeOnConfirm: false,
|
||||
animation: "slide-from-top",
|
||||
inputPlaceholder: "Write something"
|
||||
}, function(inputValue) {
|
||||
if (inputValue === false) return false;
|
||||
if (inputValue === "") {
|
||||
swal.showInputError("You need to write something!");
|
||||
return false
|
||||
}
|
||||
pass1 = inputValue;
|
||||
swal({
|
||||
title: "Locking",
|
||||
text: "Enter your passphrase again:",
|
||||
type: "input",
|
||||
showCancelButton: true,
|
||||
closeOnConfirm: false,
|
||||
animation: "slide-from-top",
|
||||
inputPlaceholder: "Write something"
|
||||
}, function(inputValue) {
|
||||
if (inputValue === false) return false;
|
||||
if (inputValue === "") {
|
||||
swal.showInputError("You need to write something!");
|
||||
return false
|
||||
}
|
||||
pass2 = inputValue
|
||||
if (pass1 == pass2) {
|
||||
swal("Locking", "Passwords match!", "success");
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
//the url where you want to sent the userName and password to
|
||||
url: '/{{ .Title }}/lock',
|
||||
dataType: 'json',
|
||||
data: JSON.stringify({
|
||||
text: $('#emit_data').val(),
|
||||
password: pass1
|
||||
}),
|
||||
success: function (data) {
|
||||
if (data['success'] == true) {
|
||||
swal("Locking", "Page locked!", "success");
|
||||
window.location.href = '/{{ .Title }}/view';
|
||||
} else {
|
||||
swal("Locking", "Something went wrong.", "error");
|
||||
}
|
||||
}
|
||||
});
|
||||
} else {
|
||||
swal("Encryption", "Passwords do not match.", "error");
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
$('.deleteable').click(function(event) {
|
||||
event.preventDefault();
|
||||
|
@ -46,9 +46,14 @@ a.deleteable {
|
||||
<li><a href="/{{ $.Title }}/view?version={{ .VersionNum }}">{{ .VersionDate }}</a></li>
|
||||
{{ end }}
|
||||
<li><a href="/{{ .Title }}/view">Current</a></li>
|
||||
{{ if .Encrypted }}
|
||||
{{ if .LockedOrEncrypted }}
|
||||
<li class="dropdown-header">Options</li>
|
||||
{{ if .Encrypted }}
|
||||
<li><a href="#" class="postdecrypt">Decrypt</a></li>
|
||||
{{end }}
|
||||
{{ if .Locked }}
|
||||
<li><a href="#" class="postunlock">Unlock</a></li>
|
||||
{{end }}
|
||||
{{ end }}
|
||||
</ul>
|
||||
</li>
|
||||
@ -73,83 +78,125 @@ a.deleteable {
|
||||
<script>
|
||||
$( document ).ready(function() {
|
||||
|
||||
{{ if .Encrypted }}
|
||||
{{ if .Prompt }}
|
||||
var pass1 = "";
|
||||
swal({
|
||||
title: "Decryption",
|
||||
text: "Enter your passphrase:",
|
||||
type: "input",
|
||||
showCancelButton: true,
|
||||
closeOnConfirm: false,
|
||||
animation: "slide-from-top",
|
||||
inputPlaceholder: "Write something"
|
||||
}, function(inputValue) {
|
||||
if (inputValue === false) return false;
|
||||
if (inputValue === "") {
|
||||
swal.showInputError("You need to write something!");
|
||||
return false
|
||||
}
|
||||
pass1 = inputValue;
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
//the url where you want to sent the userName and password to
|
||||
url: '/{{ .Title }}/decrypt',
|
||||
dataType: 'json',
|
||||
data: JSON.stringify({
|
||||
text: " ",
|
||||
password: pass1
|
||||
}),
|
||||
success: function (data) {
|
||||
if (data['success'] == true) {
|
||||
swal("Decryption", "Decrypted!", "success");
|
||||
window.location.href = '/{{ .Title }}/view';
|
||||
} else {
|
||||
swal("Decryption", data['status'], "error");
|
||||
{{ if .Encrypted }}
|
||||
{{ if .Prompt }}
|
||||
var pass1 = "";
|
||||
swal({
|
||||
title: "Decryption",
|
||||
text: "Enter your passphrase:",
|
||||
type: "input",
|
||||
showCancelButton: true,
|
||||
closeOnConfirm: false,
|
||||
animation: "slide-from-top",
|
||||
inputPlaceholder: "Write something"
|
||||
}, function(inputValue) {
|
||||
if (inputValue === false) return false;
|
||||
if (inputValue === "") {
|
||||
swal.showInputError("You need to write something!");
|
||||
return false
|
||||
}
|
||||
pass1 = inputValue;
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
//the url where you want to sent the userName and password to
|
||||
url: '/{{ .Title }}/decrypt',
|
||||
dataType: 'json',
|
||||
data: JSON.stringify({
|
||||
text: " ",
|
||||
password: pass1
|
||||
}),
|
||||
success: function (data) {
|
||||
if (data['success'] == true) {
|
||||
swal("Decryption", "Decrypted!", "success");
|
||||
window.location.href = '/{{ .Title }}/view';
|
||||
} else {
|
||||
swal("Decryption", data['status'], "error");
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
{{ end }}
|
||||
$('.postdecrypt').click(function(event) {
|
||||
var pass1 = "";
|
||||
event.preventDefault();
|
||||
swal({
|
||||
title: "Decryption",
|
||||
text: "Enter your passphrase:",
|
||||
type: "input",
|
||||
showCancelButton: true,
|
||||
closeOnConfirm: false,
|
||||
animation: "slide-from-top",
|
||||
inputPlaceholder: "Write something"
|
||||
}, function(inputValue) {
|
||||
if (inputValue === false) return false;
|
||||
if (inputValue === "") {
|
||||
swal.showInputError("You need to write something!");
|
||||
return false
|
||||
}
|
||||
pass1 = inputValue;
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
//the url where you want to sent the userName and password to
|
||||
url: '/{{ .Title }}/decrypt',
|
||||
dataType: 'json',
|
||||
data: JSON.stringify({
|
||||
text: " ",
|
||||
password: pass1
|
||||
}),
|
||||
success: function (data) {
|
||||
if (data['success'] == true) {
|
||||
swal("Decryption", "Decrypted!", "success");
|
||||
window.location.href = '/{{ .Title }}/view';
|
||||
} else {
|
||||
swal("Decryption", data['status'], "error");
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
{{ end }}
|
||||
$('.postdecrypt').click(function(event) {
|
||||
var pass1 = "";
|
||||
event.preventDefault();
|
||||
swal({
|
||||
title: "Decryption",
|
||||
text: "Enter your passphrase:",
|
||||
type: "input",
|
||||
showCancelButton: true,
|
||||
closeOnConfirm: false,
|
||||
animation: "slide-from-top",
|
||||
inputPlaceholder: "Write something"
|
||||
}, function(inputValue) {
|
||||
if (inputValue === false) return false;
|
||||
if (inputValue === "") {
|
||||
swal.showInputError("You need to write something!");
|
||||
return false
|
||||
}
|
||||
pass1 = inputValue;
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
//the url where you want to sent the userName and password to
|
||||
url: '/{{ .Title }}/decrypt',
|
||||
dataType: 'json',
|
||||
data: JSON.stringify({
|
||||
text: " ",
|
||||
password: pass1
|
||||
}),
|
||||
success: function (data) {
|
||||
if (data['success'] == true) {
|
||||
swal("Decryption", "Decrypted!", "success");
|
||||
window.location.href = '/{{ .Title }}/view';
|
||||
} else {
|
||||
swal("Decryption", data['status'], "error");
|
||||
}
|
||||
|
||||
{{ if .Locked }}
|
||||
|
||||
$('.postunlock').click(function(event) {
|
||||
var pass1 = "";
|
||||
event.preventDefault();
|
||||
swal({
|
||||
title: "Unlock",
|
||||
text: "Enter your passphrase:",
|
||||
type: "input",
|
||||
showCancelButton: true,
|
||||
closeOnConfirm: false,
|
||||
animation: "slide-from-top",
|
||||
inputPlaceholder: "Write something"
|
||||
}, function(inputValue) {
|
||||
if (inputValue === false) return false;
|
||||
if (inputValue === "") {
|
||||
swal.showInputError("You need to write something!");
|
||||
return false
|
||||
}
|
||||
pass1 = inputValue;
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
//the url where you want to sent the userName and password to
|
||||
url: '/{{ .Title }}/unlock',
|
||||
dataType: 'json',
|
||||
data: JSON.stringify({
|
||||
text: " ",
|
||||
password: pass1
|
||||
}),
|
||||
success: function (data) {
|
||||
if (data['success'] == true) {
|
||||
swal("Unlock", "Unlocked!", "success");
|
||||
window.location.href = '/{{ .Title }}';
|
||||
} else {
|
||||
swal("Unlock", data['status'], "error");
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user