HTML-FormTemplate

 view release on metacpan or  search on metacpan

lib/HTML/FormTemplate.pm  view on Meta::CPAN


use strict;
use warnings;
use vars qw($VERSION @ISA);
$VERSION = '2.03';

######################################################################

=head1 DEPENDENCIES

=head2 Perl Version

	5.004

=head2 Standard Modules

	I<none>

=head2 Nonstandard Modules

	Class::ParamParser 1.041
	HTML::EasyTags 1.071
	Data::MultiValuedHash 1.081
	CGI::MultiValuedHash 1.09

=cut

######################################################################

use Class::ParamParser 1.041;
@ISA = qw( Class::ParamParser );
use HTML::EasyTags 1.071;
use Data::MultiValuedHash 1.081;
use CGI::MultiValuedHash 1.09;

######################################################################

=head1 SYNOPSIS

	#!/usr/bin/perl
	use strict;
	use warnings;

	use HTML::FormTemplate;
	use HTML::EasyTags;

	my @definitions = (
		{
			visible_title => "What's your name?",
			type => 'textfield',
			name => 'name',
			is_required => 1,
		}, {
			visible_title => "What's the combination?",
			type => 'checkbox_group',
			name => 'words',
			'values' => ['eenie', 'meenie', 'minie', 'moe'],
			default => ['eenie', 'minie'],
		}, {
			visible_title => "What's your favorite colour?",
			type => 'popup_menu',
			name => 'color',
			'values' => ['red', 'green', 'blue', 'chartreuse'],
		}, {
			type => 'submit', 
		},
	);

	my $query_string = '';
	read( STDIN, $query_string, $ENV{'CONTENT_LENGTH'} );
	chomp( $query_string );

	my $form = HTML::FormTemplate->new();
	$form->form_submit_url( 
		'http://'.($ENV{'HTTP_HOST'} || '127.0.0.1').$ENV{'SCRIPT_NAME'} );
	$form->field_definitions( \@definitions );
	$form->user_input( $query_string );

	my ($mail_worked, $mail_failed);
	unless( $form->new_form() ) {
		if( open( MAIL, "|/usr/lib/sendmail -t") ) {
			print MAIL "To: perl\@DarrenDuncan.net\n";
			print MAIL "From: perl\@DarrenDuncan.net\n";
			print MAIL "Subject: A Simple Example HTML::FormTemplate Submission\n";
			print MAIL "\n";
			print MAIL $form->make_text_input_echo()."\n";
			close ( MAIL );
			$mail_worked = 1;
		} else {
			$mail_failed = 1;
		}
	}

	my $tagmaker = HTML::EasyTags->new();

	print
		"Status: 200 OK\n",
		"Content-type: text/html\n\n",
		$tagmaker->start_html( 'A Simple Example' ),
		$tagmaker->h1( 'A Simple Example' ),
		$form->make_html_input_form( 1 ),
		$tagmaker->hr,
		$form->new_form() ? '' : $form->make_html_input_echo( 1 ),
		$mail_worked ? "<p>Your favorites were emailed.</p>\n" : '',
		$mail_failed ? "<p>Error emailing your favorites.</p>\n" : '',
		$tagmaker->end_html;

=head1 DESCRIPTION

This Perl 5 object class can create web fill-out forms as well as parse,
error-check, and report their contents.  Forms can start out blank or with
initial values, or by repeating the user's last input values.  Facilities for
interactive user-input-correction are also provided.

The class is designed so that a form can be completely defined, using
field_definitions(), before any html is generated or any error-checking is done. 
For that reason, a form can be generated multiple times, each with a single
function call, while the form only has to be defined once.  Form descriptions can
optionally be read from a file by the calling code, making that code a lot more
generic and robust than code which had to define the field manually.

lib/HTML/FormTemplate.pm  view on Meta::CPAN

input checking, and automatically generate error messages and help text beside
the appropriate form fields when html is generated, so to show the user exactly
what they have to fix.  The "error state" for each field is stored in a hash,
which the calling code can obtain and edit using invalid_input(), so that results
of its own input checking routines are reflected in the new form.

This class also provides utility methods that you can use to create form field 
definitions that, when fed back to this class, generates field html that can be 
used by CGI scripts to allow users with their web browsers to define other form 
definitions for use with this class.

Note that this class is a subclass of Class::ParamParser, and inherits
all of its methods, "params_to_hash()" and "params_to_array()".

=head1 RECOGNIZED FORM FIELD TYPES

This class recognizes 10 form field types, and a complete field of that type can
be made either by providing a "field definition" with the same "type" attribute
value, or by calling a method with the same name as the field type.  Likewise,
groups of related form fields can be made with either a single field definition
or method call, for all of those field types.

Standalone fields of the following types are recognized:

=over 4

=item 0

B<reset> - makes a reset button

=item 0

B<submit> - makes a submit button

=item 0

B<hidden> - makes a hidden field, which the user won't see

=item 0

B<textfield> - makes a text entry field, one row high

=item 0

B<password_field> - same as textfield except contents are bulleted out

=item 0

B<textarea> - makes a big text entry field, several rows high

=item 0

B<checkbox> - makes a standalone check box

=item 0

B<radio> - makes a standalone radio button

=item 0

B<popup_menu> - makes a popup menu, one item can be selected at once

=item 0

B<scrolling_list> - makes a scrolling list, multiple selections possible

=back

Groups of related fields of the following types are recognized:

=over 4

=item 0

B<reset_group> - makes a group of related reset buttons

=item 0

B<submit_group> - makes a group of related submit buttons

=item 0

B<hidden_group> - makes a group of related hidden fields

=item 0

B<textfield_group> - makes a group of related text entry fields

=item 0

B<password_field_group> - makes a group of related password fields

=item 0

B<textarea_group> - makes a group of related big text entry fields

=item 0

B<checkbox_group> - makes a group of related checkboxes

=item 0

B<radio_group> - makes a group of related radio buttons

