Forum OpenACS Development: .vuh routing and multiple parameters

Collapse
Posted by Guan Yang on

Below is a proc that's useful for an app I'm working on that relies heavily on index.vuh routing.

Imagine that you have a url, /foo/123, where id=123. index.vuh might say:

rp_form_put id $id
rp_internal_redirect /some/template/somewhere

Now imagine that somewhere we redirect to /foo/123, for example from ad_form, or after logging in, or from a url generated by ad_return_url. ad_return_url will generate the url /foo/123?id=123, which causes problems with ad_page_contract (which sees two instances of the id parameter).

The proc is called in index.vuh instead of rp_form_put and rp_internal_redirect. If it sees something like /foo/123?id=123, it redirects to /foo/123 (but keeps any other parameters).

It's invoked like this:

um::url::internal_redirect -template foo -params {id}

or

um::url::internal_redirect -template foo -params {foo {bar $value}}

(This is the syntax of export_vars.) Ideally OpenACS should have a proc like this, and it should set a global variable with the names of parameters set with rp_form_put so ad_return_url doesn't include them. Or there should be an explanation of the Right Way to use index.vuh.

ad_proc -public um::url::internal_redirect { -return:boolean
                                              -template:required
                                             {-params ""} } {
    Only used in /www/*.vuh. Performs an internal redirect. 
    If params are present in the form parameters, we may call
    ad_returnredirect and not return.
    
    @author Guan Yang (guan@unicast.org)
    @creation-date 2005-03-28
    @see export_vars
    @see ad_returnredirect
    @see rp_internal_redirect
    @see ad_script_abort
    
    @param params   Parameters in a syntax similar to export_vars.
    @param return   Returns. Otherwise we will call
        ad_script_abort. Note that we will never return if it is
        necessary to perform a redirect.
    @param template The template to return.
} { 
    set url [ad_conn url]
    set export_spec [list]
    set params_for_export [list]
    
    # Fetch the parameters
    foreach param $params {
        set len [llength $param]
        if { $len != 1 && $len != 2 } { continue }
        
        if { $len == 2 } {
            set param_name  [lindex $param 0]
            set param_value [lindex $param 1]
            
            # We do a subst in caller's stack
            upvar __um__url__internal_redirect__value up_value
            set up_value $param_value
            uplevel {
                set __um__url__internal_redirect__subst \
                    [subst $__um__url__internal_redirect__value]
            }
            upvar __um__url__internal_redirect__subst up_substed_value
            set param_value $up_substed_value
            # Clean up in caller's stack
            unset up_value up_substed_value

            lappend params_for_export $param_name
            lappend export_spec       [list $param_name $param_value]
        } else {
            set param_name $param
            upvar $param_name param_value
            if { ![info exists param_value] } { continue }
            lappend params_for_export $param_name
            lappend export_spec       [list $param_name $param_value]
        }
    }

    # Remove parameters from the existing form
    set form [rp_getform]
    set deleted 0
    foreach param $params_for_export {
        set matches [lsearch -all -exact [ad_ns_set_keys $form] $param]

        foreach match $matches {
            incr deleted
            ns_set delete $form $match
        }
    }

    # If we deleted anything from the form, perform a redirect
    if { $deleted > 0 } {
        # We need to retain everything in the existing form
        # The form has already been pruned of offending params above
        set form_spec [list]

        # Repack array format into export_vars format
        set form_array [ns_set array $form]
        for { set i 0 } { $i < [llength $form_array] } { incr i } {
            lappend form_spec [list [lindex $form_array $i] \
                                    [lindex $form_array [incr i]]]
        }

        ad_returnredirect [export_vars -base $url $form_spec]
        ad_script_abort
    }


    # Now set the form vars and do an internal redirect
    foreach param $export_spec {
        rp_form_put [lindex $param 0] [lindex $param 1]
    }
    rp_internal_redirect $template

    if { !$return_p } {
        ad_script_abort
    }       
}
Collapse
Posted by Dave Bauer on
I like the general idea, but this seems a little complicated.

What issues are you trying to resolve?

#1 ad_return_url returns the wrong thing?

Anything else?

Collapse
Posted by Guan Yang on
That's basically it.