Reed Sandberg

NAME

CGI::AutoForm - Automated abstraction of HTML forms from a data source

SYNOPSIS

 use CGI::AutoForm;


 $form = new CGI::AutoForm($dbh,$form_name);

 $form->action($action_url);

 $form->add_group($CGI::AutoForm::EDIT_GROUP,$table_name);

 $form->add_record($current_record);

 $form_html = $self->prepare();

 # insert $form_html into the BODY section of an (X)HTML document via a template


 $group = $form->group_by_name($group_name);

 $bool = $form->validate_query($query,$callback);

 $records = $form->format_query($query);

 $form_copy = $form->clone();

 $form->reset_group();

 
 #
 # an example of customizing a data group's fields...
 #

 $rv = $form->add_group($CGI::AutoForm::INSERT_GROUP,undef,'Vote For Your Favorite Artist','ARTIST_VOTE');

 $fields = $form->db_fields('ARTIST',$CGI::AutoForm::INSERT_GROUP);

 $form->push_field($fields->{ARTIST_NAME});

 $form->add_field( {
    FIELD_NAME => 'VOTE',
    INPUT_CONTROL_TYPE => 'RADIO',
    REQUIRED => 'Y',
    HEADING => 'Vote',
    DATATYPE => 'CHAR',
    INSERTABLE => 'Y',
    },
    [
        { ID => '1', MASK => '*', },
        { ID => '2', MASK => '**', },
        { ID => '3', MASK => '***', },
        { ID => '4', MASK => '****', },
    ]);

 $form_html = $self->prepare( { 'ARTIST_VOTE.ARTIST_NAME' => 'Nonpoint', 'ARTIST_VOTE.VOTE' => 4 } );

DESCRIPTION

There are many CGI form abstractions available, (e.g. CGI.pm). A unique and powerful advantage with this abstraction is that it can be tied closely with a database schema. Each group of fields in the form can represent a database table (or view) and the table/column properties and constraints are automagically discovered so your DBA can make DDL changes that will be immediately reflected in the HTML forms (no duplication of the data dictionary in your code).

All user/operator input is checked tightly against database constraints and there is built-in magic to provide convenient select lists, etc, and to enforce a discreet set of valid values against unique/primary keys in lookup tables (see Select lists & ID masking). This means referential integrity even for MySQL. Metadata in MySQL's SET and ENUM types are also supported. This also gives the operator a chance to correct mistakes with helpful hints instead of just getting a meaningless db error code.

This design allows you to get secure, database-driven web apps up and running is as little as a few hours (see Cruddy! for an implementation http://www.thesmbexchange.com/cruddy/index.html). This is made possible with the help of the DBIx::IO abstraction, please refer to it for further details.

Another advantage this abstraction provides is the separation of presentation and style using style sheets and having human-friendly presentation attributes stored in a database table that can be managed by non-engineers.

Typical CGI apps are characterized by collecting, updating, reporting and formatting data using forms and tables. Form creation and processing can be divided into the following tasks:

1) Deciding what data to collect in order to perform the desired function.

2) Deciding how the operator will convey the desired information (input fields, checkboxes etc).

3) Form layout.

4) Imposing integrity constraints on the collected data.

5) Presentation and style.

6) Directing the collected data.

