forked from ibdknox/Noir-blog
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Signed-off-by: Chris Granger <[email protected]>
- Loading branch information
0 parents
commit 7e3e10e
Showing
17 changed files
with
621 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
pom.xml | ||
*jar | ||
/lib/ | ||
/classes/ | ||
.lein-deps-sum | ||
sdb.db |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
# Noir blog | ||
|
||
This is a fully functioning blog, that serves as an example website written in noir. | ||
|
||
## Usage | ||
|
||
```bash | ||
lein deps | ||
lein run | ||
``` | ||
|
||
To login, an initial user is created: | ||
username :: admin | ||
password :: admin | ||
|
||
## License | ||
|
||
Copyright (C) 2011 FIXME | ||
|
||
Distributed under the Eclipse Public License, the same as Clojure. | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
(defproject noir-blog "0.1.0" | ||
:description "A fully functional blog that serves an example of a noir project." | ||
:dependencies [[org.clojure/clojure "1.2.1"] | ||
[clj-time "0.3.0"] | ||
[noir "0.4.0"] | ||
[org.markdownj/markdownj "0.3.0-1.0.2b4"] | ||
[simpledb "0.1.4"]] | ||
:main noir-blog.server) | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
/* Site specific styles. */ | ||
body { font-family: Verdana; background: #2a2b2b; color: #d1d9e1; } | ||
h1 { font-size:32px; color: #004c65; paddin:55px 0 0 0;} | ||
h2 { font-size:24px; } | ||
ul {list-style:none; } | ||
a { color: #56BF90; text-decoration:none;} | ||
input { background:#3a3b3b; border:2px solid #4a4b4b; border-radius:8px; padding:10px; width: 590px; color:#babbbb; font-size:14px; } | ||
textarea { background:#3a3b3b; border:2px solid #4a4b4b; border-radius:8px; padding:10px; width: 590px; color:#babbbb; height:350px; font-size:14px; } | ||
|
||
input + input { margin-top:10px; } | ||
input + textarea { margin-top:10px; } | ||
|
||
|
||
#wrapper {width:800px; margin: 0 auto; padding-top:50px; } | ||
#wrapper > div.content { float:left; display:inline; padding-bottom:100px; } | ||
|
||
#header { float:left; display:inline; width:100%; margin-bottom:30px;} | ||
#header h1 {float:left; display:inline; color:#56BF90; } | ||
#header h1 a { color:#56BF90; } | ||
#header ul {float:right; display:inline; margin-top:15px;} | ||
#header ul li {float:left; display:inline; margin-left:15px;} | ||
|
||
ul.actions {float:right; display:inline; width:140px; } | ||
ul.actions li a { display:block; background:#3a3b3b; border-radius:5px; text-align:center; padding:5px; margin-bottom:10px; } | ||
ul.actions li a:hover { background:#4a4b4b;} | ||
ul.items { float:left; display:inline; width:600px; } | ||
ul.items li a { float:left; width:590px; display:inline; padding:8px 15px; background: #3a3b3b; color: #b1b9c1; border-radius: 5px; margin-bottom:10px; } | ||
|
||
ul.posts { float:left; display:inline; } | ||
ul.posts li.post { float:left; display:inline; width: 760px; padding:15px; background: #3a3b3b; border-radius: 10px; margin-bottom:20px; border:2px solid #4a4b4b; } | ||
ul.posts li.post h2 { float:left; display:inline; width:660px; margin-bottom:10px; } | ||
ul.posts li.post .datetime {float:right; display:inline; font-size:11px; color:#8a8b8b; } | ||
ul.posts li.post .content { float:left; display:inline; width:660px; color:#babbbb;} | ||
ul.posts li.post .content p { margin-bottom:18px; } | ||
|
||
.submit { display:none; } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
html { | ||
margin:0; | ||
padding:0; | ||
border:0; | ||
} | ||
|
||
body, div, span, object, iframe, | ||
h1, h2, h3, h4, h5, h6, p, blockquote, pre, | ||
a, abbr, acronym, address, code, | ||
del, dfn, em, img, q, dl, dt, dd, ol, ul, li, | ||
fieldset, form, label, legend, | ||
table, caption, tbody, tfoot, thead, tr, th, td, | ||
article, aside, dialog, figure, footer, header, | ||
hgroup, nav, section { | ||
margin: 0; | ||
padding: 0; | ||
border: 0; | ||
font-weight: inherit; | ||
font-style: inherit; | ||
font-size: 100%; | ||
font-family: inherit; | ||
vertical-align: baseline; | ||
} | ||
|
||
article, aside, dialog, figure, footer, header, | ||
hgroup, nav, section { | ||
display:block; | ||
} | ||
|
||
body { | ||
line-height: 1.5; | ||
background: white; | ||
} | ||
|
||
table { | ||
border-collapse: separate; | ||
border-spacing: 0; | ||
} | ||
|
||
caption, th, td { | ||
text-align: left; | ||
font-weight: normal; | ||
float:none !important; | ||
} | ||
table, th, td { | ||
vertical-align: middle; | ||
} | ||
|
||
blockquote:before, blockquote:after, q:before, q:after { content: ''; } | ||
blockquote, q { quotes: "" ""; } | ||
|
||
a img { border: none; } | ||
|
||
/*:focus { outline: 0; }*/ | ||
|
||
|
||
|
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
$(document).ready( function() { | ||
|
||
$("a.hider").click( function(event) { | ||
$(this).next().toggle("slow"); | ||
return false; | ||
}); | ||
|
||
$("a.submit").click( function(event) { | ||
|
||
$("form").submit(); | ||
return false; | ||
|
||
}); | ||
|
||
$("a.delete").click( function(event) { | ||
|
||
if( $(this).hasClass("confirm") ) { | ||
//goto link | ||
return true; | ||
} | ||
|
||
$(this).addClass("confirm"); | ||
$(this).text("Click to confirm"); | ||
return false; | ||
|
||
}); | ||
|
||
}); | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
(ns noir-blog.models | ||
(:require [simpledb.core :as db] | ||
[noir-blog.models.user :as users] | ||
[noir-blog.models.post :as posts])) | ||
|
||
(defn initialize [] | ||
(db/init) | ||
(when-not (db/get :users) | ||
;;db values need to be initialized.. this should only happen once. | ||
(users/init!) | ||
(posts/init!))) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,126 @@ | ||
(ns noir-blog.models.post | ||
(:require [simpledb.core :as db] | ||
[clj-time.core :as ctime] | ||
[clj-time.format :as tform] | ||
[clj-time.coerce :as coerce] | ||
[clojure.string :as string] | ||
[noir-blog.models.user :as users] | ||
[noir.validation :as vali] | ||
[noir.session :as session]) | ||
(:import com.petebevin.markdown.MarkdownProcessor)) | ||
|
||
(def posts-per-page 10) | ||
(def date-format (tform/formatter "MM/dd/yy" (ctime/default-time-zone))) | ||
(def time-format (tform/formatter "h:mma" (ctime/default-time-zone))) | ||
(def mdp (MarkdownProcessor.)) | ||
|
||
;; Gets | ||
|
||
(defn total [] | ||
(count (db/get :post-ids))) | ||
|
||
(defn id->post [id] | ||
(db/get-in :posts [id])) | ||
|
||
(defn ids->posts [ids] | ||
(map id->post ids)) | ||
|
||
(defn moniker->post [moniker] | ||
(id->post (db/get-in :post-monikers [moniker]))) | ||
|
||
(defn get-page [page] | ||
(let [page-num (- (Integer. page) 1) ;; make it 1-based indexing | ||
prev (* page-num posts-per-page)] | ||
(ids->posts (take posts-per-page (drop prev (db/get :post-ids)))))) | ||
|
||
(defn get-latest [] | ||
(get-page 1)) | ||
|
||
;; Mutations and checks | ||
|
||
(defn next-id [] | ||
(str (db/update! :next-post-id inc))) | ||
|
||
(defn gen-moniker [title] | ||
(-> title | ||
(string/lower-case) | ||
(string/replace #"[^a-zA-Z0-9\s]" "") | ||
(string/replace #" " "-"))) | ||
|
||
(defn new-moniker? [moniker] | ||
(not (contains? (db/get :post-monikers) moniker))) | ||
|
||
(defn perma-link [moniker] | ||
(str "/blog/view/" moniker)) | ||
|
||
(defn edit-url [{:keys [id]}] | ||
(str "/blog/admin/post/edit/" id)) | ||
|
||
(defn md->html [text] | ||
(. mdp (markdown text))) | ||
|
||
(defn wrap-moniker [{:keys [title] :as post}] | ||
(let [moniker (gen-moniker title)] | ||
(-> post | ||
(assoc :moniker moniker) | ||
(assoc :perma-link (perma-link moniker))))) | ||
|
||
(defn wrap-markdown [{:keys [body] :as post}] | ||
(assoc post :md-body (md->html body))) | ||
|
||
(defn wrap-time [post] | ||
(let [ts (ctime/now)] | ||
(-> post | ||
(assoc :ts (coerce/to-long ts)) | ||
(assoc :date (tform/unparse date-format ts)) | ||
(assoc :tme (tform/unparse time-format ts))))) | ||
|
||
(defn prepare-new [{:keys [title body] :as post}] | ||
(let [id (next-id) | ||
ts (ctime/now)] | ||
(-> post | ||
(assoc :id id) | ||
(assoc :username (users/me)) | ||
(wrap-time) | ||
(wrap-moniker) | ||
(wrap-markdown)))) | ||
|
||
(defn valid? [{:keys [title body]}] | ||
(vali/rule (vali/has-value? title) | ||
[:title "There must be a title"]) | ||
(vali/rule (new-moniker? (gen-moniker title)) | ||
[:title "That title is already taken."]) | ||
(vali/rule (vali/has-value? body) | ||
[:body "There's no post content."]) | ||
(not (vali/errors? :title :body))) | ||
|
||
;; Operations | ||
|
||
(defn add! [post] | ||
(when (valid? post) | ||
(let [{:keys [id moniker] :as final} (prepare-new post)] | ||
(db/update! :posts assoc id final) | ||
(db/update! :post-ids conj id) | ||
(db/update! :post-monikers assoc moniker id)))) | ||
|
||
(defn edit! [{:keys [id title] :as post}] | ||
(let [{orig-moniker :moniker :as original} (id->post id) | ||
{:keys [moniker] :as final} (-> post | ||
(wrap-moniker) | ||
(wrap-markdown))] | ||
(db/update! :posts assoc id (merge original final)) | ||
(db/update! :post-monikers dissoc orig-moniker) ;; remove the old moniker entry in case it changed | ||
(db/update! :post-monikers assoc moniker id))) | ||
|
||
(defn remove! [id] | ||
(let [{:keys [moniker]} (id->post id) | ||
neue-ids (remove #{id} (db/get :post-ids))] | ||
(db/put! :post-ids neue-ids) | ||
(db/update! :posts dissoc id) | ||
(db/update! :post-monikers dissoc moniker))) | ||
|
||
(defn init! [] | ||
(db/put! :next-post-id -1) | ||
(db/put! :posts {}) | ||
(db/put! :post-ids (list)) | ||
(db/put! :post-monikers {})) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
(ns noir-blog.models.user | ||
(:require [simpledb.core :as db] | ||
[noir.util.crypt :as crypt] | ||
[noir.validation :as vali] | ||
[noir.session :as session])) | ||
|
||
;; Gets | ||
|
||
(defn all [] | ||
(vals (db/get :users))) | ||
|
||
(defn get-username [username] | ||
(db/get-in :users [username])) | ||
|
||
(defn admin? [] | ||
(session/get :admin)) | ||
|
||
(defn me [] | ||
(session/get :username)) | ||
|
||
;; Mutations and Checks | ||
|
||
(defn prepare [{password :password :as user}] | ||
(assoc user :password (crypt/encrypt password))) | ||
|
||
(defn valid? [{:keys [username password]}] | ||
(vali/rule (not (db/get-in :users [username])) | ||
[:username "That username is already taken"]) | ||
(vali/rule (vali/min-length? username 3) | ||
[:username "Username must be at least 3 characters."]) | ||
(vali/rule (vali/min-length? password 5) | ||
[:password "Password must be at least 5 characters."]) | ||
(not (vali/errors? :username :password))) | ||
|
||
;; Operations | ||
|
||
(defn- store! [{username :username :as user}] | ||
(db/update! :users assoc username user)) | ||
|
||
(defn login! [{:keys [username password] :as user}] | ||
(let [{stored-pass :password} (get-username username)] | ||
(if (and stored-pass | ||
(crypt/compare password stored-pass)) | ||
(do | ||
(session/put! :admin true) | ||
(session/put! :username username)) | ||
(vali/set-error :username "Invalid username or password")))) | ||
|
||
(defn add! [{:keys [username password]}] | ||
(let [user (-> {:username username :password password} | ||
(prepare))] | ||
(when (valid? user) | ||
(store! user)))) | ||
|
||
(defn edit! [user] | ||
(add! user)) | ||
|
||
(defn remove! [username] | ||
(db/update! :users dissoc username)) | ||
|
||
(defn init! [] | ||
(db/put! :users {}) | ||
(store! (prepare {:username "admin" :password "admin"}))) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
(ns noir-blog.server | ||
(:require [noir.server :as server] | ||
[noir-blog.models :as models])) | ||
|
||
(server/load-views "src/noir_blog/views/") | ||
|
||
(defn -main [& m] | ||
(let [mode (or (first m) :dev)] | ||
(models/initialize) | ||
(server/start 8080 {:mode (keyword mode) | ||
:ns 'noir-blog}))) | ||
|
Oops, something went wrong.