Index: openacs-4/packages/acs-templating/tcl/form-procs.tcl =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/acs-templating/tcl/form-procs.tcl,v diff -u -N -r1.56 -r1.57 --- openacs-4/packages/acs-templating/tcl/form-procs.tcl 14 Sep 2018 17:37:55 -0000 1.56 +++ openacs-4/packages/acs-templating/tcl/form-procs.tcl 14 Sep 2018 17:43:51 -0000 1.57 @@ -18,8 +18,8 @@ namespace eval template {} namespace eval template::form {} -ad_proc -public form {command args} { - form is really template::form although when in +ad_proc -public form {command args} { + form is really template::form although when in the "template" namespace you may omit the template:: @@ -32,7 +32,7 @@ template::form command invokes form functions. Please see the individual functions for their arguments. The template::element API is used to manipulate form elements. - + @see template::form::create @see template::form::get_button @see template::form::get_action @@ -60,148 +60,148 @@ Initialize the data structures for a form. @param id A keyword identifier for the form, such as "add_user" or - "edit_item". The ID must be unique in the context of a + "edit_item". The ID must be unique in the context of a single page. @option method The standard METHOD attribute to specify in the HTML FORM tag at the beginning of the rendered form. Defaults to POST. @option html A list of additional name-value attribute pairs to - include in the HTML FORM tag at the beginning of the + include in the HTML FORM tag at the beginning of the rendered form. Common use for this option is to set multipart form encoding by specifying "-html { enctype multipart/form-data }". Please note that to comply with newer security features, such as CSP, one should not specify JavaScript event handlers here, as they will be rendered inline. - - @option mode If set to 'display', the form is shown in display-only mode, where + + @option mode If set to 'display', the form is shown in display-only mode, where the user cannot edit the fields. Each widget knows how to display its contents appropriately, e.g. a select widget will show the label, not the value. If set to 'edit', the form is displayed as normal, for editing. Defaults to 'edit'. Switching to edit mode when a button is clicked in display mode is handled automatically. - - @option cancel_url A url to redirect to when the user hits the Cancel button. + + @option cancel_url A url to redirect to when the user hits the Cancel button. If you do not supply a cancel_url, there will be no Cancel button. - + @option cancel_label The label of the Cancel button, if cancel_url is supplied. Default is "Cancel". - - @option actions A list of actions available on the form, which in practice means - a list of buttons to show when the form is in display mode. + + @option actions A list of actions available on the form, which in practice means + a list of buttons to show when the form is in display mode. The value should be a list of lists, with the first element being the form label and the second element being the name of the form element. Defaults to - { { "Edit" edit } }. The name of the button clicked can be retrieved using + { { "Edit" edit } }. The name of the button clicked can be retrieved using template::form::get_button. The name of the button clicked while in display mode - is called the 'action', and can be retrieved using template::form::get_action. + is called the 'action', and can be retrieved using template::form::get_action. The action is automatically carried forward to the form submission, so that the value that you get from calling template::form::get_action on the final form submission - is the name of the button which was called when the form changed from display + is the name of the button which was called when the form changed from display mode to edit mode. - @option display_buttons List of buttons to show when the form is in display mode. - Equivalent to actions. If both actions and display_buttons are present, + @option display_buttons List of buttons to show when the form is in display mode. + Equivalent to actions. If both actions and display_buttons are present, 'actions' is used. 'display_buttons' is deprecated. - @option edit_buttons List of buttons to show when the form is in edit mode. + @option edit_buttons List of buttons to show when the form is in edit mode. The value should be a list of lists, with the first element being the button label and the second element being the name. Defaults to - { { "Ok" ok } }. The name of the button clicked can be retrieved using - template::form::get_button. - + { { "Ok" ok } }. The name of the button clicked can be retrieved using + template::form::get_button. + @option has_submit Set to 1 to suppress the OK or submit button automatically - added by the form builder. Use this if your form already includes its own + added by the form builder. Use this if your form already includes its own submit button. @option has_edit Set to 1 to suppress the Edit button automatically added by the form builder. Use this if you include your own. @option elements A block of element specifications. - @option show_required_p Should the form template show which elements are required. + @option show_required_p Should the form template show which elements are required. Use 1 or t for true, 0 or f for false. Defaults to true. @see template::form::get_button @see template::form::get_action } { - set level [template::adp_level] + set level [template::adp_level] - # bump the form_count for widgets that use JavaScript to navigate through - # the form (liberated from my Greenpeace work ages ago) + # bump the form_count for widgets that use JavaScript to navigate through + # the form (liberated from my Greenpeace work ages ago) - incr ::ad_conn(form_count) + incr ::ad_conn(form_count) - # keep form properties and a list of the element items - upvar #$level $id:elements elements $id:properties opts + # keep form properties and a list of the element items + upvar #$level $id:elements elements $id:properties opts - # ensure minimal defaults for form properties - variable defaults - array set opts $defaults + # ensure minimal defaults for form properties + variable defaults + array set opts $defaults - template::util::get_opts $args + template::util::get_opts $args - set elements [list] + set elements [list] - # check whether this form is being submitted - upvar #$level $id:submission submission + # check whether this form is being submitted + upvar #$level $id:submission submission - if {$id eq "request"} { - # request is the magic ID for the form holding query parameters - set submission 1 - } else { - # If there's a form:id argument, and it's the ID of this form, - # we're being submitted - set submission [string equal $id [ns_queryget form:id]] - } + if {$id eq "request"} { + # request is the magic ID for the form holding query parameters + set submission 1 + } else { + # If there's a form:id argument, and it's the ID of this form, + # we're being submitted + set submission [string equal $id [ns_queryget form:id]] + } - set formbutton [get_button $id] + set formbutton [get_button $id] - # If the user hit a button named "cancel", redirect and about - if { $submission && $formbutton eq "cancel" && [info exists opts(cancel_url)] && $opts(cancel_url) ne ""} { - ad_returnredirect $opts(cancel_url) - ad_script_abort - } + # If the user hit a button named "cancel", redirect and about + if { $submission && $formbutton eq "cancel" && [info exists opts(cancel_url)] && $opts(cancel_url) ne ""} { + ad_returnredirect $opts(cancel_url) + ad_script_abort + } - set formaction [get_action $id] - # If we were in display mode, and a button was clicked, we should be in edit mode now - if { $submission && [ns_queryget "form:mode"] eq "display" } { - set opts(mode) "edit" - set submission 0 - } + set formaction [get_action $id] + # If we were in display mode, and a button was clicked, we should be in edit mode now + if { $submission && [ns_queryget "form:mode"] eq "display" } { + set opts(mode) "edit" + set submission 0 + } - # add elements specified at the time the form is created - if { [info exists opts(elements)] } { + # add elements specified at the time the form is created + if { [info exists opts(elements)] } { # strip carriage returns - regsub -all {\r} $opts(elements) {} element_data + regsub -all {\r} $opts(elements) {} element_data - foreach element [split $element_data "\n"] { - set element [string trim $element] - if {$element eq {}} { continue } - template::element create $id {*}$element + foreach element [split $element_data "\n"] { + set element [string trim $element] + if {$element eq {}} { continue } + template::element create $id {*}$element + } } - } } - + ad_proc -public template::form::set_properties { id args } { Set properties for a form @param id The ID of an ATS form object. - @param args Properties to set + @param args Properties to set } { - # form properties - upvar #[template::adp_level] $id:properties opts + # form properties + upvar #[template::adp_level] $id:properties opts - template::util::get_opts $args + template::util::get_opts $args } ad_proc -public template::form::get_properties { id } { Get properties of a form @param id The ID of a form } { - # form properties + # form properties upvar #[template::adp_level] $id:properties formprop if { [info exists formprop] } { @@ -219,37 +219,37 @@ @param id The ID of an ATS form object. @return the name of the button clicked } { - # keep form properties and a list of the element items - upvar #[template::adp_level] $id:button formbutton - - # If we've already found the button, just return that - if { [info exists formbutton] } { - return $formbutton - } + # keep form properties and a list of the element items + upvar #[template::adp_level] $id:button formbutton - # Otherwise, find out now + # If we've already found the button, just return that + if { [info exists formbutton] } { + return $formbutton + } - set formbutton {} + # Otherwise, find out now - # If the form isn't being submitted at all, no button was clicked - if { $id ne [ns_queryget form:id] } { - return {} - } + set formbutton {} - # Search the submit form for the button - set form [ns_getform] + # If the form isn't being submitted at all, no button was clicked + if { $id ne [ns_queryget form:id] } { + return {} + } - if { $form ne "" } { - set size [ns_set size $form] - for { set i 0 } { $i < $size } { incr i } { - if { [string match "formbutton:*" [ns_set key $form $i]] } { - set formbutton [string range [ns_set key $form $i] [string length "formbutton:"] end] - break - } - } - } + # Search the submit form for the button + set form [ns_getform] - return $formbutton + if { $form ne "" } { + set size [ns_set size $form] + for { set i 0 } { $i < $size } { incr i } { + if { [string match "formbutton:*" [ns_set key $form $i]] } { + set formbutton [string range [ns_set key $form $i] [string length "formbutton:"] end] + break + } + } + } + + return $formbutton } ad_proc -public template::form::get_action { id } { @@ -259,35 +259,35 @@ @param id The ID of an ATS form object. @return the name of the action in progress } { - # keep form properties and a list of the element items - upvar #[template::adp_level] $id:formaction formaction - - # If we've already found the action, just return that - if { [info exists formaction] } { - return $formaction - } + # keep form properties and a list of the element items + upvar #[template::adp_level] $id:formaction formaction - # Otherwise, find out now + # If we've already found the action, just return that + if { [info exists formaction] } { + return $formaction + } - set formaction {} + # Otherwise, find out now - # If the form isn't being submitted at all, there's no action - if { $id ne [ns_queryget "form:id"] } { - return {} - } + set formaction {} - set formbutton [get_button $id] + # If the form isn't being submitted at all, there's no action + if { $id ne [ns_queryget "form:id"] } { + return {} + } - # If we were in display mode, and a button was clicked, we should be in edit mode now - if { [ns_queryget "form:mode"] eq "display" && $formbutton ne "" } { - set formaction $formbutton - return $formaction - } + set formbutton [get_button $id] - # Otherwise, there should be a form:formaction variable in the form - set formaction [ns_queryget "form:formaction"] + # If we were in display mode, and a button was clicked, we should be in edit mode now + if { [ns_queryget "form:mode"] eq "display" && $formbutton ne "" } { + set formaction $formbutton + return $formaction + } - return $formaction + # Otherwise, there should be a form:formaction variable in the form + set formaction [ns_queryget "form:formaction"] + + return $formaction } ad_proc -public template::form::exists { id } { @@ -297,9 +297,9 @@ @return 1 if a form with the specified ID exists. 0 if it does not. } { - upvar #[template::adp_level] $id:elements elements + upvar #[template::adp_level] $id:elements elements - return [info exists elements] + return [info exists elements] } ad_proc -private template::form::template { id { style "" } } { @@ -313,53 +313,53 @@ @return A string containing a template for the body of the form. } { - get_reference + get_reference - # - # Elements - # RAL: moved this below so we could take advantage of the template::element - # API in the button loop above. The buttons multirow in standard.adp is - # no longer necessary. - # - set elements:rowcount 0 + # + # Elements + # RAL: moved this below so we could take advantage of the template::element + # API in the button loop above. The buttons multirow in standard.adp is + # no longer necessary. + # + set elements:rowcount 0 - foreach element_ref $elements { + foreach element_ref $elements { - incr elements:rowcount + incr elements:rowcount - # get a reference by index for the multirow data source - upvar #$level $element_ref elements:${elements:rowcount} - set "elements:${elements:rowcount}(rownum)" ${elements:rowcount} - } + # get a reference by index for the multirow data source + upvar #$level $element_ref elements:${elements:rowcount} + set "elements:${elements:rowcount}(rownum)" ${elements:rowcount} + } - if {$style eq {}} { - set style [parameter::get \ - -package_id [ad_conn subsite_id] \ - -parameter DefaultFormStyle \ - -default [parameter::get \ - -package_id [apm_package_id_from_key "acs-templating"] \ - -parameter DefaultFormStyle \ - -default "standard-lars"]] - } + if {$style eq {}} { + set style [parameter::get \ + -package_id [ad_conn subsite_id] \ + -parameter DefaultFormStyle \ + -default [parameter::get \ + -package_id [apm_package_id_from_key "acs-templating"] \ + -parameter DefaultFormStyle \ + -default "standard-lars"]] + } - set file_stub [template::resource_path -type forms -style $style] + set file_stub [template::resource_path -type forms -style $style] - if { ![file exists "$file_stub.adp"] } { - # We always have a template named 'standard' - set file_stub [template::resource_path -type forms -style standard] - } + if { ![file exists "$file_stub.adp"] } { + # We always have a template named 'standard' + set file_stub [template::resource_path -type forms -style standard] + } - # ensure that the style template has been compiled and is up-to-date - template::adp_init adp $file_stub + # ensure that the style template has been compiled and is up-to-date + template::adp_init adp $file_stub - # get result of template output procedure into __adp_output - # the only data source on which this template depends is the "elements" - # multirow data source. The output of this procedure will be - # placed in __adp_output in this stack frame. + # get result of template output procedure into __adp_output + # the only data source on which this template depends is the "elements" + # multirow data source. The output of this procedure will be + # placed in __adp_output in this stack frame. - template::code::adp::$file_stub + template::code::adp::$file_stub - return $__adp_output + return $__adp_output } ad_proc -private template::form::generate { id { style "" } } { @@ -372,32 +372,32 @@ @return A string containing the HTML for the body of the form. } { - set __adp_output [template $id $style] - - set level [template::adp_level] + set __adp_output [template $id $style] - # compile the template - set code [template::adp_compile -string $__adp_output] + set level [template::adp_level] - # these variables are expected by the formwidget and formgroup tags - set form:id $id - upvar #$level $id:elements $id:elements formerror formerror $id:properties form_properties + # compile the template + set code [template::adp_compile -string $__adp_output] - foreach element_ref [set $id:elements] { - # get a reference by element ID for formwidget and formgroup tags - upvar #$level $element_ref $element_ref - } + # these variables are expected by the formwidget and formgroup tags + set form:id $id + upvar #$level $id:elements $id:elements formerror formerror $id:properties form_properties - # evaluate the code and return the rendered HTML for the form - return [template::adp_eval code] + foreach element_ref [set $id:elements] { + # get a reference by element ID for formwidget and formgroup tags + upvar #$level $element_ref $element_ref + } + + # evaluate the code and return the rendered HTML for the form + return [template::adp_eval code] } -ad_proc -public template::form::section { - {-fieldset ""} - {-legendtext ""} - {-legend ""} - id - section +ad_proc -public template::form::section { + {-fieldset ""} + {-legendtext ""} + {-legend ""} + id + section } { Set the current section (fieldset) of the form. A form may be divided into any number of fieldsets to group related @@ -426,27 +426,27 @@ set properties(sec_fieldset) "" array set fs_attributes $fieldset foreach name [array names fs_attributes] { - if {$fs_attributes($name) eq ""} { - append properties(sec_fieldset) " $name" - } else { - append properties(sec_fieldset) " $name=\"$fs_attributes($name)\"" - } + if {$fs_attributes($name) eq ""} { + append properties(sec_fieldset) " $name" + } else { + append properties(sec_fieldset) " $name=\"$fs_attributes($name)\"" + } } # legend attributes set properties(sec_legend) "" if { $legendtext ne "" } { - array set lg_attributes $legend + array set lg_attributes $legend if {![info exists lg_attributes(class)]} { append properties(sec_legend) " class=\"form-legend\"" } - foreach name [array names lg_attributes] { - if {$lg_attributes($name) eq ""} { - append properties(sec_legend) " $name" - } else { - append properties(sec_legend) " $name=\"$lg_attributes($name)\"" - } - } + foreach name [array names lg_attributes] { + if {$lg_attributes($name) eq ""} { + append properties(sec_legend) " $name" + } else { + append properties(sec_legend) " $name=\"$lg_attributes($name)\"" + } + } } } @@ -460,193 +460,193 @@ @return A string containing the rendered tags. } { - get_reference + get_reference - #---------------------------------------------------------------------- - # Check for errors on form - #---------------------------------------------------------------------- + #---------------------------------------------------------------------- + # Check for errors on form + #---------------------------------------------------------------------- - # make a reference to the formerror array with any validation messages - upvar #$level $id:error $id:error + # make a reference to the formerror array with any validation messages + upvar #$level $id:error $id:error - # Clear the formerror array if it has - # been set by another form on the same page - upvar #$level formerror formerror - if { [info exists formerror] } { unset formerror } + # Clear the formerror array if it has + # been set by another form on the same page + upvar #$level formerror formerror + if { [info exists formerror] } { unset formerror } - if { [info exists $id:error] } { + if { [info exists $id:error] } { - uplevel #$level "upvar 0 $id:error formerror" - - # There were errors on the form, force edit mode - set properties(mode) edit - } + uplevel #$level "upvar 0 $id:error formerror" - #---------------------------------------------------------------------- - # Buttons - #---------------------------------------------------------------------- - - if { [info exists form_properties(cancel_url)] && $form_properties(cancel_url) ne ""} { - if {![info exists form_properties(cancel_label)] || $form_properties(cancel_label) eq ""} { - set form_properties(cancel_label) [_ acs-kernel.common_Cancel] + # There were errors on the form, force edit mode + set properties(mode) edit } - lappend form_properties(edit_buttons) [list $form_properties(cancel_label) cancel] - } - if { [info exists form_properties(has_submit)] - && [template::util::is_true $form_properties(has_submit)] - } { - set form_properties(edit_buttons) {} - } - - if { [info exists form_properties(has_edit)] - && [template::util::is_true $form_properties(has_edit)] - } { - set form_properties(display_buttons) {} - } + #---------------------------------------------------------------------- + # Buttons + #---------------------------------------------------------------------- - if { [info exists form_properties(actions)] - && $form_properties(actions) ne "" - } { - set form_properties(display_buttons) $form_properties(actions) - } - - # We keep this, so if anyone has an old form template that still loops over this multirow, it won't break hard - # We should remove this later, maybe 6.0 - set buttons:rowcount 0 + if { [info exists form_properties(cancel_url)] && $form_properties(cancel_url) ne ""} { + if {![info exists form_properties(cancel_label)] || $form_properties(cancel_label) eq ""} { + set form_properties(cancel_label) [_ acs-kernel.common_Cancel] + } + lappend form_properties(edit_buttons) [list $form_properties(cancel_label) cancel] + } - foreach button $form_properties(${form_properties(mode)}_buttons) { - lassign $button label name + if { [info exists form_properties(has_submit)] + && [template::util::is_true $form_properties(has_submit)] + } { + set form_properties(edit_buttons) {} + } - if {$name eq "ok"} { - # We hard-code the OK button to be wider than it otherwise would - set label " $label " + if { [info exists form_properties(has_edit)] + && [template::util::is_true $form_properties(has_edit)] + } { + set form_properties(display_buttons) {} } - set name "formbutton:$name" - - template::element create $id $name -widget submit -label $label -datatype text - } - # Propagate form mode to all form elements - foreach element_ref $elements { - - # get a reference by element ID - upvar #$level $element_ref element - - # Check if the element has an empty string mode, and in - # that case, set to form mode - if {$element(mode) eq ""} { - set element(mode) $properties(mode) + if { [info exists form_properties(actions)] + && $form_properties(actions) ne "" + } { + set form_properties(display_buttons) $form_properties(actions) } - } - # Check for errors in hidden elements - foreach element_ref $elements { - - # get a reference by element ID - upvar #$level $element_ref element - - if { $element(widget) eq "hidden" && - [info exists $id:error($element(id))] && [set $id:error($element(id))] ne "" - } { - # Submitting invalid data to hidden elements is a common attack vector. - # This does not give them much information in the response. - ad_return_complaint 1 "Your request is invalid." - ad_log Warning "Validation error in hidden form element.\ - This may be part of a vulnerability scan or attack reconnaissance: \ - '[set $id:error($element(id))]' on element '$element(id)'." - ad_script_abort + # We keep this, so if anyone has an old form template that still loops over this multirow, it won't break hard + # We should remove this later, maybe 6.0 + set buttons:rowcount 0 + + foreach button $form_properties(${form_properties(mode)}_buttons) { + lassign $button label name + + if {$name eq "ok"} { + # We hard-code the OK button to be wider than it otherwise would + set label " $label " + } + set name "formbutton:$name" + + template::element create $id $name -widget submit -label $label -datatype text } - } - # get any additional attributes developer specified to include in form tag - if { [info exists properties(html)] } { - array set attributes $properties(html) - } + # Propagate form mode to all form elements + foreach element_ref $elements { - # add on or replace with attributes specified by designer in formtemplate tag - array set attributes $tag_attributes + # get a reference by element ID + upvar #$level $element_ref element - # set the form to point back to itself if action is not specified - if { ! [info exists properties(action)] } { - set properties(action) [ns_conn url] - } - - set output "