diff --git a/.gitignore b/.gitignore
index 8addd76..b5c1da3 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,4 @@
+PrivateMakefile
dump.rdb
resources/
pom.xml
diff --git a/Makefile b/Makefile
index c815de1..d49d616 100644
--- a/Makefile
+++ b/Makefile
@@ -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:
lein run dev
diff --git a/settings b/settings
index 754f1b3..380245e 100644
--- a/settings
+++ b/settings
@@ -1,2 +1,2 @@
-page-width = 800px
+page-width = 800
max-title-length = 80
diff --git a/src/NoteHub/settings.clj b/src/NoteHub/settings.clj
index 74ea8fe..09cd6b3 100644
--- a/src/NoteHub/settings.clj
+++ b/src/NoteHub/settings.clj
@@ -1,24 +1,29 @@
(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
- "Takes a settings key, a default value and a converter function and returns a corresponding
- settings 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."
+ "Takes a settings key, a converter function and a default value, and returns a corresponding
+ 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 type."
[key & more]
- (let [default (first more)
- converter (second more)
- file-content (slurp "settings")
- 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)
+ (let [converter (first more)
+ default (second more)
+ value (settings-map key)
; Through this hack we can read security-critical settings from (previously
; set) shell variables without commiting their content to CVS
value (if-not value
(System/getenv
- (cs/upper-case
- (cs/replace (name key) #"-" ""))))]
+ (upper-case
+ (replace (name key) #"-" ""))))]
(if value
(if (fn? converter) (converter value) value)
default)))
diff --git a/src/NoteHub/storage.clj b/src/NoteHub/storage.clj
index 3672c9f..f36b16e 100644
--- a/src/NoteHub/storage.clj
+++ b/src/NoteHub/storage.clj
@@ -21,8 +21,7 @@
(defn set-note
"Creates a note with the given title and text in the given date namespace"
[date title text]
- (let [key (build-key date title)]
- (redis/hset db note key text)))
+ (redis/hset db note (build-key date title) text))
(defn get-note
"Gets the note from the given date namespaces for the specified title"
diff --git a/src/NoteHub/views/css_generator.clj b/src/NoteHub/views/css_generator.clj
index 0535b90..fd25d34 100644
--- a/src/NoteHub/views/css_generator.clj
+++ b/src/NoteHub/views/css_generator.clj
@@ -12,7 +12,7 @@
; CSS Mixins
(def page-width
(mixin
- :width (get-setting :page-width :800px keyword)))
+ :width (px (get-setting :page-width #(Integer/parseInt %) 800))))
(def helvetica-neue
(mixin
@@ -71,18 +71,32 @@
:color foreground
:margin 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"
helvetica-neue)
(rule "#hero"
:padding-top :5em
:padding-bottom :5em
:text-align :center
+ (rule "h1"
+ :font-size :2.5em)
(rule "h2"
- helvetica-neue))
+ helvetica-neue
+ :margin :2em))
(rule "article"
central-element
:line-height (% 140)
:font-family text-fonts
+ :text-align :justify
:font-size :1.2em
(rule "& > h1:first-child"
:text-align :center
@@ -97,9 +111,8 @@
(rule "textarea"
page-width
:font-family :Courier
- :font-size :1.2em
+ :font-size :1em
:border :none
- ; TODO: make this dynamic
:height :500px
:margin-bottom :2em)
(rule ".hidden"
diff --git a/src/NoteHub/views/pages.clj b/src/NoteHub/views/pages.clj
index 4988e12..5f647da 100644
--- a/src/NoteHub/views/pages.clj
+++ b/src/NoteHub/views/pages.clj
@@ -1,9 +1,9 @@
(ns NoteHub.views.pages
- (:require [NoteHub.views.common :as common])
(:require [NoteHub.crossover.lib :as lib])
(:use
[NoteHub.storage]
[NoteHub.settings]
+ [NoteHub.views.common]
[clojure.string :rename {replace sreplace} :only [split replace lower-case]]
[clojure.core.incubator :only [-?>]]
[hiccup.form]
@@ -31,15 +31,15 @@
(defn- wrap [params md-text]
(if md-text
(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))))
; Template for the error sites
(defn page-setter [code message]
(set-page! code
- (common/layout message
- [:article
- [:h1 message]])))
+ (layout message
+ [:article
+ [:h1 message]])))
; Sets a message for each corresponding HTTP status
(page-setter 404 "Nothing Found.")
@@ -58,29 +58,59 @@
; Landing Page
(defpage "/" {}
- (common/layout "Free Markdown Hosting"
- [:div#hero
- [:h1 "NoteHub"]
- [:h2 "Free hosting for markdown pages."]
- [:br]
- [:a.landing-button {:href "/new"} "New Page"]]))
+ (layout "Free Markdown Hosting"
+ [:div#hero
+ [:h1 "NoteHub"]
+ [:h2 "Free and hassle-free hosting for markdown pages."]
+ [:br]
+ [: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 tedious 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.
+ NoteHub 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 /stats to the note url:
+
notehub.org/.../title/stats+ If you want to export a note in the original Markdown format, append
/export
+ notehub.org/.../title/export+ And if you want, you also can invert the color scheme by appending
?theme=dark to the note url.
+ notehub.org/.../title?theme=dark"] + [: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 (defpage "/new" {} - (common/layout {:js true} "New Markdown Note" - [:div.central-element - (form-to [:post "/post-note"] - (hidden-field :session-key (get-flash-key)) - (hidden-field {:id :session-value} :session-value) - (text-area {:class :max-width} :draft "Loading...") - [:div#buttons.hidden - (submit-button {:style "float: left" - :class :button - :id :publish-button} "Publish") - [:button#preview-button.button {:type :button - :style "float: right"} "Preview"]])] - [:div#preview-start-line.hidden] - [:article#preview])) + (layout {:js true} "New Markdown Note" + [:div.central-element + (form-to [:post "/post-note"] + (hidden-field :session-key (get-flash-key)) + (hidden-field {:id :session-value} :session-value) + (text-area {:class :max-width} :draft "Loading...") + [:div#buttons.hidden + (submit-button {:style "float: left" + :class :button + :id :publish-button} "Publish") + [:button#preview-button.button {:type :button + :style "float: right"} "Preview"]])] + [:div#preview-start-line.hidden] + [:article#preview])) ; Display the note (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]} (let [views (get-views [year month day] title)] (if views - (common/layout "Statistics" - [:article.helvetica-neue - [:table {:style "width: 100%"} - [:tr - [:td "Published"] - [:td (interpose "-" [year month day])]] - [:tr - [:td "Article views"] - [:td views]]]]) + (layout "Statistics" + [:article.helvetica-neue + [:table {:style "width: 100%"} + [:tr + [:td "Published"] + [:td (interpose "-" [year month day])]] + [:tr + [:td "Article views"] + [:td views]]]]) (status 404 (get-page 404))))) ; New Note Posting @@ -124,7 +154,7 @@ (-> draft (split #"\n") first (sreplace " " "-") lower-case)) trim (fn [s] (apply str (drop-while #(= \- %) s))) 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)) date [year month day] title (first (drop-while #(note-exists? date %) diff --git a/test/NoteHub/test/storage.clj b/test/NoteHub/test/storage.clj index 046e141..9132fb1 100644 --- a/test/NoteHub/test/storage.clj +++ b/test/NoteHub/test/storage.clj @@ -10,7 +10,12 @@ (is (= (do (set-note date test-title test-note) (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" (is (not= (get-note date test-title) "any text"))) (testing "of note existence"