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?

