Debugging web framework and cleaning things up

db4
Slava Pestov 2008-07-09 17:04:20 -05:00
parent 095a3e984c
commit 874b123bb0
31 changed files with 217 additions and 153 deletions

View File

@ -110,7 +110,7 @@ M: action call-responder* ( path action -- response )
} case ; } case ;
M: action modify-form M: action modify-form
drop request get url>> revalidate-url-key hidden-form-field ; drop url get revalidate-url-key hidden-form-field ;
: check-validation ( -- ) : check-validation ( -- )
validation-failed? [ validation-failed ] when ; validation-failed? [ validation-failed ] when ;

View File

@ -1,10 +1,17 @@
! Copyright (C) 2008 Slava Pestov. ! Copyright (C) 2008 Slava Pestov.
! See http://factorcode.org/license.txt for BSD license. ! See http://factorcode.org/license.txt for BSD license.
USING: accessors namespaces sequences arrays kernel USING: accessors namespaces sequences arrays kernel
assocs assocs.lib hashtables math.parser urls combinators assocs hashtables math.parser urls combinators
html.elements html.templates.chloe.syntax db.types db.tuples logging db.types db.tuples
http http.server http.server.filters html.elements
furnace furnace.cache furnace.sessions furnace.redirection ; html.templates.chloe.syntax
http
http.server
http.server.filters
furnace
furnace.cache
furnace.sessions
furnace.redirection ;
IN: furnace.asides IN: furnace.asides
TUPLE: aside < server-state session method url post-data ; TUPLE: aside < server-state session method url post-data ;
@ -44,6 +51,8 @@ TUPLE: asides < server-state-manager ;
url>> path>> split-path url>> path>> split-path
asides get responder>> call-responder ; asides get responder>> call-responder ;
\ end-aside-post DEBUG add-input-logging
ERROR: end-aside-in-get-error ; ERROR: end-aside-in-get-error ;
: get-aside ( id -- aside ) : get-aside ( id -- aside )

View File

@ -1,7 +1,7 @@
! Copyright (c) 2008 Slava Pestov ! Copyright (c) 2008 Slava Pestov
! See http://factorcode.org/license.txt for BSD license. ! See http://factorcode.org/license.txt for BSD license.
USING: accessors assocs namespaces kernel sequences sets USING: accessors assocs namespaces kernel sequences sets
destructors combinators fry destructors combinators fry logging
io.encodings.utf8 io.encodings.string io.binary random io.encodings.utf8 io.encodings.string io.binary random
checksums checksums.sha2 checksums checksums.sha2
html.forms html.forms
@ -18,7 +18,11 @@ IN: furnace.auth
SYMBOL: logged-in-user SYMBOL: logged-in-user
: logged-in? ( -- ? ) logged-in-user get >boolean ; : logged-in? ( -- ? )
logged-in-user get >boolean ;
: username ( -- string/f )
logged-in-user get dup [ username>> ] when ;
GENERIC: init-user-profile ( responder -- ) GENERIC: init-user-profile ( responder -- )
@ -30,9 +34,6 @@ M: dispatcher init-user-profile
M: filter-responder init-user-profile M: filter-responder init-user-profile
responder>> init-user-profile ; responder>> init-user-profile ;
: have-capability? ( capability -- ? )
logged-in-user get capabilities>> member? ;
: profile ( -- assoc ) logged-in-user get profile>> ; : profile ( -- assoc ) logged-in-user get profile>> ;
: user-changed ( -- ) : user-changed ( -- )
@ -59,6 +60,8 @@ TUPLE: realm < dispatcher name users checksum secure ;
GENERIC: login-required* ( realm -- response ) GENERIC: login-required* ( realm -- response )
GENERIC: init-realm ( realm -- )
GENERIC: logged-in-username ( realm -- username ) GENERIC: logged-in-username ( realm -- username )
: login-required ( -- * ) realm get login-required* exit-with ; : login-required ( -- * ) realm get login-required* exit-with ;
@ -87,9 +90,16 @@ M: user-saver dispose
: init-user ( user -- ) : init-user ( user -- )
[ [ logged-in-user set ] [ save-user-after ] bi ] when* ; [ [ logged-in-user set ] [ save-user-after ] bi ] when* ;
\ init-user DEBUG add-input-logging
M: realm call-responder* ( path responder -- response ) M: realm call-responder* ( path responder -- response )
dup realm set dup realm set
dup logged-in-username dup [ users get-user ] when init-user logged-in? [
dup init-realm
dup logged-in-username
dup [ users get-user ] when
init-user
] unless
call-next-method ; call-next-method ;
: encode-password ( string salt -- bytes ) : encode-password ( string salt -- bytes )
@ -122,18 +132,18 @@ TUPLE: protected < filter-responder description capabilities ;
protected new protected new
swap >>responder ; swap >>responder ;
: check-capabilities ( responder user/f -- ? ) : have-capabilities? ( capabilities -- ? )
{ logged-in-user get {
{ [ dup not ] [ 2drop f ] } { [ dup not ] [ 2drop f ] }
{ [ dup deleted>> 1 = ] [ 2drop f ] } { [ dup deleted>> 1 = ] [ 2drop f ] }
[ [ capabilities>> ] bi@ subset? ] [ capabilities>> subset? ]
} cond ; } cond ;
M: protected call-responder* ( path responder -- response ) M: protected call-responder* ( path responder -- response )
'[ '[
, , , ,
dup protected set dup protected set
dup logged-in-user get check-capabilities dup capabilities>> have-capabilities?
[ call-next-method ] [ 2drop realm get login-required* ] if [ call-next-method ] [ 2drop realm get login-required* ] if
] if-secure-realm ; ] if-secure-realm ;

