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 and baz 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 named baz with the value of baz in the caller's environment. Since we've specified that bar should be excluded, bar won't get exported even though it's specified in the last argument. Additionally, even though foo is specified also in the last argument, the value we use is the one given in the override argument. Finally, both variables are signed, because we specified the -sign switch.

You can specify variables with three different precedences, namely override, exclude or vars. If a variable is present in override, that's what'll get exported, no matter what. If a variable is in exclude and not in override, then it will not get output. However, if it is in vars and not in either of override or exclude, then it'll get output. In other words, we check override, exclude and vars in that order of precedence.

The two variable specs, vars and override both look the same: They take a list of variable specs. Examples of variable specs are:

  • foo
  • foo:multiple,sign
  • {foo "the value"}
  • {foo {[my_function arg]}}
  • {foo:array,sign {[array get my_array]}}
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 [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:

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 of ad_page_contract, which means that each entry will get output as name.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 of ad_page_contract and makes sure that the value isn't tampered with on the client side. The -sign switch to export_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.

The argument exclude simply takes a list of names of variables that you don't want exported, even though they're specified in vars.

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 of column.

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):
%3 test_acs_subsite_test_email_confirmation acs_subsite_test_email_confirmation (test acs-subsite) export_vars export_vars test_acs_subsite_test_email_confirmation->export_vars test_create_form_with_form_instance create_form_with_form_instance (test xowiki) test_create_form_with_form_instance->export_vars test_create_workflow_with_instance create_workflow_with_instance (test xowf) test_create_workflow_with_instance->export_vars test_export_vars export_vars (test acs-tcl) test_export_vars->export_vars test_form_validate form_validate (test xowiki) test_form_validate->export_vars ad_urlencode_query ad_urlencode_query (public) export_vars->ad_urlencode_query ad_urlencode_url ad_urlencode_url (public) export_vars->ad_urlencode_url export_vars_sign export_vars_sign (private) export_vars->export_vars_sign util::skip_suspicious_query_vars util::skip_suspicious_query_vars (private) export_vars->util::skip_suspicious_query_vars Class ::Generic::Form Class ::Generic::Form (public) Class ::Generic::Form->export_vars Class ::Generic::List Class ::Generic::List (public) Class ::Generic::List->export_vars Class ::ms::Authorize Class ::ms::Authorize (public) Class ::ms::Authorize->export_vars Class ::xo::Authorize Class ::xo::Authorize (public) Class ::xo::Authorize->export_vars Class ::xowf::test_item::Answer_manager Class ::xowf::test_item::Answer_manager (public) Class ::xowf::test_item::Answer_manager->export_vars

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_string
XQL Not present:
PostgreSQL, Oracle
Generic XQL file:
packages/acs-tcl/tcl/utilities-procs.xql

[ hide source ] | [ make this the default ]
Show another procedure: