blob: b76265927e3d35687c7c54e3d79114417d0a9ddb [file] [log] [blame]
# This example shows how persistent sessions can be stored
# on the server.
# It shows two HTML forms which have one input field each.
# You can switch between these two forms using the submit
# buttons. All data given in the form
# Define a memory context to hold some configuration variables.
# The next statement makes a context called 'config' to hold
# these data. It is a cleaner way than making global variables
# because it declares them clearly to be configuration vars.
web::context config
# Now set some configuration variables. Usually put the session
# state files in a directory not accessible through the Web server.
config::cset stateDirectory /tmp/websh/state/
# Create a file counter that generates the session ids. We take
# here an easy number generator, which produces sequential numbers
# and stores the actual counter value in a file (only create one if
# the current interpreter does not already have one)
if {![llength [info commands idGenerator]]} {
web::filecounter idGenerator -filename [file join [config::cget stateDirectory] counter]
}
# Create a file context named 'state'. The option '-path' defines
# where the session contexts are stored. '-attachto' defines an
# URL parameter name that might contain an existing session.
# (This parameter name could in fact be extracted using
# 'web::param sid' whenever the session is initalized.)
web::filecontext state -path [file join [config::cget stateDirectory] %s] -attachto sid -idgen "idGenerator nextval"
# Make sure the session state directory exists
catch {file mkdir [config::cget stateDirectory]}
proc form {page code} {
# Produces a HTML FORM tag. Nested form variables must be output
# in 'code'.
# The 'page' parameter describes the web::command to call when
# the form is submitted.
web::put "<html><head><title>Session Example</title></head>"
web::put "<body bgcolor=\"#FFFFFF\">"
# form starts here
web::put "<form method=\"POST\" action=\"[web::cmdurl $page]\">"
uplevel $code
web::put "</form>"
web::put "</body></html>"
}
proc putErrorMessage {msg} {
# emit an error message in red.
web::put "<p><font color=\"\#ff0000\">[web::htmlify $msg]</font></p>"
}
proc pageOne {{errorMessage ""}} {
# Display page one of our HTML form.
form processPageOne {
if {[string length $errorMessage]} {
putErrorMessage $errorMessage
}
web::put "Numbers only: <input type=\"text\" name=\"a\" value=\"[web::htmlify [state::cget a]]\">"
web::put "<input type=\"submit\" value=\"Page 2\">"
}
}
proc pageTwo {{errorMessage ""}} {
# Display page two of our HTML form.
form processPageTwo {
if {[string length $errorMessage]} {
putErrorMessage $errorMessage
}
web::put "Not empty: <input type=\"text\" name=\"b\" value=\"[web::htmlify [state::cget b]]\">"
web::put "<input type=\"submit\" value=\"Page 1\">"
}
}
proc saveAllFields {} {
# Save all form fields to state context.
# web::formvar without parameters returns a list of HTML form
# variables sent to this script. web::formvar with the name
# of a field returns its value, if the field does not exist
# it returns an empty list (or an optional 2nd parameter 'default
# value').
# For clarity, we do not handle multiple fields with the same
# name correctly here. If a HTML field is given twice or more
# 'web::formvar -count <fieldname>' would give us the field count
# 'web::formvar <fieldname>' returns then a list of n values.
foreach field [web::formvar -names] {
state::cset $field [web::formvar $field]
}
}
# Define two dispatched commands to each show one page of the
# (mini) form. The names of theses application commands will
# be used in the submit action of the form with 'web::cmdurl'.
web::command processPageOne {
state::init
if {![regexp {^[0-9]+$} [web::formvar a]]} {
# The input field does not contain only digits, so show page one again
# including an error message.
pageOne "Please enter a number."
} else {
# Everything ok, so save the form field to persistant session
# and proceed with page two.
saveAllFields
state::commit
pageTwo
}
}
web::command processPageTwo {
state::init
if {![string length [web::formvar b]]} {
# The input field is empty, so show page two again
# including an error message.
pageTwo "Please fill in field."
} else {
# Everything ok, so save the form field to persistant session
# and proceed with page one.
saveAllFields
state::commit
pageOne
}
}
# Define the default command to show page one.
web::command default {
# Initialize a fresh state.
state::init
# Show page one initially.
pageOne
}
# Dispatch to one of the web::commands according to a parameter in the
# URL. This parameter was set using 'web::cmdurl' in the FORM tag in
# procedure 'form'.
# At the very beginning we don't have a command in the URL. Then the
# web::command default is called.
# The '-track' parameter is used to take over the URL parameter 'sid'
# from "incoming" URLs to "outgoing" URLs. This parameters holds
# the session id and makes a session survive
# web::dispatch processes the URL - i.e. extracts parameters from the
# URL - and handles HTML form input sent to this script.
web::dispatch -track sid
# cleanup state (so we have no session crosstalk)
state::delete