Merge branch 'clean-linux-x86-32' of git://factorcode.org/git/factor

db4
Eduardo Cavazos 2008-04-17 16:06:19 -05:00
commit 14456e3a13
51 changed files with 1289 additions and 441 deletions

View File

@ -1,9 +1,10 @@
! Generate a new factor.vim file for syntax highlighting
USING: http.server.templating.fhtml io.files ;
USING: http.server.templating http.server.templating.fhtml
io.files ;
IN: editors.vim.generate-syntax
: generate-vim-syntax ( -- )
"misc/factor.vim.fgen" resource-path
"misc/factor.vim.fgen" resource-path <fhtml>
"misc/factor.vim" resource-path
template-convert ;

View File

@ -54,10 +54,12 @@ IN: farkup.tests
[ "<p>=</p><h2>foo</h2>" ] [ "===foo==" convert-farkup ] unit-test
[ "<h1>foo</h1><p>=</p>" ] [ "=foo==" convert-farkup ] unit-test
[ "<span class='KEYWORD3'>int</span> <span class='FUNCTION'>main</span><span class='OPERATOR'>(</span><span class='OPERATOR'>)</span><br/>" ]
[ "<div style='white-space: pre; font-family: monospace; '><span class='KEYWORD3'>int</span> <span class='FUNCTION'>main</span><span class='OPERATOR'>(</span><span class='OPERATOR'>)</span><br/></div>" ]
[ "[c{int main()}]" convert-farkup ] unit-test
[ "<p><img src=\"lol.jpg\"/></p>" ] [ "[[image:lol.jpg]]" convert-farkup ] unit-test
[ "<p><img src=\"lol.jpg\" alt=\"teh lol\"/></p>" ] [ "[[image:lol.jpg|teh lol]]" convert-farkup ] unit-test
[ "<p><a href=\"lol.com\"></a></p>" ] [ "[[lol.com]]" convert-farkup ] unit-test
[ "<p><a href=\"lol.com\">haha</a></p>" ] [ "[[lol.com|haha]]" convert-farkup ] unit-test
[ ] [ "[{}]" convert-farkup drop ] unit-test

View File

