note editing via passwords enabled
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
## About
|
## About
|
||||||
|
|
||||||
[NoteHub](http://notehub.org) is a free and hassle-free anonymous hosting for markdown pages. It can be used for publishing of markdown-formatted text.
|
[NoteHub](http://notehub.org) is a free and hassle-free anonymous hosting for markdown pages. It can be used for publishing of markdown-formatted texts.
|
||||||
|
|
||||||
NoteHub was an one-app-one-language [experiment](http://notehub.org/2012/6/16/how-notehub-is-built) and is implemented entirely in [Clojure](http://clojure.org) (ClojureScript).
|
NoteHub was an one-app-one-language [experiment](http://notehub.org/2012/6/16/how-notehub-is-built) and is implemented entirely in [Clojure](http://clojure.org) (ClojureScript).
|
||||||
The [source code](https://github.com/chmllr/NoteHub) can be found on GitHub.
|
The [source code](https://github.com/chmllr/NoteHub) can be found on GitHub.
|
||||||
@@ -37,7 +37,10 @@ See an example of the font formatting [here](http://notehub.org/2012/6/16/how-no
|
|||||||
|
|
||||||
After you've specified this in the url, you can copy the corresponding short url of the article and share it.
|
After you've specified this in the url, you can copy the corresponding short url of the article and share it.
|
||||||
|
|
||||||
## Export & Statistics
|
## After Publishing
|
||||||
|
|
||||||
|
During the note publishing a password can be set.
|
||||||
|
This password unlocks the note for an editing.
|
||||||
|
The edit mode can be entered by appending of `/edit`to the note url.
|
||||||
By appending of `/stats` to any note url, everyone can see a rudimentary statistics (currently, the number of note views only).
|
By appending of `/stats` to any note url, everyone can see a rudimentary statistics (currently, the number of note views only).
|
||||||
By appending of `/export`, the original markdown content will be displayed in plain text format.
|
By appending of `/export`, the original markdown content will be displayed in plain text format.
|
||||||
|
|||||||
@@ -4,18 +4,22 @@ new-page = New Page
|
|||||||
|
|
||||||
status-404 = Nothing found.
|
status-404 = Nothing found.
|
||||||
status-400 = Bad Request.
|
status-400 = Bad Request.
|
||||||
|
status-403 = Wrong Password.
|
||||||
status-500 = OMG, Server Exploded.
|
status-500 = OMG, Server Exploded.
|
||||||
|
|
||||||
created-by = Created by [@chmllr](http://twitter.com/chmllr)
|
created-by = Created by [@chmllr](http://twitter.com/chmllr)
|
||||||
|
|
||||||
loading = Loading...
|
loading = Loading...
|
||||||
use-password = User password for editing:
|
set-passwd = Set password for editing:
|
||||||
|
enter-passwd = Password:
|
||||||
preview = Preview
|
preview = Preview
|
||||||
publish = Publish
|
publish = Publish
|
||||||
|
update = Save
|
||||||
published = Published
|
published = Published
|
||||||
article-views = Article Views
|
article-views = Article Views
|
||||||
statistics = Statistics
|
statistics = Statistics
|
||||||
stats = statistics
|
stats = statistics
|
||||||
export = export
|
export = export
|
||||||
|
edit = edit
|
||||||
short-url = short url
|
short-url = short url
|
||||||
new-note = New Markdown Note
|
new-note = New Markdown Note
|
||||||
|
|||||||
+33
-14
@@ -1,43 +1,62 @@
|
|||||||
(ns NoteHub.main
|
(ns NoteHub.main
|
||||||
(:use [jayq.core :only [$ xhr css inner val anim show]])
|
(:use [jayq.core :only [$ xhr css inner val anim show]])
|
||||||
(:require [goog.dom :as gdom]
|
(:require [goog.dom :as gdom]
|
||||||
|
[goog.crypt.Md5 :as md5]
|
||||||
|
[goog.crypt :as crypt]
|
||||||
[NoteHub.crossover.lib :as lib]
|
[NoteHub.crossover.lib :as lib]
|
||||||
[clojure.browser.dom :as dom]))
|
[clojure.browser.dom :as dom]))
|
||||||
|
|
||||||
; frequently used selectors
|
; frequently used selectors
|
||||||
(def $draft ($ :#draft))
|
(def $draft ($ :#draft))
|
||||||
|
(def $action ($ :#action))
|
||||||
(def $preview ($ :#preview))
|
(def $preview ($ :#preview))
|
||||||
|
(def $password ($ :#password))
|
||||||
|
(def $plain-password ($ :#plain-password))
|
||||||
(def $input-elems ($ :#input-elems))
|
(def $input-elems ($ :#input-elems))
|
||||||
(def $preview-start-line ($ :#preview-start-line))
|
(def $dashed-line ($ :#dashed-line))
|
||||||
|
|
||||||
; Markdown Converter & Sanitizer instantiation
|
; Markdown Converter & Sanitizer instantiation
|
||||||
|
|
||||||
(def md-converter (Markdown/getSanitizingConverter))
|
(def md-converter (Markdown/getSanitizingConverter))
|
||||||
|
|
||||||
|
; instantiate & reset a MD5 hash digester
|
||||||
|
(def md5 (goog.crypt.Md5.))
|
||||||
|
(.reset md5)
|
||||||
|
|
||||||
; try to detect iOS
|
; try to detect iOS
|
||||||
(def ios-detected (.match (.-userAgent js/navigator) "(iPad|iPod|iPhone)"))
|
(def ios-detected (.match (.-userAgent js/navigator) "(iPad|iPod|iPhone)"))
|
||||||
|
|
||||||
; set focus to the draft textarea (if there is one)
|
(defn update-preview
|
||||||
(when $draft
|
[]
|
||||||
|
"Updates the preview"
|
||||||
(do
|
(do
|
||||||
(val $draft "")
|
(show $dashed-line)
|
||||||
|
(show $input-elems)
|
||||||
|
(inner $preview
|
||||||
|
(.makeHtml md-converter (val $draft)))))
|
||||||
|
|
||||||
|
; set focus to the draft textarea (if there is one)
|
||||||
|
(when $action
|
||||||
|
(do
|
||||||
|
(if (= "update" (val $action))
|
||||||
|
(update-preview)
|
||||||
|
(val $draft ""))
|
||||||
; foces setting is impossible in iOS, so we border the field instead
|
; foces setting is impossible in iOS, so we border the field instead
|
||||||
(if ios-detected
|
(if ios-detected
|
||||||
(.addClass $draft "ui-border")
|
(.addClass $draft "ui-border")
|
||||||
(.focus $draft))))
|
(.focus $draft))))
|
||||||
|
|
||||||
; show the preview & publish buttons as soon as the user starts typing.
|
; show the preview & publish buttons as soon as the user starts typing.
|
||||||
(.keyup $draft
|
(.keyup $draft update-preview)
|
||||||
(fn [e]
|
|
||||||
(do
|
|
||||||
(show $preview-start-line)
|
|
||||||
(show $input-elems)
|
|
||||||
(inner $preview
|
|
||||||
(.makeHtml md-converter (val $draft))))))
|
|
||||||
|
|
||||||
; when the publish button is clicked, compute the hash of the entered text and
|
; when the publish button is clicked, compute the hash of the entered text and
|
||||||
; provided session key and assign to the field session-value
|
; provided session key and assign to the field session-value;
|
||||||
|
; moreover, compute the password hash as md5 before transmission
|
||||||
(.click ($ :#publish-button)
|
(.click ($ :#publish-button)
|
||||||
(fn [e]
|
(fn [e]
|
||||||
|
(do
|
||||||
|
(.update md5 (val $plain-password))
|
||||||
|
(val $plain-password nil)
|
||||||
|
(when (val $plain-password)
|
||||||
|
(val $password (crypt/byteArrayToHex (.digest md5))))
|
||||||
(val ($ :#session-value)
|
(val ($ :#session-value)
|
||||||
(lib/hash #(.charCodeAt % 0) (str (val $draft) (val ($ :#session-key)))))))
|
(lib/hash #(.charCodeAt % 0) (str (val $draft) (val ($ :#session-key))))))))
|
||||||
|
|||||||
+23
-8
@@ -2,7 +2,8 @@
|
|||||||
(:use [NoteHub.settings]
|
(:use [NoteHub.settings]
|
||||||
[noir.util.crypt :only [encrypt]]
|
[noir.util.crypt :only [encrypt]]
|
||||||
[noir.options :only [dev-mode?]])
|
[noir.options :only [dev-mode?]])
|
||||||
(:require [clj-redis.client :as redis]))
|
(:require [clj-redis.client :as redis]
|
||||||
|
[clojure.contrib.string :as ccs]))
|
||||||
|
|
||||||
; Initialize the data base
|
; Initialize the data base
|
||||||
(def db
|
(def db
|
||||||
@@ -12,12 +13,15 @@
|
|||||||
|
|
||||||
; DB hierarchy levels
|
; DB hierarchy levels
|
||||||
(def note "note")
|
(def note "note")
|
||||||
(def short-url "short-url")
|
|
||||||
(def views "views")
|
(def views "views")
|
||||||
|
(def password "password")
|
||||||
(def sessions "sessions")
|
(def sessions "sessions")
|
||||||
|
(def short-url "short-url")
|
||||||
|
|
||||||
; Concatenates all fields to a string
|
; Concatenates all fields to a string
|
||||||
(defn- build-key [[year month day] title]
|
(defn build-key
|
||||||
|
"Returns a storage-key for the given note coordinates"
|
||||||
|
[[year month day] title]
|
||||||
(print-str year month day title))
|
(print-str year month day title))
|
||||||
|
|
||||||
(defn create-session
|
(defn create-session
|
||||||
@@ -34,10 +38,21 @@
|
|||||||
(let [was-valid (redis/sismember db sessions token)]
|
(let [was-valid (redis/sismember db sessions token)]
|
||||||
(do (redis/srem db sessions token) was-valid))))
|
(do (redis/srem db sessions token) was-valid))))
|
||||||
|
|
||||||
|
(defn update-note
|
||||||
|
"Updates a note with the given store key if the specified password is correct"
|
||||||
|
[key text passwd]
|
||||||
|
(when (= passwd (redis/hget db password key))
|
||||||
|
(redis/hset db note key text)))
|
||||||
|
|
||||||
(defn set-note
|
(defn set-note
|
||||||
"Creates a note with the given title and text in the given date namespace"
|
"Creates a note with the given title and text in the given date namespace"
|
||||||
[date title text]
|
([date title text] (set-note date title text nil))
|
||||||
(redis/hset db note (build-key date title) text))
|
([date title text passwd]
|
||||||
|
(let [key (build-key date title)]
|
||||||
|
(do
|
||||||
|
(redis/hset db note key text)
|
||||||
|
(when (not (ccs/blank? passwd))
|
||||||
|
(redis/hset db password key passwd))))))
|
||||||
|
|
||||||
(defn get-note
|
(defn get-note
|
||||||
"Gets the note from the given date namespaces for the specified title"
|
"Gets the note from the given date namespaces for the specified title"
|
||||||
@@ -63,9 +78,9 @@
|
|||||||
"Deletes the note with the specified coordinates"
|
"Deletes the note with the specified coordinates"
|
||||||
[date title]
|
[date title]
|
||||||
(let [key (build-key date title)]
|
(let [key (build-key date title)]
|
||||||
(do
|
(doseq [kw [password views note]]
|
||||||
(redis/hdel db views key)
|
; TODO: delete short url by looking for the title
|
||||||
(redis/hdel db note key))))
|
(redis/hdel db kw key))))
|
||||||
|
|
||||||
(defn short-url-exists?
|
(defn short-url-exists?
|
||||||
"Checks whether the provided short url is taken (for testing only)"
|
"Checks whether the provided short url is taken (for testing only)"
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
[noir.core :only [defpartial]]
|
[noir.core :only [defpartial]]
|
||||||
[noir.options :only [dev-mode?]]
|
[noir.options :only [dev-mode?]]
|
||||||
[hiccup.util :only [escape-html]]
|
[hiccup.util :only [escape-html]]
|
||||||
|
[ring.util.codec :only [url-encode]]
|
||||||
[hiccup.core]
|
[hiccup.core]
|
||||||
[hiccup.page :only [include-js html5]]
|
[hiccup.page :only [include-js html5]]
|
||||||
[hiccup.element :only [javascript-tag]]))
|
[hiccup.element :only [javascript-tag]]))
|
||||||
@@ -12,7 +13,7 @@
|
|||||||
(defn url
|
(defn url
|
||||||
"Creates a local url from the given substrings"
|
"Creates a local url from the given substrings"
|
||||||
[& args]
|
[& args]
|
||||||
(apply str (interpose "/" (cons "" args))))
|
(apply str (interpose "/" (cons "" (map url-encode args)))))
|
||||||
|
|
||||||
; Creates the main html layout
|
; Creates the main html layout
|
||||||
(defpartial generate-layout
|
(defpartial generate-layout
|
||||||
|
|||||||
@@ -29,6 +29,10 @@
|
|||||||
:margin-left "auto"
|
:margin-left "auto"
|
||||||
:margin-right "auto"))
|
:margin-right "auto"))
|
||||||
|
|
||||||
|
(defn thin-border [foreground]
|
||||||
|
(mixin :border-radius :3px
|
||||||
|
:border [:1px :solid foreground]))
|
||||||
|
|
||||||
; Resolves the theme name & tone parameter to a concrete color
|
; Resolves the theme name & tone parameter to a concrete color
|
||||||
(defn- color [& keys]
|
(defn- color [& keys]
|
||||||
(get-in {:dark {:background :#333
|
(get-in {:dark {:background :#333
|
||||||
@@ -70,8 +74,15 @@
|
|||||||
(rule "&:visited"
|
(rule "&:visited"
|
||||||
:color link-visited))
|
:color link-visited))
|
||||||
(rule ".ui-border"
|
(rule ".ui-border"
|
||||||
:border-radius :3px
|
(thin-border foreground))
|
||||||
:border [:1px :solid foreground])
|
(rule ".button"
|
||||||
|
:cursor :pointer)
|
||||||
|
(rule ".ui-elem"
|
||||||
|
helvetica-neue
|
||||||
|
(thin-border foreground)
|
||||||
|
:opacity 0.8
|
||||||
|
:font-size :1em
|
||||||
|
:background background)
|
||||||
(rule ".landing-button"
|
(rule ".landing-button"
|
||||||
:box-shadow [0 :2px :5px :#aaa]
|
:box-shadow [0 :2px :5px :#aaa]
|
||||||
:text-decoration :none
|
:text-decoration :none
|
||||||
@@ -139,6 +150,8 @@
|
|||||||
:margin :2em))
|
:margin :2em))
|
||||||
(rule ".centered"
|
(rule ".centered"
|
||||||
:text-align :center)
|
:text-align :center)
|
||||||
|
(rule ".bottom-space"
|
||||||
|
:margin-bottom :7em)
|
||||||
(rule "pre"
|
(rule "pre"
|
||||||
:border-radius :3px
|
:border-radius :3px
|
||||||
:padding :0.5em
|
:padding :0.5em
|
||||||
@@ -155,20 +168,13 @@
|
|||||||
:height :500px)
|
:height :500px)
|
||||||
(rule ".hidden"
|
(rule ".hidden"
|
||||||
:display :none)
|
:display :none)
|
||||||
(rule ".button"
|
|
||||||
:-webkit-appearance :none
|
|
||||||
helvetica-neue
|
|
||||||
:cursor :pointer
|
|
||||||
:opacity 0.8
|
|
||||||
:font-size :1em
|
|
||||||
:background background)
|
|
||||||
(rule ".central-element"
|
(rule ".central-element"
|
||||||
central-element)
|
central-element)
|
||||||
(rule "fieldset"
|
(rule "fieldset"
|
||||||
:border :none)
|
:border :none)
|
||||||
(rule "h1"
|
(rule "h1"
|
||||||
:font-size :2em)
|
:font-size :2em)
|
||||||
(rule ".dashed-line"
|
(rule "#dashed-line"
|
||||||
:border-bottom [:1px :dashed foreground-halftone]
|
:border-bottom [:1px :dashed foreground-halftone]
|
||||||
:margin-top :3em
|
:margin-top :3em
|
||||||
:margin-bottom :3em)
|
:margin-bottom :3em)
|
||||||
|
|||||||
+43
-21
@@ -9,11 +9,10 @@
|
|||||||
[clojure.string :rename {replace sreplace} :only [split replace lower-case]]
|
[clojure.string :rename {replace sreplace} :only [split replace lower-case]]
|
||||||
[clojure.core.incubator :only [-?>]]
|
[clojure.core.incubator :only [-?>]]
|
||||||
[hiccup.form]
|
[hiccup.form]
|
||||||
[ring.util.codec :only [url-encode]]
|
|
||||||
[hiccup.core]
|
[hiccup.core]
|
||||||
[hiccup.element]
|
[hiccup.element]
|
||||||
[noir.response :only [redirect status content-type]]
|
[noir.response :only [redirect status content-type]]
|
||||||
[noir.core :only [defpage]]
|
[noir.core :only [defpage defpartial]]
|
||||||
[cheshire.core]
|
[cheshire.core]
|
||||||
[noir.statuses])
|
[noir.statuses])
|
||||||
(:import
|
(:import
|
||||||
@@ -30,7 +29,7 @@
|
|||||||
|
|
||||||
; Sets a custom message for each needed HTTP status.
|
; Sets a custom message for each needed HTTP status.
|
||||||
; The message to be assigned is extracted with a dynamically generated key
|
; The message to be assigned is extracted with a dynamically generated key
|
||||||
(doseq [code [400 404 500]]
|
(doseq [code [400 403 404 500]]
|
||||||
(set-page! code
|
(set-page! code
|
||||||
(let [message (get-message (keyword (str "status-" code)))]
|
(let [message (get-message (keyword (str "status-" code)))]
|
||||||
(layout message
|
(layout message
|
||||||
@@ -44,13 +43,13 @@
|
|||||||
(defn- wrap [short-url params md-text]
|
(defn- wrap [short-url params md-text]
|
||||||
(when md-text
|
(when md-text
|
||||||
(layout params (params :title)
|
(layout params (params :title)
|
||||||
[:article (md-to-html md-text)]
|
[:article.bottom-space (md-to-html md-text)]
|
||||||
(let [links (map #(link-to
|
(let [links (map #(link-to
|
||||||
(if (= :short-url %)
|
(if (= :short-url %)
|
||||||
(url short-url)
|
(url short-url)
|
||||||
(str (params :title) "/" (name %)))
|
(str (params :title) "/" (name %)))
|
||||||
(get-message %))
|
(get-message %))
|
||||||
[:stats :export :short-url])
|
[:stats :edit :export :short-url])
|
||||||
space (apply str (repeat 4 " "))
|
space (apply str (repeat 4 " "))
|
||||||
separator (str space "·" space)
|
separator (str space "·" space)
|
||||||
links (interpose separator links)]
|
links (interpose separator links)]
|
||||||
@@ -79,25 +78,42 @@
|
|||||||
[:h2 (get-message :title)]
|
[:h2 (get-message :title)]
|
||||||
[:br]
|
[:br]
|
||||||
[:a.landing-button {:href "/new" :style "color: white"} (get-message :new-page)]]
|
[:a.landing-button {:href "/new" :style "color: white"} (get-message :new-page)]]
|
||||||
[:div.dashed-line]
|
[:div#dashed-line]
|
||||||
; dynamically generates three column, retrieving corresponding messages
|
; dynamically generates three column, retrieving corresponding messages
|
||||||
[:article.helvetica-neue {:style "font-size: 1em"}
|
[:article.helvetica-neue.bottom-space {:style "font-size: 1em"}
|
||||||
(md-to-html (slurp "README.md"))]
|
(md-to-html (slurp "README.md"))]
|
||||||
[:div.centered.helvetica-neue (md-to-html (get-message :created-by))]))
|
[:div.centered.helvetica-neue (md-to-html (get-message :created-by))]))
|
||||||
|
|
||||||
|
; input form for the markdown text with a preview area
|
||||||
|
(defpartial input-form [form-url command fields content passwd-msg]
|
||||||
|
(let [css-class (when (= :publish command) :hidden)]
|
||||||
|
(layout {:js true} (get-message :new-note)
|
||||||
|
[:article#preview " "]
|
||||||
|
[:div#dashed-line {:class css-class}]
|
||||||
|
[:div.central-element.helvetica-neue {:style "margin-bottom: 3em"}
|
||||||
|
(form-to [:post form-url]
|
||||||
|
(hidden-field :action command)
|
||||||
|
(hidden-field :password)
|
||||||
|
fields
|
||||||
|
(text-area {:class :max-width} :draft content)
|
||||||
|
[:fieldset#input-elems {:class css-class}
|
||||||
|
(get-message passwd-msg)
|
||||||
|
(text-field {:class "ui-elem"} :plain-password)
|
||||||
|
(submit-button {:class "button ui-elem"
|
||||||
|
:id :publish-button} (get-message command))])])))
|
||||||
|
|
||||||
|
; Update Note Page
|
||||||
|
(defpage "/:year/:month/:day/:title/edit" {:keys [year month day title]}
|
||||||
|
(input-form "/update-note" :update
|
||||||
|
(html (hidden-field :key (build-key [year month day] title)))
|
||||||
|
(get-note [year month day] title) :enter-passwd))
|
||||||
|
|
||||||
; New Note Page
|
; New Note Page
|
||||||
(defpage "/new" {}
|
(defpage "/new" {}
|
||||||
(layout {:js true} (get-message :new-note)
|
(input-form "/post-note" :publish
|
||||||
[:article#preview " "]
|
(html (hidden-field :session-key (create-session))
|
||||||
[:div#preview-start-line.dashed-line.hidden]
|
(hidden-field {:id :session-value} :session-value))
|
||||||
[:div.central-element {:style "margin-bottom: 3em"}
|
(get-message :loading) :set-passwd))
|
||||||
(form-to [:post "/post-note"]
|
|
||||||
(hidden-field :session-key (create-session))
|
|
||||||
(hidden-field {:id :session-value} :session-value)
|
|
||||||
(text-area {:class :max-width} :draft (get-message :loading))
|
|
||||||
[:fieldset#input-elems.hidden
|
|
||||||
(submit-button {:class "button ui-border"
|
|
||||||
:id :publish-button} (get-message :publish))])]))
|
|
||||||
|
|
||||||
; Displays the note
|
; Displays the note
|
||||||
(defpage "/:year/:month/:day/:title" {:keys [year month day title theme header-font text-font] :as params}
|
(defpage "/:year/:month/:day/:title" {:keys [year month day title theme header-font text-font] :as params}
|
||||||
@@ -123,8 +139,14 @@
|
|||||||
[:td (get-message :article-views)]
|
[:td (get-message :article-views)]
|
||||||
[:td views]]])))
|
[:td views]]])))
|
||||||
|
|
||||||
|
; Updates a note
|
||||||
|
(defpage [:post "/update-note"] {:keys [key draft password]}
|
||||||
|
(if (update-note key draft password)
|
||||||
|
(redirect (apply url (split key #" ")))
|
||||||
|
(response 403)))
|
||||||
|
|
||||||
; New Note Posting — the most "complex" function in the entire app ;)
|
; New Note Posting — the most "complex" function in the entire app ;)
|
||||||
(defpage [:post "/post-note"] {:keys [draft session-key session-value]}
|
(defpage [:post "/post-note"] {:keys [draft password session-key session-value]}
|
||||||
; first we collect all info needed to evaluate the validity of the note creation request
|
; first we collect all info needed to evaluate the validity of the note creation request
|
||||||
(let [valid-session (invalidate-session session-key) ; was the note posted from a newly generated form?
|
(let [valid-session (invalidate-session session-key) ; was the note posted from a newly generated form?
|
||||||
valid-draft (not (ccs/blank? draft)) ; has the note a meaningful content?
|
valid-draft (not (ccs/blank? draft)) ; has the note a meaningful content?
|
||||||
@@ -151,8 +173,8 @@
|
|||||||
(cons proposed-title
|
(cons proposed-title
|
||||||
(map #(str proposed-title "-" (+ 2 %)) (range)))))]
|
(map #(str proposed-title "-" (+ 2 %)) (range)))))]
|
||||||
(do
|
(do
|
||||||
(set-note date title draft)
|
(set-note date title draft password)
|
||||||
(redirect (url year month day (url-encode title)))))
|
(redirect (url year month day title))))
|
||||||
(response 400))))
|
(response 400))))
|
||||||
|
|
||||||
; Resolving of a short url
|
; Resolving of a short url
|
||||||
|
|||||||
@@ -33,6 +33,19 @@
|
|||||||
(get-note date test-title)
|
(get-note date test-title)
|
||||||
(get-note-views date test-title))
|
(get-note-views date test-title))
|
||||||
"2")))
|
"2")))
|
||||||
|
(testing "of note update"
|
||||||
|
(is (= (do
|
||||||
|
(set-note date test-title test-note "12345qwert")
|
||||||
|
(get-note date test-title))
|
||||||
|
test-note))
|
||||||
|
(is (= (do
|
||||||
|
(update-note (build-key date test-title) "update" "12345qwert")
|
||||||
|
(get-note date test-title))
|
||||||
|
"update"))
|
||||||
|
(is (= (do
|
||||||
|
(update-note (build-key date test-title) "not authorized" "44444")
|
||||||
|
(get-note date test-title))
|
||||||
|
"update")))
|
||||||
(testing "of the note access"
|
(testing "of the note access"
|
||||||
(is (not= (get-note date test-title) "any text")))
|
(is (not= (get-note date test-title) "any text")))
|
||||||
(testing "session management"
|
(testing "session management"
|
||||||
|
|||||||
@@ -36,7 +36,6 @@
|
|||||||
(deftest note-creation
|
(deftest note-creation
|
||||||
(let [session-key (create-session)
|
(let [session-key (create-session)
|
||||||
date (get-date)
|
date (get-date)
|
||||||
; TODO: replace note generation by a function from pages.clj
|
|
||||||
title "this-is-a-test-note"
|
title "this-is-a-test-note"
|
||||||
[year month day] date]
|
[year month day] date]
|
||||||
(testing "Note creation"
|
(testing "Note creation"
|
||||||
@@ -54,6 +53,43 @@
|
|||||||
(delete-note date title)
|
(delete-note date title)
|
||||||
(not (note-exists? date title)))))))
|
(not (note-exists? date title)))))))
|
||||||
|
|
||||||
|
(deftest note-update
|
||||||
|
(let [session-key (create-session)
|
||||||
|
date (get-date)
|
||||||
|
title "test-note"
|
||||||
|
[year month day] date]
|
||||||
|
(testing "Note update"
|
||||||
|
(is (has-status
|
||||||
|
(send-request
|
||||||
|
[:post "/post-note"]
|
||||||
|
{:session-key session-key
|
||||||
|
:draft "test note"
|
||||||
|
:password "qwerty"
|
||||||
|
:session-value (str (lib/hash #(.codePointAt % 0)
|
||||||
|
(str "test note" session-key)))}) 302))
|
||||||
|
(is (note-exists? date title))
|
||||||
|
(is (substring? "test note"
|
||||||
|
((send-request (url year month day title)) :body)))
|
||||||
|
(is (has-status
|
||||||
|
(send-request
|
||||||
|
[:post "/update-note"]
|
||||||
|
{:key (build-key [year month day] title)
|
||||||
|
:draft "WRONG pass"
|
||||||
|
:password "qwerty1" }) 403))
|
||||||
|
(is (substring? "test note"
|
||||||
|
((send-request (url year month day title)) :body)))
|
||||||
|
(is (has-status
|
||||||
|
(send-request
|
||||||
|
[:post "/update-note"]
|
||||||
|
{:key (build-key [year month day] title)
|
||||||
|
:draft "UPDATED CONTENT"
|
||||||
|
:password "qwerty" }) 302))
|
||||||
|
(is (substring? "UPDATED CONTENT"
|
||||||
|
((send-request (url year month day title)) :body)))
|
||||||
|
(is (do
|
||||||
|
(delete-note date title)
|
||||||
|
(not (note-exists? date title)))))))
|
||||||
|
|
||||||
(deftest requests
|
(deftest requests
|
||||||
(testing "HTTP Status"
|
(testing "HTTP Status"
|
||||||
(testing "of a wrong access"
|
(testing "of a wrong access"
|
||||||
|
|||||||
Reference in New Issue
Block a user