Index: openacs-4/contrib/misc/wizard-procs-doc.html =================================================================== RCS file: /usr/local/cvsroot/openacs-4/contrib/misc/wizard-procs-doc.html,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ openacs-4/contrib/misc/wizard-procs-doc.html 10 Jul 2003 16:30:52 -0000 1.1 @@ -0,0 +1,199 @@ + + + + +Using the Wizard + + +

Overview Of How To Make A Wizard

+ +
    +
  1. Create a wizard file (ex. wizard.tcl) that contains the "template::wizard create" code. +
    ex.
    +       template::wizard create -action "wizard" -name my_wizard -params {
    +          my_param1 my_param2
    +       } -steps {
    +	  1 -label "Step 1" -url "step1"
    +	  2 -label "Step 2" -url "step2"	
    +          3 -label "Step 3" -url "step3"
    +       }
    +    
    + +
  2. + +
  3. Add the "template::wizard get_current_step" on wizard.tcl. + Make sure that you call any "template::wizard set_param" if needed before + calling get_current_step. get_current_step will redirect to the wizard -action + properly setting all -params value and its other needed http state vars

    + +

    Note: the wizard will rewrite the url always. Only self submitting forms + are preserved. Once the form is finished processing the wizard will take + over and rewrite the url.

    +
  4. + +
  5. Create the wizard template file (ex. wizard.adp). This file will include + the file based current step of the wizard +
    ex.
    +       <include src="@wizard:current_url@">
    +    
    +
  6. + +
  7. Create the individual steps, these are just normal tcl and/or adp files. + So make a step1.tcl, step1.adp, step2.tcl, step2.adp, step3.tcl and step3.adp. + Normally this files are self submitting forms +
  8. + +
  9. Add "template:wizard forward" on each step (eg. step1.tcl, step2.tcl, step3.tcl) + , usually the code where the step is processed and successful. +
  10. + +
  11. On each step add the wizard buttons on the .tcl files. Ex. step1.tcl will + include +
    +    template::wizard submit myform -buttons {back next}
    +    
    + On the last step you may want to use the following on step3.tcl +
    +    template::wizard submit myform -buttons {back next}
    +    
    + The following values are acceptable for the buttons: back, next and finish. + Back buttons are not rendered if the step is the first step, like wise next + buttons are not displayed if its the last step. Finish can appear on any step + and will finish the current wizard even if not all steps are done. +
  12. + +
+ + +

Tips And How To Use The Wizard

