From a2a44acd67f105f7f8821e6f71b2115edf9f10bb Mon Sep 17 00:00:00 2001 From: Christian Mueller Date: Sun, 24 Jun 2012 07:09:15 +0200 Subject: [PATCH] short urls added; landing merged with readme.md --- README.md | 47 ++++++++++++++++++--------- messages | 9 ------ src/NoteHub/storage.clj | 44 +++++++++++++++++++++++++ src/NoteHub/views/common.clj | 11 +------ src/NoteHub/views/css_generator.clj | 2 +- src/NoteHub/views/pages.clj | 50 ++++++++++++++++++----------- test/NoteHub/test/storage.clj | 14 ++++++++ test/NoteHub/test/views/pages.clj | 19 +++-------- 8 files changed, 128 insertions(+), 68 deletions(-) diff --git a/README.md b/README.md index 6ed3240..a74c64d 100644 --- a/README.md +++ b/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. -## 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 - -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. +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)). -## 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. -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: +## Exporting & Statistics - 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. diff --git a/messages b/messages index d67fd12..ad2ec22 100644 --- a/messages +++ b/messages @@ -2,15 +2,6 @@ title = Free and Hassle-free Hosting for Markdown Pages. 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. 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` to its url:
notehub.org/.../title/export
You also can invert the color scheme by appending `?theme=dark` to the note url:
notehub.org/.../title?theme=dark
- -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 — 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-400 = Bad Request. status-500 = OMG, Server Exploded. diff --git a/src/NoteHub/storage.clj b/src/NoteHub/storage.clj index 52981b1..3d95d72 100644 --- a/src/NoteHub/storage.clj +++ b/src/NoteHub/storage.clj @@ -12,6 +12,7 @@ ; DB hierarchy levels (def note "note") +(def short-url "short-url") (def views "views") (def sessions "sessions") @@ -65,3 +66,46 @@ (do (redis/hdel db views 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))))) diff --git a/src/NoteHub/views/common.clj b/src/NoteHub/views/common.clj index 7cbefbe..fc94408 100644 --- a/src/NoteHub/views/common.clj +++ b/src/NoteHub/views/common.clj @@ -12,16 +12,7 @@ (defn url "Creates a local url from the given substrings" [& args] - (let [params (last 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)))) + (apply str (interpose "/" (cons "" args)))) ; Creates the main html layout (defpartial generate-layout diff --git a/src/NoteHub/views/css_generator.clj b/src/NoteHub/views/css_generator.clj index b78a005..af4fefa 100644 --- a/src/NoteHub/views/css_generator.clj +++ b/src/NoteHub/views/css_generator.clj @@ -90,7 +90,7 @@ :background background-halftone :padding :0.2em :bottom :0px - :font-size :0.4em + :font-size :0.7em :text-align :center (rule "a" :border :none)) diff --git a/src/NoteHub/views/pages.clj b/src/NoteHub/views/pages.clj index 9f8e82e..9a57243 100644 --- a/src/NoteHub/views/pages.clj +++ b/src/NoteHub/views/pages.clj @@ -1,6 +1,7 @@ (ns NoteHub.views.pages (:require [NoteHub.crossover.lib :as lib] - [clojure.contrib.string :as ccs]) + [clojure.contrib.string :as ccs] + [hiccup.util :as util]) (:use [NoteHub.storage] [NoteHub.settings] @@ -40,17 +41,21 @@ (status code (get-page code))) ; 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 - (layout params title + (layout params (params :title) [: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]) space (apply str (repeat 4 " ")) separator (str space "·" space) links (interpose separator links)] [:div#panel (map identity links)])) - (status 404 (get-page 404)))) + (response 404))) (defn get-date "Returns today's date" @@ -76,15 +81,9 @@ [:br] [:a.landing-button {:href "/new" :style "color: white"} (get-message :new-page)]] [:div.dashed-line] - [:table.central-element.helvetica-neue - [:tr - ; dynamically generates three column, retrieving corresponding messages - (map - #(html - [:td.one-third-column - [:h2 (get-message %)] - (md-to-html (get-message (keyword (str (name %) "-long"))))]) - [:column-why :column-how :column-geeks])]] + ; dynamically generates three column, retrieving corresponding messages + [:article.helvetica-neue {:style "font-size: 1em"} + (md-to-html (slurp "README.md"))] [:div.centered.helvetica-neue (md-to-html (get-message :created-by))])) ; New Note Page @@ -106,16 +105,19 @@ ; Displays the note (defpage "/:year/:month/:day/:title" {:keys [year month day title theme header-font text-font] :as params} - (wrap - title - (select-keys params [:theme :header-font :text-font]) - (get-note [year month day] title))) + (wrap + (create-short-url params) + (select-keys params [:title :theme :header-font :text-font]) + (get-note [year month day] title))) ; Provides Markdown of the specified note (defpage "/:year/:month/:day/:title/export" {:keys [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)))) +(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 (defpage "/:year/:month/:day/:title/stats" {:keys [year month day title]} (let [views (get-note-views [year month day] title)] @@ -161,3 +163,15 @@ (set-note date title draft) (redirect (url year month day (url-encode title))))) (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)))) + diff --git a/test/NoteHub/test/storage.clj b/test/NoteHub/test/storage.clj index 653957e..4fcb6ce 100644 --- a/test/NoteHub/test/storage.clj +++ b/test/NoteHub/test/storage.clj @@ -4,9 +4,23 @@ (def date [2012 06 03]) (def test-title "Some title.") (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 (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" (is (= (do (set-note date test-title test-note) diff --git a/test/NoteHub/test/views/pages.clj b/test/NoteHub/test/views/pages.clj index 58006d8..da0d6e6 100644 --- a/test/NoteHub/test/views/pages.clj +++ b/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" {"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 (testing "Was a not created?" (is (= (get-note date test-title) test-note)) @@ -73,9 +65,8 @@ (testing "of corrupt note-post" (is (has-status (send-request [:post "/post-note"]) 400))) (testing "valid accesses" - (is (has-status (send-request "/new") 200)) - (is (has-status (send-request (url 2012 6 3 "some-title")) 200)) - (is (has-status (send-request (url 2012 6 3 "some-title" "export")) 200)) - (is (has-status (send-request (url 2012 6 3 "some-title" "stat")) 200)) - (is (has-status (send-request (url 2012 6 3 "some-title")) 200)) - (is (has-status (send-request "/") 200))))) + ;(is (has-status (send-request "/new") 200) "accessing /new") + (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) "accessing test note's export") + (is (has-status (send-request (url 2012 6 3 "some-title" "stats")) 200) "accessing test note's stats") + (is (has-status (send-request "/") 200) "accessing landing page"))))