Index: openacs-4/packages/acs-tcl/tcl/form-processing-procs.tcl =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/acs-tcl/tcl/form-processing-procs.tcl,v diff -u -r1.85 -r1.86 --- openacs-4/packages/acs-tcl/tcl/form-processing-procs.tcl 8 Feb 2019 01:19:20 -0000 1.85 +++ openacs-4/packages/acs-tcl/tcl/form-processing-procs.tcl 9 Feb 2019 13:58:18 -0000 1.86 @@ -6,7 +6,37 @@ } ad_proc -public ad_form { - args + -name + -extend:boolean + -action + -actions + -mode + -has_edit + -has_submit + -method + -form + -cancel_url + -cancel_label + -html + -export + -select_query + -select_query_name + -show_required_p + -on_request + -edit_request + -new_request + -confirm_template + -on_refresh + -on_submit + -new_data + -edit_data + -after_submit + -validate + -on_validation_error + -edit_buttons + -display_buttons + -fieldset + -csrf_protection_p } { This procedure implements a high-level, declarative syntax for the generation and @@ -184,219 +214,9 @@

Parameters which take a name (for instance "-name" or "-select_query_name") expect a simple name not surrounded by curly braces (in other words not a single-element list). All other parameters expect a single list to be passed in. -

- Here's a complete list of switches that are supported by ad_form: - -

- +

Two hidden values of interest are available to the caller of ad_form when processing a submit:

-

-extend

-

Extend an existing form. This allows one to build forms incrementally. Forms are built at the - template level. As a consequence one can write utility procs that use -extend to build form - snippets common to several data entry forms. Note that the full form block must be built up - (extended) and completed before any action blocks such as select_query, new_request, edit_request etc. - are defined. -

This must be the first switch passed into ad_form -

- -

-name

-

Declares the name of the form. Defaults to the name of the script being served.
- -

-action

-

The name of the script to be called when the form is submitted. Defaults to the name of the script - being served. -
- -

-actions

-

A list of lists of actions (e.g. {{" Delete " delete} {" Resolve " resolve}} ), which gets - translated to buttons at the bottom of the form. You can find out what button was pressed - with [template::form get_action form_id], usually in the -edit_request block to perform whatever - actions you deem appropriate. When the form is loaded the action will be empty. -
- -

-mode { display | edit }

-

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 -
- -

-has_edit { 0 | 1 }

-

Set to 1 to suppress the Edit button automatically added by the form builder. Use this if you - include your own. -
- -

-has_submit { 0 | 1 }

-

Set to 1 to suppress the OK button automatically added by the form builder. Use this if you - include your own. -
- -

-method

-

The standard METHOD attribute to specify in the HTML FORM tag at the beginning of the rendered - form. Defaults to POST. -
- -

-form

-

Declare form elements (described in detail below) -
- -

-cancel_url

-

The URL the cancel button should take you to. If this is specified, a cancel button will show up - during the edit phase. -
- -

-cancel_label

-

The label for the cancel button. -
- -

-html

-

The given html will be added to the "form" tag when page is rendered. This is commonly used to - define multipart file handling forms. -
- -

-export

-

This options allows one to export data in current page environment to the page receiving the form. - Variables are treated as "hidden" form elements which will be automatically generated. Each value is - either a name, in which case the Tcl variable at the caller's level is passed to the form if it exists, - or a name-value pair. - The behavior of this option replicates that for vars argument in proc - export_vars, which in turn follows specification - for input page variables in ad_page_contract. - In particular, flags :multiple, :sign and :array are allowed and - their meaning is the same as in export_vars. -
- -

-select_query

-

- Defines a query that returns a single row containing values for each - element of the form meant to be modifiable by the user. Can only be - used if an element of type key has been declared. Values returned from - the query are available in the form, but not the ADP template (for - that, use -edit_request instead). -
- -

-select_query_name

-

- Identical to -select_query, except instead of specifying the query - inline, specifies a query name. The query with that name from the - appropriate XQL file will be used. Use -select_query_name rather than - -select_query whenever possible, as query files are the mechanism used - to make the support of multiple RDMBS systems possible. -
- -

-show_required_p { 0 | 1 }

-

Should the form template show which elements are required. Use 1 or t for true, 0 or f for false. - Defaults to true. -
- -

-on_request

-

A code block which sets the values for each element of the form meant to be modifiable by - the user when the built-in key management feature is being used or to define options for - select lists etc. Set the values as local variables in the code block, and they'll get - fetched and used as element values for you. This block is executed every time the - form is loaded except when the form is being submitted (in which case the -on_submit - block is executed.) -
- -

-edit_request

-

- A code block which sets the values for each element of the form meant - to be modifiable by the user. Use this when a single query to grab - database values is insufficient. Any variables set in an -edit_request - block are available to the ADP template as well as the form, while - -select_query sets variables in the form only. Can only be used if an - element of type key is defined. This block is only executed if the - page is called with a valid key, i.e. a self-submit form to add or edit - an item called to edit the data. Set the values as local variables in - the code block, and they'll get fetched and used as element values for - you. -
- -

-new_request

-

A code block which sets the values for each element of the form meant to be modifiable by the user. Use - this when a single query to grab database values is insufficient. Can only be used if an element of - type key is defined. This block complements the -edit_request block. You just need to set the values as local - variables in the code block, and they'll get fetched and used as element values for you. -
- -

-confirm_template

-

The name of a confirmation template to be called before any on_submit, new_data or edit_data block. When - the user confirms input control will be passed to the appropriate submission block. The confirmation - template can be used to provide a bboard-like preview/confirm page. Your confirmation template should - render the form contents in a user-friendly way then include "/packages/acs-templating/resources/forms/confirm-button". - The "confirm-button" template not only provides a confirm button but includes the magic incantation that - tells ad_form that the form has been confirmed by the user and that it is safe to call the proper submission - block. -
- -

-on_refresh

-

Executed when the form comes back from being refreshed using JavaScript with the __refreshing_p flag set. -
- -

-on_submit

-

When the form is submitted, this code block will be executed before any new_data or edit_data code block. - Use this if your form doesn't interact with the database or if the database type involved includes a Tcl - API that works for both new and existing data. The values of the form's elements will be available as local variables. - Calling 'break' inside this block causes the submission process to be aborted, and neither new_data, edit_data, nor - after_submit will get executed. Useful in combination with template::form set_error to display an error on a form - element. -
- -

-new_data

-

This code block will be executed when a form for a new database row is submitted. This block should - insert the data into the database or create a new database object or content repository item containing - the data. - Calling 'break' inside this block causes the submission process to be aborted, and - after_submit will not get executed. Useful in combination with template::form set_error to display an error on a form - element. -
- -

-edit_data

-

This code block will be executed when a form for an existing database row is submitted. This block should - update the database or create a new content revision for the existing item if the data's stored in the - content repository. - Calling 'break' inside this block causes the submission process to be aborted, and - after_submit will not get executed. Useful in combination with template::form set_error to display an error on a form - element. -
- -

-after_submit

-

This code block will be executed after the three blocks on_submit, new_data or edit_data have been - executed. It is useful for putting in stuff like ad_returnredirect that is the same for new and edit. -
- -

-validate

-

A code block that validates the elements in the form. The elements are set as local values. - The block has the following form: -
-{element_name
-    {tcl code that returns 1 or 0}
-    "Message to be shown by that element in case of error"
-}
-{...}
-       
-
- - -

-on_validation_error

-

A code block that is executed if validation fails. This can be done to set - a custom page title or some similar action. -
- -

-csrf_protection_p { 0 | 1 }

-

Should the form add automatically a hidden form field for csrf protection? - Use 1 or t for true, 0 or f for false. - Defaults to false. -
- -
- - Two hidden values of interest are available to the caller of ad_form when processing a submit: - -

-

__new_p

If a database key has been declared, __new_p will be set true if the form @@ -538,43 +358,199 @@

- @see ad_form_new_p - @see ad_set_element_value - @see ad_set_form_values + @param extend Extend an existing form. This allows one to build + forms incrementally. Forms are built at the template level. + As a consequence one can write utility procs that use -extend + to build form snippets common to several data entry + forms. Note that the full form block must be built up + (extended) and completed before any action blocks such as + select_query, new_request, edit_request etc. are defined -} { - set level [template::adp_level] + @param name Declares the name of the form. Defaults to the name + of the script being served. - # Are we extending the form? + @param action The name of the script to be called when the form is + submitted. Defaults to the name of the script being served. - if {[lindex $args 0] eq "-extend"} { - set extend_p 1 - set args [lrange $args 1 end] - } else { - set extend_p 0 - } + @param actions A list of lists of actions (e.g. {{" Delete " + delete} {" Resolve " resolve}} ), which gets translated to buttons + at the bottom of the form. You can find out what button was + pressed with [template::form get_action form_id], usually in the + -edit_request block to perform whatever actions you deem + appropriate. When the form is loaded the action will be empty. - # - # Default for csrf_protection. - # - set csrf_protection_p 0 + @param mode { display | edit } 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. Switching to edit mode when a button is clicked + in display mode is handled automatically - # Parse the rest of the arguments + @param has_edit { 0 | 1 } Set to 1 to suppress the Edit button + automatically added by the form builder. Use this if you + include your own. - if { [llength $args] == 0 } { - return -code error "No arguments to ad_form" - } + @param has_submit { 0 | 1 } Set to 1 to suppress the OK button + automatically added by the form builder. Use this if you + include your own. - set valid_args { - form method action mode html name select_query select_query_name new_data - on_refresh edit_data validate on_submit after_submit confirm_template - on_request new_request edit_request export cancel_url cancel_label - has_submit has_edit actions edit_buttons display_buttons show_required_p - on_validation_error fieldset csrf_protection_p - } + @param method The standard METHOD attribute to specify in the HTML + FORM tag at the beginning of the rendered form. - ad_arg_parser $valid_args $args + @param form Declare form elements (described in detail above) + @param cancel_url The URL the cancel button should take you to. If + this is specified, a cancel button will show up during the + edit phase. + + @param cancel_label The label for the cancel button. + + @param html The given html will be added to the "form" tag when + page is rendered. This is commonly used to define multipart + file handling forms. + + @param export This options allows one to export data in current + page environment to the page receiving the form. Variables + are treated as "hidden" form elements which will be + automatically generated. Each value is either a name, in which + case the Tcl variable at the caller's level is passed to the + form if it exists, or a name-value pair. The behavior of this + option replicates that for vars argument in proc + export_vars, + which in turn follows specification for input page variables + in ad_page_contract. + In particular, flags :multiple, + :sign and :array are allowed and + their meaning is the same as in export_vars. + + @param select_query Defines a query that returns a single row + containing values for each element of the form meant to be + modifiable by the user. Can only be used if an element of type + key has been declared. Values returned from the query are + available in the form, but not the ADP template (for that, use + -edit_request instead). + + @param select_query_name Identical to -select_query, except + instead of specifying the query inline, specifies a query + name. The query with that name from the appropriate XQL file will + be used. Use -select_query_name rather than -select_query whenever + possible, as query files are the mechanism used to make the + support of multiple RDMBS systems possible. + + @param show_required_p { 0 | 1 } Should the form template show + which elements are required. Use 1 or t for true, 0 or f for + false. + + @param on_request A code block which sets the values for each + element of the form meant to be modifiable by the user when + the built-in key management feature is being used or to define + options for select lists etc. Set the values as local + variables in the code block, and they'll get fetched and used + as element values for you. This block is executed every + time the form is loaded except when the form is + being submitted (in which case the -on_submit block is + executed.) + + @param edit_request A code block which sets the values for each + element of the form meant to be modifiable by the user. Use this + when a single query to grab database values is insufficient. Any + variables set in an -edit_request block are available to the ADP + template as well as the form, while -select_query sets variables + in the form only. Can only be used if an element of type key is + defined. This block is only executed if the page is called with a + valid key, i.e. a self-submit form to add or edit an item called + to edit the data. Set the values as local variables in the code + block, and they'll get fetched and used as element values for you. + + @param new_request A code block which sets the values for each + element of the form meant to be modifiable by the user. Use + this when a single query to grab database values is + insufficient. Can only be used if an element of type key is + defined. This block complements the -edit_request block. You + just need to set the values as local variables in the code + block, and they'll get fetched and used as element values for + you. + + @param confirm_template The name of a confirmation template to be + called before any on_submit, new_data or edit_data block. + When the user confirms input control will be passed to the + appropriate submission block. The confirmation template can + be used to provide a bboard-like preview/confirm page. Your + confirmation template should render the form contents in a + user-friendly way then include + "/packages/acs-templating/resources/forms/confirm-button". + The "confirm-button" template not only provides a confirm + button but includes the magic incantation that tells ad_form + that the form has been confirmed by the user and that it is + safe to call the proper submission block. + + @param on_refresh Executed when the form comes back from being + refreshed using JavaScript with the __refreshing_p flag set. + + @param on_submit When the form is submitted, this code block will + be executed before any new_data or edit_data code block. Use + this if your form doesn't interact with the database or if the + database type involved includes a Tcl API that works for both + new and existing data. The values of the form's elements will + be available as local variables. Calling 'break' inside this + block causes the submission process to be aborted, and neither + new_data, edit_data, nor after_submit will get + executed. Useful in combination with template::form set_error + to display an error on a form element. + + @param new_data This code block will be executed when a form for a + new database row is submitted. This block should insert the + data into the database or create a new database object or + content repository item containing the data. Calling 'break' + inside this block causes the submission process to be aborted, + and after_submit will not get executed. Useful in combination + with template::form set_error to display an error on a form + element. + + @param edit_data This code block will be executed when a form for + an existing database row is submitted. This block should + update the database or create a new content revision for the + existing item if the data's stored in the content repository. + Calling 'break' inside this block causes the submission + process to be aborted, and after_submit will not get + executed. Useful in combination with template::form set_error + to display an error on a form element. + + @param after_submit This code block will be executed after the + three blocks on_submit, new_data or edit_data have been + executed. It is useful for putting in stuff like ad_returnredirect + that is the same for new and edit. + + @param validate A code block that validates the elements in the + form. The elements are set as local values. The block has the + following form: +

+{element_name
+    {tcl code that returns 1 or 0}
+    "Message to be shown by that element in case of error"
+}
+{...}
+       
+ + @param on_validation_error A code block that is executed if + validation fails. This can be done to set a custom page title + or some similar action. + + @param csrf_protection_p { 0 | 1 } Should the form add + automatically a hidden form field for csrf protection? Use 1 + or t for true, 0 or f for false. Defaults to false. + + @see ad_form_new_p + @see ad_set_element_value + @see ad_set_form_values + @see template::form::create + +} { + set level [template::adp_level] + # Set the form name, defaulting to the name of the template that called us if { [info exists name] } { @@ -598,8 +574,18 @@ set af_element_names($form_name) [list] } + # One of the features of ad_form is the possibility to configure a + # form in subsequent calls of the proc ('piecewise' + # configuration). To maintain some level of consistency, in this + # loop we enforce that only some 'extendable' parameters can be + # specified multiple times, while others will throw an error in + # that case. One drawback of this approach is one must take extra + # care when specifying default values for a flag, or it will be + # considered already specified. Also, some trick is needed to + # persist flags which where specified in previous calls + # (e.g. these global variables). global af_parts - + set valid_args [dict get [nsv_get api_proc_doc [lindex [info level 0] 0]] switches] foreach valid_arg $valid_args { if { [info exists $valid_arg] } { if { [info exists af_parts(${form_name}__$valid_arg)] @@ -810,7 +796,7 @@ template::element create $form_name "__submit_button_name" -datatype text -widget hidden -value "" template::element create $form_name "__submit_button_value" -datatype text -widget hidden -value "" - if {$csrf_protection_p} { + if {[info exists csrf_protection_p] && $csrf_protection_p} { # # Add CSRF value to every ad_form. Validation might be # application-specific (validation is not always wanted, @@ -1139,7 +1125,7 @@ } } - if {$csrf_protection_p} { + if {[template::element::exists $form_name __csrf_token]} { # # CSRF protection is activated, therfore validate the # hidden form field content.