=item 0

B<popup_menu_group> - makes a group of related popup menus

=item 0

B<scrolling_list_group> - makes a group of related scrolling lists

=back

Other field types aren't intrinsically recognized, but can still be generated as
ordinary html tags by using methods of the HTML::EasyTags class.  A list of all
the valid field types is returned by the valid_field_type_list() method.

=head1 BUGS

There is a known issue where the W3C html validator has problems with the 
generated form code, such as saying hidden fields aren't allowed where they 
are put, as well as saying that "input" tags should be in a pair.  Hopefully a 
solution for these issues will present itself soon and be in the next release.  
However, web browsers like Netscape 4.08 still display the HTML properly.

=head1 OUTPUT FROM SYNOPSIS PROGRAM

=head2 This HTML code is from the first time the program runs:

	<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN">
	<html>
	<head>
	<title>A Simple Example</title>
	</head>
	<body>
	<h1>A Simple Example</h1>
	<form method="post" action="http://nyxmydomain/dir/script.pl">
	<table>
	<input type="hidden" name=".is_submit" value="1" />
	<tr>
	<td>
	*</td> 
	<td>
	<strong>What's your name?:</strong></td> 
	<td>
	<input type="text" name="name" /></td></tr>

	<tr>
	<td></td> 
	<td>
	<strong>What's the combination?:</strong></td> 
	<td>
	<input type="checkbox" name="words" checked="1" value="eenie" />eenie
	<input type="checkbox" name="words" value="meenie" />meenie
	<input type="checkbox" name="words" checked="1" value="minie" />minie
	<input type="checkbox" name="words" value="moe" />moe</td></tr>

	<tr>
	<td></td> 
	<td>
	<strong>What's your favorite colour?:</strong></td> 
	<td>
	<select name="color" size="1">
	<option value="red" />red
	<option value="green" />green
	<option value="blue" />blue

lib/HTML/FormTemplate.pm  view on Meta::CPAN

my $KEY_FIELD_INVAL = 'field_inval';  # a hash w shows invalid user input
my $KEY_INVAL_MARK = 'inval_mark';  # appears by fields with invalid input
my $KEY_ISREQ_MARK = 'isreq_mark';  # appears by fields that must be filled in
my $KEY_PRIVA_MARK = 'priva_mark';  # appears by fields marked as private
my $KEY_EMP_ECH_STR = 'emp_ech_str';  # string to show in place of empty field

# Keys for items in form property $KEY_FIELD_DEFNA:
my $FKEY_TYPE = 'type';  # actual type of input field
my $FKEY_NAME = 'name';  # actual name of input field
my $FKEY_VALUES = 'values';  # actual list selection options
my $FKEY_DEFAULTS = 'defaults';  # default user selections/input
my $FKEY_OVERRIDE = 'override';  # force coded default values to be used
my $FKEY_LABELS = 'labels';  # visible labels of list selection options
my $FKEY_NOLABELS = 'nolabels';  # selection options always have no labels
my $FKEY_TAG_ATTR = 'tag_attr';  # hash of miscellaneous html tag attributes
my $FKEY_MIN_GRP_COUNT = 'min_grp_count';  # num to set count of group members
my $FKEY_LIST = 'list';  # force field groups to ret as list inst of scalar
my $FKEY_LINEBREAK = 'linebreak';  # make field groups join with linebreaks
my $FKEY_TABLE_COLS = 'table_cols';  # put field groups in table with n columns
my $FKEY_TABLE_ROWS = 'table_rows';  # use table with n rows; ign if tcols defin
my $FKEY_TABLE_ACRF = 'table_acrf';  # order fields across first (down if false)
my $FKEY_IS_REQUIRED = 'is_required';  # field must be filled in (any mem)
my $FKEY_REQ_MIN_COUNT = 'req_min_count';  # need min this many grp mem filled in
my $FKEY_REQ_MAX_COUNT = 'req_max_count';  # need max this many grp mem filled in
my $FKEY_REQ_OPT_MATCH = 'req_opt_match';  # bool; check if input valid select opt
my $FKEY_VALIDATION_RULE = 'validation_rule';  # a regular expression for all mem
my $FKEY_VISIBLE_TITLE = 'visible_title';  # main title/prompt for field
my $FKEY_HELP_MESSAGE = 'help_message';   # suggestions for field use
my $FKEY_ERROR_MESSAGE = 'error_message';  # appears when input invalid
my $FKEY_STR_ABOVE_INPUT = 'str_above_input';  # str adjacent before input field
my $FKEY_STR_BELOW_INPUT = 'str_below_input';  # str adjacent after input field
my $FKEY_IS_PRIVATE = 'is_private';   # field not shared with public
my $FKEY_EXCLUDE_IN_ECHO = 'exclude_in_echo';  # always exclude from reports

# List of "special" attributes of a form field definition; these all have formal 
# keys in their names; any attributes not in this list are misc html tag attribs
my @SPECIAL_ATTRIB = ($FKEY_TYPE, $FKEY_NAME, $FKEY_VALUES, $FKEY_DEFAULTS, 
	$FKEY_OVERRIDE, $FKEY_LABELS, $FKEY_NOLABELS, $FKEY_TAG_ATTR, 
	$FKEY_MIN_GRP_COUNT, $FKEY_LIST, $FKEY_LINEBREAK, $FKEY_TABLE_COLS, 
	$FKEY_TABLE_ROWS, $FKEY_TABLE_ACRF, $FKEY_IS_REQUIRED, $FKEY_REQ_MIN_COUNT, 
	$FKEY_REQ_MAX_COUNT, $FKEY_REQ_OPT_MATCH, $FKEY_VALIDATION_RULE,
	$FKEY_VISIBLE_TITLE, $FKEY_HELP_MESSAGE, $FKEY_ERROR_MESSAGE, 
	$FKEY_STR_ABOVE_INPUT, $FKEY_STR_BELOW_INPUT, $FKEY_IS_PRIVATE, 
	$FKEY_EXCLUDE_IN_ECHO);

