Browse Source

plain text hidden behind a function call (to make localization easier)

master
Christian Mueller 14 years ago
parent
commit
01754a7d57
  1. 26
      messages
  2. 1
      project.clj
  3. 9
      src/NoteHub/crossover/lib.clj
  4. 26
      src/NoteHub/settings.clj
  5. 1
      src/NoteHub/storage.clj
  6. 3
      src/NoteHub/views/common.clj
  7. 19
      src/NoteHub/views/css_generator.clj
  8. 90
      src/NoteHub/views/pages.clj

26
messages

@ -0,0 +1,26 @@
title = Free and Hassle-free Markdown Hosting.
name = NoteHub
new-page = New Page
column-why = Why?
column-why-long = 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.
column-how = How to Use?
column-how-long = First [create](/new) a new page using the [Markdown](http://daringfireball.net/projects/markdown/) syntax. Now, besides just sharing the link, you can view some rudimentary statistics by appending `/stats` to the note url: <pre>notehub.org/.../title/stats</pre> If you want to export a note in the original Markdown format, append `/export`: <pre>notehub.org/.../title/export</pre> You also can invert the color scheme by appending `?theme=dark` to the note url: <pre>notehub.org/.../title?theme=dark</pre>
column-geeks = For Geeks!
column-geeks-long = NoteHub was an one-app-one-language experiment and is implemented entirely in [Clojure](http://clojure.org) and ClojureScript. Its [source code](https://github.com/chmllr/NoteHub) can be found on GitHub. Look at the code to find some undocumented NoteHub features (or bugs) and &mdash; feel free to contribute! (If you are interested in more detailed code overview, read [this note](http://notehub.org).) 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](http://redis.io). Currently, NoteHub is hosted for free on [Heroku](http://heroku.com). Send your feedback and comments directly to [@chmllr](http://twitter.com/chmllr) or [open an issue](https://github.com/chmllr/NoteHub/issues) on GitHub.
status-404 = Nothing found.
status-400 = Bad Request.
status-500 = OMG, Server Exploded.
created-by = Created by [@chmllr](http://twitter.com/chmllr)
loading = Loading...
preview = Preview
publish = Publish
published = Published
article-views = Article Views
statistics = Statistics
new-note = New Markdown Note

1
project.clj

@ -1,6 +1,7 @@
(defproject NoteHub "0.1.0-SNAPSHOT" (defproject NoteHub "0.1.0-SNAPSHOT"
:description "A free and anonymous hosting for markdown pages." :description "A free and anonymous hosting for markdown pages."
:dependencies [[org.clojure/clojure "1.4.0"] :dependencies [[org.clojure/clojure "1.4.0"]
[org.clojure/clojure-contrib "1.2.0"]
[hiccup "1.0.0"] [hiccup "1.0.0"]
[cssgen "0.2.6"] [cssgen "0.2.6"]
[jayq "0.1.0-alpha2"] [jayq "0.1.0-alpha2"]

9
src/NoteHub/crossover/lib.clj

@ -4,15 +4,16 @@
(defn hash (defn hash
"A simple hash-function, which computes a hash from the text field "A simple hash-function, which computes a hash from the text field
content and given session number. It is intended to be used as a spam content and given session number. It is intended to be used as a spam
protection / captcha alternative. (Probably doesn't work for URF-16)" protection / captcha alternative (let's see whether spambots evaluate heavy JS).
(Probably doesn't work for UTF-16)"
[f s] [f s]
(let [short-mod #(mod % 32767) (let [short-mod #(mod % 32767)
char-codes (map f char-codes (map f (remove #(contains? #{"\n" "\r"} %) (map str s)))
(filter #(not (contains? #{"\n" "\r"} %)) (map str s)))] zip-with-index (map list char-codes (range))]
(reduce (reduce
#(short-mod (+ % #(short-mod (+ %
(short-mod (* (first %2) (short-mod (* (first %2)
((if (odd? %) ((if (odd? %)
bit-xor bit-xor
bit-and) 16381 (second %2)))))) bit-and) 16381 (second %2))))))
0 (map list char-codes (range))))) 0 zip-with-index)))

26
src/NoteHub/settings.clj

@ -1,19 +1,33 @@
(ns NoteHub.settings (ns NoteHub.settings
(:require [clojure.contrib.string :as ccs])
(:refer-clojure :exclude [replace reverse]) (:refer-clojure :exclude [replace reverse])
(:use [clojure.string])) (:use [clojure.string]))
; Load and parse te settings file returning a map ; Loads and parses the settings file; returns a key-value map.
(def settings-map ; Assumes, that all string of the setings file are in format:
(let [file-content (slurp "settings") ; key = value
lines (split file-content #"\n") (defn- get-pairs-map [file]
pairs (map #(map trim (split % #"=")) lines)] (let [file-content (slurp file)
pairs (map #(map trim (split % #"=" 2))
(remove ccs/blank? (ccs/split-lines file-content)))]
(apply hash-map (apply hash-map
(mapcat #(list (keyword (first %)) (second %)) pairs)))) (mapcat #(list (keyword (first %)) (second %)) pairs))))
(def settings-map
(get-pairs-map "settings"))
(def messages-map
(get-pairs-map "messages"))
(defn get-message [key]
"Returns messages used in layouts. Every key should be a keyword, e.g. (get-message :title)."
(messages-map key))
(defn get-setting (defn get-setting
"Takes a settings key, a converter function and a default value, and returns a corresponding "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. 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." The converter function can be provided to convert the setting from string to a needed type.
Every key should be a keyword, e.g. (get-setting :page-width)."
[key & more] [key & more]
(let [converter (first more) (let [converter (first more)
default (second more) default (second more)

1
src/NoteHub/storage.clj

@ -13,7 +13,6 @@
(def note "note") (def note "note")
(def views "views") (def views "views")
; Concatenates all fields to a string ; Concatenates all fields to a string
(defn- build-key [[year month day] title] (defn- build-key [[year month day] title]
(print-str year month day title)) (print-str year month day title))

3
src/NoteHub/views/common.clj

@ -1,5 +1,6 @@
(ns NoteHub.views.common (ns NoteHub.views.common
(:use (:use
[NoteHub.settings :only [get-message]]
[NoteHub.views.css-generator] [NoteHub.views.css-generator]
[noir.core :only [defpartial]] [noir.core :only [defpartial]]
[hiccup.page :only [include-js html5]] [hiccup.page :only [include-js html5]]
@ -10,7 +11,7 @@
[params title & content] [params title & content]
(html5 (html5
[:head [:head
[:title "NoteHub &mdash; " title] [:title (print-str (get-message :name) "&mdash;" title)]
[:link {:href [:link {:href
(clojure.string/replace (clojure.string/replace
(str "http://fonts.googleapis.com/css?family=" (str "http://fonts.googleapis.com/css?family="

19
src/NoteHub/views/css_generator.clj

@ -54,12 +54,19 @@
background-halftone (color theme :background-halftone) background-halftone (color theme :background-halftone)
foreground-halftone (color theme :foreground-halftone)] foreground-halftone (color theme :foreground-halftone)]
(css (css
(rule "a"
:color :#097
:text-decoration :none
:border-bottom [:1px :dotted]
(rule "&:hover"
:color :#0a8)
(rule "&:visited"
:color :#054))
(rule ".landing-button" (rule ".landing-button"
:box-shadow [0 :2px :5px :#aaa] :box-shadow [0 :2px :5px :#aaa]
:text-decoration :none :text-decoration :none
:font-size :1.5em :font-size :1.5em
:background :#0a2 :background :#0a2
:color :white
:border :none :border :none
:border-radius :10px :border-radius :10px
:padding :10px :padding :10px
@ -77,6 +84,7 @@
(rule "td" (rule "td"
:padding :0.5em) :padding :0.5em)
(rule ".one-third-column" (rule ".one-third-column"
:line-height (% 120)
:text-align :justify :text-align :justify
:vertical-align :top :vertical-align :top
; Replace this by arithmetic with css-lengths as soon as they fix the bug ; Replace this by arithmetic with css-lengths as soon as they fix the bug
@ -94,16 +102,19 @@
:margin :2em)) :margin :2em))
(rule "article" (rule "article"
central-element central-element
:line-height (% 140)
:font-family text-fonts :font-family text-fonts
:text-align :justify :text-align :justify
:font-size :1.2em :font-size :1.2em
(rule "p"
:line-height (% 140))
(rule "& > h1:first-child" (rule "& > h1:first-child"
:text-align :center :text-align :center
:margin :2em)) :margin :2em))
(rule ".centered"
:text-align :center)
(rule "pre" (rule "pre"
:border-radius :3px :border-radius :3px
:padding :1em :padding :0.5em
:border [:1px :dotted foreground-halftone] :border [:1px :dotted foreground-halftone]
:background background-halftone) :background background-halftone)
(rule "*:focus" (rule "*:focus"
@ -129,7 +140,7 @@
central-element) central-element)
(rule "h1" (rule "h1"
:font-size :2em) :font-size :2em)
(rule "#preview-start-line" (rule ".dashed-line"
:border-bottom [:1px :dashed foreground-halftone] :border-bottom [:1px :dashed foreground-halftone]
:margin-bottom :5em) :margin-bottom :5em)
(rule "h1, h2, h3, h4" (rule "h1, h2, h3, h4"

90
src/NoteHub/views/pages.clj

@ -1,5 +1,6 @@
(ns NoteHub.views.pages (ns NoteHub.views.pages
(:require [NoteHub.crossover.lib :as lib]) (:require [NoteHub.crossover.lib :as lib]
[clojure.contrib.string :as ccs])
(:use (:use
[NoteHub.storage] [NoteHub.storage]
[NoteHub.settings] [NoteHub.settings]
@ -34,23 +35,18 @@
(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 ; Sets a custom message for each corresponding HTTP status
(defn page-setter [code message] (doseq [code [400 404 500]]
(set-page! code (set-page! code
(layout message (let [message (get-message (keyword (str "status-" code)))]
[:article (layout message
[:h1 message]]))) [:article [:h1 message]]))))
; Sets a message for each corresponding HTTP status
(page-setter 404 "Nothing Found.")
(page-setter 400 "Bad request.")
(page-setter 500 "OMG, Server Exploded.")
; Routes ; Routes
; ====== ; ======
; This function answers to a AJAX request: it gets a sesion key and markdown text. ; This function answers to a AJAX request: it gets a sesion key and markdown text.
; IT return html version of the provided markdown and a new session key ; It returns the html code of the provided markdown and a new session key.
(defremote get-preview-md [session-key md] (defremote get-preview-md [session-key md]
(when (flash-get session-key) (when (flash-get session-key)
{:session-key (get-flash-key) {:session-key (get-flash-key)
@ -58,58 +54,38 @@
; Landing Page ; Landing Page
(defpage "/" {} (defpage "/" {}
(layout "Free Markdown Hosting" (layout (get-message :title)
[:div#hero [:div#hero
[:h1 "NoteHub"] [:h1 (get-message :name)]
[:h2 "Free and hassle-free hosting for markdown pages."] [:h2 (get-message :title)]
[:br] [:br]
[:a.landing-button {:href "/new"} "New Page"]] [:a.landing-button {:href "/new" :style "color: white"} (get-message :new-page)]]
[:div#preview-start-line] [:div.dashed-line]
[:table.central-element.helvetica-neue [:table.central-element.helvetica-neue
[:tr [:tr
[:td.one-third-column [:td.one-third-column
[:h2 "Why?"] [:h2 (get-message :column-why)] (md-to-html (get-message :column-why-long))]
"Not every person, who occasionally wants to express some thoughts, needs a blog. [:td.one-third-column
Blogs are <b>tedious</b> for writers and for readers. Most people are not interested in thoughts [:h2 (get-message :column-how)] (md-to-html (get-message :column-how-long))]
of other random people. Moreover, nowadays everything rotates around social networks and not [:td.one-third-column
individual blogs. It makes much more sense to publish something somewhere and to share [:h2 (get-message :column-geeks)] (md-to-html (get-message :column-geeks-long))]]]
the link with the audience on the community or social network of your choice, than to maintain a blog [:div.centered.helvetica-neue (md-to-html (get-message :created-by))]))
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 &mdash; 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" {}
(layout {:js true} "New Markdown Note" (layout {:js true} (get-message :new-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 (get-message :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} (get-message :publish))
[:button#preview-button.button {:type :button [:button#preview-button.button {:type :button
:style "float: right"} "Preview"]])] :style "float: right"} (get-message :preview)]])]
[:div#preview-start-line.hidden] [:div#preview-start-line.dashed-line.hidden]
[:article#preview])) [:article#preview]))
; Display the note ; Display the note
@ -127,14 +103,14 @@
(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
(layout "Statistics" (layout (get-message :statistics)
[:article.helvetica-neue [:article.helvetica-neue
[:table {:style "width: 100%"} [:table {:style "width: 100%"}
[:tr [:tr
[:td "Published"] [:td (get-message :published)]
[:td (interpose "-" [year month day])]] [:td (interpose "-" [year month day])]]
[:tr [:tr
[:td "Article views"] [:td (get-message :article-views)]
[:td views]]]]) [:td views]]]])
(status 404 (get-page 404))))) (status 404 (get-page 404)))))
@ -151,11 +127,11 @@
(let [[year month day] (map #(+ (second %) (.get (Calendar/getInstance) (first %))) (let [[year month day] (map #(+ (second %) (.get (Calendar/getInstance) (first %)))
{Calendar/YEAR 0, Calendar/MONTH 1, Calendar/DAY_OF_MONTH 0}) {Calendar/YEAR 0, Calendar/MONTH 1, Calendar/DAY_OF_MONTH 0})
untrimmed-line (filter #(or (= \- %) (Character/isLetterOrDigit %)) untrimmed-line (filter #(or (= \- %) (Character/isLetterOrDigit %))
(-> draft (split #"\n") first (sreplace " " "-") lower-case)) (-> draft ccs/split-lines 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 #(Integer/parseInt %) 80) max-length (get-setting :max-title-length #(Integer/parseInt %) 80)
title-uncut)) proposed-title (apply str (take max-length title-uncut))
date [year month day] date [year month day]
title (first (drop-while #(note-exists? date %) title (first (drop-while #(note-exists? date %)
(cons proposed-title (cons proposed-title

Loading…
Cancel
Save