@ -1,9 +1,9 @@
! Copyright (C) 2008 Doug Coleman.
! See http://factorcode.org/license.txt for BSD license.
USING: arrays io kernel memoize namespaces peg sequences strings
html.elements xml.entities xmode.code2html splitting
io.streams.string html peg.parsers html.elements sequences.deep
unicode.categories ;
USING: arrays io io.styles kernel memoize namespaces peg
sequences strings html.elements xml.entities xmode.code2html
splitting io.streams.string html peg.parsers html.elements
sequences.deep unicode.categories ;
IN: farkup
<PRIVATE
@ -55,7 +55,13 @@ MEMO: eq ( -- parser )
: render-code ( string mode -- string' )
>r string-lines r>
[ [ htmlize-lines ] with-html-stream ] with-string-writer ;
[
[
H{ { wrap-margin f } } [
htmlize-lines
] with-nesting
] with-html-stream
] with-string-writer ;
: escape-link ( href text -- href-esc text-esc )
>r escape-quoted-string r> escape-string ;

View File

@ -394,13 +394,18 @@ body ;
[ unparse-cookies "set-cookie" pick set-at ] when*
write-header ;
GENERIC: write-response-body* ( body -- )
M: f write-response-body* drop ;
M: string write-response-body* write ;
M: callable write-response-body* call ;
M: object write-response-body* stdio get stream-copy ;
: write-response-body ( response -- response )
dup body>> {
{ [ dup not ] [ drop ] }
{ [ dup string? ] [ write ] }
{ [ dup callable? ] [ call ] }
[ stdio get stream-copy ]
} cond ;
dup body>> write-response-body* ;
M: response write-response ( respose -- )
write-response-version

View File

@ -0,0 +1,9 @@
<?xml version='1.0' ?>
<t:chloe xmlns:t="http://factorcode.org/chloe/1.0">
<h1><t:write-title /></h1>
<t:call-next-template />
</t:chloe>

View File

@ -1,77 +0,0 @@
<% USING: http.server.components http.server.auth.login
http.server namespaces kernel combinators ; %>
<html>
<body>
<h1>Edit profile</h1>
<form method="POST" action="edit-profile">
<% hidden-form-field %>
<table>
<tr>
<td>User name:</td>
<td><% "username" component render-view %></td>
</tr>
<tr>
<td>Real name:</td>
<td><% "realname" component render-edit %></td>
</tr>
<tr>
<td></td>
<td>Specifying a real name is optional.</td>
</tr>
<tr>
<td>Current password:</td>
<td><% "password" component render-edit %></td>
</tr>
<tr>
<td></td>
<td>If you don't want to change your current password, leave this field blank.</td>
</tr>
<tr>
<td>New password:</td>
<td><% "new-password" component render-edit %></td>
</tr>
<tr>
<td>Verify:</td>
<td><% "verify-password" component render-edit %></td>
</tr>
<tr>
<td></td>
<td>If you are changing your password, enter it twice to ensure it is correct.</td>
</tr>
<tr>
<td>E-mail:</td>
<td><% "email" component render-edit %></td>
</tr>
<tr>
<td></td>
<td>Specifying an e-mail address is optional. It enables the "recover password" feature.</td>
</tr>
</table>
<p><input type="submit" value="Update" />
<% {
{ [ login-failed? get ] [ "invalid password" render-error ] }
{ [ password-mismatch? get ] [ "passwords do not match" render-error ] }
{ [ t ] [ ] }
} cond %>
</p>
</form>
</body>
</html>

View File

@ -0,0 +1,77 @@
<?xml version='1.0' ?>
<t:chloe xmlns:t="http://factorcode.org/chloe/1.0">
<t:title>Edit Profile</t:title>
<t:form action="edit-profile">
<table>
<tr>
<th class="field-label">User name:</th>
<td><t:view component="username" /></td>
</tr>
<tr>
<th class="field-label">Real name:</th>
<td><t:edit component="realname" /></td>
</tr>
<tr>
<td></td>
<td>Specifying a real name is optional.</td>
</tr>
<tr>
<th class="field-label">Current password:</th>
<td><t:edit component="password" /></td>
</tr>
<tr>
<td></td>
<td>If you don't want to change your current password, leave this field blank.</td>
</tr>
<tr>
<th class="field-label">New password:</th>
<td><t:edit component="new-password" /></td>
</tr>
<tr>
<th class="field-label">Verify:</th>
<td><t:edit component="verify-password" /></td>
</tr>
<tr>
<td></td>
<td>If you are changing your password, enter it twice to ensure it is correct.</td>
</tr>
<tr>
<th class="field-label">E-mail:</th>
<td><t:edit component="email" /></td>
</tr>
<tr>
<td></td>
<td>Specifying an e-mail address is optional. It enables the "recover password" feature.</td>
</tr>
</table>
<p>
<input type="submit" value="Update" />
<t:if var="http.server.auth.login:login-failed?">
<t:error>invalid password</t:error>
</t:if>
<t:if var="http.server.auth.login:password-mismatch?">
<t:error>passwords do not match</t:error>
</t:if>
</p>
</t:form>
</t:chloe>

View File

@ -15,7 +15,9 @@ http.server.actions
http.server.components
http.server.forms
http.server.sessions
http.server.templating.fhtml
http.server.boilerplate
http.server.templating
http.server.templating.chloe
http.server.validators ;
IN: http.server.auth.login
QUALIFIED: smtp
@ -40,11 +42,15 @@ M: user-saver dispose
: save-user-after ( user -- )
<user-saver> add-always-destructor ;
: login-template ( name -- template )
"resource:extra/http/server/auth/login/" swap ".xml"
3append <chloe> ;
! ! ! Login
: <login-form>
"login" <form>
"resource:extra/http/server/auth/login/login.fhtml" >>edit-template
"login" login-template >>edit-template
"username" <username>
t >>required
add-field
@ -62,10 +68,7 @@ M: user-saver dispose
<action>
[ blank-values ] >>init
[
"text/html" <content>
[ form edit-form ] >>body
] >>display
[ form edit-form ] >>display
[
blank-values
@ -86,7 +89,7 @@ M: user-saver dispose
: <register-form> ( -- form )
"register" <form>
"resource:extra/http/server/auth/login/register.fhtml" >>edit-template
"register" login-template >>edit-template
"username" <username>
t >>required
add-field
@ -114,10 +117,7 @@ SYMBOL: user-exists?
<action>
[ blank-values ] >>init
[
"text/html" <content>
[ form edit-form ] >>body
] >>display
[ form edit-form ] >>display
[
blank-values
@ -147,7 +147,7 @@ SYMBOL: user-exists?
: <edit-profile-form> ( -- form )
"edit-profile" <form>
"resource:extra/http/server/auth/login/edit-profile.fhtml" >>edit-template
"edit-profile" login-template >>edit-template
"username" <username> add-field
"realname" <string> add-field
"password" <password> add-field
@ -168,10 +168,7 @@ SYMBOL: previous-page
dup email>> "email" set-value
] >>init
[
"text/html" <content>
[ form edit-form ] >>body
] >>display
[ form edit-form ] >>display
[
blank-values
@ -242,7 +239,7 @@ SYMBOL: lost-password-from
: <recover-form-1> ( -- form )
"register" <form>
"resource:extra/http/server/auth/login/recover-1.fhtml" >>edit-template
"recover-1" login-template >>edit-template
"username" <username>
t >>required
add-field
@ -256,10 +253,7 @@ SYMBOL: lost-password-from
<action>
[ blank-values ] >>init
[
"text/html" <content>
[ form edit-form ] >>body
] >>display
[ form edit-form ] >>display
[
blank-values
@ -271,13 +265,13 @@ SYMBOL: lost-password-from
send-password-email
] when*
"resource:extra/http/server/auth/login/recover-2.fhtml" serve-template
"recover-2" login-template serve-template
] >>submit
] ;
: <recover-form-3>
"new-password" <form>
"resource:extra/http/server/auth/login/recover-3.fhtml" >>edit-template
"recover-3" login-template >>edit-template
"username" <username>
hidden >>renderer
t >>required
@ -308,10 +302,7 @@ SYMBOL: lost-password-from
] H{ } make-assoc values set
] >>init
[
"text/html" <content>
[ <recover-form-3> edit-form ] >>body
] >>display
[ <recover-form-3> edit-form ] >>display
[
blank-values
@ -326,8 +317,7 @@ SYMBOL: lost-password-from
"new-password" value >>password
users update-user
"resource:extra/http/server/auth/login/recover-4.fhtml"
serve-template
"recover-4" login-template serve-template
] [
<400>
] if*
@ -367,24 +357,32 @@ M: login call-responder ( path responder -- response )
dup login set
call-next-method ;
: <login-boilerplate> ( responder -- responder' )
<boilerplate>
"boilerplate" login-template >>template ;
: <login> ( responder -- auth )
login new-dispatcher
swap <protected> >>default
<login-action> "login" add-responder
<logout-action> "logout" add-responder
<login-action> <login-boilerplate> "login" add-responder
<logout-action> <login-boilerplate> "logout" add-responder
no-users >>users ;
! ! ! Configuration
: allow-edit-profile ( login -- login )
<edit-profile-action> <protected> "edit-profile" add-responder ;
<edit-profile-action> <protected> <login-boilerplate>
"edit-profile" add-responder ;
: allow-registration ( login -- login )
<register-action> "register" add-responder ;
<register-action> <login-boilerplate>
"register" add-responder ;
: allow-password-recovery ( login -- login )
<recover-action-1> "recover-password" add-responder
<recover-action-3> "new-password" add-responder ;
<recover-action-1> <login-boilerplate>
"recover-password" add-responder
<recover-action-3> <login-boilerplate>
"new-password" add-responder ;
: allow-edit-profile? ( -- ? )
login get responders>> "edit-profile" swap key? ;

View File

@ -1,46 +0,0 @@
<% USING: http.server.auth.login http.server.components http.server
kernel namespaces ; %>
<html>
<body>
<h1>Login required</h1>
<form method="POST" action="login">
<% hidden-form-field %>
<table>
<tr>
<td>User name:</td>
<td><% "username" component render-edit %></td>
</tr>
<tr>
<td>Password:</td>
<td><% "password" component render-edit %></td>
</tr>
</table>
<p><input type="submit" value="Log in" />
<%
login-failed? get
[ "Invalid username or password" render-error ] when
%>
</p>
</form>
<p>
<% allow-registration? [ %>
<a href="<% "register" f write-link %>">Register</a>
<% ] when %>
<% allow-password-recovery? [ %>
<a href="<% "recover-password" f write-link %>">
Recover Password
</a>
<% ] when %>
</p>
</body>
</html>

View File

@ -0,0 +1,44 @@
<?xml version='1.0' ?>
<t:chloe xmlns:t="http://factorcode.org/chloe/1.0">
<t:title>Login</t:title>
<t:form action="login">
<table>
<tr>
<th class="field-label">User name:</th>
<td><t:edit component="username" /></td>
</tr>
<tr>
<th class="field-label">Password:</th>
<td><t:edit component="password" /></td>
</tr>
</table>
<p>
<input type="submit" value="Log in" />
<t:if var="http.server.auth.login:login-failed?">
<t:error>invalid username or password</t:error>
</t:if>
</p>
</t:form>
<p>
<t:if code="http.server.auth.login:login-failed?">
<t:a href="register">Register</t:a>
</t:if>
|
<t:if code="http.server.auth.login:allow-password-recovery?">
<t:a href="recover-password">Recover Password</t:a>
</t:if>
</p>
</t:chloe>

View File

@ -1,41 +0,0 @@
<% USING: http.server.components http.server ; %>
<html>
<body>
<h1>Recover lost password: step 1 of 4</h1>
<p>Enter the username and e-mail address you used to register for this site, and you will receive a link for activating a new password.</p>
<form method="POST" action="recover-password">
<% hidden-form-field %>
<table>
<tr>
<td>User name:</td>
<td><% "username" component render-edit %></td>
</tr>
<tr>
<td>E-mail:</td>
<td><% "email" component render-edit %></td>
</tr>
<tr>
<td>Captcha:</td>
<td><% "captcha" component render-edit %></td>
</tr>
<tr>
<td></td>
<td>Leave the captcha blank. Spam-bots will fill it indiscriminantly, so their attempts to e-mail you will be blocked.</td>
</tr>
</table>
<input type="submit" value="Recover password" />
</form>
</body>
</html>

View File

@ -0,0 +1,39 @@
<?xml version='1.0' ?>
<t:chloe xmlns:t="http://factorcode.org/chloe/1.0">
<t:title>Recover lost password: step 1 of 4</t:title>
<p>Enter the username and e-mail address you used to register for this site, and you will receive a link for activating a new password.</p>
<t:form action="recover-password">
<table>
<tr>
<th class="field-label">User name:</th>
<td><t:edit component="username" /></td>
</tr>
<tr>
<th class="field-label">E-mail:</th>
<td><t:edit component="email" /></td>
</tr>
<tr>
<th class="field-label">Captcha:</th>
<td><t:edit component="captcha" /></td>
</tr>
<tr>
<td></td>
<td>Leave the captcha blank. Spam-bots will fill it indiscriminantly, so their attempts to e-mail you will be blocked.</td>
</tr>
</table>
<input type="submit" value="Recover password" />
</t:form>
</t:chloe>

View File

@ -1,9 +0,0 @@
<% USING: http.server.components ; %>
<html>
<body>
<h1>Recover lost password: step 2 of 4</h1>
<p>If you entered the correct username and e-mail address, you should receive an e-mail shortly. Click the link in the e-mail for further instructions.</p>
</body>
</html>

View File

@ -0,0 +1,9 @@
<?xml version='1.0' ?>
<t:chloe xmlns:t="http://factorcode.org/chloe/1.0">
<t:title>Recover lost password: step 2 of 4</t:title>
<p>If you entered the correct username and e-mail address, you should receive an e-mail shortly. Click the link in the e-mail for further instructions.</p>
</t:chloe>

View File

@ -1,46 +0,0 @@
<% USING: http.server.components http.server.auth.login http.server
namespaces kernel combinators ; %>
<html>
<body>
<h1>Recover lost password: step 3 of 4</h1>
<p>Choose a new password for your account.</p>
<form method="POST" action="new-password">
<% hidden-form-field %>
<table>
<% "username" component render-edit %>
<% "ticket" component render-edit %>
<tr>
<td>Password:</td>
<td><% "new-password" component render-edit %></td>
</tr>
<tr>
<td>Verify password:</td>
<td><% "verify-password" component render-edit %></td>
</tr>
<tr>
<td></td>
<td>Enter your password twice to ensure it is correct.</td>
</tr>
</table>
<p><input type="submit" value="Set password" />
<% password-mismatch? get [
"passwords do not match" render-error
] when %>
</p>
</form>
</body>
</html>

View File

@ -0,0 +1,43 @@
<?xml version='1.0' ?>
<t:chloe xmlns:t="http://factorcode.org/chloe/1.0">
<t:title>Recover lost password: step 3 of 4</t:title>
<p>Choose a new password for your account.</p>
<t:form action="new-password">
<table>
<t:edit component="username" />
<t:edit component="ticket" />
<tr>
<th class="field-label">Password:</th>
<td><t:edit component="new-password" /></td>
</tr>
<tr>
<th class="field-label">Verify password:</th>
<td><t:edit component="verify-password" /></td>
</tr>
<tr>
<td></td>
<td>Enter your password twice to ensure it is correct.</td>
</tr>
</table>
<p>
<input type="submit" value="Set password" />
<t:if var="http.server.auth.login:password-mismatch?">
<t:error>passwords do not match</t:error>
</t:if>
</p>
</t:form>
</t:chloe>

View File

@ -1,10 +0,0 @@
<% USING: http.server ; %>
<html>
<body>
<h1>Recover lost password: step 4 of 4</h1>
<p>Your password has been reset.
You may now <a href="<% "login" f write-link %>">log in</a>.</p>
</body>
</html>

View File

@ -0,0 +1,9 @@
<?xml version='1.0' ?>
<t:chloe xmlns:t="http://factorcode.org/chloe/1.0">
<t:title>Recover lost password: step 4 of 4</t:title>
<p>Your password has been reset. You may now <t:a href="login">log in</t:a>.</p>
</t:chloe>

View File

@ -1,77 +0,0 @@
<% USING: http.server.components http.server.auth.login
http.server namespaces kernel combinators ; %>
<html>
<body>
<h1>New user registration</h1>
<form method="POST" action="register">
<% hidden-form-field %>
<table>
<tr>
<td>User name:</td>
<td><% "username" component render-edit %></td>
</tr>
<tr>
<td>Real name:</td>
<td><% "realname" component render-edit %></td>
</tr>
<tr>
<td></td>
<td>Specifying a real name is optional.</td>
</tr>
<tr>
<td>Password:</td>
<td><% "new-password" component render-edit %></td>
</tr>
<tr>
<td>Verify:</td>
<td><% "verify-password" component render-edit %></td>
</tr>
<tr>
<td></td>
<td>Enter your password twice to ensure it is correct.</td>
</tr>
<tr>
<td>E-mail:</td>
<td><% "email" component render-edit %></td>
</tr>
<tr>
<td></td>
<td>Specifying an e-mail address is optional. It enables the "recover password" feature.</td>
</tr>
<tr>
<td>Captcha:</td>
<td><% "captcha" component render-edit %></td>
</tr>
<tr>
<td></td>
<td>Leave the captcha blank. Spam-bots will fill it indiscriminantly, so their attempts to register will be blocked.</td>
</tr>
</table>
<p><input type="submit" value="Register" />
<% {
{ [ password-mismatch? get ] [ "passwords do not match" render-error ] }
{ [ user-exists? get ] [ "username taken" render-error ] }
{ [ t ] [ ] }
} cond %>
</p>
</form>
</body>
</html>

View File

@ -0,0 +1,79 @@
<?xml version='1.0' ?>
<t:chloe xmlns:t="http://factorcode.org/chloe/1.0">
<t:title>New User Registration</t:title>
<t:form action="register">
<table>
<tr>
<th class="field-label">User name:</th>
<td><t:edit component="username" /></td>
</tr>
<tr>
<th class="field-label">Real name:</th>
<td><t:edit component="realname" /></td>
</tr>
<tr>
<td></td>
<td>Specifying a real name is optional.</td>
</tr>
<tr>
<th class="field-label">Password:</th>
<td><t:edit component="new-password" /></td>
</tr>
<tr>
<th class="field-label">Verify:</th>
<td><t:edit component="verify-password" /></td>
</tr>
<tr>
<td></td>
<td>Enter your password twice to ensure it is correct.</td>
</tr>
<tr>
<th class="field-label">E-mail:</th>
<td><t:edit component="email" /></td>
</tr>
<tr>
<td></td>
<td>Specifying an e-mail address is optional. It enables the "recover password" feature.</td>
</tr>
<tr>
<th class="field-label">Captcha:</th>
<td><t:edit component="captcha" /></td>
</tr>
<tr>
<td></td>
<td>Leave the captcha blank. Spam-bots will fill it indiscriminantly, so their attempts to register will be blocked.</td>
</tr>
</table>
<p>
<input type="submit" value="Register" />
<t:if var="http.server.auth.login:user-exists?">
<t:error>username taken</t:error>
</t:if>
<t:if var="http.server.auth.login:password-mismatch?">
<t:error>passwords do not match</t:error>
</t:if>
</p>
</t:form>
</t:chloe>

View File

@ -0,0 +1,58 @@
! 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
http
http.server
http.server.templating ;
IN: http.server.boilerplate
TUPLE: boilerplate responder template ;
: <boilerplate> f boilerplate boa ;
SYMBOL: title
: set-title ( string -- )
title get >box ;
: write-title ( -- )
title get value>> write ;
SYMBOL: style
: add-style ( string -- )
"\n" style get push-all
style get push-all ;
: write-style ( -- )
style get >string write ;
SYMBOL: nested-template?
SYMBOL: next-template
: call-next-template ( -- )
next-template get write ;
M: f call-template drop call-next-template ;
: with-boilerplate ( body template -- )
[
title get [ <box> title set ] unless
style get [ SBUF" " clone style set ] unless
[
[
nested-template? on
write-response-body*
] with-string-writer
next-template set
]
[ call-template ]
bi*
] with-scope ; inline
M: boilerplate call-responder
[ responder>> call-responder clone ] [ template>> ] bi
[ [ with-boilerplate ] 2curry ] curry change-body ;

View File

@ -1,11 +1,10 @@
IN: http.server.components.tests
USING: http.server.components http.server.forms
http.server.validators namespaces tools.test kernel accessors
tuple-syntax mirrors http.server.actions
tuple-syntax mirrors
http http.server.actions http.server.templating.fhtml
io.streams.string io.streams.null ;
\ render-edit must-infer
validation-failed? off
[ 3 ] [ "3" "n" <number> validate ] unit-test
@ -49,8 +48,8 @@ TUPLE: test-tuple text number more-text ;
: <test-form> ( -- form )
"test" <form>
"resource:extra/http/server/components/test/form.fhtml" >>view-template
"resource:extra/http/server/components/test/form.fhtml" >>edit-template
"resource:extra/http/server/components/test/form.fhtml" <fhtml> >>view-template
"resource:extra/http/server/components/test/form.fhtml" <fhtml> >>edit-template
"text" <string>
t >>required
add-field
@ -64,9 +63,9 @@ TUPLE: test-tuple text number more-text ;
"hi" >>default
add-field ;
[ ] [ <test-tuple> <mirror> values set <test-form> view-form ] unit-test
[ ] [ <test-tuple> <mirror> values set <test-form> view-form write-response-body drop ] unit-test
[ ] [ <test-tuple> <mirror> values set <test-form> edit-form ] unit-test
[ ] [ <test-tuple> <mirror> values set <test-form> edit-form write-response-body drop ] unit-test
[ TUPLE{ test-tuple number: 123 more-text: "hi" } ] [
<test-tuple> from-tuple

View File

@ -7,9 +7,12 @@ continuations math ;
IN: http.server.components
! Renderer protocol
GENERIC: render-summary* ( value renderer -- )
GENERIC: render-view* ( value renderer -- )
GENERIC: render-edit* ( value id renderer -- )
M: object render-summary* render-view* ;
TUPLE: field type ;
C: <field> field
@ -203,22 +206,67 @@ M: captcha validate*
drop v-captcha ;
! Text areas
TUPLE: textarea-renderer ;
TUPLE: textarea-renderer rows cols ;
: textarea-renderer T{ textarea-renderer } ;
: new-textarea-renderer ( class -- renderer )
new
60 >>cols
20 >>rows ;
: <textarea-renderer> ( -- renderer )
textarea-renderer new-textarea-renderer ;
M: textarea-renderer render-view*
drop write ;
M: textarea-renderer render-edit*
drop <textarea [ =id ] [ =name ] bi textarea> write </textarea> ;
<textarea
[ rows>> [ number>string =rows ] when* ]
[ cols>> [ number>string =cols ] when* ] bi
[ =id ]
[ =name ] bi
textarea>
write
</textarea> ;
TUPLE: text < string ;
: new-text ( id class -- component )
new-string
f >>one-line
textarea-renderer >>renderer ;
<textarea-renderer> >>renderer ;
: <text> ( id -- component )
text new-text ;
! List components
SYMBOL: +plain+
SYMBOL: +ordered+
SYMBOL: +unordered+
TUPLE: list-renderer component type ;
C: <list-renderer> list-renderer
: render-list ( value component -- )
[ render-summary* ] curry each ;
: render-ordered-list ( value component -- )
[ <li> render-summary* </li> ] curry each ;
: render-unordered-list ( value component -- )
[ <li> render-summary* </li> ] curry each ;
M: list-renderer render-view*
[ component>> ] [ type>> ] bi {
{ +plain+ [ render-list ] }
{ +ordered+ [ <ol> render-ordered-list </ol> ] }
{ +unordered+ [ <ul> render-unordered-list </ul> ] }
} case ;
TUPLE: list < component ;
: <list> ( id component type -- list )
<list-renderer> list swap new-component ;
M: list component-string drop ;

View File

@ -6,11 +6,12 @@ IN: http.server.components.farkup
TUPLE: farkup-renderer < textarea-renderer ;
: farkup-renderer T{ farkup-renderer } ;
: <farkup-renderer>
farkup-renderer new-textarea-renderer ;
M: farkup-renderer render-view*
drop string-lines "\n" join convert-farkup write ;
: <farkup> ( id -- component )
<text>
farkup-renderer >>renderer ;
<farkup-renderer> >>renderer ;

View File

@ -15,49 +15,33 @@ IN: http.server.crud
[ "id" get ctor call select-tuple from-tuple ] >>init
[
"text/html" <content>
[ form view-form ] >>body
] >>display ;
[ form view-form ] >>display ;
: <id-redirect> ( id next -- response )
swap number>string "id" associate <permanent-redirect> ;
:: <create-action> ( form ctor next -- action )
<action>
[ f ctor call from-tuple form set-defaults ] >>init
[
"text/html" <content>
[ form edit-form ] >>body
] >>display
[
f ctor call from-tuple
form validate-form
values-tuple insert-tuple
"id" value next <id-redirect>
] >>submit ;
:: <edit-action> ( form ctor next -- action )
<action>
{ { "id" [ v-number ] } } >>get-params
[ "id" get ctor call select-tuple from-tuple ] >>init
{ { "id" [ [ v-number ] v-optional ] } } >>get-params
[
"text/html" <content>
[ form edit-form ] >>body
] >>display
"id" get ctor call
"id" get
[ select-tuple from-tuple ]
[ from-tuple form set-defaults ]
if
] >>init
[ form edit-form ] >>display
[
f ctor call from-tuple
form validate-form
values-tuple update-tuple
values-tuple
"id" value [ update-tuple ] [ insert-tuple ] if
"id" value next <id-redirect>
] >>submit ;
@ -71,3 +55,13 @@ IN: http.server.crud
next f <permanent-redirect>
] >>submit ;
:: <list-action> ( form ctor -- action )
<action>
[
blank-values
f ctor call select-tuples "list" set-value
form view-form
] >>display ;

