@ -5,7 +5,7 @@
[ NoteHub.settings ]
[ NoteHub.settings ]
[ NoteHub.views.common ]
[ NoteHub.views.common ]
[ clojure.string :rename { replace sreplace }
[ clojure.string :rename { replace sreplace }
:only [ escape split replace blank? split-lines lower-case ] ]
:only [ escape split replace blank? split-lines lower-case ] ]
[ clojure.core.incubator :only [ -?> ] ]
[ clojure.core.incubator :only [ -?> ] ]
[ hiccup.form ]
[ hiccup.form ]
[ hiccup.core ]
[ hiccup.core ]
@ -85,48 +85,48 @@
; Landing Page
; Landing Page
( defpage "/" { }
( defpage "/" { }
( layout ( get-message :title )
( layout ( get-message :page- title )
[ :div#hero
[ :div#hero
[ :h1 ( get-message :name ) ]
[ :h1 ( get-message :name ) ]
[ :h2 ( get-message :title ) ]
[ :h2 ( get-message :title ) ]
[ :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 ]
; dynamically generates three column, retrieving corresponding messages
; dynamically generates three column, retrieving corresponding messages
[ :article.helvetica-neue.bottom-space { :style "font-size: 1em" }
[ :article.helvetica-neue.bottom-space { :style "font-size: 1em" }
( md-to-html ( slurp "README .md" ) ) ]
( md-to-html ( slurp "LANDING .md" ) ) ]
[ :div.centered.helvetica-neue ( md-to-html ( get-message :created-by ) ) ] ) )
[ :div.centered.helvetica-neue ( md-to-html ( get-message :footer ) ) ] ) )
; input form for the markdown text with a preview area
; input form for the markdown text with a preview area
( defpartial input-form [ form-url command fields content passwd-msg ]
( defpartial input-form [ form-url command fields content passwd-msg ]
( let [ css-class ( when ( = :publish command ) :hidden ) ]
( let [ css-class ( when ( = :publish command ) :hidden ) ]
( layout { :js true } ( get-message :new-note )
( layout { :js true } ( get-message :new-note )
[ :article#preview " " ]
[ :article#preview " " ]
[ :div#dashed-line { :class css-class } ]
[ :div#dashed-line { :class css-class } ]
[ :div.central-element.helvetica-neue { :style "margin-bottom: 3em" }
[ :div.central-element.helvetica-neue { :style "margin-bottom: 3em" }
( form-to { :autocomplete :off } [ :post form-url ]
( form-to { :autocomplete :off } [ :post form-url ]
( hidden-field :action command )
( hidden-field :action command )
( hidden-field :password )
( hidden-field :password )
fields
fields
( text-area { :class :max-width } :draft content )
( text-area { :class :max-width } :draft content )
[ :fieldset#input-elems { :class css-class }
[ :fieldset#input-elems { :class css-class }
( text-field { :class "ui-elem" :placeholder ( get-message passwd-msg ) }
( text-field { :class "ui-elem" :placeholder ( get-message passwd-msg ) }
:plain-password )
:plain-password )
( submit-button { :class "button ui-elem"
( submit-button { :class "button ui-elem"
:id :publish-button } ( get-message command ) ) ] ) ] ) ) )
:id :publish-button } ( get-message command ) ) ] ) ] ) ) )
; Update Note Page
; Update Note Page
( defpage "/:year/:month/:day/:title/edit" { :keys [ year month day title ] }
( defpage "/:year/:month/:day/:title/edit" { :keys [ year month day title ] }
( input-form "/update-note" :update
( input-form "/update-note" :update
( html ( hidden-field :key ( build-key [ year month day ] title ) ) )
( html ( hidden-field :key ( build-key [ year month day ] title ) ) )
( get-note [ year month day ] title ) :enter-passwd ) )
( get-note [ year month day ] title ) :enter-passwd ) )
; New Note Page
; New Note Page
( defpage "/new" { }
( defpage "/new" { }
( input-form "/post-note" :publish
( input-form "/post-note" :publish
( html ( hidden-field :session-key ( create-session ) )
( html ( hidden-field :session-key ( create-session ) )
( hidden-field { :id :session-value } :session-value ) )
( hidden-field { :id :session-value } :session-value ) )
( get-message :loading ) :set-passwd ) )
( get-message :loading ) :set-passwd ) )
; 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 }
@ -137,66 +137,66 @@
; 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 ] }
( when-let [ md-text ( get-note [ year month day ] title ) ]
( when-let [ md-text ( get-note [ year month day ] title ) ]
( content-type "text/plain; charset=utf-8" md-text ) ) )
( content-type "text/plain; charset=utf-8" md-text ) ) )
; 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 ] }
( when-let [ views ( get-note-views [ year month day ] title ) ]
( when-let [ views ( get-note-views [ year month day ] title ) ]
( layout ( get-message :statistics )
( layout ( get-message :statistics )
[ :table#stats.helvetica-neue.central-element
[ :table#stats.helvetica-neue.central-element
[ :tr
[ :tr
[ :td ( get-message :published ) ]
[ :td ( get-message :published ) ]
[ :td ( interpose "-" [ year month day ] ) ] ]
[ :td ( interpose "-" [ year month day ] ) ] ]
[ :tr
[ :tr
[ :td ( get-message :article-views ) ]
[ :td ( get-message :article-views ) ]
[ :td views ] ] ] ) ) )
[ :td views ] ] ] ) ) )
; Updates a note
; Updates a note
( defpage [ :post "/update-note" ] { :keys [ key draft password ] }
( defpage [ :post "/update-note" ] { :keys [ key draft password ] }
( if ( update-note key draft password )
( if ( update-note key draft password )
( redirect ( apply url ( split key # " " ) ) )
( redirect ( apply url ( split key # " " ) ) )
( response 403 ) ) )
( response 403 ) ) )
; New Note Posting — the most "complex" function in the entire app ;)
; New Note Posting — the most "complex" function in the entire app ;)
( defpage [ :post "/post-note" ] { :keys [ draft password session-key session-value ] }
( defpage [ :post "/post-note" ] { :keys [ draft password session-key session-value ] }
; first we collect all info needed to evaluate the validity of the note creation request
; first we collect all info needed to evaluate the validity of the note creation request
( let [ valid-session ( invalidate-session session-key ) ; was the note posted from a newly generated form?
( let [ valid-session ( invalidate-session session-key ) ; was the note posted from a newly generated form?
valid-draft ( not ( blank? draft ) ) ; has the note a meaningful content?
valid-draft ( not ( blank? draft ) ) ; has the note a meaningful content?
; is the hash code correct?
; is the hash code correct?
valid-hash ( try
valid-hash ( try
( = ( Short/parseShort session-value )
( = ( Short/parseShort session-value )
( get-hash ( str draft session-key ) ) )
( get-hash ( str draft session-key ) ) )
( catch Exception e nil ) ) ]
( catch Exception e nil ) ) ]
; check whether the new note can be added
; check whether the new note can be added
( if ( and valid-session valid-draft valid-hash )
( if ( and valid-session valid-draft valid-hash )
; if yes, we compute the current date, extract a title string from the text,
; if yes, we compute the current date, extract a title string from the text,
; which will be a part of the url and look whether this title is free today;
; which will be a part of the url and look whether this title is free today;
; if not, append "-n", where "n" is the next free number
; if not, append "-n", where "n" is the next free number
( let [ [ year month day ] ( get-date )
( let [ [ year month day ] ( get-date )
untrimmed-line ( filter # ( or ( = \- % ) ( Character/isLetterOrDigit % ) )
untrimmed-line ( filter # ( or ( = \- % ) ( Character/isLetterOrDigit % ) )
( -> draft split-lines first ( sreplace " " "-" ) lower-case ) )
( -> draft 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 )
max-length ( get-setting :max-title-length # ( Integer/parseInt % ) 80 )
max-length ( get-setting :max-title-length # ( Integer/parseInt % ) 80 )
; TODO: replace to ccs/take when it gets fixed
; TODO: replace to ccs/take when it gets fixed
proposed-title ( apply str ( take max-length 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
( map # ( str proposed-title "-" ( + 2 % ) ) ( range ) ) ) ) ) ]
( map # ( str proposed-title "-" ( + 2 % ) ) ( range ) ) ) ) ) ]
( do
( do
( set-note date title draft password )
( set-note date title draft password )
( redirect ( url year month day title ) ) ) )
( redirect ( url year month day title ) ) ) )
( response 400 ) ) ) )
( response 400 ) ) ) )
; Resolving of a short url
; Resolving of a short url
( defpage "/:short-url" { :keys [ short-url ] }
( defpage "/:short-url" { :keys [ short-url ] }
( when-let [ params ( resolve-url short-url ) ]
( when-let [ params ( resolve-url short-url ) ]
( let [ { :keys [ year month day title ] } params
( let [ { :keys [ year month day title ] } params
rest-params ( dissoc params :year :month :day :title )
rest-params ( dissoc params :year :month :day :title )
core-url ( url 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 ) ) ]
long-url ( if ( empty? rest-params ) core-url ( util/url core-url rest-params ) ) ]
( redirect long-url ) ) ) )
( redirect long-url ) ) ) )