Within a session template, you get a few built-in commands:save - saves all variables and arrays within sessionexit - utterly destroys the session and any persistent datadynamic - makes this page's generation dynamic (default)cache - causes this page to be cached.value - get session variable value (values pertaining to session management)group - get value pertaining to this session 'group'session - get session idsequence - get session sequence (for strictly sequenced sessions)You also get some arrays (these don't persist):data - per-socket data for this transactionpage - some page related dataquery - query data encoded in URLAdditionally, any arrays or variables you create in the .stml code will persist if you have [save]d the session.
custom/stml.tcl:
# stml.tcl # # Per-session interpreter template substitution for tclhttpd # # CMcC 20040929 - Changed to make use of generic Cache module DirList_IndexFile index.{stml,tml,html,shtml,thtml,htm,subst} # Files with a .stml suffix or .sstml are templated and cached as with .tml files # Template evaluation occurs within a cookie-session (a safe cookie session, for .ssmtl) # Cookie session state can persist in file by calling Session_Save Mtype_Add .stml application/x-tcl-session Mtype_Add .sstml application/x-tcl-safe-session proc Doc_application/x-tcl-safe-session {path suffix sock} { return [Doc_application/x-tcl-session $path $suffix $sock 1] } proc Doc_application/x-tcl-session {path suffix sock {safe 0}} { upvar #0 Httpd$sock data # return a cached version, if possible if {[Cache_Fetch $path]} { return } # fetch query array set query [list session new] ;# session id to create a new session array set query [Url_DecodeQueryOnly $data(query)] # decode session type from file names # files like x.type.stml will create a session of type, default 'stml' set type [string trimleft [file extension [file rootname $suffix]] .] if {$type == ""} { set type stml } # create or restore a cookie session set session_error "" set session [Session_Cookie [array get query session*] $type session_error $safe] if {[string match Session:* $session_error]} { # somehow our cookie and our session are out of whack Session_Destroy $session ;# forget the session Session_CookieDestroy $type ;# destroy the session cookies Redirect_To $data(uri) ;# reload to get the new cookies return ;# redundant - Redirect_To throws an error } set query(session) $session # now we have a viable session with an interpreter upvar #0 Session:$session state set interp $state(interp) if {[interp alias $interp exit] == ""} { # create an 'exit' command interp alias $interp exit {} Session_Destroy $session } if {[interp alias $interp save] == ""} { # create a 'save' command interp alias $interp save {} Session_Save $session } interp alias $interp cache $interp set page(dynamic) 0 interp alias $interp dynamic $interp set page(dynamic) 1 # generate interpreter script to source .tml files from the root downward. global Template set libs [Doc_GetPath $sock $path] foreach libdir $libs { set libfile [file join $libdir $Template(tmlExt)] if {[file exists $libfile]} { append script "source $libfile" \n } } # generate interpreter initialization script append script [subst { catch {unset page} catch {unset query} catch {unset data} array set page { url $data(url) dynamic 1 directory [file join {*}$libs] } array set query [list [array get query]] array set data [list [array get data]] }] set cleanup { catch {unset page} catch {unset query} catch {unset data} } # initialize the interpreter set code [catch {interp eval $interp $script} content eo] # Process the template itself if {!$code} { set code [catch {Subst_File $path $interp} content eo] } # Save return cookies, if any Cookie_Save $sock # process errors now if {$code} { # delete the per-page session data interp eval $interp $cleanup # pass errors up - specifically Redirect return code return -options $eo $content } # calculate mime type of return if {[interp eval $interp {info exists data(contentType)}]} { set ctype [interp eval $interp {set data(contentType)}] ;# set by a template } else { set ctype text/html } # delete the per-page session data interp eval $interp $cleanup # Cache the result if {![interp eval $interp {set page(dynamic)}]} { # this page is cacheable Cache_Store $sock $path $content $ctype } else { # return the result - filters will be applied en route return [Httpd_ReturnData $sock $ctype $content] } }
Here's a test file: test.stml
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <!-- [package require html] --> <html> <head> <title>Test session templates</title> </head> <body> <h1>Test session templates</h1> <h2>Session: [session]</h2> <p>type: [value type]</p> <p>interp:[value interp]</p> <p>start: [value start]</p> <p>current: [value current]</p> <p>count: [value count]</p> <p>[::html::tableFromArray data]</p> <p>[::html::tableFromArray query]</p> <p>[::html::tableFromArray page]</p> <hr> <address><a href="mailto:colin at sharedtech dot dyndns dot org">Colin McCormack</a></address> <!-- Created: Thu Sep 16 08:53:25 EST 2004 --> <!-- hhmts start --> Last modified: Thu Sep 16 09:14:32 EST 2004 <!-- hhmts end --> </body> </html>
Colin, I NB could not get this test page to work, using a tclkit85a4, and tclhttpd from cvs. I loaded generic caching and stml w/o errors. When I tried the above example, in the root of my site, I got errors about not finding mypage, faq etc. I removed these from the main .tml file and got a error 'bout not finding the html package...Seems that the slave interp is not aware of the packages that the main interp uses ...Maybe?