View File

@ -1,11 +1,16 @@
USING: kernel accessors assocs namespaces io.files fry
! Copyright (C) 2008 Slava Pestov
! See http://factorcode.org/license.txt for BSD license.
USING: kernel accessors assocs namespaces io.files sequences fry
http.server
http.server.actions
http.server.components
http.server.validators
http.server.templating.fhtml ;
http.server.templating ;
IN: http.server.forms
TUPLE: form < component view-template edit-template components ;
TUPLE: form < component
view-template edit-template summary-template
components ;
M: form init V{ } clone >>components ;
@ -15,8 +20,11 @@ M: form init V{ } clone >>components ;
: add-field ( form component -- form )
dup id>> pick components>> set-at ;
: set-components ( form -- )
components>> components set ;
: with-form ( form quot -- )
>r components>> components r> with-variable ; inline
[ [ set-components ] [ call ] bi* ] with-scope ; inline
: set-defaults ( form -- )
[
@ -27,11 +35,16 @@ M: form init V{ } clone >>components ;
] assoc-each
] with-form ;
: view-form ( form -- )
dup view-template>> '[ , run-template ] with-form ;
: <form-response> ( form template -- response )
[ components>> components set ]
[ "text/html" <content> swap >>body ]
bi* ;
: edit-form ( form -- )
dup edit-template>> '[ , run-template ] with-form ;
: view-form ( form -- response )
dup view-template>> <form-response> ;
: edit-form ( form -- response )
dup edit-template>> <form-response> ;
: validate-param ( id component -- )
[ [ params get at ] [ validate ] bi* ]
@ -46,3 +59,20 @@ M: form init V{ } clone >>components ;
: validate-form ( form -- )
(validate-form) [ validation-failed ] when ;
: render-form ( value form template -- )
[
[ from-tuple ]
[ set-components ]
[ call-template ]
tri*
] with-scope ;
M: form render-summary*
dup summary-template>> render-form ;
M: form render-view*
dup view-template>> render-form ;
M: form render-edit*
dup edit-template>> render-form ;

View File

@ -0,0 +1,97 @@
USING: http.server.templating http.server.templating.chloe
http.server.components http.server.boilerplate tools.test
io.streams.string kernel sequences ascii boxes namespaces xml
splitting ;
IN: http.server.templating.chloe.tests
[ "foo" ]
[ "<a href=\"foo\">blah</a>" string>xml "href" required-attr ]
unit-test
[ "<a name=\"foo\">blah</a>" string>xml "href" required-attr ]
[ "href attribute is required" = ]
must-fail-with
[ f ] [ f parse-query-attr ] unit-test
[ f ] [ "" parse-query-attr ] unit-test
[ H{ { "a" "b" } } ] [
blank-values
"b" "a" set-value
"a" parse-query-attr
] unit-test
[ H{ { "a" "b" } { "c" "d" } } ] [
blank-values
"b" "a" set-value
"d" "c" set-value
"a,c" parse-query-attr
] unit-test
: run-template
with-string-writer [ "\r\n\t" member? not ] subset
"?>" split1 nip ; inline
: test-template ( name -- template )
"resource:extra/http/server/templating/chloe/test/"
swap
".xml" 3append <chloe> ;
[ "Hello world" ] [
[
"test1" test-template call-template
] run-template
] unit-test
[ "Blah blah" "Hello world" ] [
[
<box> title set
[
"test2" test-template call-template
] run-template
title get box>
] with-scope
] unit-test
[ "<html><head><title>Hello world</title></head><body>Blah blah</body></html>" ] [
[
[
"test2" test-template call-template
] "test3" test-template with-boilerplate
] run-template
] unit-test
: test4-aux? t ;
[ "True" ] [
[
"test4" test-template call-template
] run-template
] unit-test
: test5-aux? f ;
[ "" ] [
[
"test5" test-template call-template
] run-template
] unit-test
SYMBOL: test6-aux?
[ "True" ] [
[
test6-aux? on
"test6" test-template call-template
] run-template
] unit-test
SYMBOL: test7-aux?
[ "" ] [
[
test7-aux? off
"test7" test-template call-template
] run-template
] unit-test

