! Copyright (C) 2008 Slava Pestov ! See http://factorcode.org/license.txt for BSD license. USING: kernel accessors sequences sorting math.order math.parser urls validators html.components db.types db.tuples calendar http.server.dispatchers furnace furnace.actions furnace.auth.login furnace.boilerplate furnace.sessions furnace.syndication ; IN: webapps.blogs TUPLE: blogs < dispatcher ; : view-post-url ( id -- url ) number>string "$blogs/post/" prepend >url ; : view-comment-url ( parent id -- url ) [ view-post-url ] dip >>anchor ; : list-posts-url ( -- url ) URL" $blogs/" ; : user-posts-url ( author -- url ) "$blogs/by/" prepend >url ; TUPLE: entity id author date content ; GENERIC: entity-url ( entity -- url ) M: entity feed-entry-url entity-url ; entity f { { "id" "ID" INTEGER +db-assigned-id+ } { "author" "AUTHOR" { VARCHAR 256 } +not-null+ } ! uid { "date" "DATE" TIMESTAMP +not-null+ } { "content" "CONTENT" TEXT +not-null+ } } define-persistent M: entity feed-entry-date date>> ; TUPLE: post < entity title comments ; M: post feed-entry-title [ author>> ] [ drop ": " ] [ title>> ] tri 3append ; M: post entity-url id>> view-post-url ; \ post "BLOG_POSTS" { { "title" "TITLE" { VARCHAR 256 } +not-null+ } } define-persistent : ( id -- post ) \ post new swap >>id ; : init-posts-table ( -- ) \ post ensure-table ; TUPLE: comment < entity parent ; comment "COMMENTS" { { "parent" "PARENT" INTEGER +not-null+ } ! post id } define-persistent M: comment feed-entry-title author>> "Comment by " prepend ; M: comment entity-url [ parent>> ] [ id>> ] bi view-comment-url ; : ( parent id -- post ) comment new swap >>id swap >>parent ; : init-comments-table ( -- ) comment ensure-table ; : post ( id -- post ) [ select-tuple ] [ f select-tuples ] bi >>comments ; : reverse-chronological-order ( seq -- sorted ) [ [ date>> ] compare invert-comparison ] sort ; : validate-author ( -- ) { { "author" [ [ v-username ] v-optional ] } } validate-params ; : list-posts ( -- posts ) f "author" value >>author select-tuples [ dup id>> f count-tuples >>comments ] map reverse-chronological-order ; : ( -- action ) [ list-posts "posts" set-value ] >>init { blogs "list-posts" } >>template ; : ( -- action ) [ "Recent Posts" ] >>title [ list-posts ] >>entries [ list-posts-url ] >>url ; : ( -- action ) "author" >>rest [ validate-author list-posts "posts" set-value ] >>init { blogs "user-posts" } >>template ; : ( -- action ) [ validate-author ] >>init [ "Recent Posts by " "author" value append ] >>title [ list-posts ] >>entries [ "author" value user-posts-url ] >>url ; : ( -- action ) [ validate-integer-id "id" value post "post" set-value ] >>init [ "post" value feed-entry-title ] >>title [ "post" value entity-url ] >>url [ "post" value comments>> ] >>entries ; : ( -- action ) "id" >>rest [ validate-integer-id "id" value post from-object "id" value "new-comment" [ "parent" set-value ] nest-values ] >>init { blogs "view-post" } >>template ; : validate-post ( -- ) { { "title" [ v-one-line ] } { "content" [ v-required ] } } validate-params ; : ( -- action ) [ validate-post uid "author" set-value ] >>validate [ f dup { "title" "content" } deposit-slots uid >>author now >>date [ insert-tuple ] [ entity-url ] bi ] >>submit { blogs "new-post" } >>template ; : ( -- action ) [ validate-integer-id "id" value select-tuple from-object ] >>init [ validate-integer-id validate-post ] >>validate [ "id" value select-tuple dup { "title" "content" } deposit-slots [ update-tuple ] [ entity-url ] bi ] >>submit { blogs "edit-post" } >>template ; : ( -- action ) [ validate-integer-id { { "author" [ v-username ] } } validate-params ] >>validate [ "id" value delete-tuples "author" value user-posts-url ] >>submit ; : validate-comment ( -- ) { { "parent" [ v-integer ] } { "content" [ v-required ] } } validate-params ; : ( -- action ) [ validate-comment uid "author" set-value ] >>validate [ "parent" value f "content" value >>content uid >>author now >>date [ insert-tuple ] [ entity-url ] bi ] >>submit ; : ( -- action ) [ validate-integer-id { { "parent" [ v-integer ] } } validate-params ] >>validate [ f "id" value delete-tuples "parent" value view-post-url ] >>submit ; : ( -- dispatcher ) blogs new-dispatcher "" add-responder "posts.atom" add-responder "by" add-responder "by.atom" add-responder "post" add-responder "post.atom" add-responder "make a new blog post" >>description "new-post" add-responder "edit a blog post" >>description "edit-post" add-responder "delete a blog post" >>description "delete-post" add-responder "make a comment" >>description "new-comment" add-responder "delete a comment" >>description "delete-comment" add-responder { blogs "blogs-common" } >>template ;