This class allows (but doesn't force) form elements and constraints to be defined in a database. This way, the definitions are organized in a central repository, so they can be managed in a shared environment separate from the code. Vanilla HTML is generated and several HTML classes are used so that presentation and style can be dictated with style sheets (again separate from the code). For flexibility, methods are given to modify form definitions and layout programmatically as well.

DETAILS

Form object structure

A form contains of a list of data groups. Each data group contains a list of form fields, and a list of 0 or more data records that correspond to the form fields. Each form field is a hash of attributes describing how the field should be rendered as a form element along with constraints, access controls and such.

A form object is blessed and will be referred throughout these docs as $form. Of its many attributes, it holds an arrayref and a hashref of data groups, each referred to as $group. Each $group is a (non-blessed) object-like hashref (sub-object) and of its many attributes, it holds an arrayref and a hashref of data fields, each referred to as $field. Each $field in turn is a (non-blessed) object-like hashref (sub-object) as well. Because these sub-objects are stored as a list and a hash by their parent, it is best to use the methods provided when mutating the list/hash structures themselves. However, modifying individual sub-object attributes may be done by accessing the hash keys directly (or iterating the lists).

If using a database on the backend to manage form data, a data group would correspond to a table, each of its fields would correspond to columns, likewise, with the group's list of records.

One of the primary attributes of a data group is its type or usage and must be one of the following:

Insert

Gather and validate data, e.g. for subsequent database insert.

Display/Update

Display a record with certain fields available for editing.

Gather criteria for running a database report (with the help of DBIx::IO::Search).

Display Only

Display read-only data, e.g. a database report generated from a search form.

Each of these types is referred to as $usage throughout these docs (see add_group for defined constants).

Each form object will be in a certain state with respect to the groups, fields and records associated with it. The state is defined in terms of the current group#, record# and field#. The state is altered when adding a new data group to a form or through the iterative methods such as next_group, next_record, next_field. reset_group will zero these state properties. State is important for methods such as add_record, push_field and such. Methods dependent on state will be annotated accordingly.

Form field attire

Form field attributes may be kept and managed in a database table (see contrib/ for DDL contained in the Cruddy! distribution http://www.thesmbexchange.com/cruddy/index.html). The default name of the table that holds these attributes is UI_TABLE_COLUMN and may be overridden per $form object by setting $form->{attr_defn_table_name}. NOTE MySQL users! If your database has case-sensitive table names then pay attention to this name.

Each record in UI_TABLE_COLUMN describes attributes/constraints of a single field in an HTML form. Each HTML form field usually corresponds to a table column (or a view column for search forms or possibly updatable views otherwise). The data dictionary will be queried if a form's field group corresponds to a table that does not have any fields defined in UI_TABLE_COLUMN and appropriate default attributes will be used (schema auto-discovery). NOTE! however that this is all or nothing - if using a database table to store form data and that table has at least one field defined in UI_TABLE_COLUMN, then all fields in that table must have a record in UI_TABLE_COLUMN or bad things can happen. The exception here are Oracle LOB type fields (BLOB, etc) - these fields are completely ignored by this module.

You may also elect to leave some of the fields in UI_TABLE_COLUMN NULL and they will be taken from the data dictionary. This is recommended to avoid data duplication/syncing issues; essentially, it allows the DBA to make changes that will be automagically reflected in the HTML forms (see use_data_dict).

The following is a list of fields in UI_TABLE_COLUMN and how each influences the form HTML, presentation, access control, constraint checking, etc:

TABLE_NAME

UPPER CASE name of a table (or group of fields) that corresponds to a data group of a form object

FIELD_NAME

UPPER CASE name of a field associated with table_name

APPEAR_ORDER

Relative order the fields will appear in the form's data group (integer, recommended to use sequence of 10's - 10, 20, 30...)

HEADING

User-friendly name of FIELD_NAME that will appear in the form

SEARCHABLE

True/false - allows this field to appear in a search group

UPDATABLE

True/false - allows this field to be updated (e.g. set false for a primary key sequence)

INSERTABLE

True/false - allows this field to be defined by the operator for inserts (e.g. set false for auto-generated primary key sequence)

INPUT_CONTROL_TYPE

Form control type for operator input (update/insert groups) one of: TEXT, TEXTAREA, PASSWORD, DATE, DATETIME, FILE, SELECT, RADIO, CHECKGROUP

MULTI_INSERT_DELIMITER

For the CHECKGROUP input control, will insert multiple values as one field with each value serialized by this string (deserialized automagically on display)

SEARCH_CONTROL_TYPE

Form control type for search groups, one of: SELECT, CHECKBOX, RADIO, TEXT, MATCH TEXT, COMMALIST, DATE, DATETIME, DATERANGE, DATETRANGE (query on a range of date + time), RANGE

SEARCH_MULT_SELECT

For search groups, allow multi-select on a select box (values will be OR'ed using the IN operator) accepts an integer that gives the height of the control

USE_DATA_DICT

If true, datatype, default_value, required, input_size and input_maxlength will be taken from the data dictionary if those fields are NULL (recommended to use this whenever possible)

DATATYPE

Datatype constraint for this field, one of CHAR, DATE, NUMBER, DATETIME, INT, INT UNSIGNED

DEFAULT_VALUE

Default value for insert groups, can be the special value _SYSDATE for date types, meaning insert the current date

REQUIRED

True/false constraint - is empty input (NULL) acceptable?

INPUT_SIZE

For input_control_type of TEXT or PASSWORD - the width of the control box

INPUT_MAXLENGTH

For input_control_type of TEXT, PASSWORD - the maximum length of text that can be entered

BRIEF_HEADING

Short, user-friendly heading for the brief tabular display results (see Tabular data groups)

ALT_MASK_FIELD

For auto-recognition of associated lookup table, an alternative name for the USER_MASK field (see Select lists & ID masking)

MASK_TABLE_NAME

For configured recognition of associated lookup table, the name of the table with primary keys that match this foreign key (see Select lists & ID masking)

MASK_FIELD_NAME

For configured recognition of associated lookup table, the name of the field that contains the user-friendly values (see Select lists & ID masking)

ID_FIELD_NAME

For configured recognition of associated lookup table, the name of the field that contains the ID values as a unique or primary key (see Select lists & ID masking)

NO_CACHE

True/false - use this for caching of the lookup table. If the records in the lookup table change frequently set this to TRUE. The default is FALSE, which will cache the lookup table

RADIO_CHECKBOX_COLS

Integer value of the number of columns of buttons/controls for RADIO, CHECKBOX and CHECKGROUP controls.

FIELD_GROUP

Special considerations for a group of controls that govern a single field, only supported value is CONFIRM, which is useful for PASSWORD fields

ELEMENT_ATTRS

Additional attributes that will be added to the HTML control element (e.g. enter 'rows="5" cols="10"' to size a TEXTAREA control box)

HELP_SUMMARY

The heading of this field will be an active link and when clicked, will render this summary as a js alert giving useful hints to the operator about the use of this field

Notes:

All values in TABLE_NAME and FIELD_NAME must be UPPER CASE. If you have use an RDBMS where table names are case-sensitive (MySQL on Linux/UNIX) and you have two or more tables with the same name but different letter cases, this is probably not a good idea to begin with but there is no workaround.

True/false fields accept 'Y', 'N' or NULL only (NULL => false).

Doesn't make much sense to have a value for input_control_type if insertable AND updatable are set FALSE.

Values left NULL will be given reasonable defaults.

If CONFIRM is used for field_group, two controls will be presented and the values entered into each must match. This is useful for PASSWORD fields where the operator can't see the input or other important fields that can be mistyped (email TEXT box, for example).

For a well-defined set of records (with a common table_name, where use_data_dict is FALSE) table_name does not have to refer to a database table or any RDBMS entity at all. Feel free to make up a schema that doesn't even exist - I've done this to manage an LDAP tree on the backend. You can even create your own data groups/controls that don't exist in UI_TABLE_COLUMN by defining a record in a perl hash variable with the keys of the hash being field names of UI_TABLE_COLUMN (in UPPER CASE, example given in SYNOPSIS).

You'll want to set use_data_dict TRUE if the data group is supported by a database table so that properties/constraints are automagically taken from the database schema.

For updates/inserts of submitted data, you may want to use only those fields that are updatable/insertable; e.g. $table->insert({ %{$rec}{ map { $_->{UPDATABLE} eq 'Y' ? $_->{FIELD_NAME} : () } @$field_list } }).

For search groups, multiple values selected in a select box or checkbox will be OR'ed together using the IN SQL operator.

Either alt_mask_field is populated or (mask_table_name, mask_field_name, id_field_name) as a group is populated (or none at all). See Select lists & ID masking for details.

Careful with NO_CACHE as it can be confusing - the default (false) is to cache the underlying lookup table for the set of acceptable values. If you have meta-data that changes on a daily basis (which is many times the case) set this to true ('Y').

This module caches data dictionary info from the database. So if you're using a persistent interpreter (mod_perl), you'll need to restart the webserver to recognize changes to the database structures (DDL modifications).

Clarification on RADIO, CHECKBOX and CHECKGROUP control types.

A RADIO set of controls only allows one value to be selected. CHECKBOX allows multiple values to be selected and is valid for SEARCH_CONTROL_TYPE where the values are or'd together in the search criteria (SELECT with SEARCH_MULT_SELECT set to an integer value will have the same behavior). CHECKGROUP allows for multiple values on INSERT and UPDATES such that these values are serialized into one field by the value of MULTI_INSERT_DELIMITER. If you use CHECKGROUP then you'll want to set MULTI_INSERT_DELIMITER or the default value will be used, which is not what you want. This behavior is very similar to the MySQL SET data type. In fact you should be using CHECKGROUP for any MySQL SET fields, in which case the value for MULTI_INSERT_DELIMITER is optional and ignored anyway because MySQL always uses a comma.

Using CHECKBOX (or SELECT with SEARCH_MULT_SELECT set to an integer value) for SEARCH_CONTROL_TYPE where INPUT_CONTROL_TYPE is set to CHECKBOX will probably not do what you want. If you want to do subset searching within a field of multiple values, use MATCH TEXT, which will accept % as a wildcard.

Tip: use a select list for search_control_type and text input box for input_control_type for tables with numeric ID's as the primary key. This will give a select list when doing a search masked with readable names and will give the numeric ID value on inspection (not recommended for large tables as the select list becomes too large).

Select lists & ID masking

There is a significant amount of magic to mask ID values with related lookup tables (meta-data) and verify referential integrity thereof. Take the example of a schema model of a CD collection:

  ARTIST
+-------------------------+
| artist_id               |
| artist_name (user_mask) |
| ...                     |
+-------------------------+
          |
          |
          |
  TITLE  /|\
+--------------+
| artist_id    |
| title        |
| year         |
| ...          |
+--------------+

In this simple example, you'd want to join these tables and present artist_name to the operator rather than the meaningless-to-humans artist_id. The magic starts by specifying a discreet HTML form control type (UI_TABLE_COLUMN.INPUT_CONTROL_TYPE) e.g. SELECT or RADIO where table_name = 'TITLE' and field_name = 'ARTIST_ID'. This is enough to automagically populate the control with values from the related lookup table (ARTIST) with the meaningful artist names and will put a constraint check on the server side (I claimed "high" security after all) to verify referential integrity. The masked values will then be translated back on insert/update.

This magic occurs when an appropriate control type is used and when there is a singular foreign key where the foreign key column name (with the _ID suffix stripped off, if present) matches a table name containing the unique/primary keys. Additionally, the ID field name in the lookup table must match the foreign key name and the human-friendly mask field must be named user_mask.

In this example above, all conditions are met except the mask field name is artist_name (not user_mask) so we'll populate the set of fields (mask_table_name, mask_field_name, id_field_name) with ('ARTIST', 'ARTIST_ID', 'ARTIST_NAME') respectively where field_name = ARTIST_ID and table_name = TITLE for ultimate control over table/field names of related lookup tables.

An example where you might use just alt_mask_field instead of the set (of fields (mask_table_name, mask_field_name, id_field_name) is perhaps if you have a table COUNTRY with fields (country, user_mask) where country is the country code and user_mask is the country name and a field in ARTIST (ARTIST.ORIGIN_COUNTRY) you'd simply set UI_TABLE_COLUMN.ALT_MASK_FIELD to 'COUNTRY' where field_name = ORIGIN_COUNTRY and table_name = ARTIST and the magic will happen.

If the underlying RDBMS is MySQL some additional magic parses allowed values for SET and ENUM data types to obtain this pick list (no related table with a foreign key is necessary).

If using a form control that demands a discreet set of values where none of the above conditions apply, you must specify the list (see $pick_list under create_field).

This magic provides a great deal of convenience and security not only for translating ID values for human operators but also for enforcing a discreet set of allowable values for certain form fields.

Tabular data groups

If passing a true value for $tabular to add_group, that group's data (via add_record) will be displayed in a tabular form - one column for each field in the record (read-only). This is how you display multiple records in a data group. The only fields that will be shown in a tabular view are the ones with a non-empty value for UI_TABLE_COLUMN.BRIEF_HEADING.

If $tabular is false (the default), a vertical form with a field heading and field value on each line is produced; each use HTML table elements however (see Form preparation, HTML generation & customization for details).

Form preparation, HTML generation & customization

Once the data group(s) of the form object have been defined, prepare will generate the HTML, which should be inserted into the BODY section of an HTML document (presumably using a templating system). The structure of the generated HTML follows:

 Form Heading
 <form>
   <div>Data Group1 Heading</div>
   <table>
   tabular view of search results (see below)
    -or-
   vertical view of data group1 fields (see below)
   </table>
   [<div>Data Group2 Heading...</div>
   ...]
 </form>

 for the tabular view of a data group:
 <thead><tr><th>Field1 name</th>[<th>Field2 name</th>...]</tr></thead>
 <tr><td>Value1</td>[<td>Value2</td>...]</tr>
 ...

 for the vertical view of a data group (updatable/insertable groups):
 <tr><td><label>field name</label></td><td>field value/form control</td></tr>
 ...

See prepare for further details of the layout. See the Cruddy! demo for the default layout:

http://www.thesmbexchange.com/cruddy/index.html

The HTML generated by prepare can be influenced by a number of attributes of the form object and group/field sub-object(s) (manipulate via hash keys, only some accessor methods have been defined as yet). To get the $field sub-object (hashref) try $group->{field_hash}{FIELD_NAME} (see also field_hash). Some of the following attributes may have content already so it is best to append to them, rather than assign/replace their values. prepare also accepts some callbacks to allow further customization. Unless otherwise noted, custom content is expected to be HTML (encode with HTML entities, etc, see escape).

$form->{top_message}

Content displayed at the very top of the form.

$form->{heading} (or $form->heading())

Header content near the top of the form describing the form. Will be enclosed in an h2 block.

$form->{heading2}

Sub-header content near the top of the form.

$form->{verr_msg}

Error message displayed near the top of the form when there are validation errors (see validate_query).

$form->{noscript}

Content enclosed in a noscript block.

$form->{name} (or $form->name())

Will be used in the name attribute of the form element.

$form->{action} (or $form->action() this needs to be set somewhere in your code)

Will be used in the action attribute of the form element.

$form->{submit_value} (or $form->submit_value())

Will be used in the value attribute of the submit button. The value will be HTML-escaped (don't encode with HTML entities).

$form->{submit_button_attrs}

Appended to the list of attributes of the input button controls (submit, reset, etc).

$group->{heading}

Header content at the top of the data group describing that group.

$group->{GT}

Appended to the list of attributes of the outermost table element of each group.

$group->{js}

A block of javascript added to the HTML block of each data group.

$form->{head_html}
$form->{tail_html}
$group->{head_html}
$group->{tail_html}

If these are defined before calling prepare, their contents completely override those blocks of HTML (see prepare for details).

The following affect the presentation of data groups in the tabular view
$group->{TABULAR_TH_ATTRS}

For a tabular data group, this will append attributes to all th elements in the data group (don't include a class attribute).

$field->{TABULAR_TH_ATTRS}

For a tabular data group, this will append attributes to the th element for a particular $field sub-object (don't include a class attribute).

$field->{TABULAR_TD_ATTRS}

For a tabular data group, this will append attributes to the td elements for a particular $field sub-object (don't include a class attribute).

$field->{TABULAR_TD_STYLE}

For a tabular data group, this will append style properties to the style attribute of the td elements for a particular $field sub-object.

$group->{TABULAR_TD_TAIL_ATTRS}

For a tabular data group, this will append attributes to the td elements on the final row in the table only (don't include a class attribute).

$group->{TABULAR_TD_TAIL_STYLE}

For a tabular data group, this will append style properties to the style attribute of the td elements on the final row in the table only.

$group->{TABULAR_TR_TAIL_ATTRS}

For a tabular data group, this will add to the list of attributes of the tr element on the final row in the table only.

The following affect the presentation of data groups in the vertical view (updatable/insertable)
$form->{VFR}

For vertical view data groups (update/insert), this will add to the list of attributes of the tr elements.

$form->{VFL}

For vertical view data groups (update/insert), this will add to the list of attributes of the td elements containing the label.

$form->{VFE}

For vertical view data groups (update/insert), this will add to the list of attributes of the td elements containing the value (control).

$form->{dontescape}
$form->{dontunescape}

These don't really affect the presentation but for lack of a better place to document, these attributes prevent any (un)escaping of HTML content (using HTML entities) during HTML generation (dontescape) or during query extraction/normalization/validation (dontunescape).

Style sheets

Class attributes are hooked into the HTML that is generated to allow custom styling with CSS. The following is a list of class names and how/where they influence the presentation.

REQ

For fields that require input (NOT NULL), label elements will be tagged with this class.

REQI

For fields that require input (NOT NULL), input elements will be tagged with this class.

GH

For each data group heading, the opening div element will be tagged with this class.

GT

For each data group, the outermost table element will be tagged with this class.

TABULAR_TH

For a tabular data group, the th elements will be tagged with this class.

TABULAR_TD

For a tabular data group, the td elements will be tagged with this class.

GTAIL_TR

For a tabular data group, the tr element will be tagged with this class on the final row in the table only.

AS

For fields with UI_TABLE_COLUMN.HELP_SUMMARY populated, the a element will be tagged with this class.

VFR

For vertical view data groups (update/insert), the tr elements will be tagged with this class.

VFL

For vertical view data groups (update/insert), the td elements containing the label will be tagged with this class.

VFE

For vertical view data groups (update/insert), the td elements containing the value (control) will be tagged with this class.

CONFIRM

For fields that use CONFIRM for UI_TABLE_COLUMN.FIELD_GROUP, the label element will be tagged with this class for the second confirmation field.

VERR_MSG

If there are validation errors (see validate_query) the enclosing div tag of the error message will be tagged with this class.

VERR

If there are validation errors font, span and label elements of the vertical view of a data group will be tagged with this class for invalid fields. For tabular groups, the td elements of invalid fields will be tagged.

Form field names

To help avoid form field namespace collisions, the name attribute of form controls will use the following format:

<group_name>.<field_name>

where <group_name> is typically the name of the table but must be unique.

structure_query, extract_query_group and extract_cut_query_group provide handy ways of organizing form data with this naming convention.

METHODS

new (constructor)
 $form = new CGI::AutoForm([$dbh],[$form_name]);

Create a new $form object given an optional database handle and optional name, which is helpful for CGI::AutoForm::Session.

Form Attribute Accessors/Modifiers

Get the values of these READ-ONLY attributes.

 $dbh           = $form->dbh();

Get or set the values of these attributes.

 $bool          = $form->readonly();
 $name          = $form->name();
 $action        = $form->action();
 $heading       = $form->heading();
 $submit_value  = $form->submit_value();
 $hidden_fields = $form->hidden();
 $meta          = $form->meta();
 $continue      = $form->continue();
readonly

Setting this attribute will force all data to be displayed as read-only such that no form controls will be used.

add_group
 $group_count = $form->add_group($usage[,$table_name,$heading,$group_name,$tabular,$defaults,$mid]);

Add a data group to $form of type $usage. Elements of the group are taken from UI_TABLE_COLUMN according to $usage and $table_name.

$usage can be one of:

 $CGI::AutoForm::DISPLAY_ONLY_GROUP
 $CGI::AutoForm::INSERT_GROUP
 $CGI::AutoForm::EDIT_GROUP
 $CGI::AutoForm::SEARCH_GROUP

If $table_name is given, fields of the group are taken from UI_TABLE_COLUMN or the data dictionary as a last resort for that table name. If $table_name is not given, you are expected to add fields (see create_field or add_field).

$heading is used to label the group on the HTML form and has a reasonable default according to $group_name. $group_name defaults to $table_name and can be specified in the case where more than one group with the same $table_name will appear on the same form. The groups of a form must have unique names.

A true value for $tabular means the data that the group accumulates will be displayed in a tabular view (see Tabular data groups). If false, it does not make sense to include more than one record for add_record (or simply passing $query to prepare is sufficient).

$defaults can be specified as a hashref of FIELD_NAME => value pairs to be used as default values for fields in the group. These values will override those in UI_TABLE_COLUMN.DEFAULT_VALUE or those determined from the data dictionary.

$mid will insert the group at a midpoint in the group list. Default is the end of the list; index starts at 0 so say you want to insert a group at position 4 on the form - you would specify 3 for $mid.

 Return the new number of groups for $form if successful.
 Return 0 if no fields exist in UI_TABLE_COLUMN or the data dictionary for $table_name.
 Return -1 if a pick_list could not be determined but is required (see C<create_field>).
 Return -2 if $group_name is not unique.
 Return -3 if data dictionary attributes were requested via UI_TABLE_COLUMN but could not be found.
 Return undef if db error.

This method affects the state of $form by incrementing the group#.

Group Attribute Accessors/Modifiers

Get the values of these READ-ONLY group attributes. These are state dependent (refer to Form object structure).

 $name          = $form->current_group_name();
 $usage         = $form->group_usage();
 $name          = $form->curr_table_name();
current_group
 $rec = $form->current_group();

Return the $group structure of the current group. If there is no current group, the first one will be returned and curr_group_no modified.

group_by_name
 $group = $form->group_by_name($group_name);

Return the $group structure identified by $group_name.

reset_group
 $form->reset_group();

Zero the state of $form by resetting the group#, record# and field#.

control_type
 $control_type = $form->control_type($field);

Return the control type of $field specified in either UI_TABLE_COLUMN.INPUT_CONTROL_TYPE or UI_TABLE_COLUMN.SEARCH_CONTROL_TYPE depending upon the usage of the current data group (state dependent).

Customizing groups & fields

The following methods allow you to manipulate and customize form definitions taken from UI_TABLE_COLUMN or the data dictionary. You may even roll your own fields by creating a hashref of NAME => value pairs that emulate a record from UI_TABLE_COLUMN.

create_field
 $group_field = $form->create_field($field[,$pick_list,$all_db_defn]);

Given $field - a hashref of NAME => value pairs that resembles a record in UI_TABLE_COLUMN, return a hashref that is suitable to add to an existing data group structure ($group) using push_field, unshift_field, etc.

$field is copied so be sure to use the return value.

$pick_list is a list of discreet values that may be used for a field with a SELECT control list, RADIO or CHECKBOX group, etc. It is an arrayref of hashes, each hash with two keys: ID => value attribute of the control element MASK => the human-friendly description the operator sees. You may also add/replace/modify the picklist after calling this method using replace_picklist or by manipulating the following structures: $field->{PICK_LIST} $field->{PICK_HASH}

If this field has a control type of SELECT, RADIO, CHECKBOX, etc., or if $all_db_defn is true, a picklist will be looked for in the database or data dictionary (see Select lists & ID masking) unless $pick_list is passed to this method.

Advanced: The $field->{REQUIRED} attribute is always set false for SEARCHABLE data groups as no fields are required for running reports. If you really know what you're doing, it is OK to set this to true after calling this method.

May return 0 if $pick_list was not given for a SELECT, RADIO or CHECKBOX field and one could not be generated from the database using DBIx::IO::Mask. May return undef if a db error occurred with DBIx::IO::Mask

add_field
 $field_count = $form->add_field($field[,$picklist,$mid]);

Conveniently combines create_field and push_field.

replace_picklist
 $pick_hash = $form->replace_picklist($field_name,$pick_list[,$group]);

Replace the picklist of $field_name with $pick_list for $group (which defaults to the current group).

push_group
 $group_count = $form->push_group($group[,$mid]);

Push $group on to the end of the group list or at the position $mid (using splice) if provided. Alters the current group number (state) to point to the $group just added.

push_field
 $field_count = $form->push_field($field[,$mid]);

Push $field (see create_field) on to the end of the field list of the current group (or at the position $mid if provided).

unshift_field
 $field_count = $form->unshift_field($field);

unshift $field (see create_field) on to the beginning of the field list of the current group.

delete_field
 $deleted_field = $form->delete_field($field_name[,$group]);

Delete the field hashref identified by $field_name from $group (defaults to the current group). Return the deleted field hashref.

db_fields
 $fields = $form->db_fields($table_name,$usage);

Return a hashref of $field structures from the database using $table_name. Check UI_TABLE_COLUMN first and then the data dictionary. This is useful for customizing a data group or adding fields from different tables into the same data group, etc.

 Return 0 if no fields exist in UI_TABLE_COLUMN or the data dictionary for $table_name.
 Return -1 if a pick_list could not be determined but is required (see C<create_field>).
 Return -3 if data dictionary attributes were requested via UI_TABLE_COLUMN but could not be found.
 Return undef if db error.

HTML Generation and Other Accessors

hidden

hidden is a form object attribute that stores a hashref of name => value pairs that will be included as hidden INPUT form elements. The hash can be accessed via this method or directly with $form->{hidden}.

The values of the hash can be a scalar or an array ref where multiple form elements of the same name for each array value will be rendered.

You can add/remove keys from this hash but don't replace it as it is updated internally. Keys will be removed internally if they conflict with updatable/insertable visible form fields. Keys may be overwritten if $query is used (see prepare) or by add_record() for an updatable group.

The HTML from this hash is automatically stored in the html attr.

hidden_html
 $html_block = $form->hidden_html();

Create a block of HTML hidden INPUT elements using the $form->{hidden} attribute hash.

prepare
 $form_html = $self->prepare([$query],[$val_callback],[$rec_callback],[$head_callback]);

$query can be given to impose a state on forms between instantiations via otherwise stateless HTTP (see also CGI::AutoForm::Session). $query is a hashref of NAME => value pairs where the value can be a scalar or arrayref. An arrayref is used where NAME is associated with more than one value. $query is typically derived from a GET query string or POST name=value pairs from an HTTP response.

If present, $query values are sticky in the sense that they will override any defaults and any values introduced with add_record.

For vertical views (insert/update groups), assign values to the fields according to the following priority scheme: 1) If a query was submitted, use it as a source of data if it has values for the current group. 2) If a current record exists for the current group, use it as a data source. 3) Use the field's DEFAULT_VALUE attribute, if any. The exception here is that if the $form->{coalesce_default_insert} attribute is true then: Use the value from current record if it exists otherwise use the DEFAULT_VALUE attribute, if any.

To give you more control over the HTML that is generated, callback functions are accepted. $head_callback and $val_callback functions will be called for each field in the form as (except for tabular groups where $head_callback is not called at all):

 &callback($value,$field,$form)

and must return the desired HTML snippet for that element. $field is a hashref of all columns in UI_TABLE_COLUMN plus any added by this class while processing (see create_field). $form is the form object.

For $head_callback, $value will be the heading text to be displayed for each field.

For $val_callback, $value will be the display value or HTML form input element depending on the type of data group it is part of (read-only/writable).

For $rec_callback, the prototype follows:

 &callback($value,$field,$group,$form)

where $value will be the entire HTML row tr block and $group is the data group structure. For a tabular display of search results, $field will instead be the data record for that row (hashref of NAME => value pairs).

An ugly, unformatted gob of HTML is returned as a scalar reference for performance reasons. If only certain parts of the HTML generated are useful, the following attributes can be used to extract certain sections. Some can also be completely overridden if defined before calling this method (see Form preparation, HTML generation & customization), in which case they should contain certain opening/closing HTML elements noted below:

For each field in $group->{export_rec}: $field->{html}

Note that the export_rec attr for each group is only available after calling this method.

For each $group in $form:

 $group->{html}, a composite of:
 $group->{head_html} (must contain the opening C<table> tag)
 $group->{body_html}
 $group->{tail_html} (must contain the closing C<table> tag)
 
 $form->{body_html}, a composite of:
 $form->{hidden_html}
 $group->{html} for each group in $form
 
 $form->{html}, a composite of:
 $form->{head_html} (must contain the opening C<form> tag)
 $form->{body_html}
 $form->{tail_html} (must contain the closing C<form> tag and submit button)
require_js
 $form->require_js();

Add a default noscript summary to require the client to support javascript. You may also set $form->{noscript} yourself.

field_by_name
 $field = $form->field_by_name($field_name);

Return a hashref of properties for $field_name from the current group including attributes from UI_TABLE_COLUMN and VALUE that will be displayed determined from the priority list (see prepare and _export_field).

The returned $field structure is a copy so don't expect to change its attributes and have them be reflected in the generated HTML. To manipulate field attributes that will affect the generated HTML, access the field hashref directly using $form->field_hash()->{field_name}

field_list
 $fields = $form->field_list();

Return a list of fields from the current group.

field_hash
 $fields = $form->field_hash();

Return a list of fields from the current group.

next_group
 $fields = $form->next_group();

Return the next data group structure (hashref) in the list. Changes state of $form by incrementing curr_group_no.

is_readonly
 $bool = $form->is_readonly([$field]);

Determime if the current data group is read-only, and if the particular $field is read-only if given. State-dependent. Search data groups are never read-only.

add_record
 $rec_count = $form->add_record($record[,$group_name]);

Add data to $form's current group (state dependent) or the group identified by $group_name if given. $record can be a hashref of FIELD_NAME => value pairs or an arrayref of such records (for tabular groups of type 'DISPLAY ONLY').

This method should be used for groups of type 'DISPLAY EDIT' and 'DISPLAY ONLY', which require data to be present (unless passing $query to prepare).

See prepare for assignment priority when using CGI query data.

Returns the number of records currently stored.

current_record
 $rec = $form->current_record();

Return the current record. If there is no current record, the first one will be returned and curr_rec_no modified.

validate_query
 $bool = $form->validate_query($query[,$callback]);

Validate input fields submitted from a form previously created by this class. The structure of $query is explained in prepare except that multi-valued fields are not checked, which should never be used for insert/update groups anyway (notwithstanding pseudo multi-valued fields packed with MULTI_INSERT_DELIMITER, which WILL be split out and individually validated).

Numerous checks are done and there is magic to validate input that meets database constraints (if using a db on the backend) so the operator can correct mistakes instead of just getting a meaningless db error code. For discreet value controls (SELECT box, etc) this will also confirm submitted values fall withing the list (beware hackers).

For each field that fails, its VALID_ERROR attribute is set to an appropriate error message/user hint. Normally if an error occurs this form would be presented to the user again. The HTML generated from prepare uses the VALID_ERROR attribute to set an HTML class name of VALID_ERROR for the field's heading which is enclosed in a LABEL element so emphasis can be placed through style sheets. Additionally, the error message is displayed inline with the heading to give the user a hint of what went wrong.

$callback is optional and allows you to perform further validation checking; it is used as:

 ($success,$err_message) = &$callback($value,$field,$group,$form,$query);

Where $success indicates valid input, $err_message is set when the input is invalid (its use is described above). $value is the value to be verified. $field is the field sub-object, $group is the data group sub-object structure. $form is the form object and $query is a normalized version of the $query originally passed in.

See CGI::AutoForm::Session for an example implementation.

format_query
 $formatted_query = $form->format_query($query);

Class or object method. Normalize and structure input fields submitted by a form created by this class. The structure of $query is explained in prepare.

Convenience method that executes normalize_query and then structure_query on $query (see those methods for details).

normalize_query
 $norm_query = $form->normalize_query($query);

Class or object method (must use as object method for a non-default multi-value delimiter).

All date fields will be normalized in the YYYYMMDDHH24MISS format (in Oracle-speak) for consumption by DBIx::IO::Table and friends (see also DBIx::IO::GenLib). CONFIRM fields are de-duped and checked for equality. If unequal, the value will be given as an empty hashref. All field names are converted to UPPER CASE. Removes leading and trailing whitespace, remove all NULL chars and removes elements that have an empty or undefined value.

Whereas format_query will execute this method AND structure_query, sometimes just this call is useful to manipulate a query in its normalized form before passing to prepare, which won't accept a structured query.

See prepare for the structure of $query.

structure_query
 $struct_query = CGI::AutoForm->structure_query($query);

Class or object method.

The returned $struct_query will be structured like $struct_query->{group_name}{FIELD_NAME} from properly named form fields (see Form field names). This structure can facilitate record extraction for each group in the form, e.g. $formatted_query->{group_name} will give a record available for direct insert using DBIx::IO::Table.

See prepare for the structure of $query.

clone
 $form_copy = $form->clone();

Perform a deep copy of $form. HTML attributes generated from prepare will not be copied.

Useful for caching form objects in environments like mod_perl, see CGI::AutoForm::Session for an example.

Return the new object.

extract_query_group
 $form_fields = CGI::AutoForm->extract_query_group($query,$group_name);

Object or class method.

Return a hashref of form field '<group_name>.<field_name>' => value pairs from a data group named $group_name. Must use properly named form fields (see Form field names).

See prepare for the structure of $query.

extract_cut_query_group
 $form_fields = CGI::AutoForm->extract_cut_query_group($query,$group_name);

Object or class method.

Return a hashref of form field <field_name> => value pairs from a data group named $group_name. Similar to extract_query_group except the keys of the hashref don't have the <group_name> component. Must use properly named form fields (see Form field names).

See prepare for the structure of $query.

escape
 $form_fields = CGI::AutoForm->escape($query,$group_name);

Object or class method (use the object method invocation with the dontescape attribute).

Utility method to transform text into an HTML compatible format by escaping (encoding) certain characters with HTML entities. Ignored if $form->{dontescape} is TRUE.

unescape
 $form_fields = CGI::AutoForm->unescape($query,$group_name);

Object or class method (use the object method invocation with the dontunescape attribute).

Utility method to reverse the transformation of escape. Ignored if $form->{dontunescape} is TRUE.

BUGS

This file is way too long - it should be divided into smaller classes each with limited scope (e.g. create a CGI::AutoForm::Group class).

No quoting of object (table) names is done within SQL (DBIx::IO) so object names containing reserved words or otherwise need quoting (e.g. `mysql_reserved_word` or "oracle_reserved_word") in your respective RDBMS will be problematic.

SEE ALSO

CGI::AutoForm::Session, DBIx::IO, DBIx::IO::Table, DBIx::IO::Search, DBIx::IO::Mask, Cruddy! http://www.thesmbexchange.com/cruddy/index.html

AUTHOR

Reed Sandberg, <reed_sandberg Ӓ yahoo>

COPYRIGHT AND LICENSE

Copyright (C) 2000-2008 Reed Sandberg

This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself.

The full text of the license can be found in the LICENSE file included with this module.

9 POD Errors

The following errors were encountered while parsing the POD:

Around line 818:

You forgot a '=back' before '=head2'

Around line 845:

'=item' outside of any '=over'

Around line 1015:

You forgot a '=back' before '=head2'

Around line 1049:

'=item' outside of any '=over'

Around line 1156:

You forgot a '=back' before '=head2'

Around line 1166:

'=item' outside of any '=over'

Around line 1993:

You forgot a '=back' before '=head2'

Around line 1995:

'=item' outside of any '=over'

Around line 3939:

Non-ASCII character seen before =encoding in 'Ӓ'. Assuming UTF-8