View File

@ -0,0 +1,174 @@
USING: accessors kernel sequences combinators kernel namespaces
classes.tuple assocs splitting words arrays
io.files io.encodings.utf8 html.elements unicode.case
tuple-syntax xml xml.data xml.writer xml.utilities
http.server
http.server.auth
http.server.components
http.server.sessions
http.server.templating
http.server.boilerplate ;
IN: http.server.templating.chloe
! Chloe is Ed's favorite web designer
TUPLE: chloe path ;
C: <chloe> chloe
DEFER: process-template
: chloe-ns TUPLE{ name url: "http://factorcode.org/chloe/1.0" } ;
: chloe-tag? ( tag -- ? )
{
{ [ dup tag? not ] [ f ] }
{ [ dup chloe-ns names-match? not ] [ f ] }
[ t ]
} cond nip ;
SYMBOL: tags
: required-attr ( tag name -- value )
dup rot at*
[ nip ] [ drop " attribute is required" append throw ] if ;
: optional-attr ( tag name -- value )
swap at ;
: write-title-tag ( tag -- )
drop
"head" tags get member? "title" tags get member? not and
[ <title> write-title </title> ] [ write-title ] if ;
: style-tag ( tag -- )
dup "include" optional-attr dup [
swap children>string empty? [
"style tag cannot have both an include attribute and a body" throw
] unless
utf8 file-contents
] [
drop children>string
] if add-style ;
: write-style-tag ( tag -- )
drop <style> write-style </style> ;
: component-attr ( tag -- name )
"component" required-attr ;
: view-tag ( tag -- )
component-attr component render-view ;
: edit-tag ( tag -- )
component-attr component render-edit ;
: parse-query-attr ( string -- assoc )
dup empty?
[ drop f ] [ "," split [ dup value ] H{ } map>assoc ] if ;
: a-start-tag ( tag -- )
<a
[ "href" required-attr ]
[ "query" optional-attr parse-query-attr ]
bi link>string =href
a> ;
: process-tag-children ( tag -- )
[ process-template ] each ;
: a-tag ( tag -- )
[ a-start-tag ]
[ process-tag-children ]
[ drop </a> ]
tri ;
: form-start-tag ( tag -- )
<form
"POST" =method
tag-attrs print-attrs
form>
hidden-form-field ;
: form-tag ( tag -- )
[ form-start-tag ]
[ process-tag-children ]
[ drop </form> ]
tri ;
: attr>word ( value -- word/f )
dup ":" split1 swap lookup
[ ] [ "No such word: " swap append throw ] ?if ;
: attr>var ( value -- word/f )
attr>word dup symbol? [
"Must be a symbol: " swap append throw
] unless ;
: if-satisfied? ( tag -- ? )
{
[ "code" optional-attr [ attr>word execute ] [ t ] if* ]
[ "var" optional-attr [ attr>var get ] [ t ] if* ]
[ "svar" optional-attr [ attr>var sget ] [ t ] if* ]
[ "uvar" optional-attr [ attr>var uget ] [ t ] if* ]
} cleave 4array [ ] all? ;
: if-tag ( tag -- )
dup if-satisfied? [ process-tag-children ] [ drop ] if ;
: error-tag ( tag -- )
children>string render-error ;
: process-chloe-tag ( tag -- )
dup name-tag {
{ "chloe" [ [ process-template ] each ] }
{ "title" [ children>string set-title ] }
{ "write-title" [ write-title-tag ] }
{ "style" [ style-tag ] }
{ "write-style" [ write-style-tag ] }
{ "view" [ view-tag ] }
{ "edit" [ edit-tag ] }
{ "a" [ a-tag ] }
{ "form" [ form-tag ] }
{ "error" [ error-tag ] }
{ "if" [ if-tag ] }
{ "call-next-template" [ drop call-next-template ] }
[ "Unknown chloe tag: " swap append throw ]
} case ;
: process-tag ( tag -- )
{
[ name-tag >lower tags get push ]
[ write-start-tag ]
[ process-tag-children ]
[ write-end-tag ]
[ drop tags get pop* ]
} cleave ;
: process-template ( xml -- )
{
{ [ dup [ chloe-tag? ] is? ] [ process-chloe-tag ] }
{ [ dup [ tag? ] is? ] [ process-tag ] }
{ [ t ] [ write-item ] }
} cond ;
: process-chloe ( xml -- )
[
V{ } clone tags set
nested-template? get [
process-template
] [
{
[ xml-prolog write-prolog ]
[ xml-before write-chunk ]
[ process-template ]
[ xml-after write-chunk ]
} cleave
] if
] with-scope ;
M: chloe call-template
path>> utf8 <file-reader> read-xml process-chloe ;
INSTANCE: chloe template