+ + + + + + + Index: openacs-4/contrib/misc/wizard-procs.tcl =================================================================== RCS file: /usr/local/cvsroot/openacs-4/contrib/misc/wizard-procs.tcl,v diff -u --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ openacs-4/contrib/misc/wizard-procs.tcl 10 Jul 2003 16:30:52 -0000 1.1 @@ -0,0 +1,604 @@ +# Wizard tool for the ArsDigita Templating System + +# Copyright (C) 1999-2000 ArsDigita Corporation +# Authors: Karl Goldstein (karlg@arsdigita.com) +# heavily modified by Jun Yamog on June 2003 + +# wizard-procs.tcl,v 1.1.2.1 2001/01/04 20:14:57 brech Exp + +# This is free software distributed under the terms of the GNU Public +# License. Full text of the license is available from the GNU Project: +# http://www.fsf.org/copyleft/gpl.html + +ad_proc -public template::wizard { command args } { + alias proc to call the real template::wizard::proc +} { + eval wizard::$command $args +} + +# create a wizard from a set of steps + +ad_proc -public template::wizard::create { args } { +
example:
+       template::wizard create -action "wizard" -name my_wizard -params {
+          my_param1 my_param2
+       } -steps {
+	  1 -label "Step 1" -url "step1"
+	  2 -label "Step 2" -url "step2"	
+          3 -label "Step 3" -url "step3"
+       }
+    
+ + +} { + + set level [template::adp_level] + + variable parse_level + set parse_level $level + + # keep wizard properties and a list of the steps + upvar #$level wizard:steps steps wizard:properties opts + upvar #$level wizard:rowcount rowcount + upvar #$level wizard:name wizard_name + upvar #$level wizard:wizards wizards + + template::util::get_opts $args + + set steps [list] + set rowcount 0 + set wizard_name $opts(name) + set wizards [get_wizards] + + # lets add the visited step param + lappend opts(params) wizard_visitedstep${wizard_name} + + # add steps specified at the time the wizard is created + if { [info exists opts(steps)] } { + + # strip carriage returns + regsub -all {\r} $opts(steps) {} step_data + + foreach step [split $step_data "\n"] { + + set step [string trim $step] + if { [string equal $step {}] } { continue } + + eval add $step + } + } +} + +ad_proc -public template::wizard::get_param { name } { +

Get a wizard's param value

+

+ "template::wizard get_param" has the advantage over ad_page_contract of getting the + param value during the response time. What does this mean? It will properly + get the current value of the param which was set by "template::wizard set_param", + while ad_page_contract will not pick that up since it will get what is the request + http var value. This is because "template::wizard get_param" gets the value + from the tcl var while ad_page_contract gets the value from the http var. + So while processing in tcl that value may change. +

+} { + + set level [template::adp_level] + + upvar #$level wizard:params params + + if { [info exists params($name)] } { + set value $params($name) + } else { + set value [ns_queryget $name] + } + return $value + +} + + +ad_proc -public template::wizard::set_param { name value } { +

Set a wizard's param for passthrough

+ +

Normally you place this in the steps of the wizard where the + form has been processed. A param + is normally used when you want to reuse a value across the steps.

+ +

Note: if you are to use "template::wizard set_param" on a wizard file ex. + (wizard.tcl). Make sure to do it before "template::wizard get_current_step". + So when "template::wizard get_current_step" redirects it will properly set + the correct values of the param to the new value.

+} { + + set level [template::adp_level] + + upvar #$level wizard:params params + set params($name) $value + +} + + +ad_proc -public template::wizard::set_finish_url { finish_url } { +

if the finish url is set, when a the finish button is pressed + it will redirect to this url

+} { + get_reference + set wizard_finish_url $finish_url + +} + + +ad_proc -private template::wizard::add { step_id args } { + Append a step to a wizard +} { + get_reference + + lappend steps $step_id + + # add the reference to the steps lookup array for the wizard + upvar #$level wizard:$step_id opts wizard:rowcount rowcount + incr rowcount + set opts(id) $step_id + set opts(rownum) $rowcount + set opts(link) [get_forward_url $opts(id)] + + # copy the reference for access as a multirow data source as well + upvar #$level wizard:$rowcount props + + template::util::get_opts $args + + array set props [array get opts] +} + + +ad_proc -public template::wizard::get_current_step {} { +

Set the step to display for this particular request This is + determined by the wizard_step parameter. If not set, the first step + is used.

+ +

Make sure that you call any "template::wizard set_param" if needed before + calling get_current_step. get_current_step will redirect to the wizard -action + properly setting all -params value and its other needed http state vars

+ +

The wizard will rewrite the url always. Only self submitting forms + are preserved. Once the form is finished processing the wizard will take + over and rewrite the url.

+} { + get_reference + + upvar #$level wizard:current_id current_id + set current_id [ns_queryget wizard_step${wizard_name} [lindex $steps 0]] + + upvar #$level wizard:visited_step visited_step + set visited_step [get_visited_step] + + # if there is no step state, we are likely in the first step. + # lets redirect with the proper state vars + if {[string equal [ns_queryget wizard_step${wizard_name}] ""]} { + template::forward [get_forward_url $current_id] + } + + # get a reference to the step + upvar #$level wizard:$current_id step + + upvar #$level wizard:current_url current_url + + # lets see if this step exists, if not we are finished with wizard and pass the steps + if [info exists step(url)] { + set current_url $step(url) + } else { + # if we have set_finish_url then we redirect to that url when we are finished + # otherwise increment the parent wizard step + if {[info exists wizard_finish_url]} { + template::forward $wizard_finish_url + } else { + + # lets set the current wizard name to the parent wizard + set parent_wizard [lindex $wizards 0] + set wizard_name $parent_wizard + + # lets now increment step of the parent wizard + set parent_step [expr [ns_queryget wizard_step${parent_wizard}] + 1] + template::forward [get_forward_url $parent_step] + } + + } + + # check for a "back" submission and forward immediately if so + # also check if we are backing up the current wizard or another wizard + + if { [ns_queryexists wizard_submit_back] && [string equal $wizard_name [ns_queryget wizard_name]]} { + + set last_index [expr [lsearch -exact $steps $current_id] - 1] + set last_id [lindex $steps $last_index] + template::forward [get_forward_url $last_id] + } +} + +ad_proc -private template::wizard::current_step {} { + convinience method to get the step for the http params or from the + wizard step definition +} { + + get_reference + + return [ns_queryget wizard_step${wizard_name} [lindex $steps 0]] +} + + +ad_proc -public template::wizard::get_visited_step {} { + get the last visited step +} { + + get_reference + + # lets create the visited steps for the current + # lets see if the current step is greater what we have visited + # otherwise we keep the current value + set last_visitedstep [get_param wizard_visitedstep${wizard_name}] + set current_step [current_step] + if { ($last_visitedstep < $current_step) || [string equal $last_visitedstep ""] } { + return $current_step + } else { + return $last_visitedstep + } + +} + +ad_proc -public template::wizard::set_visited_step {step_id} { + set the last visited step +} { + + get_reference + set_param wizard_visitedstep${wizard_name} $step_id +} + + +ad_proc -public template::wizard::get_current_name {} { + get the current wizard name +} { + + get_reference + + return $wizard_name +} + + +ad_proc -private template::wizard::get_wizards_levels {} { + internal helper proc to get the different levels of wizards + from current to parent +} { + variable parse_level + set level [expr $parse_level - 1] + + set levels {} + for {set i $level} {$i > 1} {set i [expr $i - 1]} { + upvar #$i wizard:name parent_wizard + if [info exists parent_wizard] { + lappend levels $i + } else { + break + } + } + + return $levels + +} + + +ad_proc -private template::wizard::get_wizards {} { + we will get all the wizards that we have passed through +} { + + set wizards {} + set levels [get_wizards_levels] + + foreach i $levels { + upvar #$i wizard:name parent_wizard + if [info exists parent_wizard] { + lappend wizards $parent_wizard + } + } + + return $wizards +} + + +ad_proc -public template::wizard::submit { form_id args } { +

Add the appropriate buttons to the submit wizard + Also create a list of all the buttons + The optional -buttons parameter is a list of name-value pairs, + in form {name label} {name label...} + The valid button names are back, next, repeat, finish

+ +

Also writes the params to the form as hidden elements to keep the + state of the wizard.

+ +

The following values are acceptable for the buttons: back, next and finish. + Back buttons are not rendered if the step is the first step, like wise next + buttons are not displayed if its the last step. Finish can appear on any step + and will finish the current wizard even if not all steps are done.

+ +} { + + variable default_button_labels + + get_reference + upvar 2 wizard_submit_buttons buttons + set buttons [list] + + set param_level [template::adp_level] + upvar #$param_level wizard:params params + + template::util::get_opts $args + + # Handle the -buttons parameter + if { ![info exists opts(buttons)] } { + # jkyamog - is this really correct? when no buttons is present we put all of the buttons? + upvar 0 default_button_labels button_labels + } else { + foreach pair $opts(buttons) { + # If provided with just a name, use default label + if { [llength $pair] == 1 } { + set button_labels($pair) $default_button_labels($pair) + } else { + set button_labels([lindex $pair 0]) [lindex $pair 1] + } + } + } + + # Add a hidden element for the current wizard name + template::element create $form_id wizard_name -widget hidden -value $wizard_name -datatype keyword + + set current_id [current_step] + + # Add a hidden element with the current ID + template::element create $form_id wizard_step${wizard_name} -widget hidden -value $current_id -datatype keyword + + + set step_index [expr [lsearch -exact $steps $current_id] + 1] + + # If not the first one and it is allowed than add a "Back" button + if { $step_index > 1 && [info exists button_labels(back)] } { + template::element create $form_id wizard_submit_back -widget submit \ + -label $button_labels(back) -optional -datatype text + + lappend buttons wizard_submit_back + } + + # If iteration is allowed than add a "Repeat" button + upvar #$level wizard:$current_id step + if { [info exists step(repeat)] && [info exists button_labels(repeat)]} { + template::element create $form_id wizard_submit_repeat -widget submit \ + -label $button_labels(repeat) -optional -datatype text + lappend buttons wizard_submit_repeat + } + + # If not the last one than add a "Next" button + if { $step_index < [llength $steps] && [info exists button_labels(next)] } { + template::element create $form_id wizard_submit_next -widget submit \ + -label $button_labels(next) -optional -datatype text + lappend buttons wizard_submit_next + } + + # Always finish + if { [info exists button_labels(finish) ] } { + template::element create $form_id wizard_submit_finish -widget submit \ + -label $button_labels(finish) -optional -datatype text + lappend buttons wizard_submit_finish + } + + + # Create hidden variables for wizard parameters + set levels [get_wizards_levels] + lappend levels $level + + foreach onelevel $levels { + upvar #$onelevel wizard:properties properties + foreach param $properties(params) { + if { ![template::element::exists $form_id $param] } { + if { [info exists params($param)] } { + template::element create $form_id $param -widget hidden -datatype text -optional -param -value $params($param) + } else { + template::element create $form_id $param -widget hidden -datatype text -optional -param + } + } + } + + } + + # Create hidden variables for the other wizard steps and visited steps + foreach one_wizard $wizards { + if { ![template::element::exists $form_id wizard_step${one_wizard}] } { + template::element create $form_id wizard_step${one_wizard} -widget hidden \ + -datatype keyword -value [ns_queryget wizard_step${one_wizard}] + } + if { ![template::element::exists $form_id wizard_visitedstep${one_wizard}] } { + template::element create $form_id wizard_visitedstep${one_wizard} -widget hidden \ + -datatype keyword -value [ns_queryget wizard_visitedstep${one_wizard}] + } + } + +} + + +ad_proc -private template::wizard::get_reference {} { + Get a reference to the wizard steps (internal helper) +} { + + uplevel { + + variable parse_level + set level $parse_level + + upvar #$level wizard:steps steps wizard:properties properties wizard:name wizard_name wizard:wizards wizards wizard:finish_url wizard_finish_url + if { ! [info exists steps] } { + error "Wizard does not exist" + } + } +} + + +ad_proc -public template::wizard::exists {} { + @return 1 if a wizard is currently defined +} { + variable parse_level + + if { ![info exists parse_level] } { + return 0 + } + + upvar #$parse_level wizard:steps steps + + return [info exists steps] +} + + +ad_proc -public template::wizard::forward {} { + call when a step has been validated and completed. + checks which submit button was pressed and proceeds accordingly. +} { + + get_reference + + upvar #$level wizard:current_id current_id + set current_index [expr [lsearch -exact $steps $current_id] + 1] + + if { [ns_queryexists wizard_submit_next] } { + + # figure out the next step and go there + + set next_id [lindex $steps $current_index] + template::forward [get_forward_url $next_id] + + } elseif { [ns_queryexists wizard_submit_back] } { + + set last_id [lindex $steps [expr $current_index - 2]] + template::forward [get_forward_url $last_id] + + } elseif { [ns_queryexists wizard_submit_repeat] } { + + template::forward "[get_forward_url $current_id]&wizard_submit_repeat=t" + + } elseif { [ns_queryexists wizard_submit_finish] } { + +# template::forward $properties(action) +# NOTE : we are changing the behaviour of wizard, when its finish it will not reset and go back +# to step 1, it will blindly go forward and we will catch this on get_current_step + set next_id [expr $current_index + 1] + template::forward [get_forward_url $next_id] + } +} + +ad_proc -public template::wizard::get_forward_url { step_id } { + Build the redirect URL for the next step +} { + + get_reference + + set param_level [template::adp_level] + upvar #$param_level wizard:params params + + set url [ns_conn url]?wizard_step${wizard_name}=$step_id&wizard_name=$wizard_name + + # create the wizards and keep track of their steps too + foreach one_wizard $wizards { + append url "&wizard_step${one_wizard}=[ns_queryget wizard_step${one_wizard}]" + append url "&wizard_visitedstep${one_wizard}=[ns_queryget wizard_visitedstep${one_wizard}]" + } + + # check for passthrough parameters + + set levels [get_wizards_levels] + lappend levels $level + set allparams {} + + foreach onelevel $levels { + upvar #$onelevel wizard:properties properties + if { [info exists properties(params)] } { + foreach param $properties(params) { + + if { [info exists params($param)] } { + set value $params($param) + } else { + set value [ns_queryget $param] + } + # we will only append unique params + if {[lsearch $allparams $param] == -1} { + append url "&$param=[ns_urlencode $value]" + lappend allparams $param + } + } + } + } + + return $url +} + +ad_proc -public template::wizard::get_action_url {} { + Retreive the URL to the action +} + + get_reference + + return $properties(action) +} + + +ad_proc -public template::wizard::load_last_visited_step { + -key:required +} { + loads the last visited step from the db + + @creation-date june 2003 + @author Jun Yamog + + @param key unique identifier for a particular wizard normally the main object_id the wizard + is manipulating + + use this step before get_current_step +} { + + get_reference + + # check the old visited step on the the state manager + set visited_step [ad_get_client_property -default "" $key ${wizard_name}visited] + if {![string equal $visited_step ""]} { + template::wizard::set_visited_step $visited_step + } + +} + + +ad_proc -public template::wizard::save_last_visited_step { + -key:required +} { + saves the last visisted step to the db + + @creation-date june 2003 + @author Jun Yamog + + @param key unique identifier for a particular wizard normally the main object_id the wizard + is manipulating + + use this step after get_current_step +} { + + get_reference + + # save the state of the visited step for this wizard + if { ![string equal $key ""] } { + ad_set_client_property $key ${wizard_name}visited [template::wizard::get_visited_step] + } + +} +