# Declare handlers for different form field types
my %FIELD_TYPES = ();
my $TKEY_VISIBL = 'visibl';  # a boolean - is this field user-visible or not
my $TKEY_EDITAB = 'editab';  # a boolean - can user set value by typing or select
my $TKEY_SELECT = 'select';  # a boolean - does the user select from list
my $TKEY_FLDGRP = 'fldgrp';  # a boolean - is this a field group or not
my $TKEY_MULTIV = 'multiv';  # a boolean - can field use >1 member of VALUES arg
my $TKEY_METHOD = 'method';  # a scalar - what method to use for html rendering
my $TKEY_PARSER = 'parser';  # always a 3-element array - for parsing definitions
my $TKEY_ATTRIB = 'attrib';  # an array - valid defin attribs for this type

# First set the 6 simpler %FIELD_TYPES atributes: 
# visible, editable, selectable, field group, multivalued, rendering method
{
	foreach my $type (qw( reset submit hidden textfield password_field textarea
			checkbox radio popup_menu scrolling_list )) {
		$FIELD_TYPES{$type} = {
			$TKEY_VISIBL => 1,  # true with 9/10, not hidden
			$TKEY_EDITAB => 1,  # true with 7/10, not reset submit hidden
			$TKEY_SELECT => 0,  # true with 6/10, not check radio popup scroll
			$TKEY_FLDGRP => 0,  # true with 10/10
			$TKEY_MULTIV => 0,  # true with 8/10, not popup scroll
			$TKEY_METHOD => '_make_input_html',  # true with 7/10, n txa pop scr
		};
		$FIELD_TYPES{$type."_group"} = {
			$TKEY_VISIBL => 1,  # true with 9/10, not hidden
			$TKEY_EDITAB => 1,  # true with 7/10, not reset, submit, hidden
			$TKEY_SELECT => 0,  # true with 6/10, not check radio popup scroll
			$TKEY_FLDGRP => 1,  # true with 10/10
			$TKEY_MULTIV => 1,  # true with 10/10
			$TKEY_METHOD => '_make_input_group_html',  # true with 7/10, n ...
		};
	}
	foreach my $type (qw( hidden )) {
		$FIELD_TYPES{$type}->{$TKEY_VISIBL} = 0;
		$FIELD_TYPES{$type."_group"}->{$TKEY_VISIBL} = 0;
	}
	foreach my $type (qw( reset submit hidden )) {
		$FIELD_TYPES{$type}->{$TKEY_EDITAB} = 0;
		$FIELD_TYPES{$type."_group"}->{$TKEY_EDITAB} = 0;
	}
	foreach my $type (qw( checkbox radio popup_menu scrolling_list )) {
		$FIELD_TYPES{$type}->{$TKEY_SELECT} = 1;
		$FIELD_TYPES{$type."_group"}->{$TKEY_SELECT} = 1;
	}
	foreach my $type (qw( popup_menu scrolling_list )) {
		$FIELD_TYPES{$type}->{$TKEY_MULTIV} = 1;
	}
	foreach my $type (qw( textarea )) {
		$FIELD_TYPES{$type}->{$TKEY_METHOD} = '_make_textarea_html';
		$FIELD_TYPES{$type."_group"}->{$TKEY_METHOD} = '_make_textarea_group_html';
	}
	foreach my $type (qw( popup_menu scrolling_list )) {
		$FIELD_TYPES{$type}->{$TKEY_METHOD} = '_make_select_html';
		$FIELD_TYPES{$type."_group"}->{$TKEY_METHOD} = '_make_select_group_html';
	}
}

# Next set the input parser attribute of %FIELD_TYPES: 
{
	foreach my $type (qw( reset submit )) {
		my $names = [ $FKEY_NAME, $FKEY_DEFAULTS ];
		my $rename = {
			'values' => $FKEY_DEFAULTS, value => $FKEY_DEFAULTS,
			labels => $FKEY_DEFAULTS, label => $FKEY_DEFAULTS,
		};
		my $rem = '';
		$FIELD_TYPES{$type}->{$TKEY_PARSER} = [$names, $rename, $rem];
		$FIELD_TYPES{$type."_group"}->{$TKEY_PARSER} = [$names, $rename, $rem];
	}
	foreach my $type (qw( hidden )) {
		my $names = [ $FKEY_NAME, $FKEY_DEFAULTS ];
		my $rename = {
			'values' => $FKEY_DEFAULTS, value => $FKEY_DEFAULTS,
		};
		my $rem = '';
		$FIELD_TYPES{$type}->{$TKEY_PARSER} = [$names, $rename, $rem];
		$FIELD_TYPES{$type."_group"}->{$TKEY_PARSER} = [$names, $rename, $rem];
	}
	foreach my $type (qw( textfield password_field )) {
		my $names = [ $FKEY_NAME, $FKEY_DEFAULTS, 'size', 'maxlength' ];
		my $names_group = [ $FKEY_NAME, $FKEY_DEFAULTS, 
			$FKEY_LINEBREAK, 'size', 'maxlength' ];
		my $rename = {
			'values' => $FKEY_DEFAULTS, value => $FKEY_DEFAULTS,
		};
		my $rem = '';
		$FIELD_TYPES{$type}->{$TKEY_PARSER} = [$names, $rename, $rem];
		$FIELD_TYPES{$type."_group"}->{$TKEY_PARSER} = 
			[$names_group, $rename, $rem];
	}
	foreach my $type (qw( textarea )) {
		my $names = [ $FKEY_NAME, $FKEY_DEFAULTS, 'rows', 'cols' ];
		my $names_group = [ $FKEY_NAME, $FKEY_DEFAULTS, 
			$FKEY_LINEBREAK, 'rows', 'cols' ];
		my $rename = {
			'values' => $FKEY_DEFAULTS, value => $FKEY_DEFAULTS, 
			text => $FKEY_DEFAULTS, columns => 'cols',
		};
		my $rem = $FKEY_DEFAULTS;
		$FIELD_TYPES{$type}->{$TKEY_PARSER} = [$names, $rename, $rem];
		$FIELD_TYPES{$type."_group"}->{$TKEY_PARSER} = 
			[$names_group, $rename, $rem];
	}
	foreach my $type (qw( checkbox radio popup_menu scrolling_list )) {
		my $names = [ $FKEY_NAME, $FKEY_DEFAULTS, $FKEY_VALUES, $FKEY_LABELS ];
		my $names_group = [ $FKEY_NAME, $FKEY_VALUES, $FKEY_DEFAULTS, 
			$FKEY_LINEBREAK, $FKEY_LABELS ];
		my $rename = {
			value => $FKEY_VALUES, checked => $FKEY_DEFAULTS,
			selected => $FKEY_DEFAULTS, on => $FKEY_DEFAULTS,
			label => $FKEY_LABELS, text => $FKEY_LABELS,
		};
		my $rem = $FKEY_LABELS;
		$FIELD_TYPES{$type}->{$TKEY_PARSER} = [$names, $rename, $rem];
		$FIELD_TYPES{$type."_group"}->{$TKEY_PARSER} = 
			[$names_group, $rename, $rem];
	}
	foreach my $type (keys %FIELD_TYPES) {
		my $rename = $FIELD_TYPES{$type}->{$TKEY_PARSER}->[1];
		$rename->{default} = $FKEY_DEFAULTS;
		$rename->{nolabel} = $FKEY_NOLABELS;
		$rename->{force} = $FKEY_OVERRIDE;
	}
	foreach my $type (qw( checkbox_group radio_group )) {
		my $rename = $FIELD_TYPES{$type}->{$TKEY_PARSER}->[1];
		$rename->{cols} = $FKEY_TABLE_COLS;
		$rename->{columns} = $FKEY_TABLE_COLS;
		$rename->{rows} = $FKEY_TABLE_ROWS;
	}
}