View File

@ -0,0 +1,5 @@
<?xml version='1.0' ?>
<t:chloe xmlns:t="http://factorcode.org/chloe/1.0">
Hello world
</t:chloe>

View File

@ -0,0 +1,6 @@
<?xml version='1.0' ?>
<t:chloe xmlns:t="http://factorcode.org/chloe/1.0">
<t:title>Hello world</t:title>
Blah blah
</t:chloe>

View File

@ -0,0 +1,5 @@
<?xml version='1.0' ?>
<t:chloe xmlns:t="http://factorcode.org/chloe/1.0">
<t:title>Hello world</t:title>
</t:chloe>

View File

@ -0,0 +1,12 @@
<?xml version='1.0' ?>
<t:chloe xmlns:t="http://factorcode.org/chloe/1.0">
<html>
<head>
<t:write-title />
</head>
<body>
<t:call-next-template />
</body>
</html>
</t:chloe>

View File

@ -0,0 +1,9 @@
<?xml version='1.0' ?>
<t:chloe xmlns:t="http://factorcode.org/chloe/1.0">
<t:if code="http.server.templating.chloe.tests:test4-aux?">
True
</t:if>
</t:chloe>

View File

@ -0,0 +1,9 @@
<?xml version='1.0' ?>
<t:chloe xmlns:t="http://factorcode.org/chloe/1.0">
<t:if code="http.server.templating.chloe.tests:test5-aux?">
True
</t:if>
</t:chloe>

View File

@ -0,0 +1,9 @@
<?xml version='1.0' ?>
<t:chloe xmlns:t="http://factorcode.org/chloe/1.0">
<t:if var="http.server.templating.chloe.tests:test6-aux?">
True
</t:if>
</t:chloe>

View File

@ -0,0 +1,9 @@
<?xml version='1.0' ?>
<t:chloe xmlns:t="http://factorcode.org/chloe/1.0">
<t:if var="http.server.templating.chloe.tests:test7-aux?">
True
</t:if>
</t:chloe>

View File

@ -1,13 +1,13 @@
USING: io io.files io.streams.string io.encodings.utf8
http.server.templating.fhtml kernel tools.test sequences
parser ;
http.server.templating http.server.templating.fhtml kernel
tools.test sequences parser ;
IN: http.server.templating.fhtml.tests
: test-template ( path -- ? )
"resource:extra/http/server/templating/fhtml/test/"
prepend
[
".fhtml" append [ run-template ] with-string-writer
".fhtml" append <fhtml> [ call-template ] with-string-writer
] keep
".html" append utf8 file-contents = ;

