lots of minor refactorings; landing page extended
This commit is contained in:
@@ -1,3 +1,4 @@
|
|||||||
|
PrivateMakefile
|
||||||
dump.rdb
|
dump.rdb
|
||||||
resources/
|
resources/
|
||||||
pom.xml
|
pom.xml
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
# This one is necessary to start the app in :dev mode, because
|
||||||
|
# I changed the default mode to the production, because AFAIK
|
||||||
|
# it's not possible to parameterize the app start on Heroku.
|
||||||
run:
|
run:
|
||||||
lein run dev
|
lein run dev
|
||||||
|
|
||||||
|
|||||||
@@ -1,2 +1,2 @@
|
|||||||
page-width = 800px
|
page-width = 800
|
||||||
max-title-length = 80
|
max-title-length = 80
|
||||||
|
|||||||
+18
-13
@@ -1,24 +1,29 @@
|
|||||||
(ns NoteHub.settings
|
(ns NoteHub.settings
|
||||||
(:require [clojure.string :as cs]))
|
(:refer-clojure :exclude [replace reverse])
|
||||||
|
(:use [clojure.string]))
|
||||||
|
|
||||||
|
; Load and parse te settings file returning a map
|
||||||
|
(def settings-map
|
||||||
|
(let [file-content (slurp "settings")
|
||||||
|
lines (split file-content #"\n")
|
||||||
|
pairs (map #(map trim (split % #"=")) lines)]
|
||||||
|
(apply hash-map
|
||||||
|
(mapcat #(list (keyword (first %)) (second %)) pairs))))
|
||||||
|
|
||||||
(defn get-setting
|
(defn get-setting
|
||||||
"Takes a settings key, a default value and a converter function and returns a corresponding
|
"Takes a settings key, a converter function and a default value, and returns a corresponding
|
||||||
settings value. The default value is returned back when no setting value was found.
|
setting value. The default value is returned back when no setting value was found.
|
||||||
The converter function can be provided to convert the setting from string to a needed format."
|
The converter function can be provided to convert the setting from string to a needed type."
|
||||||
[key & more]
|
[key & more]
|
||||||
(let [default (first more)
|
(let [converter (first more)
|
||||||
converter (second more)
|
default (second more)
|
||||||
file-content (slurp "settings")
|
value (settings-map key)
|
||||||
lines (cs/split file-content #"\n")
|
|
||||||
pairs (map #(map cs/trim %) (map #(cs/split % #"=") lines))
|
|
||||||
config-map (apply hash-map (mapcat #(list (keyword (first %)) (second %)) pairs))
|
|
||||||
value (config-map key)
|
|
||||||
; Through this hack we can read security-critical settings from (previously
|
; Through this hack we can read security-critical settings from (previously
|
||||||
; set) shell variables without commiting their content to CVS
|
; set) shell variables without commiting their content to CVS
|
||||||
value (if-not value
|
value (if-not value
|
||||||
(System/getenv
|
(System/getenv
|
||||||
(cs/upper-case
|
(upper-case
|
||||||
(cs/replace (name key) #"-" ""))))]
|
(replace (name key) #"-" ""))))]
|
||||||
(if value
|
(if value
|
||||||
(if (fn? converter) (converter value) value)
|
(if (fn? converter) (converter value) value)
|
||||||
default)))
|
default)))
|
||||||
|
|||||||
@@ -21,8 +21,7 @@
|
|||||||
(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]
|
||||||
(let [key (build-key date title)]
|
(redis/hset db note (build-key date title) text))
|
||||||
(redis/hset db note key text)))
|
|
||||||
|
|
||||||
(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"
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
; CSS Mixins
|
; CSS Mixins
|
||||||
(def page-width
|
(def page-width
|
||||||
(mixin
|
(mixin
|
||||||
:width (get-setting :page-width :800px keyword)))
|
:width (px (get-setting :page-width #(Integer/parseInt %) 800))))
|
||||||
|
|
||||||
(def helvetica-neue
|
(def helvetica-neue
|
||||||
(mixin
|
(mixin
|
||||||
@@ -71,18 +71,32 @@
|
|||||||
:color foreground
|
:color foreground
|
||||||
:margin 0
|
:margin 0
|
||||||
:padding 0)
|
:padding 0)
|
||||||
|
(rule "table,tr,td"
|
||||||
|
:margin 0
|
||||||
|
:border :none)
|
||||||
|
(rule "td"
|
||||||
|
:padding :0.5em)
|
||||||
|
(rule ".one-third-column"
|
||||||
|
:text-align :justify
|
||||||
|
:vertical-align :top
|
||||||
|
; Replace this by arithmetic with css-lengths as soon as they fix the bug
|
||||||
|
:width (px (quot (get-setting :page-width #(Integer/parseInt %) 800) 3)))
|
||||||
(rule ".helvetica-neue"
|
(rule ".helvetica-neue"
|
||||||
helvetica-neue)
|
helvetica-neue)
|
||||||
(rule "#hero"
|
(rule "#hero"
|
||||||
:padding-top :5em
|
:padding-top :5em
|
||||||
:padding-bottom :5em
|
:padding-bottom :5em
|
||||||
:text-align :center
|
:text-align :center
|
||||||
|
(rule "h1"
|
||||||
|
:font-size :2.5em)
|
||||||
(rule "h2"
|
(rule "h2"
|
||||||
helvetica-neue))
|
helvetica-neue
|
||||||
|
:margin :2em))
|
||||||
(rule "article"
|
(rule "article"
|
||||||
central-element
|
central-element
|
||||||
:line-height (% 140)
|
:line-height (% 140)
|
||||||
:font-family text-fonts
|
:font-family text-fonts
|
||||||
|
:text-align :justify
|
||||||
:font-size :1.2em
|
:font-size :1.2em
|
||||||
(rule "& > h1:first-child"
|
(rule "& > h1:first-child"
|
||||||
:text-align :center
|
:text-align :center
|
||||||
@@ -97,9 +111,8 @@
|
|||||||
(rule "textarea"
|
(rule "textarea"
|
||||||
page-width
|
page-width
|
||||||
:font-family :Courier
|
:font-family :Courier
|
||||||
:font-size :1.2em
|
:font-size :1em
|
||||||
:border :none
|
:border :none
|
||||||
; TODO: make this dynamic
|
|
||||||
:height :500px
|
:height :500px
|
||||||
:margin-bottom :2em)
|
:margin-bottom :2em)
|
||||||
(rule ".hidden"
|
(rule ".hidden"
|
||||||
|
|||||||
+65
-35
@@ -1,9 +1,9 @@
|
|||||||
(ns NoteHub.views.pages
|
(ns NoteHub.views.pages
|
||||||
(:require [NoteHub.views.common :as common])
|
|
||||||
(:require [NoteHub.crossover.lib :as lib])
|
(:require [NoteHub.crossover.lib :as lib])
|
||||||
(:use
|
(:use
|
||||||
[NoteHub.storage]
|
[NoteHub.storage]
|
||||||
[NoteHub.settings]
|
[NoteHub.settings]
|
||||||
|
[NoteHub.views.common]
|
||||||
[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]
|
||||||
@@ -31,15 +31,15 @@
|
|||||||
(defn- wrap [params md-text]
|
(defn- wrap [params md-text]
|
||||||
(if md-text
|
(if md-text
|
||||||
(let [title (-?> md-text (split #"\n") first (sreplace #"[_\*#]" ""))]
|
(let [title (-?> md-text (split #"\n") first (sreplace #"[_\*#]" ""))]
|
||||||
(common/layout params title [:article (md-to-html md-text)]))
|
(layout params title [:article (md-to-html md-text)]))
|
||||||
(status 404 (get-page 404))))
|
(status 404 (get-page 404))))
|
||||||
|
|
||||||
; Template for the error sites
|
; Template for the error sites
|
||||||
(defn page-setter [code message]
|
(defn page-setter [code message]
|
||||||
(set-page! code
|
(set-page! code
|
||||||
(common/layout message
|
(layout message
|
||||||
[:article
|
[:article
|
||||||
[:h1 message]])))
|
[:h1 message]])))
|
||||||
|
|
||||||
; Sets a message for each corresponding HTTP status
|
; Sets a message for each corresponding HTTP status
|
||||||
(page-setter 404 "Nothing Found.")
|
(page-setter 404 "Nothing Found.")
|
||||||
@@ -58,29 +58,59 @@
|
|||||||
|
|
||||||
; Landing Page
|
; Landing Page
|
||||||
(defpage "/" {}
|
(defpage "/" {}
|
||||||
(common/layout "Free Markdown Hosting"
|
(layout "Free Markdown Hosting"
|
||||||
[:div#hero
|
[:div#hero
|
||||||
[:h1 "NoteHub"]
|
[:h1 "NoteHub"]
|
||||||
[:h2 "Free hosting for markdown pages."]
|
[:h2 "Free and hassle-free hosting for markdown pages."]
|
||||||
[:br]
|
[:br]
|
||||||
[:a.landing-button {:href "/new"} "New Page"]]))
|
[:a.landing-button {:href "/new"} "New Page"]]
|
||||||
|
[:div#preview-start-line]
|
||||||
|
[:table.central-element.helvetica-neue
|
||||||
|
[:tr
|
||||||
|
[:td.one-third-column
|
||||||
|
[:h2 "Why?"]
|
||||||
|
"Not every person, who occasionally wants to express some thoughts, needs a blog.
|
||||||
|
Blogs are <b>tedious</b> for writers and for readers. Most people are not interested in thoughts
|
||||||
|
of other random people. Moreover, nowadays everything rotates around social networks and not
|
||||||
|
individual blogs. It makes much more sense to publish something somewhere and to share
|
||||||
|
the link with the audience on the community or social network of your choice, than to maintain a blog
|
||||||
|
trying to keep your readers interested.
|
||||||
|
<b>NoteHub</b> should be the place, where you can publish your thoughts without hassle."]
|
||||||
|
[:td.one-third-column
|
||||||
|
[:h2 "How to Use?"]
|
||||||
|
"First create a new page using the markdown syntax. Now, besides just sharing the link, you can
|
||||||
|
view some rudimentary statistics by appending <code>/stats</code> to the note url:
|
||||||
|
<pre>notehub.org/.../title/stats</pre>
|
||||||
|
If you want to export a note in the original Markdown format, append <code>/export</code>
|
||||||
|
<pre>notehub.org/.../title/export</pre>
|
||||||
|
And if you want, you also can invert the color scheme by appending <code>?theme=dark</code> to the note url.
|
||||||
|
<pre>notehub.org/.../title?theme=dark</pre>"]
|
||||||
|
[:td.one-third-column
|
||||||
|
[:h2 "For Geeks!"]
|
||||||
|
"NoteHub was an experiment and is implemented entirely in Clojure and ClojureScript. Its source code can
|
||||||
|
be found on GitHub. Look at the code to find some undocumented NoteHub features (or bugs) and — feel free to contribute!
|
||||||
|
(If you are interested in more detailed code overview, read the following note.) NoteHub's design
|
||||||
|
is intentionally kept extremelly simple and minimalistic, and should stay like this.
|
||||||
|
NoteHub's persistence layer bases on the key-value store redis.
|
||||||
|
Currently, NoteHub is hosted for free on Heroku.
|
||||||
|
Send your feedback and comments directly to @chmllr."]]]))
|
||||||
|
|
||||||
; New Note Page
|
; New Note Page
|
||||||
(defpage "/new" {}
|
(defpage "/new" {}
|
||||||
(common/layout {:js true} "New Markdown Note"
|
(layout {:js true} "New Markdown Note"
|
||||||
[:div.central-element
|
[:div.central-element
|
||||||
(form-to [:post "/post-note"]
|
(form-to [:post "/post-note"]
|
||||||
(hidden-field :session-key (get-flash-key))
|
(hidden-field :session-key (get-flash-key))
|
||||||
(hidden-field {:id :session-value} :session-value)
|
(hidden-field {:id :session-value} :session-value)
|
||||||
(text-area {:class :max-width} :draft "Loading...")
|
(text-area {:class :max-width} :draft "Loading...")
|
||||||
[:div#buttons.hidden
|
[:div#buttons.hidden
|
||||||
(submit-button {:style "float: left"
|
(submit-button {:style "float: left"
|
||||||
:class :button
|
:class :button
|
||||||
:id :publish-button} "Publish")
|
:id :publish-button} "Publish")
|
||||||
[:button#preview-button.button {:type :button
|
[:button#preview-button.button {:type :button
|
||||||
:style "float: right"} "Preview"]])]
|
:style "float: right"} "Preview"]])]
|
||||||
[:div#preview-start-line.hidden]
|
[:div#preview-start-line.hidden]
|
||||||
[:article#preview]))
|
[:article#preview]))
|
||||||
|
|
||||||
; Display the note
|
; Display 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}
|
||||||
@@ -97,15 +127,15 @@
|
|||||||
(defpage "/:year/:month/:day/:title/stat" {:keys [year month day title]}
|
(defpage "/:year/:month/:day/:title/stat" {:keys [year month day title]}
|
||||||
(let [views (get-views [year month day] title)]
|
(let [views (get-views [year month day] title)]
|
||||||
(if views
|
(if views
|
||||||
(common/layout "Statistics"
|
(layout "Statistics"
|
||||||
[:article.helvetica-neue
|
[:article.helvetica-neue
|
||||||
[:table {:style "width: 100%"}
|
[:table {:style "width: 100%"}
|
||||||
[:tr
|
[:tr
|
||||||
[:td "Published"]
|
[:td "Published"]
|
||||||
[:td (interpose "-" [year month day])]]
|
[:td (interpose "-" [year month day])]]
|
||||||
[:tr
|
[:tr
|
||||||
[:td "Article views"]
|
[:td "Article views"]
|
||||||
[:td views]]]])
|
[:td views]]]])
|
||||||
(status 404 (get-page 404)))))
|
(status 404 (get-page 404)))))
|
||||||
|
|
||||||
; New Note Posting
|
; New Note Posting
|
||||||
@@ -124,7 +154,7 @@
|
|||||||
(-> draft (split #"\n") first (sreplace " " "-") lower-case))
|
(-> draft (split #"\n") first (sreplace " " "-") lower-case))
|
||||||
trim (fn [s] (apply str (drop-while #(= \- %) s)))
|
trim (fn [s] (apply str (drop-while #(= \- %) s)))
|
||||||
title-uncut (-> untrimmed-line trim reverse trim reverse)
|
title-uncut (-> untrimmed-line trim reverse trim reverse)
|
||||||
proposed-title (apply str (take (get-setting :max-title-length 80 #(Integer/parseInt %))
|
proposed-title (apply str (take (get-setting :max-title-length #(Integer/parseInt %) 80)
|
||||||
title-uncut))
|
title-uncut))
|
||||||
date [year month day]
|
date [year month day]
|
||||||
title (first (drop-while #(note-exists? date %)
|
title (first (drop-while #(note-exists? date %)
|
||||||
|
|||||||
@@ -10,7 +10,12 @@
|
|||||||
(is (= (do
|
(is (= (do
|
||||||
(set-note date test-title test-note)
|
(set-note date test-title test-note)
|
||||||
(get-note date test-title))
|
(get-note date test-title))
|
||||||
test-note)))
|
test-note))
|
||||||
|
(is (= "1" (get-views date test-title)))
|
||||||
|
(is (= (do
|
||||||
|
(get-note date test-title)
|
||||||
|
(get-views date test-title))
|
||||||
|
"2")))
|
||||||
(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 "of note existence"
|
(testing "of note existence"
|
||||||
|
|||||||
Reference in New Issue
Block a user