# Next set the valid-attributes attribute of %FIELD_TYPES: 
{
	foreach my $type (keys %FIELD_TYPES) {
		my $typerec = $FIELD_TYPES{$type};
		my @attrib = ($FKEY_TYPE, $FKEY_NAME, $FKEY_DEFAULTS, $FKEY_OVERRIDE, 
			$FKEY_TAG_ATTR, $FKEY_IS_REQUIRED, $FKEY_REQ_OPT_MATCH, 
			$FKEY_VALIDATION_RULE, $FKEY_VISIBLE_TITLE, $FKEY_HELP_MESSAGE, 
			$FKEY_ERROR_MESSAGE, $FKEY_STR_ABOVE_INPUT, $FKEY_STR_BELOW_INPUT, 
			$FKEY_IS_PRIVATE, $FKEY_EXCLUDE_IN_ECHO);
		if( $typerec->{$TKEY_FLDGRP} ) {
			push( @attrib, $FKEY_MIN_GRP_COUNT, $FKEY_LIST, $FKEY_LINEBREAK, 
				$FKEY_TABLE_COLS, $FKEY_TABLE_ROWS, $FKEY_TABLE_ACRF, 
				$FKEY_REQ_MIN_COUNT, $FKEY_REQ_MAX_COUNT );
		}
		$typerec->{$TKEY_ATTRIB} = \@attrib;
	}
	foreach my $type (qw( checkbox radio popup_menu scrolling_list )) {
		my @attrib = ($FKEY_VALUES, $FKEY_LABELS);
		push( @{$FIELD_TYPES{$type}->{$TKEY_ATTRIB}}, @attrib );
		push( @{$FIELD_TYPES{$type."_group"}->{$TKEY_ATTRIB}}, @attrib );
	}
	foreach my $type (qw( checkbox radio )) {
		my @attrib = ($FKEY_NOLABELS);
		push( @{$FIELD_TYPES{$type}->{$TKEY_ATTRIB}}, @attrib );
		push( @{$FIELD_TYPES{$type."_group"}->{$TKEY_ATTRIB}}, @attrib );
	}
	foreach my $type (qw( textfield password_field )) {
		my @attrib = ('size', 'maxlength');
		push( @{$FIELD_TYPES{$type}->{$TKEY_ATTRIB}}, @attrib );
		push( @{$FIELD_TYPES{$type."_group"}->{$TKEY_ATTRIB}}, @attrib );
	}
	foreach my $type (qw( textarea )) {
		my @attrib = ('rows', 'cols');
		push( @{$FIELD_TYPES{$type}->{$TKEY_ATTRIB}}, @attrib );
		push( @{$FIELD_TYPES{$type."_group"}->{$TKEY_ATTRIB}}, @attrib );
	}
	foreach my $type (qw( scrolling_list )) {
		my @attrib = ('size', 'multiple');
		push( @{$FIELD_TYPES{$type}->{$TKEY_ATTRIB}}, @attrib );
		push( @{$FIELD_TYPES{$type."_group"}->{$TKEY_ATTRIB}}, @attrib );
	}
}

# Used by _make_input_html() to convert our field types to actual
# <INPUT> tag TYPE arguments.
my %INPUT_TAG_IMPL_TYPE = (
	'reset' => 'reset',
	submit => 'submit',
	hidden => 'hidden',
	textfield => 'text',
	password_field => 'password',
	checkbox => 'checkbox',
	radio => 'radio',
	reset_group => 'reset',
	submit_group => 'submit',
	hidden_group => 'hidden',
	textfield_group => 'text',
	password_field_group => 'password',
	checkbox_group => 'checkbox',
	radio_group => 'radio',
);

######################################################################

=head1 SYNTAX

This class does not export any functions or methods, so you need to call them
using object notation.  This means using B<Class-E<gt>function()> for functions
and B<$object-E<gt>method()> for methods.  If you are inheriting this class for
your own modules, then that often means something like B<$self-E<gt>method()>. 