View File

@ -1,12 +1,12 @@
! Copyright (C) 2005 Alex Chapman
! Copyright (C) 2006, 2008 Slava Pestov
! See http://factorcode.org/license.txt for BSD license.
USING: continuations sequences kernel parser namespaces io
io.files io.streams.string html html.elements source-files
debugger combinators math quotations generic strings splitting
accessors http.server.static http.server assocs
io.encodings.utf8 fry accessors ;
USING: continuations sequences kernel namespaces debugger
combinators math quotations generic strings splitting
accessors assocs fry
parser io io.files io.streams.string io.encodings.utf8 source-files
html html.elements
http.server.static http.server http.server.templating ;
IN: http.server.templating.fhtml
: templating-vocab ( -- vocab-name ) "http.server.templating.fhtml" ;
@ -72,9 +72,13 @@ DEFER: <% delimiter
: html-error. ( error -- )
<pre> error. </pre> ;
: run-template ( filename -- )
TUPLE: fhtml path ;
C: <fhtml> fhtml
M: fhtml call-template ( filename -- )
'[
, [
, path>> [
"quiet" on
parser-notes off
templating-vocab use+
@ -85,16 +89,10 @@ DEFER: <% delimiter
] with-file-vocabs
] assert-depth ;
: template-convert ( infile outfile -- )
utf8 [ run-template ] with-file-writer ;
! responder integration
: serve-template ( name -- response )
"text/html" <content>
swap '[ , run-template ] >>body ;
! file responder integration
: enable-fhtml ( responder -- responder )
[ serve-template ]
[ <fhtml> serve-template ]
"application/x-factor-server-page"
pick special>> set-at ;
INSTANCE: fhtml template

View File

@ -0,0 +1,17 @@
USING: accessors kernel fry io.encodings.utf8 io.files
http http.server ;
IN: http.server.templating
MIXIN: template
GENERIC: call-template ( template -- )
M: template write-response-body* call-template ;
: template-convert ( template output -- )
utf8 [ call-template ] with-file-writer ;
! responder integration
: serve-template ( template -- response )
"text/html" <content>
swap '[ , call-template ] >>body ;

View File

