--- /dev/null
+USING: checksums ;
+IN: checksums.null
+
+SINGLETON: null
+
+INSTANCE: null checksum
+
+M: null checksum-bytes ;
USING: http tools.test multiline tuple-syntax
io.streams.string kernel arrays splitting sequences
-assocs io.sockets db db.sqlite ;
+assocs io.sockets db db.sqlite continuations ;
IN: http.tests
[ "hello%20world" ] [ "hello world" url-encode ] unit-test
STRING: read-response-test-1
HTTP/1.1 404 not found
-Content-Type: text/html
+Content-Type: text/html; charset=UTF8
blah
;
version: "1.1"
code: 404
message: "not found"
- header: H{ { "content-type" "text/html" } }
+ header: H{ { "content-type" "text/html; charset=UTF8" } }
cookies: V{ }
+ content-type: "text/html"
+ content-charset: "UTF8"
}
] [
read-response-test-1 lf>crlf
STRING: read-response-test-1'
HTTP/1.1 404 not found
-content-type: text/html
+content-type: text/html; charset=UTF8
;
: add-quit-action
<action>
- [ stop-server "text/html" <content> [ "Goodbye" write ] >>body ] >>display
+ [ stop-server [ "Goodbye" write ] <html-content> ] >>display
"quit" add-responder ;
: test-db "test.db" temp-file sqlite-db ;
+[ test-db drop delete-file ] ignore-errors
+
test-db [
init-sessions-table
] with-db
[ ] [
[
<dispatcher>
- <action> <protected>
+ <action> f <protected>
<login>
<sessions>
"" add-responder
: extract-cookies ( request -- request )
dup "cookie" header [ parse-cookies >>cookies ] when* ;
+: parse-content-type-attributes ( string -- attributes )
+ " " split [ empty? not ] filter [ "=" split1 >r >lower r> ] { } map>assoc ;
+
+: parse-content-type ( content-type -- type encoding )
+ ";" split1 parse-content-type-attributes "charset" swap at ;
+
: read-request ( -- request )
<request>
read-method
message
header
cookies
+content-type
+content-charset
body ;
: <response>
: read-response-header
read-header >>header
- dup "set-cookie" header [ parse-cookies >>cookies ] when* ;
+ extract-cookies
+ dup "content-type" header [
+ parse-content-type [ >>content-type ] [ >>content-charset ] bi*
+ ] when* ;
: read-response ( -- response )
<response>
: write-response-message ( response -- response )
dup message>> write crlf ;
+: unparse-content-type ( request -- content-type )
+ [ content-type>> "application/octet-stream" or ]
+ [ content-charset>> ] bi
+ [ "; charset=" swap 3append ] when* ;
+
: write-response-header ( response -- response )
dup header>> clone
- over cookies>> f like
- [ unparse-cookies "set-cookie" pick set-at ] when*
+ over cookies>> f like [ unparse-cookies "set-cookie" pick set-at ] when*
+ over unparse-content-type "content-type" pick set-at
write-header ;
GENERIC: write-response-body* ( body -- )
dup write-response
swap method>> "HEAD" = [ write-response-body ] unless ;
-: set-content-type ( request/response content-type -- request/response )
- "content-type" set-header ;
-
: get-cookie ( request/response name -- cookie/f )
>r cookies>> r> '[ , _ name>> = ] find nip ;
[ name>> dupd get-cookie [ dupd delete-cookie ] when* ] keep
over cookies>> push ;
-TUPLE: raw-response
+TUPLE: raw-response
version
code
message
http.server.auth.providers
http.server.auth.providers.db
http.server.auth.login
+http.server.auth
http.server.forms
http.server.components.inspector
http.server.components
"new-password" <password> t >>required add-field
"verify-password" <password> t >>required add-field
"email" <email> add-field ;
+ ! "capabilities" <capabilities> add-field ;
: <edit-user-form> ( -- form )
"user" <form>
"verify-password" <password> add-field
"email" <email> add-field
"profile" <inspector> add-field ;
+ ! "capabilities" <capabilities> add-field ;
: <user-list-form> ( -- form )
"user-list" <form>
"username" value <user>
"realname" value >>realname
"email" value >>email
- "new-password" value >>password
+ "new-password" value >>encoded-password
H{ } clone >>profile
insert-tuple
{ "new-password" "verify-password" }
[ value empty? ] all? [
same-password-twice
- "new-password" value >>password
+ "new-password" value >>encoded-password
] unless
update-tuple
TUPLE: user-admin < dispatcher ;
+SYMBOL: can-administer-users?
+
+can-administer-users? define-capability
+
:: <user-admin> ( -- responder )
[let | ctor [ [ <user> ] ] |
user-admin new-dispatcher
ctor "$user-admin" <delete-user-action> "delete" add-responder
<boilerplate>
"admin" admin-template >>template
- <protected>
+ { can-administer-users? } <protected>
] ;
! Copyright (c) 2008 Slava Pestov\r
! See http://factorcode.org/license.txt for BSD license.\r
-USING: accessors assocs namespaces kernel\r
+USING: accessors assocs namespaces kernel sequences\r
http.server\r
http.server.sessions\r
http.server.auth.providers ;\r
: uchange ( quot key -- )\r
profile swap change-at\r
user-changed ; inline\r
+\r
+SYMBOL: capabilities\r
+\r
+V{ } clone capabilities set-global\r
+\r
+: define-capability ( word -- ) capabilities get push-new ;\r
! Copyright (c) 2008 Slava Pestov\r
! See http://factorcode.org/license.txt for BSD license.\r
USING: accessors quotations assocs kernel splitting\r
-base64 io combinators sequences io.files namespaces hashtables\r
-fry io.sockets arrays threads locals qualified continuations\r
+combinators sequences namespaces hashtables\r
+fry arrays threads locals qualified random\r
+io\r
+io.sockets\r
+io.encodings.utf8\r
+io.encodings.string\r
+io.binary\r
+continuations\r
destructors\r
-\r
+checksums\r
+checksums.sha2\r
html.elements\r
http\r
http.server\r
http.server.auth\r
http.server.auth.providers\r
-http.server.auth.providers.null\r
+http.server.auth.providers.db\r
http.server.actions\r
http.server.components\r
http.server.flows\r
\r
SYMBOL: login-failed?\r
\r
-TUPLE: login < dispatcher users ;\r
+TUPLE: login < dispatcher users checksum ;\r
+\r
+: users ( -- provider )\r
+ login get users>> ;\r
+\r
+: encode-password ( string salt -- bytes )\r
+ [ utf8 encode ] [ 4 >be ] bi* append\r
+ login get checksum>> checksum-bytes ;\r
\r
-: users login get users>> ;\r
+: >>encoded-password ( user string -- user )\r
+ 32 random-bits [ encode-password ] keep\r
+ [ >>password ] [ >>salt ] bi* ; inline\r
+\r
+: valid-login? ( password user -- ? )\r
+ [ salt>> encode-password ] [ password>> ] bi = ;\r
+\r
+: check-login ( password username -- user/f )\r
+ users get-user dup [ [ valid-login? ] keep and ] [ 2drop f ] if ;\r
\r
! Destructor\r
TUPLE: user-saver user ;\r
\r
form validate-form\r
\r
- "password" value "username" value\r
- users check-login [\r
+ "password" value "username" value check-login [\r
successful-login\r
] [\r
login-failed? on\r
\r
"username" value <user>\r
"realname" value >>realname\r
- "new-password" value >>password\r
+ "new-password" value >>encoded-password\r
"email" value >>email\r
H{ } clone >>profile\r
\r
[ value empty? ] all? [\r
same-password-twice\r
\r
- "password" value uid users check-login\r
+ "password" value uid check-login\r
[ login-failed? on validation-failed ] unless\r
\r
- "new-password" value >>password\r
+ "new-password" value >>encoded-password\r
] unless\r
\r
"realname" value >>realname\r
"ticket" value\r
"username" value\r
users claim-ticket [\r
- "new-password" value >>password\r
+ "new-password" value >>encoded-password\r
users update-user\r
\r
"recover-4" login-template serve-template\r
\r
! ! ! Authentication logic\r
\r
-TUPLE: protected < filter-responder ;\r
+TUPLE: protected < filter-responder capabilities ;\r
\r
C: <protected> protected\r
\r
begin-flow\r
"$login/login" f <standard-redirect> ;\r
\r
+: check-capabilities ( responder user -- ? )\r
+ [ capabilities>> ] [ profile>> ] bi* '[ , at ] all? ;\r
+\r
M: protected call-responder* ( path responder -- response )\r
uid dup [\r
- users get-user\r
- [ logged-in-user set ] [ save-user-after ] bi\r
- call-next-method\r
+ users get-user 2dup check-capabilities [\r
+ [ logged-in-user set ] [ save-user-after ] bi\r
+ call-next-method\r
+ ] [\r
+ 3drop show-login-page\r
+ ] if\r
] [\r
3drop show-login-page\r
] if ;\r
swap >>default\r
<login-action> <login-boilerplate> "login" add-responder\r
<logout-action> <login-boilerplate> "logout" add-responder\r
- no-users >>users ;\r
+ users-in-db >>users\r
+ sha-256 >>checksum ;\r
\r
! ! ! Configuration\r
\r
: allow-edit-profile ( login -- login )\r
- <edit-profile-action> <protected> <login-boilerplate>\r
+ <edit-profile-action> f <protected> <login-boilerplate>\r
"edit-profile" add-responder ;\r
\r
: allow-registration ( login -- login )\r
IN: http.server.auth.providers.assoc.tests\r
-USING: http.server.auth.providers \r
+USING: http.server.actions http.server.auth.providers \r
http.server.auth.providers.assoc tools.test\r
namespaces accessors kernel ;\r
\r
-<users-in-memory> "provider" set\r
+<action> <login>\r
+ <users-in-memory> >>users\r
+login set\r
\r
[ t ] [\r
"slava" <user>\r
- "foobar" >>password\r
+ "foobar" >>encoded-password\r
"slava@factorcode.org" >>email\r
H{ } clone >>profile\r
- "provider" get new-user\r
+ users new-user\r
username>> "slava" =\r
] unit-test\r
\r
[ f ] [\r
"slava" <user>\r
H{ } clone >>profile\r
- "provider" get new-user\r
+ users new-user\r
] unit-test\r
\r
-[ f ] [ "fdasf" "slava" "provider" get check-login >boolean ] unit-test\r
+[ f ] [ "fdasf" "slava" check-login >boolean ] unit-test\r
\r
-[ ] [ "foobar" "slava" "provider" get check-login "user" set ] unit-test\r
+[ ] [ "foobar" "slava" check-login "user" set ] unit-test\r
\r
[ t ] [ "user" get >boolean ] unit-test\r
\r
-[ ] [ "user" get "fdasf" >>password drop ] unit-test\r
+[ ] [ "user" get "fdasf" >>encoded-password drop ] unit-test\r
\r
-[ t ] [ "fdasf" "slava" "provider" get check-login >boolean ] unit-test\r
+[ t ] [ "fdasf" "slava" check-login >boolean ] unit-test\r
\r
-[ f ] [ "foobar" "slava" "provider" get check-login >boolean ] unit-test\r
+[ f ] [ "foobar" "slava" check-login >boolean ] unit-test\r
IN: http.server.auth.providers.db.tests\r
-USING: http.server.auth.providers\r
+USING: http.server.actions\r
+http.server.auth.login\r
+http.server.auth.providers\r
http.server.auth.providers.db tools.test\r
namespaces db db.sqlite db.tuples continuations\r
io.files accessors kernel ;\r
\r
-users-in-db "provider" set\r
+<action> <login>\r
+ users-in-db >>users\r
+login set\r
\r
[ "auth-test.db" temp-file delete-file ] ignore-errors\r
\r
\r
[ t ] [\r
"slava" <user>\r
- "foobar" >>password\r
+ "foobar" >>encoded-password\r
"slava@factorcode.org" >>email\r
H{ } clone >>profile\r
- "provider" get new-user\r
+ users new-user\r
username>> "slava" =\r
] unit-test\r
\r
[ f ] [\r
"slava" <user>\r
H{ } clone >>profile\r
- "provider" get new-user\r
+ users new-user\r
] unit-test\r
\r
- [ f ] [ "fdasf" "slava" "provider" get check-login >boolean ] unit-test\r
+ [ f ] [ "fdasf" "slava" check-login >boolean ] unit-test\r
\r
- [ ] [ "foobar" "slava" "provider" get check-login "user" set ] unit-test\r
+ [ ] [ "foobar" "slava" check-login "user" set ] unit-test\r
\r
[ t ] [ "user" get >boolean ] unit-test\r
\r
- [ ] [ "user" get "fdasf" >>password drop ] unit-test\r
+ [ ] [ "user" get "fdasf" >>encoded-password drop ] unit-test\r
\r
- [ ] [ "user" get "provider" get update-user ] unit-test\r
+ [ ] [ "user" get users update-user ] unit-test\r
\r
- [ t ] [ "fdasf" "slava" "provider" get check-login >boolean ] unit-test\r
+ [ t ] [ "fdasf" "slava" check-login >boolean ] unit-test\r
\r
- [ f ] [ "foobar" "slava" "provider" get check-login >boolean ] unit-test\r
+ [ f ] [ "foobar" "slava" check-login >boolean ] unit-test\r
] with-db\r
{
{ "username" "USERNAME" { VARCHAR 256 } +user-assigned-id+ }
{ "realname" "REALNAME" { VARCHAR 256 } }
- { "password" "PASSWORD" { VARCHAR 256 } +not-null+ }
+ { "password" "PASSWORD" BLOB +not-null+ }
+ { "salt" "SALT" INTEGER +not-null+ }
{ "email" "EMAIL" { VARCHAR 256 } }
{ "ticket" "TICKET" { VARCHAR 256 } }
{ "profile" "PROFILE" FACTOR-BLOB }
sequences math ;\r
IN: http.server.auth.providers\r
\r
-TUPLE: user username realname password email ticket profile deleted changed? ;\r
+TUPLE: user username realname password salt email ticket profile deleted changed? ;\r
\r
: <user> ( username -- user )\r
user new\r
\r
GENERIC: new-user ( user provider -- user/f )\r
\r
-: check-login ( password username provider -- user/f )\r
- get-user dup [ [ password>> = ] keep and ] [ 2drop f ] if ;\r
-\r
! Password recovery support\r
\r
:: issue-ticket ( email username provider -- user/f )\r
! Copyright (c) 2008 Slava Pestov
! See http://factorcode.org/license.txt for BSD license.
USING: accessors kernel namespaces boxes sequences strings
-io io.streams.string arrays
+io io.streams.string arrays locals
html.elements
http
http.server
SYMBOL: next-template
: call-next-template ( -- )
- next-template get write ;
+ next-template get write-html ;
M: f call-template* drop call-next-template ;
bi*
] with-scope ; inline
-M: boilerplate call-responder*
- tuck call-next-method
- dup "content-type" header "text/html" = [
- clone swap template>>
- [ [ with-boilerplate ] 2curry ] curry change-body
- ] [ nip ] if ;
+M:: boilerplate call-responder* ( path responder -- )
+ path responder call-next-method
+ dup content-type>> "text/html" = [
+ clone [| body |
+ [ body responder template>> with-boilerplate ]
+ ] change-body
+ ] when ;
<action> [\r
[\r
"hello" print\r
- "text/html" <content> swap '[ , write ] >>body\r
+ '[ , write ] <html-content>\r
] show-page\r
"byebye" print\r
[ 123 ] show-final\r
! Copyright (C) 2008 Slava Pestov
! See http://factorcode.org/license.txt for BSD license.
USING: splitting kernel io sequences xmode.code2html accessors
-http.server.components ;
+http.server.components xml.entities ;
IN: http.server.components.code
TUPLE: code-renderer < text-renderer mode ;
USING: accessors namespaces kernel io math.parser assocs classes
words classes.tuple arrays sequences splitting mirrors
hashtables fry combinators continuations math
-calendar.format html.elements
+calendar.format html.elements xml.entities
http.server.validators ;
IN: http.server.components
C: <field> field
-M: field render-view* drop write ;
+M: field render-view* drop escape-string write ;
M: field render-edit*
<input type>> =type [ =id ] [ =name ] bi =value input/> ;
: render-error ( message -- )
- <span "error" =class span> write </span> ;
+ <span "error" =class span> escape-string write </span> ;
TUPLE: hidden < field ;
text-renderer new-text-renderer ;
M: text-renderer render-view*
- drop write ;
+ drop escape-string write ;
M: text-renderer render-edit*
<textarea
[ =id ]
[ =name ] bi
textarea>
- write
+ escape-string write
</textarea> ;
TUPLE: text < string ;
html-text-renderer new-text-renderer ;
M: html-text-renderer render-view*
- drop write ;
+ drop escape-string write ;
TUPLE: html-text < text ;
SINGLETON: link-renderer
M: link-renderer render-view*
- drop <a dup link-href =href a> link-title write </a> ;
+ drop <a dup link-href =href a> link-title escape-string write </a> ;
TUPLE: link < string ;
C: <choice-renderer> choice-renderer
M: choice-renderer render-view*
- drop write ;
+ drop escape-string write ;
+
+: render-option ( text selected? -- )
+ <option [ "true" =selected ] when option>
+ escape-string write
+ </option> ;
+
+: render-options ( text selected -- )
+ [ [ drop ] [ member? ] 2bi render-option ] curry each ;
M: choice-renderer render-edit*
<select swap =name select>
- choices>> [
- <option [ = [ "true" =selected ] when ] keep option>
- write
- </option>
- ] with each
+ choices>> swap 1array render-options
</select> ;
TUPLE: choice < string ;
: <choice> ( id choices -- component )
swap choice new-string
swap <choice-renderer> >>renderer ;
+
+! Menu
+TUPLE: menu-renderer choices size ;
+
+C: <menu-renderer> menu-renderer
+
+M: menu-renderer render-edit*
+ <select dup size>> [ number>string =size ] when* swap =name select>
+ choices>> render-options
+ </select> ;
+
+TUPLE: menu < string ;
+
+: <menu> ( id choices -- component )
+ swap menu new-string
+ swap <menu-renderer> >>renderer ;
! Copyright (C) 2008 Slava Pestov\r
! See http://factorcode.org/license.txt for BSD license.\r
USING: splitting kernel io sequences farkup accessors\r
-http.server.components ;\r
+http.server.components xml.entities ;\r
IN: http.server.components.farkup\r
\r
TUPLE: farkup-renderer < text-renderer ;\r
! Copyright (C) 2008 Slava Pestov
! See http://factorcode.org/license.txt for BSD license.
USING: splitting kernel io sequences inspector accessors
-http.server.components ;
+http.server.components xml.entities html ;
IN: http.server.components.inspector
SINGLETON: inspector-renderer
M: inspector-renderer render-view*
- drop describe ;
+ drop [ describe ] with-html-stream ;
TUPLE: inspector < component ;
] with-form ;
: <form-response> ( form template -- response )
- [ components>> components set ]
- [ "text/html" <content> swap >>body ]
- bi* ;
+ [ components>> components set ] [ <html-content> ] bi* ;
: view-form ( form -- response )
dup view-template>> <form-response> ;
! Copyright (C) 2003, 2008 Slava Pestov.
! See http://factorcode.org/license.txt for BSD license.
USING: assocs kernel namespaces io io.timeouts strings splitting
-threads http sequences prettyprint io.server logging calendar
-html.elements accessors math.parser combinators.lib
-tools.vocabs debugger html continuations random combinators
+threads sequences prettyprint io.server logging calendar
+http html html.elements accessors math.parser combinators.lib
+tools.vocabs debugger continuations random combinators
destructors io.encodings.8-bit fry classes words ;
IN: http.server
<response>
200 >>code
"Document follows" >>message
- swap set-content-type ;
+ swap >>content-type ;
+
+: <html-content> ( quot -- response )
+ "text/html" <content> swap >>body ;
TUPLE: trivial-responder response ;
</html> ;
: <trivial-response> ( code message -- response )
- 2dup '[ , , trivial-response-body ]
- "text/html" <content>
- swap >>body
+ 2dup '[ , , trivial-response-body ] <html-content>
swap >>message
swap >>code ;
] with-destructors response set\r
] unit-test\r
\r
- [ "text/plain" ] [ response get "content-type" header ] unit-test\r
+ [ "text/plain" ] [ response get content-type>> ] unit-test\r
\r
[ f ] [ response get cookies>> empty? ] unit-test\r
] with-scope\r
! Copyright (C) 2004, 2008 Slava Pestov.\r
! See http://factorcode.org/license.txt for BSD license.\r
-USING: calendar html io io.files kernel math math.parser http\r
-http.server namespaces parser sequences strings assocs\r
-hashtables debugger http.mime sorting html.elements logging\r
-calendar.format accessors io.encodings.binary fry ;\r
+USING: calendar html io io.files kernel math math.order\r
+math.parser http http.server namespaces parser sequences strings\r
+assocs hashtables debugger http.mime sorting html.elements\r
+logging calendar.format accessors io.encodings.binary fry ;\r
IN: http.server.static\r
\r
! special maps mime types to quots with effect ( path -- )\r
TUPLE: file-responder root hook special ;\r
\r
-: file-http-date ( filename -- string )\r
- file-info modified>> timestamp>http-string ;\r
-\r
-: last-modified-matches? ( filename -- ? )\r
- file-http-date dup [\r
- request get "if-modified-since" header =\r
- ] when ;\r
+: modified-since? ( filename -- ? )\r
+ request get "if-modified-since" header dup [\r
+ [ file-info modified>> ] [ rfc822>timestamp ] bi* after?\r
+ ] [\r
+ 2drop t\r
+ ] if ;\r
\r
: <304> ( -- response )\r
304 "Not modified" <trivial-response> ;\r
: <static> ( root -- responder )\r
[\r
<content>\r
- swap\r
- [ file-info size>> "content-length" set-header ]\r
- [ file-http-date "last-modified" set-header ]\r
- [ '[ , binary <file-reader> stdio get stream-copy ] >>body ]\r
- tri\r
+ swap [\r
+ file-info\r
+ [ size>> "content-length" set-header ]\r
+ [ modified>> "last-modified" set-header ] bi\r
+ ]\r
+ [ '[ , binary <file-reader> stdio get stream-copy ] >>body ] bi\r
] <file-responder> ;\r
\r
: serve-static ( filename mime-type -- response )\r
- over last-modified-matches?\r
- [ 2drop <304> ] [ file-responder get hook>> call ] if ;\r
+ over modified-since?\r
+ [ file-responder get hook>> call ] [ 2drop <304> ] if ;\r
\r
: serving-path ( filename -- filename )\r
file-responder get root>> right-trim-separators\r
] simple-html-document ;\r
\r
: list-directory ( directory -- response )\r
- "text/html" <content>\r
- swap '[ , directory. ] >>body ;\r
+ '[ , directory. ] <html-content> ;\r
\r
: find-index ( filename -- path )\r
"index.html" append-path dup exists? [ drop f ] unless ;\r
! responder integration
: serve-template ( template -- response )
- "text/html" <content>
- swap '[ , call-template ] >>body ;
+ '[ , call-template ] <html-content> ;
http.server.components
http.server.components.code
http.server.templating.chloe
+http.server.auth
http.server.auth.login
http.server.boilerplate
http.server.validators
TUPLE: pastebin < dispatcher ;
+SYMBOL: can-delete-pastes?
+
+can-delete-pastes? define-capability
+
: <pastebin> ( -- responder )
pastebin new-dispatcher
<paste-list-action> "list" add-main-responder
<feed-action> "feed.xml" add-responder
<paste-form> [ <paste> ] <view-paste-action> "view-paste" add-responder
- [ <paste> ] "$pastebin/list" <delete-paste-action> <protected> "delete-paste" add-responder
- [ <annotation> ] "$pastebin/view-paste" <delete-annotation-action> <protected> "delete-annotation" add-responder
+ [ <paste> ] "$pastebin/list" <delete-paste-action> { can-delete-pastes? } <protected> "delete-paste" add-responder
+ [ <annotation> ] "$pastebin/view-paste" { can-delete-pastes? } <delete-annotation-action> <protected> "delete-annotation" add-responder
<paste-form> [ <paste> ] <view-paste-action> "$pastebin/view-paste" add-responder
<new-paste-form> [ <paste> now >>date ] "$pastebin/view-paste" <new-paste-action> "new-paste" add-responder
<new-annotation-form> [ <annotation> now >>date ] "$pastebin/view-paste" <annotate-action> "annotate" add-responder
http.server.boilerplate
http.server.templating.chloe
http.server.components
-http.server.auth.login ;
+http.server.auth.login
+http.server.auth ;
IN: webapps.planet
TUPLE: planet-factor < dispatcher postings ;
blog-form blog-ctor "$planet-factor/admin" <edit-action> "edit-blog" add-responder
] ;
+SYMBOL: can-administer-planet-factor?
+
+can-administer-planet-factor? define-capability
+
: <planet-factor> ( -- responder )
planet-factor new-dispatcher
dup <planet-action> "list" add-main-responder
dup <feed-action> "feed.xml" add-responder
- dup <planet-factor-admin> <protected> "admin" add-responder
+ dup <planet-factor-admin> { can-administer-planet-factor? } <protected> "admin" add-responder
<boilerplate>
"planet" planet-template >>template ;
<t:title>Edit Item</t:title>
- <t:form action="$todo-list/edit">
- <t:edit component="id" />
+ <t:form t:action="$todo-list/edit">
+ <t:edit t:component="id" />
<table>
- <tr><th class="field-label">Summary: </th><td><t:edit component="summary" /></td></tr>
- <tr><th class="field-label">Priority: </th><td><t:edit component="priority" /></td></tr>
- <tr><th class="field-label big-field-label">Description:</th><td><t:edit component="description" /></td></tr>
+ <tr><th class="field-label">Summary: </th><td><t:edit t:component="summary" /></td></tr>
+ <tr><th class="field-label">Priority: </th><td><t:edit t:component="priority" /></td></tr>
+ <tr><th class="field-label big-field-label">Description:</th><td><t:edit t:component="description" /></td></tr>
</table>
<input type="SUBMIT" value="Done" />
</t:form>
- <t:a href="$todo-list/view" query="id">View</t:a>
+ <t:a t:href="$todo-list/view" t:query="id">View</t:a>
|
- <t:form action="$todo-list/delete" class="inline">
- <t:edit component="id" />
+ <t:form t:action="$todo-list/delete" t:class="inline">
+ <t:edit t:component="id" />
<button type="submit" class="link-button link">Delete</button>
</t:form>
ctor "$todo-list/list" <delete-action> "delete" add-responder
<boilerplate>
"todo" todo-template >>template
- <protected>
+ f <protected>
] ;
<t:title>View Item</t:title>
<table>
- <tr><th class="field-label">Summary: </th><td><t:view component="summary" /></td></tr>
- <tr><th class="field-label">Priority: </th><td><t:view component="priority" /></td></tr>
+ <tr><th class="field-label">Summary: </th><td><t:view t:component="summary" /></td></tr>
+ <tr><th class="field-label">Priority: </th><td><t:view t:component="priority" /></td></tr>
</table>
<div class="description">