Methods of this class always "return" their results, rather than printing them
out to a file or the screen.  Not only is this simpler, but it gives the calling
code the maximum amount of control over what happens in the program.  They may
wish to do post-processing with the generated HTML, or want to output it in a
different order than it is generated.

lib/HTML/FormTemplate.pm  view on Meta::CPAN

=head2 textfield( NAME[, DEFAULT[, SIZE[, MAXLENGTH]]] )

	NAME
	[DEFAULT or VALUE]
	SIZE
	MAXLENGTH

This method makes a single text entry field that has NAME for its name and 
DEFAULT as its value.  The field is one line high and is wide enough to display 
SIZE characters at once.  The user can enter a maximum of MAXLENGTH characters 
if that argument is set, or is not limited otherwise.

=head2 password_field( NAME[, DEFAULT[, SIZE[, MAXLENGTH]]] )

	NAME
	[DEFAULT or VALUE]
	SIZE
	MAXLENGTH

This method makes a single password entry field that has NAME for its name and 
DEFAULT as its value.  The arguments are the same as a textfield but the 
displayed value is visually bulleted out by the browser.

=head2 textarea( NAME[, DEFAULT[, ROWS[, COLS]]] )

	NAME
	[DEFAULT or VALUE or TEXT]
	ROWS
	[COLS or COLUMNS]

This method makes a single big text field that has NAME for its name and 
DEFAULT as its value.  The field is ROWS lines high and is wide enough to 
display COLS characters at once.

=head2 checkbox( NAME[, DEFAULT[, VALUE[, LABEL]]] )

	NAME
	VALUE
	[DEFAULT or CHECKED or SELECTED or ON]
	[LABEL or TEXT]
	NOLABEL

This method makes a single checkbox that has NAME for its name and 
VALUE as its value.  VALUE defaults to 'on' if it is not defined.
If DEFAULT is true then the box is checked; otherwise it is not.  
Unless NOLABEL is true, there is always a user-visible text label 
that appears beside the checkbox.  If LABEL is defined then that is used as 
the label text; otherwise NAME is used by default.

=head2 radio( NAME[, DEFAULT[, VALUE[, LABEL]]] )

	NAME
	VALUE
	[DEFAULT or CHECKED or SELECTED or ON]
	[LABEL or TEXT]
	NOLABEL

This method makes a single radio option that has NAME for its name and 
VALUE as its value.  The arguments are the same as for a checkbox.

=head2 popup_menu( NAME, [DEFAULTS], VALUES[, LABELS] )

	NAME
	VALUES
	[DEFAULTS or CHECKED or SELECTED or ON]
	[LABELS or TEXT]

This method makes a single popup menu that has NAME for its name and option 
values populated from the VALUES array ref argument.  VALUES defaults to a 
one-element list containing 'on' if not defined.  If DEFAULTS is a hash ref 
then its keys are matched with elements of VALUES and wherever its values are 
true then the corresponding menu option is selected; otherwise, DEFAULTS is 
taken as a list of option VALUES that are to be selected; by default, no 
options are selected.  Similarly, if LABELS is a hash ref then its keys are 
matched with elements of VALUES and its values provide labels for them; 
otherwise, LABELS is taken as a list of labels which are matched to VALUES 
by their corresponding array indices.  Since options must always have 
user-visible labels, any one for which LABELS is undefined will default to 
using its value as a label.  Note that a popup menu is a simplified case of 
a scrolling list where only one option can be selected and the selected option 
is the only one visible while the field doesn't have the user's focus (the menu 
visually opens up when the field has focus).

=head2 scrolling_list( NAME, [DEFAULTS], VALUES[, LABELS] )

	NAME
	VALUES
	[DEFAULTS or CHECKED or SELECTED or ON]
	[LABELS or TEXT]
	SIZE
	MULTIPLE

This method makes a single scrolling list that has NAME for its name and option 
values populated from the VALUES array ref argument.  The arguments are the same 
as for a popup menu, except that scrolling lists also support SIZE and MULTIPLE.
If MULTIPLE is true then the user can select multiple options; otherwise they 
can select only one.  If SIZE is a number greater than one then that number of 
options is visually displayed at once; this argument defaults to the count of 
elements in VALUES if false.  Note that setting SIZE to 1 will cause this 
field to be a popup menu instead.

=head2 reset_group( NAME[, DEFAULTS] )

	NAME
	[DEFAULTS or VALUES or LABELS]

This method makes a group of related reset buttons, which have NAME in common.
There is one group member for each element in the array ref DEFAULTS.

=head2 submit_group( NAME[, DEFAULTS] )

	NAME
	[DEFAULTS or VALUES or LABELS]

This method makes a group of related submit buttons, which have NAME in common.
There is one group member for each element in the array ref DEFAULTS.

=head2 hidden_group( NAME, DEFAULTS )

	NAME
	[DEFAULTS or VALUES]

This method makes a group of related hidden fields, which have NAME in common.
There is one group member for each element in the array ref DEFAULTS.

=head2 textfield_group( NAME[, DEFAULTS[, LINEBREAK[, SIZE[, MAXLENGTH]]]] )

	NAME
	[DEFAULTS or VALUES]
	SIZE
	MAXLENGTH

This method makes a group of related text entry fields, which have NAME in common.
There is one group member for each element in the array ref DEFAULTS.

=head2 password_field_group( NAME[, DEFAULTS[, LINEBREAK[, SIZE[, MAXLENGTH]]]] )

	NAME
	[DEFAULTS or VALUES]
	SIZE
	MAXLENGTH

This method makes a group of related password entry fields, which have NAME in common.
There is one group member for each element in the array ref DEFAULTS.

=head2 textarea_group( NAME[, DEFAULTS[, LINEBREAK[, ROWS[, COLS]]]] )

	NAME
	[DEFAULTS or VALUES or TEXT]
	ROWS
	[COLS or COLUMNS]