@ -11,8 +11,7 @@ TUPLE: validation-error value reason ;
C: <validation-error> validation-error
: with-validator ( value quot -- result )
[ validation-failed? on <validation-error> ] recover ;
inline
[ validation-failed? on <validation-error> ] recover ; inline
: v-default ( str def -- str )
over empty? spin ? ;
@ -20,6 +19,9 @@ C: <validation-error> validation-error
: v-required ( str -- str )
dup empty? [ "required" throw ] when ;
: v-optional ( str quot -- str )
over empty? [ 2drop f ] [ call ] if ; inline
: v-min-length ( str n -- str )
over length over < [
[ "must be at least " % # " characters" % ] "" make

View File

@ -0,0 +1,33 @@
! Copyright (c) 2008 Eric Mertens
! See http://factorcode.org/license.txt for BSD license.
USING: arrays assocs kernel math math.ranges sequences ;
IN: project-euler.164
! http://projecteuler.net/index.php?section=problems&id=164
! DESCRIPTION
! -----------
! How many 20 digit numbers n (without any leading zero) exist such
! that no three consecutive digits of n have a sum greater than 9?
! SOLUTION
! --------
<PRIVATE
: next-keys ( key -- keys )
[ peek ] [ 10 swap sum - ] bi [ 2array ] with map ;
: next-table ( assoc -- assoc )
H{ } clone swap
[ swap next-keys [ pick at+ ] with each ] assoc-each ;
: init-table ( -- assoc )
9 [1,b] [ 1array 1 ] H{ } map>assoc ;
PRIVATE>
: euler164 ( -- n )
init-table 19 [ next-table ] times values sum ;

View File

@ -0,0 +1,26 @@
<?xml version='1.0' ?>
<t:chloe xmlns:t="http://factorcode.org/chloe/1.0">
<t:title>Edit Item</t:title>
<t:form action="edit">
<t:edit 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>
</table>
<input type="SUBMIT" value="Done" />
</t:form>
<t:a href="view" query="id">View</t:a>
|
<t:form action="delete" class="inline">
<t:edit component="id" />
<button type="submit" class="link-button link">Delete</button>
</t:form>
</t:chloe>

View File

@ -0,0 +1,45 @@
<?xml version='1.0' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<t:chloe xmlns:t="http://factorcode.org/chloe/1.0">
<head>
<t:write-title />
<t:style>
body, button {
font:9pt "Lucida Grande", "Lucida Sans Unicode", verdana, geneva, sans-serif;
color:#444;
}
a, .link {
color: #222;
border-bottom:1px dotted #666;
text-decoration:none;
}
a:hover, .link:hover {
border-bottom:1px solid #66a;
}
.error { color: #a00; }
.field-label {
text-align: right;
}
</t:style>
<t:write-style />
</head>
<body>
<t:call-next-template />
</body>
</t:chloe>
</html>

View File

@ -0,0 +1,12 @@
<?xml version='1.0' ?>
<t:chloe xmlns:t="http://factorcode.org/chloe/1.0">
<t:title>My Todo List</t:title>
<table class="todo-list">
<tr><th>Summary</th><th>Priority</th><th>View</th><th>Edit</th></tr>
<t:view component="list" />
</table>
</t:chloe>

View File

@ -0,0 +1,20 @@
<?xml version='1.0' ?>
<t:chloe xmlns:t="http://factorcode.org/chloe/1.0">
<tr>
<td>
<t:view component="summary" />
</td>
<td>
<t:view component="priority" />
</td>
<td>
<t:a href="view" query="id">View</t:a>
</td>
<td>
<t:a href="edit" query="id">Edit</t:a>
</td>
</tr>
</t:chloe>

View File

@ -0,0 +1,41 @@
.big-field-label {
vertical-align: top;
}
.description {
border: 1px dashed #ccc;
background-color: #f5f5f5;
padding: 5px;
font-size: 150%;
color: #000000;
}
.link-button {
padding: 0px;
background: none;
border: none;
}
.navbar {
background-color: #eeeeee;
padding: 5px;
border: 1px solid #ccc;
}
.inline {
display: inline;
}
pre {
font-size: 75%;
}
.todo-list {
border-style: none;
}
.todo-list td, .todo-list th {
border-width: 1px;
padding: 2px;
border-style: solid;
}

113
extra/webapps/todo/todo.factor Executable file
View File

@ -0,0 +1,113 @@
! Copyright (c) 2008 Slava Pestov
! See http://factorcode.org/license.txt for BSD license.
USING: accessors kernel locals sequences
db db.types db.tuples
http.server.components http.server.components.farkup
http.server.forms http.server.templating.chloe
http.server.boilerplate http.server.crud http.server.auth
http.server.actions http.server.db
http.server ;
IN: webapps.todo
TUPLE: todo uid id priority summary description ;
todo "TODO"
{
{ "uid" "UID" { VARCHAR 256 } +not-null+ }
{ "id" "ID" +native-id+ }
{ "priority" "PRIORITY" INTEGER +not-null+ }
{ "summary" "SUMMARY" { VARCHAR 256 } +not-null+ }
{ "description" "DESCRIPTION" { VARCHAR 256 } }
} define-persistent
: init-todo-table todo ensure-table ;
: <todo> ( id -- todo )
todo new
swap >>id
uid >>uid ;
: todo-template ( name -- template )
"resource:extra/webapps/todo/" swap ".xml" 3append <chloe> ;
: <todo-form> ( -- form )
"todo" <form>
"view-todo" todo-template >>view-template
"edit-todo" todo-template >>edit-template
"todo-summary" todo-template >>summary-template
"id" <integer>
hidden >>renderer
add-field
"summary" <string>
t >>required
add-field
"priority" <integer>
t >>required
0 >>default
0 >>min-value
10 >>max-value
add-field
"description" <farkup>
add-field ;
: <todo-list-form> ( -- form )
"todo-list" <form>
"todo-list" todo-template >>view-template
"list" <todo-form> +plain+ <list>
add-field ;
TUPLE: todo-responder < dispatcher ;
:: <todo-responder> ( -- responder )
[let | todo-form [ <todo-form> ]
list-form [ <todo-list-form> ]
ctor [ [ <todo> ] ] |
todo-responder new-dispatcher
list-form ctor <list-action> "list" add-main-responder
todo-form ctor <view-action> "view" add-responder
todo-form ctor "view" <edit-action> "edit" add-responder
ctor "list" <delete-action> "delete" add-responder
<boilerplate>
"todo" todo-template >>template
] ;
! What follows below is somewhat akin to a 'deployment descriptor'
! for the todo application. The <todo-responder> can be integrated
! into an existing web app that provides session management and
! login facilities, or <todo-app> can be used to run a
! self-contained todo instance.
USING: namespaces io.files io.sockets
db.sqlite smtp
http.server.sessions
http.server.auth.login
http.server.auth.providers.db
http.server.sessions.storage.db ;
: test-db "todo.db" resource-path sqlite-db ;
: <todo-app> ( -- responder )
<todo-responder>
<login>
users-in-db >>users
allow-registration
allow-password-recovery
allow-edit-profile
<boilerplate>
"page" todo-template >>template
<url-sessions>
sessions-in-db >>sessions
test-db <db-persistence> ;
: init-todo ( -- )
"factorcode.org" 25 <inet> smtp-server set-global
"todo@factorcode.org" lost-password-from set-global
test-db [
init-todo-table
init-users-table
init-sessions-table
] with-db
<dispatcher>
<todo-app> "todo" add-responder
main-responder set-global ;

View File

@ -0,0 +1,26 @@
<?xml version='1.0' ?>
<t:chloe xmlns:t="http://factorcode.org/chloe/1.0">
<t:style include="resource:extra/webapps/todo/todo.css" />
<t:style include="resource:extra/xmode/code2html/stylesheet.css" />
<div class="navbar">
<t:a href="list">List Items</t:a>
| <t:a href="edit">Add Item</t:a>
<t:if code="http.server.auth.login:allow-edit-profile?">
| <t:a href="edit-profile">Edit Profile</t:a>
</t:if>
<t:form action="logout" class="inline">
| <button type="submit" class="link-button link">Logout</button>
</t:form>
</div>
<h1><t:write-title /></h1>
<t:call-next-template />
</t:chloe>

View File

@ -0,0 +1,23 @@
<?xml version='1.0' ?>
<t:chloe xmlns:t="http://factorcode.org/chloe/1.0">
<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>
</table>
<div class="description">
<t:view component="description" />
</div>
<t:a href="edit" query="id">Edit</t:a>
|
<t:form action="delete" class="inline">
<t:edit component="id" />
<button class="link-button link">Delete</button>
</t:form>
</t:chloe>

View File

@ -1,8 +1,8 @@
! Copyright (C) 2005, 2006 Daniel Ehrenberg
! See http://factorcode.org/license.txt for BSD license.
USING: hashtables kernel math namespaces sequences strings
io io.streams.string xml.data assocs wrap xml.entities
unicode.categories ;
assocs combinators io io.streams.string
xml.data wrap xml.entities unicode.categories ;
IN: xml.writer
SYMBOL: xml-pprint?
@ -61,6 +61,9 @@ M: string write-item
?indent CHAR: < write1
dup print-name tag-attrs print-attrs ;
: write-start-tag ( tag -- )
write-tag ">" write ;
M: contained-tag write-item
write-tag "/>" write ;
@ -72,11 +75,14 @@ M: contained-tag write-item
?indent "</" write print-name CHAR: > write1 ;
M: open-tag write-item
xml-pprint? [ [
over sensitive? not and xml-pprint? set
dup write-tag CHAR: > write1
dup write-children write-end-tag
] keep ] change ;
xml-pprint? get >r
{
[ sensitive? not xml-pprint? get and xml-pprint? set ]
[ write-start-tag ]
[ write-children ]
[ write-end-tag ]
} cleave
r> xml-pprint? set ;
M: comment write-item
"<!--" write comment-text write "-->" write ;
@ -97,10 +103,12 @@ M: instruction write-item
[ write-item ] each ;
: write-xml ( xml -- )
dup xml-prolog write-prolog
dup xml-before write-chunk
dup write-item
xml-after write-chunk ;
{
[ xml-prolog write-prolog ]
[ xml-before write-chunk ]
[ write-item ]
[ xml-after write-chunk ]
} cleave ;
: print-xml ( xml -- )
write-xml nl ;

View File

@ -36,9 +36,13 @@ TAGS>
f \ modes set-global ;
MEMO: (load-mode) ( name -- rule-sets )
modes at mode-file
"extra/xmode/modes/" prepend
resource-path utf8 <file-reader> parse-mode ;
modes at [
mode-file
"extra/xmode/modes/" prepend
resource-path utf8 <file-reader> parse-mode
] [
"text" (load-mode)
] if* ;
SYMBOL: rule-sets