http.server\r
http.server.responses\r
furnace\r
-furnace.flash\r
+furnace.redirection\r
+furnace.conversations\r
html.forms\r
html.elements\r
html.components\r
: <action> ( -- action )\r
action new-action ;\r
\r
+: merge-forms ( form -- )\r
+ form get\r
+ [ [ errors>> ] bi@ push-all ]\r
+ [ [ values>> ] bi@ swap update ]\r
+ [ swap validation-failed>> >>validation-failed drop ]\r
+ 2tri ;\r
+\r
: set-nested-form ( form name -- )\r
dup empty? [\r
- drop form set\r
+ drop merge-forms\r
] [\r
- dup length 1 = [\r
- first set-value\r
- ] [\r
- unclip [ set-nested-form ] nest-form\r
- ] if\r
+ unclip [ set-nested-form ] nest-form\r
] if ;\r
\r
: restore-validation-errors ( -- )\r
- form fget [\r
- nested-forms fget set-nested-form\r
+ form cget [\r
+ nested-forms cget set-nested-form\r
] when* ;\r
\r
: handle-get ( action -- response )\r
revalidate-url-key param\r
dup [ >url [ same-host? ] keep and ] when ;\r
\r
-: validation-failed ( flashed -- * )\r
- post-request? revalidate-url and dup [\r
- nested-forms-key param " " split harvest nested-forms set\r
- swap { form nested-forms } append <flash-redirect>\r
- ] [ 2drop <400> ] if\r
+: validation-failed ( -- * )\r
+ post-request? revalidate-url and [\r
+ begin-conversation\r
+ nested-forms-key param " " split harvest nested-forms cset\r
+ form get form cset\r
+ <redirect>\r
+ ] [ <400> ] if*\r
exit-with ;\r
\r
: handle-post ( action -- response )\r
drop url get revalidate-url-key hidden-form-field ;\r
\r
: check-validation ( -- )\r
- validation-failed? [ { } validation-failed ] when ;\r
+ validation-failed? [ validation-failed ] when ;\r
\r
: validate-params ( validators -- )\r
params get swap validate-values check-validation ;\r
! Copyright (C) 2008 Slava Pestov.
! See http://factorcode.org/license.txt for BSD license.
USING: kernel sequences db.tuples alarms calendar db fry
+furnace.db
furnace.cache
-furnace.asides
-furnace.flash
-furnace.sessions
furnace.referrer
-furnace.db
+furnace.sessions
+furnace.conversations
furnace.auth.providers
furnace.auth.login.permits ;
IN: furnace.alloy
: <alloy> ( responder db params -- responder' )
'[
- <asides>
- <flash-scopes>
+ <conversations>
<sessions>
, , <db-persistence>
<check-form-submissions>
] call ;
-: state-classes { session flash-scope aside permit } ; inline
+: state-classes { session conversation permit } ; inline
: init-furnace-tables ( -- )
state-classes ensure-tables
+++ /dev/null
-! Copyright (C) 2008 Slava Pestov.
-! See http://factorcode.org/license.txt for BSD license.
-USING: accessors namespaces sequences arrays kernel
-assocs hashtables math.parser urls combinators
-logging db.types db.tuples
-html.elements
-html.templates.chloe.syntax
-http
-http.server
-http.server.filters
-furnace
-furnace.cache
-furnace.sessions
-furnace.redirection ;
-IN: furnace.asides
-
-TUPLE: aside < server-state session method url post-data ;
-
-: <aside> ( id -- aside )
- aside new-server-state ;
-
-aside "ASIDES"
-{
- { "session" "SESSION" BIG-INTEGER +not-null+ }
- { "method" "METHOD" { VARCHAR 10 } +not-null+ }
- { "url" "URL" URL +not-null+ }
- { "post-data" "POST_DATA" FACTOR-BLOB }
-} define-persistent
-
-TUPLE: asides < server-state-manager ;
-
-: <asides> ( responder -- responder' )
- asides new-server-state-manager ;
-
-: begin-aside* ( -- id )
- f <aside>
- session get id>> >>session
- request get
- [ method>> >>method ]
- [ url>> >>url ]
- [ post-data>> >>post-data ]
- tri
- [ asides get touch-state ] [ insert-tuple ] [ id>> ] tri ;
-
-: end-aside-post ( aside -- response )
- request [
- clone
- over post-data>> >>post-data
- over url>> >>url
- ] change
- url>> path>> split-path
- asides get responder>> call-responder ;
-
-\ end-aside-post DEBUG add-input-logging
-
-ERROR: end-aside-in-get-error ;
-
-: get-aside ( id -- aside )
- dup [ aside get-state ] when
- dup [ dup session>> session get id>> = [ drop f ] unless ] when ;
-
-: end-aside* ( url id -- response )
- post-request? [ end-aside-in-get-error ] unless
- aside get-state [
- dup method>> {
- { "GET" [ url>> <redirect> ] }
- { "HEAD" [ url>> <redirect> ] }
- { "POST" [ end-aside-post ] }
- } case
- ] [ <redirect> ] ?if ;
-
-SYMBOL: aside-id
-
-: aside-id-key "__a" ;
-
-: begin-aside ( -- )
- begin-aside* aside-id set ;
-
-: end-aside ( default -- response )
- aside-id [ f ] change end-aside* ;
-
-: request-aside-id ( request -- aside-id )
- aside-id-key swap request-params at string>number ;
-
-M: asides call-responder*
- dup asides set
- request get request-aside-id aside-id set
- call-next-method ;
-
-M: asides link-attr ( tag -- )
- drop
- "aside" optional-attr {
- { "none" [ aside-id off ] }
- { "begin" [ begin-aside ] }
- { "current" [ ] }
- { f [ ] }
- } case ;
-
-M: asides modify-query ( query responder -- query' )
- drop
- aside-id get [ aside-id-key associate assoc-union ] when* ;
-
-M: asides modify-form ( responder -- )
- drop aside-id get aside-id-key hidden-form-field ;
\r
TUPLE: realm < dispatcher name users checksum secure ;\r
\r
-GENERIC: login-required* ( realm -- response )\r
+GENERIC: login-required* ( description capabilities realm -- response )\r
\r
GENERIC: init-realm ( realm -- )\r
\r
GENERIC: logged-in-username ( realm -- username )\r
\r
-: login-required ( -- * ) realm get login-required* exit-with ;\r
+: login-required ( description capabilities -- * )\r
+ realm get login-required* exit-with ;\r
\r
: new-realm ( responder name class -- realm )\r
new-dispatcher\r
, ,\r
dup protected set\r
dup capabilities>> have-capabilities?\r
- [ call-next-method ] [ 2drop realm get login-required* ] if\r
+ [ call-next-method ] [\r
+ [ drop ] [ [ description>> ] [ capabilities>> ] bi ] bi*\r
+ realm get login-required*\r
+ ] if\r
] if-secure-realm ;\r
\r
: <auth-boilerplate> ( responder -- responder' )\r
\r
: password-mismatch ( -- * )\r
"passwords do not match" validation-error\r
- { } validation-failed ;\r
+ validation-failed ;\r
\r
: same-password-twice ( -- )\r
"new-password" value "verify-password" value =\r
\r
: user-exists ( -- * )\r
"username taken" validation-error\r
- { } validation-failed ;\r
+ validation-failed ;\r
401 "Invalid username or password" <trivial-response>\r
[ "Basic realm=\"" % swap % "\"" % ] "" make "WWW-Authenticate" set-header ;\r
\r
-M: basic-auth-realm login-required* ( realm -- response )\r
- name>> <401> ;\r
+M: basic-auth-realm login-required* ( description capabilities realm -- response )\r
+ 2nip name>> <401> ;\r
\r
M: basic-auth-realm logged-in-username ( realm -- uid )\r
drop\r
! See http://factorcode.org/license.txt for BSD license.
USING: kernel assocs namespaces accessors db db.tuples urls
http.server.dispatchers
-furnace.asides furnace.actions furnace.auth furnace.auth.providers ;
+furnace.conversations
+furnace.actions
+furnace.auth
+furnace.auth.providers ;
IN: furnace.auth.features.deactivate-user
: <deactivate-user-action> ( -- action )
! Copyright (c) 2008 Slava Pestov.
! See http://factorcode.org/license.txt for BSD license.
USING: kernel accessors namespaces sequences assocs
-validators urls
-html.forms
-http.server.dispatchers
+validators urls html.forms http.server.dispatchers
furnace.auth
-furnace.asides
-furnace.actions ;
+furnace.actions
+furnace.conversations ;
IN: furnace.auth.features.edit-profile
: <edit-profile-action> ( -- action )
drop
- URL" $login" end-aside
+ URL" $realm" end-aside
] >>submit
<protected>
http http.server http.server.dispatchers\r
furnace\r
furnace.auth\r
-furnace.flash\r
-furnace.asides\r
furnace.actions\r
furnace.sessions\r
furnace.utilities\r
furnace.redirection\r
+furnace.conversations\r
furnace.auth.login.permits ;\r
IN: furnace.auth.login\r
\r
\r
: login-failed ( -- * )\r
"invalid username or password" validation-error\r
- flashed-variables validation-failed ;\r
+ validation-failed ;\r
\r
: <login-action> ( -- action )\r
<page-action>\r
[\r
- flashed-variables restore-flash\r
- description get "description" set-value\r
- capabilities get words>strings "capabilities" set-value\r
+ description cget "description" set-value\r
+ capabilities cget words>strings "capabilities" set-value\r
] >>init\r
\r
{ login-realm "login" } >>template\r
\r
: <logout-action> ( -- action )\r
<action>\r
- [ logout ] >>submit\r
- <protected>\r
- "logout" >>description ;\r
+ [ logout ] >>submit ;\r
\r
-M: login-realm login-required*\r
- drop\r
+M: login-realm login-required* ( description capabilities login -- response )\r
begin-aside\r
- protected get description>> description set\r
- protected get capabilities>> capabilities set\r
- URL" $realm/login" >secure-url flashed-variables <flash-redirect> ;\r
+ [ description cset ] [ capabilities cset ] [ drop ] tri*\r
+ URL" $realm/login" >secure-url <redirect> ;\r
\r
: <login-realm> ( responder name -- auth )\r
login-realm new-realm\r
{ "post-data" "POST_DATA" FACTOR-BLOB }
} define-persistent
-: conversation-id-key "__f" ;
+: conversation-id-key "__c" ;
TUPLE: conversations < server-state-manager ;
: request-conversation ( request -- conversation )
request-conversation-id get-conversation ;
-: init-conversations ( -- )
+: save-conversation-after ( conversation -- )
+ conversations get save-scope-after ;
+
+: set-conversation ( conversation -- )
+ [
+ [ conversation set ]
+ [ id>> conversation-id set ]
+ [ save-conversation-after ]
+ tri
+ ] when* ;
+
+: init-conversations ( conversations -- )
+ conversations set
request get request-conversation-id
- [ conversation-id set ]
- [ get-conversation conversation set ]
- bi ;
+ get-conversation
+ set-conversation ;
M: conversations call-responder*
- init-conversations
- [ conversations set ] [ call-next-method ] bi ;
+ [ init-conversations ]
+ [ conversations set ]
+ [ call-next-method ]
+ tri ;
: empty-conversastion ( -- conversation )
conversation empty-scope
session get id>> >>session ;
-: add-conversation ( conversation -- id )
- [ conversations get touch-state ] [ insert-tuple ] [ id>> ] tri ;
+: touch-conversation ( conversation -- )
+ conversations get touch-state ;
-: begin-conversation* ( -- id )
- empty-conversastion add-conversation ;
+: add-conversation ( conversation -- )
+ [ touch-conversation ] [ insert-tuple ] bi ;
+
+: begin-conversation* ( -- conversation )
+ empty-conversastion dup add-conversation ;
: begin-conversation ( -- )
- conversation-id [ [ begin-conversation* ] unless* ] change ;
+ conversation get [
+ begin-conversation*
+ set-conversation
+ ] unless ;
+
+: end-conversation ( -- )
+ conversation off
+ conversation-id off ;
: <conversation-redirect> ( url seq -- response )
begin-conversation
bi
] [ 2drop ] if ;
-: begin-aside* ( -- id )
- empty-conversastion
+: begin-aside ( -- )
+ begin-conversation
+ conversation get
request get
[ method>> >>method ]
[ url>> >>url ]
[ post-data>> >>post-data ]
tri
- add-conversation ;
-
-: begin-aside ( -- )
- begin-aside* conversation-id set ;
+ touch-conversation ;
: end-aside-post ( aside -- response )
request [
ERROR: end-aside-in-get-error ;
-: end-aside* ( url id -- response )
+: move-on ( id -- response )
post-request? [ end-aside-in-get-error ] unless
- get-conversation [
- dup method>> {
- { "GET" [ url>> <redirect> ] }
- { "HEAD" [ url>> <redirect> ] }
- { "POST" [ end-aside-post ] }
- } case
- ] [ <redirect> ] ?if ;
+ dup method>> {
+ { "GET" [ url>> <redirect> ] }
+ { "HEAD" [ url>> <redirect> ] }
+ { "POST" [ end-aside-post ] }
+ } case ;
+
+: get-aside ( id -- conversation )
+ get-conversation dup [ dup method>> [ drop f ] unless ] when ;
+
+: end-aside* ( url id -- response )
+ get-aside [ move-on ] [ <redirect> ] ?if ;
: end-aside ( default -- response )
- conversation-id [ f ] change end-aside* ;
+ conversation-id get
+ end-conversation
+ end-aside* ;
M: conversations link-attr ( tag -- )
drop
+++ /dev/null
-! Copyright (C) 2008 Slava Pestov.
-! See http://factorcode.org/license.txt for BSD license.
-USING: namespaces assocs kernel sequences accessors
-urls db.types db.tuples math.parser fry
-http http.server http.server.filters http.server.redirection
-furnace furnace.cache furnace.sessions furnace.redirection ;
-IN: furnace.flash
-
-TUPLE: flash-scope < server-state session namespace ;
-
-: <flash-scope> ( id -- aside )
- flash-scope new-server-state ;
-
-flash-scope "FLASH_SCOPES" {
- { "session" "SESSION" BIG-INTEGER +not-null+ }
- { "namespace" "NAMESPACE" FACTOR-BLOB +not-null+ }
-} define-persistent
-
-: flash-id-key "__f" ;
-
-TUPLE: flash-scopes < server-state-manager ;
-
-: <flash-scopes> ( responder -- responder' )
- flash-scopes new-server-state-manager ;
-
-SYMBOL: flash-scope
-
-: fget ( key -- value )
- flash-scope get dup
- [ namespace>> at ] [ 2drop f ] if ;
-
-: get-flash-scope ( id -- flash-scope )
- dup [ flash-scope get-state ] when
- dup [ dup session>> session get id>> = [ drop f ] unless ] when ;
-
-: request-flash-scope ( request -- flash-scope )
- flash-id-key swap request-params at string>number get-flash-scope ;
-
-M: flash-scopes call-responder*
- dup flash-scopes set
- request get request-flash-scope flash-scope set
- call-next-method ;
-
-: make-flash-scope ( seq -- id )
- f <flash-scope>
- session get id>> >>session
- swap [ dup get ] H{ } map>assoc >>namespace
- [ flash-scopes get touch-state ] [ insert-tuple ] [ id>> ] tri ;
-
-: <flash-redirect> ( url seq -- response )
- [ clone ] dip
- make-flash-scope flash-id-key set-query-param
- <redirect> ;
-
-: restore-flash ( seq -- )
- flash-scope get dup [
- namespace>>
- [ '[ , key? ] filter ]
- [ '[ [ , at ] keep set ] each ]
- bi
- ] [ 2drop ] if ;
empty-session [ init-session ] [ insert-tuple ] [ ] tri ;
: save-session-after ( session -- )
- sessions get <scope-saver> &dispose drop ;
+ sessions get save-scope-after ;
: existing-session ( path session -- response )
[ session set ] [ save-session-after ] bi
USING: html.components html.elements html.forms
xml xml.utilities validators
-furnace furnace.flash ;
+furnace furnace.conversations ;
SYMBOL: a
[ [ <html> "a" <field> render </html> ] "text/html" <content> ] >>display
[ { { "a" [ v-integer ] } } validate-params ] >>validate
[ "a" value a set-global URL" " <redirect> ] >>submit
- <flash-scopes>
+ <conversations>
<sessions>
>>default
add-quit-action
IN: http.server.redirection.tests
USING: http http.server.redirection urls accessors
-namespaces tools.test present ;
+namespaces tools.test present kernel ;
\ relative-to-request must-infer
"www.apple.com" >>host
"/xxx/bar" >>path
{ { "a" "b" } } >>query
+ dup url set
>>url
request set
: authorize-author ( author -- )
username =
{ can-administer-blogs? } have-capabilities? or
- [ login-required ] unless ;
+ [ "edit a blog post" f login-required ] unless ;
: do-post-action ( -- )
validate-integer-id