This method makes a group of related big text fields, which have NAME in common.
There is one group member for each element in the array ref DEFAULTS.

=head2 checkbox_group( NAME, VALUES[, DEFAULTS[, LINEBREAK[, LABELS]]] )

	NAME
	VALUES
	[DEFAULTS or CHECKED or SELECTED or ON]
	[LABELS or TEXT]
	NOLABELS

This method makes a group of related checkboxes, which have NAME in common. There
is one group member for each element in the array ref VALUES.  VALUES defaults to
a one-element list containing 'on' if not defined.  If DEFAULTS is a hash ref
then its keys are matched with elements of VALUES and wherever its values are
true then the corresponding box is checked; otherwise, DEFAULTS is taken as a
list of box VALUES that are to be checked; by default, no boxes are checked. 
Similarly, if LABELS is a hash ref then its keys are matched with elements of
VALUES and its values provide labels for them; otherwise, LABELS is taken as a
list of labels which are matched to VALUES by their corresponding array indices.
Unless NOLABELS is true, there is always a user-visible text label that appears
beside each checkbox.  Any checkbox for which LABELS is undefined will default to
using its value for a label.

=head2 radio_group( NAME, VALUES[, DEFAULTS[, LINEBREAK[, LABELS]]] )

	NAME
	VALUES
	[DEFAULTS or CHECKED or SELECTED or ON]
	[LABELS or TEXT]
	NOLABELS

This method makes a group of related radio options, which have NAME in common.
There is one group member for each element in the array ref VALUES.
The arguments are the same as for a checkbox_group.

=head2 popup_menu_group( NAME, VALUES[, DEFAULTS[, LINEBREAK[, LABELS]]] )

	NAME
	VALUES
	[DEFAULTS or CHECKED or SELECTED or ON]
	[LABELS or TEXT]

This method makes a group of related popup menus, which have NAME in common.
There is one group member for each element in the array ref DEFAULTS.

=head2 scrolling_list_group( NAME, VALUES[, DEFAULTS[, LINEBREAK[, LABELS]]] )

	NAME
	VALUES
	[DEFAULTS or CHECKED or SELECTED or ON]
	[LABELS or TEXT]
	SIZE
	MULTIPLE

This method makes a group of related scrolling lists, which have NAME in common.
There is one group member for each element in the array ref DEFAULTS.

=cut

######################################################################

sub reset          { $_[0]->_proxy( 'reset',          \@_ ) }
sub submit         { $_[0]->_proxy( 'submit',         \@_ ) }
sub hidden         { $_[0]->_proxy( 'hidden',         \@_ ) }
sub textfield      { $_[0]->_proxy( 'textfield',      \@_ ) }
sub password_field { $_[0]->_proxy( 'password_field', \@_ ) }
sub textarea       { $_[0]->_proxy( 'textarea',       \@_ ) }
sub checkbox       { $_[0]->_proxy( 'checkbox',       \@_ ) }
sub radio          { $_[0]->_proxy( 'radio',          \@_ ) }
sub popup_menu     { $_[0]->_proxy( 'popup_menu',     \@_ ) }
sub scrolling_list { $_[0]->_proxy( 'scrolling_list', \@_ ) }

sub reset_group          { $_[0]->_proxy( 'reset_group',          \@_ ) }
sub submit_group         { $_[0]->_proxy( 'submit_group',         \@_ ) }
sub hidden_group         { $_[0]->_proxy( 'hidden_group',         \@_ ) }
sub textfield_group      { $_[0]->_proxy( 'textfield_group',      \@_ ) }
sub password_field_group { $_[0]->_proxy( 'password_field_group', \@_ ) }
sub textarea_group       { $_[0]->_proxy( 'textarea_group',       \@_ ) }
sub checkbox_group       { $_[0]->_proxy( 'checkbox_group',       \@_ ) }
sub radio_group          { $_[0]->_proxy( 'radio_group',          \@_ ) }
sub popup_menu_group     { $_[0]->_proxy( 'popup_menu_group',     \@_ ) }
sub scrolling_list_group { $_[0]->_proxy( 'scrolling_list_group', \@_ ) }

######################################################################

=head1 METHODS FOR MAKING TOPS AND BOTTOMS OF HTML FORMS

Besides the field-type methods above, these can be used to make pieces of forms 
at a time giving you more control of the whole form layout.

=head2 start_form([ METHOD[, ACTION] ])

This method returns the top of an HTML form.  It consists of the opening 'form'
tag.  This method can take its optional two arguments in either named or
positional format; in the first case, the names look the same as the positional
placeholders above, except they must be in lower case.  The two arguments, METHOD
and ACTION, are scalars which respectively define the method that the form are
submitted with and the URL it is submitted to.  If either argument is undefined,
then the appropriate scalar properties of this object are used instead, and their
defaults are "POST" for METHOD and "127.0.0.1" for ACTION.  See the
form_submit_url() and form_submit_method() methods to access these properties.

=cut

######################################################################

sub start_form {
	my $self = shift( @_ );
	my $rh_params = $self->params_to_hash( \@_, $self->{$KEY_AUTO_POSIT}, 
		['method', 'action'], undef, undef, 1 );
	$rh_params->{'method'} ||= $self->{$KEY_SUBMIT_MET};
	$rh_params->{'action'} ||= $self->{$KEY_SUBMIT_URL};
	my $tagmaker = $self->{$KEY_TAG_MAKER};
	return( $tagmaker->make_html_tag( 'form', $rh_params, undef, 'start' ) );
}

######################################################################

=head2 end_form()

This method returns the bottom of an HTML form.  It consists of the closing
'form' tag.

=cut

######################################################################

sub end_form {
	my $self = shift( @_ );
	my $tagmaker = $self->{$KEY_TAG_MAKER};
	return( $tagmaker->make_html_tag( 'form', {}, undef, 'end' ) );
}

######################################################################

=head2 form_submit_url([ VALUE ])

