Browse Source

short urls added; landing merged with readme.md

master
Christian Mueller 14 years ago
parent
commit
a2a44acd67
  1. 47
      README.md
  2. 9
      messages
  3. 44
      src/NoteHub/storage.clj
  4. 11
      src/NoteHub/views/common.clj
  5. 2
      src/NoteHub/views/css_generator.clj
  6. 50
      src/NoteHub/views/pages.clj
  7. 14
      test/NoteHub/test/storage.clj
  8. 19
      test/NoteHub/test/views/pages.clj

47
README.md

@ -1,28 +1,43 @@
# NoteHub Readme ## 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 text.
## Writing NoteHub was an one-app-one-language [experiment](/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.
NoteHub's persistence layer is based 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.
A new note can be entered and previewed at [notehub.org/new](http://notehub.org/new). ## Why?
Not every person, who occasionally wants to express some thoughts, needs a blog.
Blogs are __tedious__ for writers and for readers.
As readers we are not interested in every thought of other random people.
As writers, we know, that everything rotates nowadays around social networks and not individual blogs.
It's easier to publish something somewhere and to share the link with the audience on the community or social network of choice, than to maintain a blog trying to keep the readers interested.
## Publishing __NoteHub__ should be the place, where you can publish your thoughts without hassle.
Once a page is published, it gets accessible through an URL of the format: ## How to Use?
First, create [a new page](http://notehub.org/new) using the [Markdown syntax](http://daringfireball.net/projects/markdown/).
When the note is published, you'll see a subtle panel at the bottom of the screen.
From this panel you can go to a rudimentary statistics of the article, or you can export the original markdown, or copy the short url of the note.
Besides this, you also can invert the color scheme by appending to the note url ([example](/2012/6/16/how-notehub-is-built?theme=dark)):
notehub.org/.../title?theme=dark
The same way you can specify a [Google Web Font](http://www.google.com/webfonts/) for headers by appending to the note url:
http://notehub.org/%YEAR/%MONTH/%DAY/%TITLE notehub.org/.../title?header-font=FONT-NAME
and for the text itself:
Hence, every date represents a name space. notehub.org/.../title?header-font=FONT-NAME&text-font=FONT-NAME2
## Exporting & Statistics See an example of the font formatting ([here](/2012/6/16/how-notehub-is-built?header-font=Berkshire+Swash&text-font=Swanky+and+Moo+Moo)).
By appending of `/stat` 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.
## Displaying After you've specified this in the url, you can copy the corresponding short url of the article and share it.
There are some experimental features, which allow to change via the note URL the design of the note page. ## Exporting & Statistics
E.g., by appending of `&theme=dark` to the URL, the design colors will be inverted.
Currently it is also possible to change the header and the article text by appending of the Google Webfont names like this:
notehub.org/.../title?header-font=Anton&text-font=Cagliostro By appending of `/stat` 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.

9
messages

@ -2,15 +2,6 @@ title = Free and Hassle-free Hosting for Markdown Pages.
name = NoteHub name = NoteHub
new-page = New Page 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. 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` to its url: <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. The [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](/2012/6/16/how-notehub-is-built) note. NoteHub's design is kept intentionally extremelly simple and minimalistic, and should stay so. 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-404 = Nothing found.
status-400 = Bad Request. status-400 = Bad Request.
status-500 = OMG, Server Exploded. status-500 = OMG, Server Exploded.

44
src/NoteHub/storage.clj

@ -12,6 +12,7 @@
; DB hierarchy levels ; DB hierarchy levels
(def note "note") (def note "note")
(def short-url "short-url")
(def views "views") (def views "views")
(def sessions "sessions") (def sessions "sessions")
@ -65,3 +66,46 @@
(do (do
(redis/hdel db views key) (redis/hdel db views key)
(redis/hdel db note key)))) (redis/hdel db note key))))
(defn short-url-exists?
"Checks whether the provided short url is taken (for testing only)"
[url]
(redis/hexists db short-url url))
(defn resolve-url
"Resolves short url by providing all metadata of the request"
[url]
(let [value (redis/hget db short-url url)]
(when value
(read-string value))))
(defn delete-short-url
"Deletes a short url (for testing only)"
[key]
(let [value (redis/hget db short-url key)]
(do
(redis/hdel db short-url key)
(redis/hdel db short-url value))))
(defn create-short-url
"Creates a short url for the given request metadata or extracts
one if it was already created"
[metadata]
(let [request (str (into (sorted-map) metadata))]
(if (short-url-exists? request)
(redis/hget db short-url request)
(let [hash-stream (partition 5 (repeatedly #(rand-int 36)))
hash-to-string (fn [hash]
(apply str
; map first 10 numbers to digits
; and the rest to chars
(map #(char (+ (if (< 9 %) 87 48) %)) hash)))
url (first
(remove short-url-exists?
(map hash-to-string hash-stream)))]
(do
; we create two mappings: request params -> short url and back,
; s.t. we can later easily check whether a short url already exists
(redis/hset db short-url url request)
(redis/hset db short-url request url)
url)))))

11
src/NoteHub/views/common.clj

@ -12,16 +12,7 @@
(defn url (defn url
"Creates a local url from the given substrings" "Creates a local url from the given substrings"
[& args] [& args]
(let [params (last args) (apply str (interpose "/" (cons "" args))))
params (if (map? params)
(apply str
(interpose "&"
(map #(let [[k v] %] (str k "=" v))
(map #(map name %) params)))))
args (if params (butlast args) args)
params (if params (str "?" params) "")
[leading-slash args] (if (= :local (first args)) ["" (rest args)] ["/" args])]
(str (apply str leading-slash (interpose "/" args)) (apply str params))))
; Creates the main html layout ; Creates the main html layout
(defpartial generate-layout (defpartial generate-layout

2
src/NoteHub/views/css_generator.clj

@ -90,7 +90,7 @@
:background background-halftone :background background-halftone
:padding :0.2em :padding :0.2em
:bottom :0px :bottom :0px
:font-size :0.4em :font-size :0.7em
:text-align :center :text-align :center
(rule "a" (rule "a"
:border :none)) :border :none))

50
src/NoteHub/views/pages.clj

@ -1,6 +1,7 @@
(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]) [clojure.contrib.string :as ccs]
[hiccup.util :as util])
(:use (:use
[NoteHub.storage] [NoteHub.storage]
[NoteHub.settings] [NoteHub.settings]
@ -40,17 +41,21 @@
(status code (get-page code))) (status code (get-page code)))
; Converts given markdown to html and wraps with the main layout ; Converts given markdown to html and wraps with the main layout
(defn- wrap [title params md-text] (defn- wrap [short-url params md-text]
(if md-text (if md-text
(layout params title (layout params (params :title)
[:article (md-to-html md-text)] [:article (md-to-html md-text)]
(let [links (map #(link-to (url :local (str title "/" (name %)) params) (get-message %)) (let [links (map #(link-to
(if (= :short-url %)
(url short-url)
(str (params :title) "/" (name %)))
(get-message %))
[:stats :export :short-url]) [:stats :export :short-url])
space (apply str (repeat 4 "&nbsp;")) space (apply str (repeat 4 "&nbsp;"))
separator (str space "&middot;" space) separator (str space "&middot;" space)
links (interpose separator links)] links (interpose separator links)]
[:div#panel (map identity links)])) [:div#panel (map identity links)]))
(status 404 (get-page 404)))) (response 404)))
(defn get-date (defn get-date
"Returns today's date" "Returns today's date"
@ -76,15 +81,9 @@
[: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]
[:table.central-element.helvetica-neue ; dynamically generates three column, retrieving corresponding messages
[:tr [:article.helvetica-neue {:style "font-size: 1em"}
; dynamically generates three column, retrieving corresponding messages (md-to-html (slurp "README.md"))]
(map
#(html
[:td.one-third-column
[:h2 (get-message %)]
(md-to-html (get-message (keyword (str (name %) "-long"))))])
[:column-why :column-how :column-geeks])]]
[:div.centered.helvetica-neue (md-to-html (get-message :created-by))])) [:div.centered.helvetica-neue (md-to-html (get-message :created-by))]))
; New Note Page ; New Note Page
@ -106,16 +105,19 @@
; 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}
(wrap (wrap
title (create-short-url params)
(select-keys params [:theme :header-font :text-font]) (select-keys params [:title :theme :header-font :text-font])
(get-note [year month day] title))) (get-note [year month day] title)))
; Provides Markdown of the specified note ; Provides Markdown of the specified note
(defpage "/:year/:month/:day/:title/export" {:keys [year month day title]} (defpage "/:year/:month/:day/:title/export" {:keys [year month day title]}
(let [md-text (get-note [year month day] title)] (let [md-text (get-note [year month day] title)]
(if md-text (content-type "text/plain; charset=utf-8" md-text) (response 404)))) (if md-text (content-type "text/plain; charset=utf-8" md-text) (response 404))))
(defpage "/:year/:month/:day/:title/short-url" params
(layout (get-message :short-url) (url (create-short-url params))))
; Provides the number of views of the specified note ; Provides the number of views of the specified note
(defpage "/:year/:month/:day/:title/stats" {:keys [year month day title]} (defpage "/:year/:month/:day/:title/stats" {:keys [year month day title]}
(let [views (get-note-views [year month day] title)] (let [views (get-note-views [year month day] title)]
@ -161,3 +163,15 @@
(set-note date title draft) (set-note date title draft)
(redirect (url year month day (url-encode title))))) (redirect (url year month day (url-encode title)))))
(response 400)))) (response 400))))
; Resolving of a short url
(defpage "/:short-url" {:keys [short-url]}
(let [params (resolve-url short-url)]
(if params
(let [{:keys [year month day title]} params
rest-params (dissoc params :year :month :day :title)
core-url (url year month day title)
long-url (if (empty? rest-params) core-url (util/url core-url rest-params))]
(redirect long-url))
(response 404))))

14
test/NoteHub/test/storage.clj

@ -4,9 +4,23 @@
(def date [2012 06 03]) (def date [2012 06 03])
(def test-title "Some title.") (def test-title "Some title.")
(def test-note "This is a test note.") (def test-note "This is a test note.")
(def metadata {:year 2012,
:month 6,
:day 23,
:title test-title,
:theme "dark",
:header-font "Anton"})
(deftest storage (deftest storage
(testing "Storage" (testing "Storage"
(testing "of short-url mechanism"
(let [url (create-short-url metadata)]
(is (short-url-exists? url))
(is (= metadata (resolve-url url)))
(is (not (do
(delete-short-url url)
(short-url-exists? url))))))
(testing "of correct note creation" (testing "of correct note creation"
(is (= (do (is (= (do
(set-note date test-title test-note) (set-note date test-title test-note)

19
test/NoteHub/test/views/pages.clj

@ -20,14 +20,6 @@
(is (= (url 2010 05 06 "test-title" "export") "/2010/5/6/test-title/export")) (is (= (url 2010 05 06 "test-title" "export") "/2010/5/6/test-title/export"))
(is (= (url 2010 05 06 "test-title" "export" {"theme" "dark"}) "/2010/5/6/test-title/export?theme=dark"))
(is (= (url 2010 05 06 "test-title" "export" {:theme "dark"}) "/2010/5/6/test-title/export?theme=dark"))
(is (= (url 2010 05 06 "test-title" "export" {:theme :dark}) "/2010/5/6/test-title/export?theme=dark"))
(is (= (url :local 2010 05 06 "test-title" "export" {:theme :dark}) "2010/5/6/test-title/export?theme=dark"))
(deftest testing-fixture (deftest testing-fixture
(testing "Was a not created?" (testing "Was a not created?"
(is (= (get-note date test-title) test-note)) (is (= (get-note date test-title) test-note))
@ -73,9 +65,8 @@
(testing "of corrupt note-post" (testing "of corrupt note-post"
(is (has-status (send-request [:post "/post-note"]) 400))) (is (has-status (send-request [:post "/post-note"]) 400)))
(testing "valid accesses" (testing "valid accesses"
(is (has-status (send-request "/new") 200)) ;(is (has-status (send-request "/new") 200) "accessing /new")
(is (has-status (send-request (url 2012 6 3 "some-title")) 200)) (is (has-status (send-request (url 2012 6 3 "some-title")) 200) "accessing test note")
(is (has-status (send-request (url 2012 6 3 "some-title" "export")) 200)) (is (has-status (send-request (url 2012 6 3 "some-title" "export")) 200) "accessing test note's export")
(is (has-status (send-request (url 2012 6 3 "some-title" "stat")) 200)) (is (has-status (send-request (url 2012 6 3 "some-title" "stats")) 200) "accessing test note's stats")
(is (has-status (send-request (url 2012 6 3 "some-title")) 200)) (is (has-status (send-request "/") 200) "accessing landing page"))))
(is (has-status (send-request "/") 200)))))

Loading…
Cancel
Save