View File

@ -22,7 +22,7 @@ IN: furnace.auth.features.edit-profile
{ realm "features/edit-profile/edit-profile" } >>template { realm "features/edit-profile/edit-profile" } >>template
[ [
logged-in-user get username>> "username" set-value username "username" set-value
{ {
{ "realname" [ [ v-one-line ] v-optional ] } { "realname" [ [ v-one-line ] v-optional ] }
@ -34,7 +34,7 @@ IN: furnace.auth.features.edit-profile
{ "password" "new-password" "verify-password" } { "password" "new-password" "verify-password" }
[ value empty? not ] contains? [ [ value empty? not ] contains? [
"password" value logged-in-user get username>> check-login "password" value username check-login
[ "incorrect password" validation-error ] unless [ "incorrect password" validation-error ] unless
same-password-twice same-password-twice

View File

@ -11,7 +11,7 @@ IN: furnace.auth.features.recover-password
SYMBOL: lost-password-from SYMBOL: lost-password-from
: current-host ( -- string ) : current-host ( -- string )
request get url>> host>> host-name or ; url get host>> host-name or ;
: new-password-url ( user -- url ) : new-password-url ( user -- url )
URL" recover-3" clone URL" recover-3" clone

View File

@ -1,7 +1,7 @@
! Copyright (c) 2008 Slava Pestov ! Copyright (c) 2008 Slava Pestov
! See http://factorcode.org/license.txt for BSD license. ! See http://factorcode.org/license.txt for BSD license.
USING: kernel accessors namespaces sequences math.parser USING: kernel accessors namespaces sequences math.parser
calendar validators urls html.forms calendar validators urls logging html.forms
http http.server http.server.dispatchers http http.server http.server.dispatchers
furnace furnace
furnace.auth furnace.auth
@ -25,10 +25,8 @@ SYMBOL: permit-id
TUPLE: login-realm < realm timeout domain ; TUPLE: login-realm < realm timeout domain ;
M: login-realm call-responder* M: login-realm init-realm
[ name>> client-permit-id permit-id set ] name>> client-permit-id permit-id set ;
[ call-next-method ]
bi ;
M: login-realm logged-in-username M: login-realm logged-in-username
drop permit-id get dup [ get-permit-uid ] when ; drop permit-id get dup [ get-permit-uid ] when ;
@ -47,11 +45,15 @@ M: login-realm modify-form ( responder -- )
: put-permit-cookie ( response -- response' ) : put-permit-cookie ( response -- response' )
<permit-cookie> put-cookie ; <permit-cookie> put-cookie ;
\ put-permit-cookie DEBUG add-input-logging
: successful-login ( user -- response ) : successful-login ( user -- response )
[ username>> make-permit permit-id set ] [ init-user ] bi [ username>> make-permit permit-id set ] [ init-user ] bi
URL" $realm" end-aside URL" $realm" end-aside
put-permit-cookie ; put-permit-cookie ;
\ successful-login DEBUG add-input-logging
: logout ( -- ) : logout ( -- )
permit-id get [ delete-permit ] when* permit-id get [ delete-permit ] when*
URL" $realm" end-aside ; URL" $realm" end-aside ;

View File

@ -1,7 +1,5 @@
USING: accessors namespaces combinators.lib kernel USING: accessors namespaces kernel combinators.short-circuit
db.tuples db.types db.tuples db.types furnace.auth furnace.sessions furnace.cache ;
furnace.auth furnace.sessions furnace.cache
combinators.short-circuit ;
IN: furnace.auth.login.permits IN: furnace.auth.login.permits

View File

@ -1,13 +1,12 @@
! Copyright (c) 2008 Slava Pestov ! Copyright (c) 2008 Slava Pestov
! See http://factorcode.org/license.txt for BSD license. ! See http://factorcode.org/license.txt for BSD license.
USING: accessors kernel math.order namespaces combinators.lib USING: accessors kernel math.order namespaces furnace combinators.short-circuit
html.forms html.forms
html.templates html.templates
html.templates.chloe html.templates.chloe
locals locals
http.server http.server
http.server.filters http.server.filters ;
furnace combinators.short-circuit ;
IN: furnace.boilerplate IN: furnace.boilerplate
TUPLE: boilerplate < filter-responder template init ; TUPLE: boilerplate < filter-responder template init ;

View File

@ -1,6 +1,6 @@
! Copyright (C) 2008 Slava Pestov. ! Copyright (C) 2008 Slava Pestov.
! See http://factorcode.org/license.txt for BSD license. ! See http://factorcode.org/license.txt for BSD license.
USING: namespaces assocs assocs.lib kernel sequences accessors USING: namespaces assocs kernel sequences accessors
urls db.types db.tuples math.parser fry urls db.types db.tuples math.parser fry
http http.server http.server.filters http.server.redirection http http.server http.server.filters http.server.redirection
furnace furnace.cache furnace.sessions furnace.redirection ; furnace furnace.cache furnace.sessions furnace.redirection ;

View File

@ -86,7 +86,7 @@ M: object modify-form drop ;
"user-agent" request get header>> at "" or ; "user-agent" request get header>> at "" or ;
: same-host? ( url -- ? ) : same-host? ( url -- ? )
request get url>> url get
[ [ protocol>> ] [ host>> ] [ port>> ] tri 3array ] bi@ = ; [ [ protocol>> ] [ host>> ] [ port>> ] tri 3array ] bi@ = ;
: cookie-client-state ( key request -- value/f ) : cookie-client-state ( key request -- value/f )

View File

@ -1,7 +1,7 @@
! Copyright (C) 2008 Slava Pestov. ! Copyright (C) 2008 Slava Pestov.
! See http://factorcode.org/license.txt for BSD license. ! See http://factorcode.org/license.txt for BSD license.
USING: kernel accessors combinators namespaces fry USING: kernel accessors combinators namespaces fry
io.servers.connection io.servers.connection urls
http http.server http.server.redirection http.server.filters http http.server http.server.redirection http.server.filters
furnace ; furnace ;
IN: furnace.redirection IN: furnace.redirection
@ -33,8 +33,8 @@ TUPLE: secure-only < filter-responder ;
C: <secure-only> secure-only C: <secure-only> secure-only
: if-secure ( quot -- ) : if-secure ( quot -- )
>r request get url>> protocol>> "http" = >r url get protocol>> "http" =
[ request get url>> <secure-redirect> ] [ url get <secure-redirect> ]
r> if ; inline r> if ; inline
M: secure-only call-responder* M: secure-only call-responder*

View File

@ -2,12 +2,12 @@
! See http://factorcode.org/license.txt for BSD license. ! See http://factorcode.org/license.txt for BSD license.
USING: assocs kernel math.intervals math.parser namespaces USING: assocs kernel math.intervals math.parser namespaces
strings random accessors quotations hashtables sequences continuations strings random accessors quotations hashtables sequences continuations
fry calendar combinators combinators.lib destructors alarms fry calendar combinators combinators.short-circuit destructors alarms
io.servers.connection io.servers.connection
db db.tuples db.types db db.tuples db.types
http http.server http.server.dispatchers http.server.filters http http.server http.server.dispatchers http.server.filters
html.elements html.elements
furnace furnace.cache combinators.short-circuit ; furnace furnace.cache ;
IN: furnace.sessions IN: furnace.sessions
TUPLE: session < server-state namespace user-agent client changed? ; TUPLE: session < server-state namespace user-agent client changed? ;

View File

@ -1,6 +1,6 @@
! Copyright (C) 2008 Slava Pestov. ! Copyright (C) 2008 Slava Pestov.
! See http://factorcode.org/license.txt for BSD license. ! See http://factorcode.org/license.txt for BSD license.
USING: accessors kernel sequences fry sequences.lib USING: accessors kernel sequences fry
combinators syndication combinators syndication
http.server.responses http.server.redirection http.server.responses http.server.redirection
furnace furnace.actions ; furnace furnace.actions ;

View File

@ -114,10 +114,13 @@ TUPLE: cookie name value version comment path domain expires max-age http-only s
] ]
} case ; } case ;
: check-cookie-value ( string -- string )
[ "Cookie value must not be f" throw ] unless* ;
: (unparse-cookie) ( cookie -- strings ) : (unparse-cookie) ( cookie -- strings )
[ [
dup name>> check-cookie-string >lower dup name>> check-cookie-string >lower
over value>> unparse-cookie-value over value>> check-cookie-value unparse-cookie-value
"$path" over path>> unparse-cookie-value "$path" over path>> unparse-cookie-value
"$domain" over domain>> unparse-cookie-value "$domain" over domain>> unparse-cookie-value
drop drop
@ -129,7 +132,7 @@ TUPLE: cookie name value version comment path domain expires max-age http-only s
: unparse-set-cookie ( cookie -- string ) : unparse-set-cookie ( cookie -- string )
[ [
dup name>> check-cookie-string >lower dup name>> check-cookie-string >lower
over value>> unparse-cookie-value over value>> check-cookie-value unparse-cookie-value
"path" over path>> unparse-cookie-value "path" over path>> unparse-cookie-value
"domain" over domain>> unparse-cookie-value "domain" over domain>> unparse-cookie-value
"expires" over expires>> unparse-cookie-value "expires" over expires>> unparse-cookie-value

View File

@ -1,4 +1,4 @@
USING: combinators.short-circuit math math.order math.parser kernel combinators.lib USING: combinators.short-circuit math math.order math.parser kernel
sequences sequences.deep peg peg.parsers assocs arrays sequences sequences.deep peg peg.parsers assocs arrays
hashtables strings unicode.case namespaces ascii ; hashtables strings unicode.case namespaces ascii ;
IN: http.parsers IN: http.parsers

View File

@ -14,10 +14,10 @@ IN: http.server.cgi
[ "PATH_TRANSLATED" set ] [ "SCRIPT_FILENAME" set ] bi [ "PATH_TRANSLATED" set ] [ "SCRIPT_FILENAME" set ] bi
request get url>> path>> "SCRIPT_NAME" set url get path>> "SCRIPT_NAME" set
request get url>> host>> "SERVER_NAME" set url get host>> "SERVER_NAME" set
request get url>> port>> number>string "SERVER_PORT" set url get port>> number>string "SERVER_PORT" set
"" "PATH_INFO" set "" "PATH_INFO" set
"" "REMOTE_HOST" set "" "REMOTE_HOST" set
"" "REMOTE_ADDR" set "" "REMOTE_ADDR" set
@ -26,7 +26,7 @@ IN: http.server.cgi
"" "REMOTE_IDENT" set "" "REMOTE_IDENT" set
request get method>> "REQUEST_METHOD" set request get method>> "REQUEST_METHOD" set
request get url>> query>> assoc>query "QUERY_STRING" set url get query>> assoc>query "QUERY_STRING" set
request get "cookie" header "HTTP_COOKIE" set request get "cookie" header "HTTP_COOKIE" set
request get "user-agent" header "HTTP_USER_AGENT" set request get "user-agent" header "HTTP_USER_AGENT" set

View File

@ -1,7 +1,7 @@
! Copyright (C) 2008 Slava Pestov. ! Copyright (C) 2008 Slava Pestov.
! See http://factorcode.org/license.txt for BSD license. ! See http://factorcode.org/license.txt for BSD license.
USING: kernel namespaces sequences assocs accessors splitting USING: kernel namespaces sequences assocs accessors splitting
unicode.case http http.server http.server.responses ; unicode.case urls http http.server http.server.responses ;
IN: http.server.dispatchers IN: http.server.dispatchers
TUPLE: dispatcher default responders ; TUPLE: dispatcher default responders ;
@ -35,7 +35,7 @@ TUPLE: vhost-dispatcher default responders ;
>lower "www." ?head drop "." ?tail drop ; >lower "www." ?head drop "." ?tail drop ;
: find-vhost ( dispatcher -- responder ) : find-vhost ( dispatcher -- responder )
request get url>> host>> canonical-host over responders>> at* url get host>> canonical-host over responders>> at*
[ nip ] [ drop default>> ] if ; [ nip ] [ drop default>> ] if ;
M: vhost-dispatcher call-responder* ( path dispatcher -- response ) M: vhost-dispatcher call-responder* ( path dispatcher -- response )

View File

@ -9,7 +9,7 @@ GENERIC: relative-to-request ( url -- url' )
M: string relative-to-request ; M: string relative-to-request ;
M: url relative-to-request M: url relative-to-request
request get url>> url get
clone clone
f >>query f >>query
swap derive-url ensure-port ; swap derive-url ensure-port ;

View File

@ -81,8 +81,7 @@ GENERIC: write-full-response ( request response -- )
: ensure-domain ( cookie -- cookie ) : ensure-domain ( cookie -- cookie )
[ [
request get url>> url get host>> dup "localhost" =
host>> dup "localhost" =
[ drop ] [ or ] if [ drop ] [ or ] if
] change-domain ; ] change-domain ;
@ -189,7 +188,7 @@ LOG: httpd-header NOTICE
"/" split harvest ; "/" split harvest ;
: init-request ( request -- ) : init-request ( request -- )
request set [ request set ] [ url>> url set ] bi
V{ } clone responder-nesting set ; V{ } clone responder-nesting set ;
: dispatch-request ( request -- response ) : dispatch-request ( request -- response )
@ -224,7 +223,7 @@ LOG: httpd-benchmark DEBUG
: ?benchmark ( quot -- ) : ?benchmark ( quot -- )
benchmark? get [ benchmark? get [
[ benchmark ] [ first ] bi request get url>> rot 3array [ benchmark ] [ first ] bi url get rot 3array
httpd-benchmark httpd-benchmark
] [ call ] if ; inline ] [ call ] if ; inline
@ -235,7 +234,7 @@ M: http-server handle-client*
[ [
64 1024 * limit-input 64 1024 * limit-input
?refresh-all ?refresh-all
read-request [ read-request ] ?benchmark
[ do-request ] ?benchmark [ do-request ] ?benchmark
[ do-response ] ?benchmark [ do-response ] ?benchmark
] with-destructors ; ] with-destructors ;

View File

@ -82,12 +82,12 @@ TUPLE: file-responder root hook special allow-listings ;
"index.html" append-path dup exists? [ drop f ] unless ; "index.html" append-path dup exists? [ drop f ] unless ;
: serve-directory ( filename -- response ) : serve-directory ( filename -- response )
request get url>> path>> "/" tail? [ url get path>> "/" tail? [
dup dup
find-index [ serve-file ] [ list-directory ] ?if find-index [ serve-file ] [ list-directory ] ?if
] [ ] [
drop drop
request get url>> clone [ "/" append ] change-path <permanent-redirect> url get clone [ "/" append ] change-path <permanent-redirect>
] if ; ] if ;
: serve-object ( filename -- response ) : serve-object ( filename -- response )

View File

@ -160,13 +160,13 @@ M: comment entity-url
[ [
validate-post validate-post
logged-in-user get username>> "author" set-value username "author" set-value
] >>validate ] >>validate
[ [
f <post> f <post>
dup { "title" "content" } to-object dup { "title" "content" } to-object
logged-in-user get username>> >>author username >>author
now >>date now >>date
[ insert-tuple ] [ entity-url <redirect> ] bi [ insert-tuple ] [ entity-url <redirect> ] bi
] >>submit ] >>submit
@ -177,8 +177,8 @@ M: comment entity-url
"make a new blog post" >>description ; "make a new blog post" >>description ;
: authorize-author ( author -- ) : authorize-author ( author -- )
logged-in-user get username>> = username =
can-administer-blogs? have-capability? or { can-administer-blogs? } have-capabilities? or
[ login-required ] unless ; [ login-required ] unless ;
: do-post-action ( -- ) : do-post-action ( -- )
@ -254,13 +254,13 @@ M: comment entity-url
[ [
validate-comment validate-comment
logged-in-user get username>> "author" set-value username "author" set-value
] >>validate ] >>validate
[ [
"parent" value f <comment> "parent" value f <comment>
"content" value >>content "content" value >>content
logged-in-user get username>> >>author username >>author
now >>date now >>date
[ insert-tuple ] [ entity-url <redirect> ] bi [ insert-tuple ] [ entity-url <redirect> ] bi
] >>submit ] >>submit

View File

@ -32,7 +32,7 @@ todo "TODO"
: <todo> ( id -- todo ) : <todo> ( id -- todo )
todo new todo new
swap >>id swap >>id
logged-in-user get username>> >>uid ; username >>uid ;
: <view-action> ( -- action ) : <view-action> ( -- action )
<page-action> <page-action>

View File

@ -4,26 +4,4 @@
<t:title>Recent Changes</t:title> <t:title>Recent Changes</t:title>
<div class="revisions">
<table>
<tr>
<th>Article</th>
<th>Date</th>
<th>By</th>
</tr>
<t:bind-each t:name="changes">
<tr>
<td><t:a t:href="$wiki/view" t:rest="title"><t:label t:name="title" /></t:a></td>
<td><t:a t:href="$wiki/revision" t:rest="id"><t:label t:name="date" /></t:a></td>
<td><t:a t:href="$wiki/user-edits" t:rest="author"><t:label t:name="author" /></t:a></td>
</tr>
</t:bind-each>
</table>
</div>
</t:chloe> </t:chloe>

View File

@ -13,7 +13,7 @@
</tr> </tr>
<tr> <tr>
<th class="field-label">New revision:</th> <th class="field-label">New revision:</th>
<t:bind t:name="old"> <t:bind t:name="new">
<td>Created on <t:label t:name="date" /> by <t:a t:href="user-edits" t:rest="author"><t:label t:name="author" /></t:a>.</td> <td>Created on <t:label t:name="date" /> by <t:a t:href="user-edits" t:rest="author"><t:label t:name="author" /></t:a>.</td>
</t:bind> </t:bind>
</tr> </tr>

View File

@ -4,12 +4,17 @@
<t:title>Edit: <t:label t:name="title" /></t:title> <t:title>Edit: <t:label t:name="title" /></t:title>
<t:form t:action="$wiki/edit" t:for="title"> <t:form t:action="$wiki/submit" t:for="title">
<p> <p>
<t:textarea t:name="content" t:rows="30" t:cols="80" /> <t:textarea t:name="content" t:rows="30" t:cols="80" />
</p> </p>
<p>
Describe this revision:
<t:field t:name="description" t:size="60" />
</p>
<p> <p>
<input type="submit" value="Save" /> <input type="submit" value="Save" />
</p> </p>

View File

@ -0,0 +1,33 @@
<?xml version='1.0' ?>
<t:chloe xmlns:t="http://factorcode.org/chloe/1.0">
<div class="revisions">
<table>
<tr>
<th>Article</th>
<th>Date</th>
<th>By</th>
<th>Description</th>
<th>Rollback</th>
</tr>
<t:bind-each t:name="revisions">
<tr>
<td> <t:a t:href="$wiki/view" t:rest="title"><t:label t:name="title" /></t:a> </td>
<td> <t:a t:href="$wiki/revision" t:rest="id"><t:label t:name="date" /></t:a> </td>
<td> <t:a t:href="$wiki/user-edits" t:rest="author"><t:label t:name="author" /> </t:a></td>
<td> <t:label t:name="description" /> </td>
<td> <t:button class="link link-button" t:action="$wiki/rollback" t:for="id">Rollback</t:button> </td>
</tr>
</t:bind-each>
</table>
</div>
<t:call-next-template />
</t:chloe>

View File

@ -4,24 +4,6 @@
<t:title>Revisions of <t:label t:name="title" /></t:title> <t:title>Revisions of <t:label t:name="title" /></t:title>
<div class="revisions">
<table>
<tr>
<th>Revision</th>
<th>By</th>
<th>Rollback</th>
</tr>
<t:bind-each t:name="revisions">
<tr>
<td> <t:a t:href="$wiki/revision" t:rest="id"><t:label t:name="date" /></t:a> </td>
<td> <t:a t:href="$wiki/user-edits" t:rest="author"><t:label t:name="author" /></t:a> </td>
<td> <t:button t:action="$wiki/rollback" t:for="id" class="link link-button">Rollback</t:button> </td>
</tr>
</t:bind-each>
</table>
</div>
<h2>View Differences</h2> <h2>View Differences</h2>
<t:form t:action="$wiki/diff" t:method="get"> <t:form t:action="$wiki/diff" t:method="get">

View File

@ -8,14 +8,4 @@
<t:title>Edits by <t:label t:name="author" /></t:title> <t:title>Edits by <t:label t:name="author" /></t:title>
<ul>
<t:bind-each t:name="user-edits">
<li>
<t:a t:href="$wiki/view" t:rest="title"><t:label t:name="title" /></t:a>
on
<t:a t:href="$wiki/revision" t:rest="id"><t:label t:name="date" /></t:a>
</li>
</t:bind-each>
</ul>
</t:chloe> </t:chloe>

View File

@ -8,6 +8,12 @@
<t:farkup t:name="content" /> <t:farkup t:name="content" />
</div> </div>
<p><em>This revision created on <t:label t:name="date" /> by <t:a t:href="$wiki/user-edits" t:rest="author"><t:label t:name="author" /></t:a>.</em></p> <p>
<em>This revision created on <t:label t:name="date" /> by <t:a t:href="$wiki/user-edits" t:rest="author"><t:label t:name="author" /></t:a>
<t:if t:value="description">
(<t:label t:name="description" />)
</t:if>
</em>
</p>
</t:chloe> </t:chloe>

View File

@ -47,6 +47,7 @@
</t:if> </t:if>
</tr> </tr>
<t:if t:value="footer">
<tr> <tr>
<td> <td>
<t:bind t:name="footer"> <t:bind t:name="footer">
@ -56,6 +57,7 @@
</t:bind> </t:bind>
</td> </td>
</tr> </tr>
</t:if>
</table> </table>
</t:chloe> </t:chloe>

View File

@ -47,7 +47,7 @@ article "ARTICLES" {
: <article> ( title -- article ) article new swap >>title ; : <article> ( title -- article ) article new swap >>title ;
TUPLE: revision id title author date content ; TUPLE: revision id title author date content description ;
revision "REVISIONS" { revision "REVISIONS" {
{ "id" "ID" INTEGER +db-assigned-id+ } { "id" "ID" INTEGER +db-assigned-id+ }
@ -55,6 +55,7 @@ revision "REVISIONS" {
{ "author" "AUTHOR" { VARCHAR 256 } +not-null+ } ! uid { "author" "AUTHOR" { VARCHAR 256 } +not-null+ } ! uid
{ "date" "DATE" TIMESTAMP +not-null+ } { "date" "DATE" TIMESTAMP +not-null+ }
{ "content" "CONTENT" TEXT +not-null+ } { "content" "CONTENT" TEXT +not-null+ }
{ "description" "DESCRIPTION" TEXT }
} define-persistent } define-persistent
M: revision feed-entry-title M: revision feed-entry-title
@ -76,6 +77,10 @@ M: revision feed-entry-url id>> revision-url ;
: validate-author ( -- ) : validate-author ( -- )
{ { "author" [ v-username ] } } validate-params ; { { "author" [ v-username ] } } validate-params ;
: <article-boilerplate> ( responder -- responder' )
<boilerplate>
{ wiki "page-common" } >>template ;
: <main-article-action> ( -- action ) : <main-article-action> ( -- action )
<action> <action>
[ "Front Page" view-url <redirect> ] >>display ; [ "Front Page" view-url <redirect> ] >>display ;
@ -100,7 +105,9 @@ M: revision feed-entry-url id>> revision-url ;
] [ ] [
edit-url <redirect> edit-url <redirect>
] ?if ] ?if
] >>display ; ] >>display
<article-boilerplate> ;
: <view-revision-action> ( -- action ) : <view-revision-action> ( -- action )
<page-action> <page-action>
@ -114,7 +121,9 @@ M: revision feed-entry-url id>> revision-url ;
URL" $wiki/view/" adjust-url present relative-link-prefix set URL" $wiki/view/" adjust-url present relative-link-prefix set
] >>init ] >>init
{ wiki "view" } >>template ; { wiki "view" } >>template
<article-boilerplate> ;
: <random-article-action> ( -- action ) : <random-article-action> ( -- action )
<action> <action>
@ -144,28 +153,47 @@ M: revision feed-entry-url id>> revision-url ;
[ [
validate-title validate-title
"title" value <article> select-tuple [
revision>> <revision> select-tuple from-object "title" value <article> select-tuple
] when* [ revision>> <revision> select-tuple ]
[ f <revision> "title" value >>title ]
if*
[ title>> "title" set-value ]
[ content>> "content" set-value ]
bi
] >>init ] >>init
{ wiki "edit" } >>template { wiki "edit" } >>template
<article-boilerplate> ;
: <submit-article-action> ( -- action )
<action>
[ [
validate-title validate-title
{ { "content" [ v-required ] } } validate-params
{
{ "content" [ v-required ] }
{ "description" [ [ v-one-line ] v-optional ] }
} validate-params
f <revision> f <revision>
"title" value >>title "title" value >>title
now >>date now >>date
logged-in-user get username>> >>author username >>author
"content" value >>content "content" value >>content
"description" value >>description
[ add-revision ] [ title>> view-url <redirect> ] bi [ add-revision ] [ title>> view-url <redirect> ] bi
] >>submit ] >>submit
<protected> <protected>
"edit wiki articles" >>description ; "edit wiki articles" >>description ;
: <revisions-boilerplate> ( responder -- responder )
<boilerplate>
{ wiki "revisions-common" } >>template ;
: list-revisions ( -- seq ) : list-revisions ( -- seq )
f <revision> "title" value >>title select-tuples f <revision> "title" value >>title select-tuples
reverse-chronological-order ; reverse-chronological-order ;
@ -180,7 +208,10 @@ M: revision feed-entry-url id>> revision-url ;
list-revisions "revisions" set-value list-revisions "revisions" set-value
] >>init ] >>init
{ wiki "revisions" } >>template ; { wiki "revisions" } >>template
<revisions-boilerplate>
<article-boilerplate> ;
: <list-revisions-feed-action> ( -- action ) : <list-revisions-feed-action> ( -- action )
<feed-action> <feed-action>
@ -195,15 +226,26 @@ M: revision feed-entry-url id>> revision-url ;
[ list-revisions ] >>entries ; [ list-revisions ] >>entries ;
: rollback-description ( description -- description' )
[ "Rollback of '" swap "'" 3append ] [ "Rollback" ] if* ;
: <rollback-action> ( -- action ) : <rollback-action> ( -- action )
<action> <action>
[ validate-integer-id ] >>validate [ validate-integer-id ] >>validate
[ [
"id" value <revision> select-tuple clone f >>id "id" value <revision> select-tuple
[ add-revision ] [ title>> view-url <redirect> ] bi f >>id
] >>submit ; now >>date
username >>author
[ rollback-description ] change-description
[ add-revision ]
[ title>> revisions-url <redirect> ] bi
] >>submit
<protected>
"rollback wiki articles" >>description ;
: list-changes ( -- seq ) : list-changes ( -- seq )
f <revision> select-tuples f <revision> select-tuples
@ -211,8 +253,10 @@ M: revision feed-entry-url id>> revision-url ;
: <list-changes-action> ( -- action ) : <list-changes-action> ( -- action )
<page-action> <page-action>
[ list-changes "changes" set-value ] >>init [ list-changes "revisions" set-value ] >>init
{ wiki "changes" } >>template ; { wiki "changes" } >>template
<revisions-boilerplate> ;
: <list-changes-feed-action> ( -- action ) : <list-changes-feed-action> ( -- action )
<feed-action> <feed-action>
@ -237,6 +281,7 @@ M: revision feed-entry-url id>> revision-url ;
: <diff-action> ( -- action ) : <diff-action> ( -- action )
<page-action> <page-action>
[ [
{ {
{ "old-id" [ v-integer ] } { "old-id" [ v-integer ] }
@ -246,14 +291,18 @@ M: revision feed-entry-url id>> revision-url ;
"old-id" "new-id" "old-id" "new-id"
[ value <revision> select-tuple ] bi@ [ value <revision> select-tuple ] bi@
[ [
[ [ title>> "title" set-value ] [ "old" [ from-object ] nest-form ] bi ] over title>> "title" set-value
[ "new" [ from-object ] nest-form ] bi* [ "old" [ from-object ] nest-form ]
[ "new" [ from-object ] nest-form ]
bi*
] ]
[ [ content>> string-lines ] bi@ diff "diff" set-value ] [ [ content>> string-lines ] bi@ diff "diff" set-value ]
2bi 2bi
] >>init ] >>init
{ wiki "diff" } >>template ; { wiki "diff" } >>template
<article-boilerplate> ;
: <list-articles-action> ( -- action ) : <list-articles-action> ( -- action )
<page-action> <page-action>
@ -277,10 +326,12 @@ M: revision feed-entry-url id>> revision-url ;
[ [
validate-author validate-author
list-user-edits "user-edits" set-value list-user-edits "revisions" set-value
] >>init ] >>init
{ wiki "user-edits" } >>template ; { wiki "user-edits" } >>template
<revisions-boilerplate> ;
: <user-edits-feed-action> ( -- action ) : <user-edits-feed-action> ( -- action )
<feed-action> <feed-action>
@ -290,24 +341,21 @@ M: revision feed-entry-url id>> revision-url ;
[ "author" value user-edits-url ] >>url [ "author" value user-edits-url ] >>url
[ list-user-edits ] >>entries ; [ list-user-edits ] >>entries ;
: <article-boilerplate> ( responder -- responder' )
<boilerplate>
{ wiki "page-common" } >>template ;
: init-sidebar ( -- ) : init-sidebar ( -- )
"Sidebar" latest-revision [ "sidebar" [ from-object ] nest-form ] when* "Sidebar" latest-revision [ "sidebar" [ from-object ] nest-form ] when*
"Footer" latest-revision [ "footer" [ from-object ] nest-form ] when* ; "Footer" latest-revision [ "footer" [ from-object ] nest-form ] when* ;
: <wiki> ( -- dispatcher ) : <wiki> ( -- dispatcher )
wiki new-dispatcher wiki new-dispatcher
<main-article-action> <article-boilerplate> "" add-responder <main-article-action> "" add-responder
<view-article-action> <article-boilerplate> "view" add-responder <view-article-action> "view" add-responder
<view-revision-action> <article-boilerplate> "revision" add-responder <view-revision-action> "revision" add-responder
<random-article-action> "random" add-responder <random-article-action> "random" add-responder
<list-revisions-action> <article-boilerplate> "revisions" add-responder <list-revisions-action> "revisions" add-responder
<list-revisions-feed-action> "revisions.atom" add-responder <list-revisions-feed-action> "revisions.atom" add-responder
<diff-action> <article-boilerplate> "diff" add-responder <diff-action> "diff" add-responder
<edit-article-action> <article-boilerplate> "edit" add-responder <edit-article-action> "edit" add-responder
<submit-article-action> "submit" add-responder
<rollback-action> "rollback" add-responder <rollback-action> "rollback" add-responder
<user-edits-action> "user-edits" add-responder <user-edits-action> "user-edits" add-responder
<list-articles-action> "articles" add-responder <list-articles-action> "articles" add-responder