This method is an accessor for the scalar "submit url" property of this object,
which it returns.  If VALUE is defined, this property is set to it.  This
property defines the URL of a processing script that the web browser would use to
process the generated form.  The default value is "127.0.0.1".

lib/HTML/FormTemplate.pm  view on Meta::CPAN

	} else {
		my $wanted = $defin->fetch_value( $FKEY_MIN_GRP_COUNT );
		my $have = @{$params{value}};
		if( $have < $wanted ) {
			push( @{$params{value}}, [map { '' } (1..($wanted - $have))] );
		}
	}

	# Make the field HTML and return it.

	my $tagmaker = $self->{$KEY_TAG_MAKER};
	return( $tagmaker->make_html_tag_group( 'input', \%params, \@labels, 1 ) );
}

######################################################################
# _make_select_html( DEFIN )
# This private method assists _make_field_html() by specializing in making 
# single "<SELECT></SELECT>" form tags, which include a group of <OPTION> tags.

sub _make_select_html {
	my ($self, $defin) = @_;

	# Set up default attributes for the option tags.

	my $ra_values = $defin->fetch( $FKEY_VALUES ) || ['on'];

	# The definition property "defaults" is handled the same way as the 
	# same property for checkbox groups, so refer to the documentation there.

	my $ra_defaults = $defin->fetch( $FKEY_DEFAULTS ) || [];  # array
	if( ref( $ra_defaults->[0] ) eq 'HASH' ) {
		$ra_defaults = $ra_defaults->[0];  # hash
	}
	if( ref( $ra_defaults ) eq 'ARRAY' ) {
		$ra_defaults = {map { ( $_ => 1 ) } @{$ra_defaults}};  # hash
	}
	$ra_defaults = [map { $ra_defaults->{$_} } @{$ra_values}];  # ary

	# The definition property "labels" is handled the same way as the 
	# same property for checkbox groups, so refer to the documentation there.

	my $ra_labels = $defin->fetch( $FKEY_LABELS ) || [];  # array
	if( ref( $ra_labels->[0] ) eq 'HASH' ) {
		$ra_labels = $ra_labels->[0];  # hash
		$ra_labels = [map { $ra_labels->{$_} } @{$ra_values}];  # ary
	}
	foreach my $index (0..$#{$ra_values}) {
		unless( defined( $ra_labels->[$index] ) ) {
			$ra_labels->[$index] = $ra_values->[$index];
		}
	}

	# Set up default attributes common to all select tags.

	my %params = (
		%{$defin->fetch_value( $FKEY_TAG_ATTR )},
		name => $defin->fetch_value( $FKEY_NAME ),
	);
	$params{size} ||= scalar( @{$ra_values} );

	# Set up attributes that are unique to popup menus.  They are 
	# different in that only one item can be displayed at a time, and 
	# correspondingly the user can only choose one item at a time.

	if( $defin->fetch_value( $FKEY_TYPE ) eq 'popup_menu' ) {
		$params{size} = 1;
		$params{multiple} = 0;
	}

	# Make the field HTML and return it.

	my $tagmaker = $self->{$KEY_TAG_MAKER};
	return( join( '', 
		$tagmaker->make_html_tag( 'select', \%params, undef, 'start' ),
		@{$tagmaker->make_html_tag_group( 'option', { value => $ra_values, 
			selected => $ra_defaults },	$ra_labels, 1 )},
		$tagmaker->make_html_tag( 'select', {}, undef, 'end' ),
	) );
}

######################################################################
# _make_select_group_html( DEFIN )
# This private method assists _make_field_html() by specializing in making 
# a group of "<SELECT></SELECT>" form tags.

sub _make_select_group_html {
	my ($self, $defin) = @_;

	# Set up default attributes for the option tags.

	my $ra_values = $defin->fetch( $FKEY_VALUES ) || ['on'];

	# The definition property "labels" is handled the same way as the 
	# same property for checkbox groups, so refer to the documentation there.

	my $ra_labels = $defin->fetch( $FKEY_LABELS ) || [];  # array
	if( ref( $ra_labels->[0] ) eq 'HASH' ) {
		$ra_labels = $ra_labels->[0];  # hash
		$ra_labels = [map { $ra_labels->{$_} } @{$ra_values}];  # ary
	}
	foreach my $index (0..$#{$ra_values}) {
		unless( defined( $ra_labels->[$index] ) ) {
			$ra_labels->[$index] = $ra_values->[$index];
		}
	}

	# Set up default attributes common to all select tags.

	my %params = (
		%{$defin->fetch_value( $FKEY_TAG_ATTR )},
		name => $defin->fetch_value( $FKEY_NAME ),
	);
	$params{size} ||= scalar( @{$ra_values} );

	# Set up attributes that are unique to popup menus.  They are 
	# different in that only one item can be displayed at a time, and 
	# correspondingly the user can only choose one item at a time.

	if( $defin->fetch_value( $FKEY_TYPE ) eq 'popup_menu_group' ) {
		$params{size} = 1;
		$params{multiple} = 0;
	}

	# Make sure we have a list of valid default values, and hash of said also.
	# The valid list is an intersection of current defaults and field values.

	my @defaults = $defin->fetch( $FKEY_DEFAULTS );
	my $rh_defaults = $defaults[0];
	unless( ref( $rh_defaults ) eq 'HASH' ) {
		$rh_defaults = {map { ( $_ => 1 ) } @defaults};
	}
	@defaults = grep { $rh_defaults->{$_} } @defaults;

	# Make sure we have enough group members.

	my $wanted = $defin->fetch_value( $FKEY_MIN_GRP_COUNT );
	my $have = @defaults;
	if( $have < $wanted ) {
		push( @defaults, [map { '' } (1..($wanted - $have))] );
	}

	# Make the field HTML and return it.

	my $tagmaker = $self->{$KEY_TAG_MAKER};
	my @field_list = ();
	foreach my $default (@defaults) {
		my $ra_defaults = [map { $_ eq $default } @{$ra_values}];
		push( @field_list, join( '', 
			$tagmaker->make_html_tag( 'select', \%params, undef, 'start' ),
			@{$tagmaker->make_html_tag_group( 'option', { value => $ra_values, 
				selected => $ra_defaults },	$ra_labels, 1 )},
			$tagmaker->make_html_tag( 'select', {}, undef, 'end' ),
		) );
	}
	return( \@field_list );
}

