1 ! Copyright (C) 2008 Slava Pestov
2 ! See http://factorcode.org/license.txt for BSD license.
3 USING: kernel accessors sequences sorting math.order math.parser
4 urls validators html.components db.types db.tuples calendar
5 http.server.dispatchers
6 furnace furnace.actions furnace.auth.login furnace.boilerplate
7 furnace.sessions furnace.syndication ;
10 TUPLE: blogs < dispatcher ;
12 : view-post-url ( id -- url )
13 number>string "$blogs/post/" prepend >url ;
15 : view-comment-url ( parent id -- url )
16 [ view-post-url ] dip >>anchor ;
18 : list-posts-url ( -- url )
21 : user-posts-url ( author -- url )
22 "$blogs/by/" prepend >url ;
24 TUPLE: entity id author date content ;
26 GENERIC: entity-url ( entity -- url )
28 M: entity feed-entry-url entity-url ;
31 { "id" "ID" INTEGER +db-assigned-id+ }
32 { "author" "AUTHOR" { VARCHAR 256 } +not-null+ } ! uid
33 { "date" "DATE" TIMESTAMP +not-null+ }
34 { "content" "CONTENT" TEXT +not-null+ }
37 M: entity feed-entry-date date>> ;
39 TUPLE: post < entity title comments ;
41 M: post feed-entry-title
42 [ author>> ] [ drop ": " ] [ title>> ] tri 3append ;
48 { "title" "TITLE" { VARCHAR 256 } +not-null+ }
51 : <post> ( id -- post ) \ post new swap >>id ;
53 : init-posts-table ( -- ) \ post ensure-table ;
55 TUPLE: comment < entity parent ;
58 { "parent" "PARENT" INTEGER +not-null+ } ! post id
61 M: comment feed-entry-title
62 author>> "Comment by " prepend ;
65 [ parent>> ] [ id>> ] bi view-comment-url ;
67 : <comment> ( parent id -- post )
72 : init-comments-table ( -- ) comment ensure-table ;
75 [ <post> select-tuple ] [ f <comment> select-tuples ] bi
78 : reverse-chronological-order ( seq -- sorted )
79 [ [ date>> ] compare invert-comparison ] sort ;
81 : validate-author ( -- )
82 { { "author" [ [ v-username ] v-optional ] } } validate-params ;
84 : list-posts ( -- posts )
85 f <post> "author" value >>author
86 select-tuples [ dup id>> f <comment> count-tuples >>comments ] map
87 reverse-chronological-order ;
89 : <list-posts-action> ( -- action )
92 list-posts "posts" set-value
95 { blogs "list-posts" } >>template ;
97 : <list-posts-feed-action> ( -- action )
99 [ "Recent Posts" ] >>title
100 [ list-posts ] >>entries
101 [ list-posts-url ] >>url ;
103 : <user-posts-action> ( -- action )
108 list-posts "posts" set-value
110 { blogs "user-posts" } >>template ;
112 : <user-posts-feed-action> ( -- action )
114 [ validate-author ] >>init
115 [ "Recent Posts by " "author" value append ] >>title
116 [ list-posts ] >>entries
117 [ "author" value user-posts-url ] >>url ;
119 : <post-feed-action> ( -- action )
121 [ validate-integer-id "id" value post "post" set-value ] >>init
122 [ "post" value feed-entry-title ] >>title
123 [ "post" value entity-url ] >>url
124 [ "post" value comments>> ] >>entries ;
126 : <view-post-action> ( -- action )
132 "id" value post from-object
140 { blogs "view-post" } >>template ;
142 : validate-post ( -- )
144 { "title" [ v-one-line ] }
145 { "content" [ v-required ] }
148 : <new-post-action> ( -- action )
152 uid "author" set-value
157 dup { "title" "content" } deposit-slots
160 [ insert-tuple ] [ entity-url <redirect> ] bi
163 { blogs "new-post" } >>template ;
165 : <edit-post-action> ( -- action )
169 "id" value <post> select-tuple from-object
178 "id" value <post> select-tuple
179 dup { "title" "content" } deposit-slots
180 [ update-tuple ] [ entity-url <redirect> ] bi
183 { blogs "edit-post" } >>template ;
185 : <delete-post-action> ( -- action )
189 { { "author" [ v-username ] } } validate-params
192 "id" value <post> delete-tuples
193 "author" value user-posts-url <redirect>
196 : validate-comment ( -- )
198 { "parent" [ v-integer ] }
199 { "content" [ v-required ] }
202 : <new-comment-action> ( -- action )
207 uid "author" set-value
211 "parent" value f <comment>
212 "content" value >>content
215 [ insert-tuple ] [ entity-url <redirect> ] bi
218 : <delete-comment-action> ( -- action )
222 { { "parent" [ v-integer ] } } validate-params
225 f "id" value <comment> delete-tuples
226 "parent" value view-post-url <redirect>
229 : <blogs> ( -- dispatcher )
231 <list-posts-action> "" add-responder
232 <list-posts-feed-action> "posts.atom" add-responder
233 <user-posts-action> "by" add-responder
234 <user-posts-feed-action> "by.atom" add-responder
235 <view-post-action> "post" add-responder
236 <post-feed-action> "post.atom" add-responder
237 <new-post-action> <protected>
238 "make a new blog post" >>description
239 "new-post" add-responder
240 <edit-post-action> <protected>
241 "edit a blog post" >>description
242 "edit-post" add-responder
243 <delete-post-action> <protected>
244 "delete a blog post" >>description
245 "delete-post" add-responder
246 <new-comment-action> <protected>
247 "make a comment" >>description
248 "new-comment" add-responder
249 <delete-comment-action> <protected>
250 "delete a comment" >>description
251 "delete-comment" add-responder
253 { blogs "blogs-common" } >>template ;