export_vars (public)
export_vars [ -sign ] [ -form ] [ -url ] [ -quotehtml ] \ [ -entire_form ] [ -no_empty ] [ -base base ] [ -no_base_encode ] \ [ -anchor anchor ] [ -exclude exclude ] [ -override override ] \ [ -set set ] [ -formvars formvars ] [ vars ]
Defined in packages/acs-tcl/tcl/utilities-procs.tcl
Exports variables either as a URL or in the form of hidden form variables. The result is properly urlencoded, unless flags prohibit this.
Example usages:
set html [export_vars -form { foo bar baz }] set url [export_vars { foo bar baz }]This will export the three variables
foo
,bar
andbaz
as hidden HTML form fields. It does exactly the same as[export_vars -form {foo bar baz}]
.Example usage:
export_vars -sign -override {{foo "new value"}} -exclude { bar } { foo bar baz }This will export a variable named
foo
with the value "new value" and a variable namedbaz
with the value ofbaz
in the caller's environment. Since we've specified thatbar
should be excluded,bar
won't get exported even though it's specified in the last argument. Additionally, even thoughfoo
is specified also in the last argument, the value we use is the one given in theoverride
argument. Finally, both variables are signed, because we specified the-sign
switch.You can specify variables with three different precedences, namely
override
,exclude
orvars
. If a variable is present inoverride
, that's what'll get exported, no matter what. If a variable is inexclude
and not inoverride
, then it will not get output. However, if it is invars
and not in either ofoverride
orexclude
, then it'll get output. In other words, we checkoverride
,exclude
andvars
in that order of precedence.The two variable specs,
vars
andoverride
both look the same: They take a list of variable specs. Examples of variable specs are:In general, there's one or two elements. If there are two, the second element is the value we should use. If one, we pull the value from the variable of the same name in the caller's environment. Note that when you specify the value directly here, we call the Tcl command subst on it, so backslashes, square brackets and variables will get substituted correctly. Therefore, make sure you use curly braces to surround this instead of the
- foo
- foo:multiple,sign
- {foo "the value"}
- {foo {[my_function arg]}}
- {foo:array,sign {[array get my_array]}}
[list]
command; otherwise the contents will get substituted twice, and you'll be in trouble.Right after the name, you may specify a colon and some flags, separated by commas. Valid flags are:
The argument
- multiple
- Treat the value as a list and output each element separately.
- array
- The value is an array and should be exported in a way compliant with the
:array
flag ofad_page_contract
, which means that each entry will get output asname.key=value
.If you don't specify a value directly, but want it pulled out of the Tcl environment, then you don't need to specify
:array
. If you do, and the variable is in fact not an array, an error will be thrown.
- sign
- Sign this variable. This goes hand-in-hand with the
:verify
flag ofad_page_contract
and makes sure that the value isn't tampered with on the client side. The-sign
switch toexport_vars
, is a short-hand for specifying the:sign
switch on every variable.For example, one can use "user_id:sign(max_age=60)" in export_vars to let the exported variable after 60 seconds. Other potential arguments for sign are "user" or "csrf" to bind the signature to a user or to the CSRF token.
exclude
simply takes a list of names of variables that you don't want exported, even though they're specified invars
.Intended use: A page may have a set of variables that it cares about. You can store this in a variable once and pass that to
export_vars
like this:set my_vars { user_id sort_by filter_by }
... [export_vars $my_vars] ...Then, say one of them contains a column to filter on. When you want to clear that column, you can say
[export_vars -exclude { filter_by } $my_vars]
.Similarly, if you want to change the sort order, you can say
[export_vars -override { { sort_by $column } } $my_vars]
, and sorting will be done according to the new value ofcolumn
.If the variable name contains a colon (:), that colon must be escaped with a backslash, so for example "form:id" becomes "form\:id". Sorry.
- Switches:
- -sign (optional, boolean)
- Sign all variables.
- -form (optional, boolean)
- Export in form format. You can't specify both URL and form format.
- -url (optional, boolean)
- Export in URL format. This is the default.
- -quotehtml (optional, boolean)
- HTML quote the entire resulting string. This is an interim solution while we're waiting for the templating system to do the quoting for us.
- -entire_form (optional, boolean)
- Export the entire form from the GET query string or the POST.
- -no_empty (optional, boolean)
- If specified, variables with an empty string value will be suppressed from being exported. This avoids cluttering up the URLs with lots of unnecessary variables.
- -base (optional)
- The base URL to make a link to. The provided value should be a plain value (i.e. urldecoded). In case the provided value is urlencoded, use the flag "-no_base_encode". The value of this parameter will be prepended to the query string along with a question mark (?), if the query is nonempty. The returned string can be used directly in a link (when output is in URL format).
- -no_base_encode (optional, boolean)
- Decides whether argument passed as
base
option will be encoded by ad_urlencode_url proc- -anchor (optional)
- fragment component that will be preceded by a hash (#) in the result URL
- -exclude (optional)
- list of variables that will not be exported
- -override (optional)
- variable specs, overriding the specs in 'vars'
- -set (optional)
- an ns_set that we want to export together with our variables. It has no effect when also the '-entire_form' flag is specified and will otherwise behave as if the current request form data was the supplied ns_set.
- -formvars (optional)
- a list of parameters that will be looked up into the current request and exported. Won't have any effect if '-entire_form' or '-set' are specified and will otherwise behave as if the current request form data was a subset of the whole form containing only the selected variables.
- Parameters:
- vars (optional)
- variable specs for export
- Author:
- Lars Pind <lars@pinds.com>
- Created:
- December 7, 2000
- Partial Call Graph (max 5 caller/called nodes):
- Testcases:
- acs_subsite_test_email_confirmation, export_vars, postman_echo, util_http_post_vars, create_workflow_with_instance, package_normalize_path, xowiki_test_cases, link_tests, slot_interactions, path_resolve, create_form_with_form_instance, form_validate
Source code: if { $form_p && $url_p } { return -code error "You must select either form format or url format, not both." } # default to URL format if { !$form_p && !$url_p } { set url_p 1 } # # TODO: At least the parsing of the options should be transformed # to produce a single dict, containing the properties of all form # vars (probably optionally) and specified arguments. The dict # should be the straightforward source for the generation of the # output set. One should be able to speed the code significantly # up (at least for the standard cases). # # -Gustaf Neumann # # 'noprocessing_vars' is yet another container of variables, # only this one doesn't have the values subst'ed # and we don't try to find :multiple and :array flags in the namespec set noprocessing_vars [list] if { $entire_form_p } { # # We are exporting all of the request's variables. # set the_form [ns_getform] } elseif { $set ne "" } { # # We are exporting a custom ns_set # set the_form $set } elseif { $formvars ne "" } { # # We are exporting a subset of the request's variables. # set the_form [ns_set create] foreach var $formvars { if {[ns_queryexists $var]} { ns_set put $the_form $var [ns_queryget $var] } } } else { # # We won't export any ns_set # set the_form "" } # Note that ns_getform will return the empty string outside a # connection. if { $the_form ne "" } { foreach {varname varvalue} [ns_set array $the_form] { lappend noprocessing_vars [list $varname $varvalue] } } ##### # # Parse the arguments # ##### # 1. if they're in override, use those # 2. if they're in vars, but not in exclude or override, use those # There'll always be an entry here if the variable is to be exported array set exp_precedence_type [list] # This contains entries of the form exp_flag(name:flag) e.g., exp_flag(foo:multiple) array set exp_flag [list] # This contains the value if provided, otherwise we'll pull it out of the caller's environment array set exp_value [list] foreach precedence_type { override exclude vars noprocessing_vars } { foreach var_spec [set $precedence_type] { if { [llength $var_spec] > 2 } { return -code error "A varspec must have either one or two elements." } if { $precedence_type ne "noprocessing_vars" } { # Hide escaped colons for below split regsub -all -- {\\:} $var_spec "!!cOlOn!!" var_spec set name_spec [split [lindex $var_spec 0] ":"] if {[lindex $name_spec 0] == ""} { set name_spec :[lindex $name_spec 1] } # Replace escaped colons with single colon regsub -all -- {!!cOlOn!!} $name_spec ":" name_spec set name [lindex $name_spec 0] } else { set name [lindex $var_spec 0] # Nothing after the colon, since we don't interpret any colons set name_spec [list $name {}] } set export_name [string trimleft $name :] # If we've already encountered this varname, ignore it if { ![info exists exp_precedence_type($export_name)] } { set exp_precedence_type($export_name) $precedence_type if { $precedence_type ne "exclude" } { foreach flag [split [lindex $name_spec 1] ","] { set exp_flag($name:$flag) 0 if {[regexp {^(\w+)[\(](.+)[\)]$} $flag . flag value]} { set exp_flag($name:$flag) $value } } if { $sign_p } { set exp_flag($name:sign) "" } if { [llength $var_spec] > 1 } { if { $precedence_type ne "noprocessing_vars" } { #if {[util::potentially_unsafe_eval_p -- [lindex $var_spec 1]]} { # ad_log warning "potentially_unsafe_eval in variable/value pair $var_spec" #} set value [uplevel [list subst [lindex $var_spec 1]]] } else { set value [lindex $var_spec 1] } set exp_value($name) $value # If the value is specified explicitly, we include it even if the value is empty } else { upvar 1 $name upvar_variable if { [info exists upvar_variable] } { if { [array exists upvar_variable] } { if { $no_empty_p } { # If the no_empty_p flag is set, remove empty string values first set exp_value($export_name) [list] foreach { key value } [array get upvar_variable] { if { $value ne "" } { lappend exp_value($export_name) $key $value } } } else { # If no_empty_p isn't set, just do an array get set exp_value($export_name) [array get upvar_variable] } set exp_flag($export_name:array) 0 } else { if { [info exists exp_flag($export_name:array)] } { return -code error "Variable \"$name\" is not an array" } if { !$no_empty_p } { set exp_value($export_name) $upvar_variable } else { # no_empty_p flag set, remove empty strings if { [info exists exp_flag($export_name:multiple)] } { # This is a list, remove empty entries set exp_value($export_name) {} foreach elm $upvar_variable { if { $elm ne "" } { lappend exp_value($export_name) $elm } } } else { # Simple value, this is easy if { $upvar_variable ne "" } { set exp_value($export_name) $upvar_variable } } } } } } } } } } ##### # # Put the variables into the export_set # ##### # We use an ns_set, because there may be more than one entry with the same name set export_set [ns_set create] foreach name [array names exp_precedence_type] { if { $exp_precedence_type($name) ne "exclude" } { if { [info exists exp_value($name)] } { if { [info exists exp_flag($name:array)] } { if { [info exists exp_flag($name:multiple)] } { foreach { key value } $exp_value($name) { foreach item $value { ns_set put $export_set "${name}.${key}" $item } } } else { foreach { key value } $exp_value($name) { ns_set put $export_set "${name}.${key}" $value } } if { [info exists exp_flag($name:sign)] } { # DRB: array get does not define the order in which elements are returned, # meaning that arrays constructed in different ways can have different # signatures unless we sort the returned list. I ran into this the # very first time I tried to sign an array passed to a page that used # ad_page_contract to verify the veracity of the parameter. ns_set put $export_set "$name:sig" [export_vars_sign -params $exp_flag($name:sign) [lsort $exp_value($name)]] } } else { if { [info exists exp_flag($name:multiple)] } { foreach item $exp_value($name) { ns_set put $export_set $name $item } } else { ns_set put $export_set $name "$exp_value($name)" } if { [info exists exp_flag($name:sign)] } { ns_set put $export_set "$name:sig" [export_vars_sign -params $exp_flag($name:sign) $exp_value($name)] } } } } } ##### # # Translate it into the appropriate format # ##### set export_string {} if { $url_p } { foreach {key value} [ns_set array $export_set] { lappend export_string [ad_urlencode_query $key]=[ad_urlencode_query $value] } set export_string [join $export_string "&"] } else { foreach {key value} [ns_set array $export_set] { append export_string [subst {<div><input type="hidden" name="[ns_quotehtml $key]" value="[ns_quotehtml $value]"></div> }] } } if { $quotehtml_p } { set export_string [ns_quotehtml $export_string] } # Prepend with the base URL if { [info exists base] && $base ne "" } { set parsedURL [ns_parseurl $base] if {[dict exists $parsedURL query]} { # # The base already has query variables - but it might be # empty; however, in this case, the trailing question mark # is not regarded as part of the path, which has to be # encoded; the code assumes that the path up to this point # is already correctly encoded. # set newQuery [::util::skip_suspicious_query_vars [dict get $parsedURL query]] dict set parsedURL query [expr {$newQuery eq "" || $export_string eq "" ? [string cat $newQuery $export_string] : [string cat $newQuery & $export_string] }] set export_string [ns_joinurl $parsedURL] } else { # The base has no query vars: encode URL part if not # explicitly said otherwise. Include also as exception # trivial case of the base being the dummy url '#'. if {!$no_base_encode_p && $base ne "#"} { set base [ad_urlencode_url $base] } set export_string $base[expr {$export_string ne "" ? "?$export_string" : ""}] } } # Append anchor if { [info exists anchor] && $anchor ne "" } { append export_string "\#$anchor" } return $export_stringXQL Not present: PostgreSQL, Oracle Generic XQL file: packages/acs-tcl/tcl/utilities-procs.xql