######################################################################
# _join_field_group_html( DEFIN, LIST )
# This private method assists _make_field_html() by joining together a list of 
# field group html, LIST, according to the field preferences in DEFIN.  This 
# method will check a series of field definition properties in order until it 
# finds one that is true; it then joins the fields in accordance with that one.
# These are the properties in order of precedence: 1. 'list' causes the LIST 
# elements to be returned as is (in an array ref), one field per element; 
# 2. 'linebreak' creates a scalar with group members delimited by <br /> tags; 
# 3. 'table_cols' or 'table_rows' causes the group members to be formatted into 
# an HTML table, returned as a scalar; 4. otherwise, we join on ''.

sub _join_field_group_html {
	my ($self, $defin, $ra_tag_html) = @_;

	# First, see if definition wants a list returned.

	$defin->fetch_value( $FKEY_LIST ) and return( $ra_tag_html );

	# Second, see if definition wants linebreak-delimited fields.

	$defin->fetch_value( $FKEY_LINEBREAK ) and 
		return( join( '<br />', @{$ra_tag_html} ) );

	# Third, see if definition wants fields returned in an HTML table.

	my $cols = $defin->fetch_value( $FKEY_TABLE_COLS );  # 3 lines chg 2.01
	my $rows = $defin->fetch_value( $FKEY_TABLE_ROWS );
	my $acr_first = $defin->fetch_value( $FKEY_TABLE_ACRF );
	if( $cols or $rows ) {
		return( $self->make_table_from_list( $ra_tag_html, 
			$cols, $rows, $acr_first ) );
	}

	# If none of the above, then return fields concatenated as is.

	return( join( '', @{$ra_tag_html} ) );
}

######################################################################

1;
__END__

=head1 PROPERTIES OF FORM FIELD DEFINITIONS

The following sections detail all of the properties of form field definitions 
that are used by this class.  That is, if a field definition were a hash, then 
these properties are the keys and values.  The term "argument" may be used here 
to refer to properties, since they are arguments to the field-type methods.

=head1 PROPERTIES FOR BASIC FIELD HTML

These properties are the standard ones for making form field html regardless of 
the method you use to request that HTML; they are used with the field-type 
methods and with field_definitions() and with field_html_from_defin().
Please see the METHODS NAMED AFTER FIELD-TYPES section above for more detail on 
usage of these basic properties as well as the circumstances where certain 
aliases are or are not valid.  The singular and plural versions of [value, 
default, label, nolabel] are always aliases for each other.

=head2 type

This string argument specifies which kind of field we are going to make, and it 
must be in the list given in RECOGNIZED FORM FIELD TYPES.  This property 
defaults to default_field_type() if not valid.  This property is used to 
determine how to handle all of the other properties, so it is important to have.  
The only time that you don't use this property is with the field-type methods, 
because the field type is explicitely provided as the method name itself.

=head2 name

This string argument is the name of the field we will make.  This is needed for 
matching up user input with the fields it came from on a form submission, so we 
can do validation, error correction, and reporting.  This property defaults to 
default_field_name() if not provided.  Users do not see this name, but the web 
browsers care about it.

=head2 values

This list argument is used with selection-type fields to set the list of options 
that the user can select from, and can be used with checkbox, radio, popup menu, 
scrolling list, and groups of each.  This property defaults to 'on' if not set 
for most field types, and to NAME for single checkboxes and radio buttons.

=head2 defaults

This list/hash argument provides default user input for the field, which could 
be from actual live or stored user input, or could be coded default values for 
the field.  Aliases include [values, labels, text, checked, selected, on] 
depending on the field type.

=head2 override

When this boolean argument is true, it ensures that coded DEFAULT values are 
always used instead of persistant user input for subsequent form invocations.
You can use FORCE as an alias for OVERRIDE with all field types.

=head2 labels

This list/hash argument provides user-visible text that appears in 
selection-type fields; these list elements correspond to VALUES elements.
If this argument is not provided, the actual VALUES are used as labels.

=head2 nolabels

This boolean argument suppresses any field value labels from showing with single 
or groups of checkboxes or radio buttons.

=head2 tag_attr

This optional argument is a hash ref containing miscellaneous html attributes 
which will be inserted into new form field tags as-is; for field groups these 
are replicated across all of the group members just as NAME is.  Similarly, 
any named arguments which are not explicitely recognized by this class are 
treated as html tag attributes as well; note that in the case of a name 
conflict, the attributes that are pre-existing in TAG_ATTR have lower precedence.
Use TAG_ATTR if you want to pass tag attributes which begin with a "-", as such 
prefixes are removed from normal named method arguments.  Under most 
circumstances, any [size, maxlength, rows, cols/columns, multiple] arguments 
are moved into here.

=head2 min_grp_count

When this numerical argument is defined, methods that make form group fields will
make sure that there are at least this many group members; otherwise, there are
as many group members made as the greater of 1 and the count of DEFAULTS.  This
argument can not be used with checkbox_group and radio_group since they always
have as many group members as VALUES elements.

=head2 list

When this boolean argument is true, methods that make form field groups will 
return their results in an array ref rather than a string, with the html for 
each group member in a separate array element.  Using this lets you delimit the 
fields in any way you choose, rather than only the ways this class understands.

=head2 linebreak

When this boolean argument is true, methods that make form field groups will 
join the html for all group members into a string with the members being 
delimited by linebreaks, that is, '<br />' tags.



( run in 1.021 second using v1.01-cache-2.11-cpan-364913b4093 )