package Maypole::Model::CDBI::AsForm;

use 5.006;

use strict;
use warnings;

use base 'Exporter';
use Data::Dumper;
use Class::DBI::Plugin::Type ();
use HTML::Element;

our $OLD_STYLE = 0;
# pjs  --  Added new methods to @EXPORT 
our @EXPORT = qw( to_cgi to_field _to_textarea _to_textfield _to_select
				  type_of _to_foreign_inputs _to_enum_select _to_bool_select
				  to_select_from_many _to_select_from_related hasmany_class
				  _to_hidden _rename_foreign_input _to_readonly
                                  make_param_foreign make_hidden_elmnt make_hidden_elmnt 
                                  a_select_box unselect_element do_select search_inputs);
				  
				  

our $VERSION = '2.41'; 
# PJS VERSION .05
# Changes :
# 08-09-05 - fixed broken has_a select box 
#          - fiked some docs
#          - _to_foreign_inputs now takes 3 positional parameters 
#            (accssr,  fields, accssr_meta_info)


=head1 NAME

Maypole::Model:CDBI::AsForm - Produce HTML form elements for database columns

=head1 SYNOPSIS

    package Music::CD;
    use Maypole::Model::CDBI::AsForm;
    use base 'Class::DBI';
    use CGI;
    ...

    sub create_or_edit {
        my $class = shift;
        my %cgi_field = $class->to_cgi;
        return start_form,
               (map { "<b>$_</b>: ". $cgi_field{$_}->as_HTML." <br>" } 
                    $class->Columns),
               end_form;
    }

    # <form method="post"...>
    # Title: <input type="text" name="Title" /> <br>
    # Artist: <select name="Artist"> 
    #           <option value=1>Grateful Dead</option>
    #           ...
    #         </select>
    # ...
    # </form>

=head1 DESCRIPTION

This module helps to generate HTML forms for creating new database rows
or editing existing rows. It maps column names in a database table to
HTML form elements which fit the schema. Large text fields are turned
into textareas, and fields with a has-a relationship to other
C<Class::DBI> tables are turned into select drop-downs populated with
objects from the joined class.

=head1 METHODS

The module is a mix-in which adds two additional methods to your
C<Class::DBI>-derived class. 


=head2 search_inputs

Returns hashref of search inputs elements to use in cgi.

Uses fields specified in search_fields, makes foreign inputs if necessary.

=cut

sub search_inputs {
  my ($class, $r) = @_;
  warn "In model search_inputs " if $class->model_debug;
  $class = ref $class || $class;
  #my $accssr_class = { $class->accessor_classes };
  my %cgi;
  my $sfs = $class->search_fields;

  foreach my $field ( @$sfs ) {
    if ( ref $field eq "HASH" ) { # foreign search fields
      my ($accssr, $cols)  = each %$field;
      unless (  @$cols ) {
				# default to search fields for related 
				#$cols =  $accssr_class->{$accssr}->search_fields;
	die ("$class search_fields error: Must specify at least one column to search in the foreign object named '$accssr'");
      }
      my $fcgi  = $class->_to_foreign_inputs($accssr, $cols);
      # unset the default values for a select box
      foreach (keys %$fcgi) {
	$class->unselect_element($fcgi->{$_});
      }
      $cgi{$accssr} = $fcgi;
      #warn "Searchy inputs for field $field is " . Dumper($cgi{$accssr});
    } else {  
      $cgi{$field} = $class->to_field($field);
      $class->unselect_element($cgi{$field});
    }
  }
  return \%cgi;
}


=head2 do_select

Retrieves object selected from a select box and puts in $r->objects[0].
The select box input must be named the same as the primary key.

NOTE only works with tables with single primary key for now.

=cut

sub do_select {
    my ($self, $r) = @_;
	$r->objects([ $self->retrieve($r->params->{$self->primary_column}) ]);
	$r->template('view');
}


=head2 unselect_element

Unselects all options in a HTML::Element of type select.
It does nothing if element is not a select element.

=cut

sub unselect_element {
	my ($self, $el) = @_;
	#unless (ref $el eq 'HTML::Element') { 
	#$self->_croak ('Need an HTML::Element to unselect. You gave a ' . Dumper($el)); }
	if ($el->tag eq 'select') {
		foreach my $opt ($el->content_list) {
			$opt->attr('selected', undef) if $opt->attr('selected');
		}
	}
}


# make a select box from args
sub a_select_box {
	my ($self, $name, $vals, $selected_val, $contents) = @_;
	die "Usage: Need a name and array ref of values to make a select boxes" unless ($name && $vals);
	$selected_val ||= "";
	$contents ||= $vals ;

	my $a = HTML::Element->new('select', 'name' => $name);
	my $i = 0;
	my $c;
	foreach my $v ( @$vals ) {
		my $opt = HTML::Element->new('option', 'value' => $v);
		$opt->attr('selected' => 'selected') if $v eq $selected_val;
	 	$c = $contents->[$i++] || $v;
		$opt->push_content($c);
		$a->push_content($opt);
	}
	$a;
}


=head2 make_hidden_elmnt

Makes a hidden HTML::Element and puts it in template_args{hidden_elements}
$model->make_hidden_elmnt($name, $val);

=cut

sub make_hidden_elmnt {
	my ($self, $r, $col, $val) = @_;
	my $elmnt = HTML::Element->new('input', 'type'=>'hidden', 'name'=>$col, 'value'=>$val);

	$r->{template_args}{hidden_elements} ||= [];  
	push @{ $r->{template_args}{hidden_elements} }, $elmnt;
}



=head2 make_param_foreign

Makes a new foreign parameter out of parameter and accessor
Just puts accssr__FOREIGN__ in front of param name 

=cut

sub make_param_foreign {
	my ($self, $r, $p, $accssr) = @_;
	$r->params->{$accssr . '__FOREIGN__' . $p} = $r->params->{$p};
}

=head2 to_cgi

This returns a hash mapping all the column names of the class to
HTML::Element objects representing form widgets.

pjs -- added a columns list argument to specify which columns to make
inputs for.

=cut

sub to_cgi {
	my ($class, @columns) = @_; # pjs -- added columns arg
	@columns = $class->columns unless (@columns);
	map { $_ => $class->to_field($_) } @columns;
}


=head2 to_field($field [, $how])

This maps an individual column to a form element. The C<how> argument
can be used to force the field type into one of C<textfield>, C<textarea>
or C<select>; you can use this is you want to avoid the automatic detection
of has-a relationships.

# pjs 
   -- added support for enum and bool.   Note for enum and bool you need 
      a better column_type method than the Plugin::Type ' s as it won't work 
      if you are using MySQL. I have not tried others.  
      See those method's docs below.
   -- Foreign inputs for might_have, must_have, has_own, and has_many(i think).
   -- Really any relationship except has_a and is_a as has_a gets a select box
      and is_a are not considered foreign. 
   -- Note a good column_type sub can be 
      used to get the correct type for is_a columns.
   -- More efficient _to_select -- no object creation.
   -- Attempts to set default value in field for you using a "column_default" 
      method you write yourself or your CDBI driver like mysql writes.
   -- _to_hidden 

=cut

sub to_field {
	my ($self, $field, $how) = @_;
	my $class = ref $self || $self;
	if ($how and $how =~ /^(text(area|field)|select)$/) {
		no strict 'refs';
		my $meth = "_to_$how";
		return $self->$meth($field);
	}

	my $meta = $self->meta_info;
	my ($rel_type) = grep{ defined $meta->{$_}{$field} } keys %$meta;
	$rel_type ||= ''; 
        my $fclass = $rel_type ? $meta->{$rel_type}{$field}{foreign_class} : '';
	my $fclass_is_cdbi = $fclass ? $fclass->isa('Class::DBI') : 0;

	# maybe has_a select 
    return  $self->_to_select($field, $fclass) if $rel_type eq 'has_a' and
		$fclass_is_cdbi;

	# maybe foreign inputs 
	my %local_cols = map { $_ => 1 } $self->columns; # includes is_a cols
	if ($fclass_is_cdbi and (not $local_cols{$field} or $rel_type eq 'has_own'))
	{
		return $self->_to_foreign_inputs($field, undef, $meta->{$rel_type}{$field});
	}
			
	# the rest 
	my $type;
    if ($class->can('column_type')) {
		$type = $class->column_type($field);
	}	
	else {
    	# Right, have some of this
    	eval "package $class; Class::DBI::Plugin::Type->import()";
    	$type = $class->column_type($field);
	}

	#return $self->_to_textfield($field)
	#	if $type  and $type =~ /(var)?char/i;  #common type
	return $self->_to_textarea($field)
		if $type and $type =~ /^(TEXT|BLOB)$/i;
	return $self->_to_enum_select($field, $type)  
		if $type and  $type =~ /^ENUM\((.*?)\)$/i; 
	return $self->_to_bool_select($field, $type)
		if $type and  $type =~ /^BOOL/i; 
	return $self->_to_readonly($field)
	    if $type and $type =~ /^readonly$/i;
	return $self->_to_textfield($field);
}

sub _to_textarea {
	my ($self, $col) = @_;
 	# pjs added default	
	my $a =
		HTML::Element->new("textarea", name => $col, rows => "3", cols => "22");
	my $val;
	if (ref $self) { 
		$val = $self->$col; 
	}
	else { 
		$val = eval {$self->column_default($col);}; 
	    $val = '' unless defined $val;  
	}
	$a->push_content($val);
	$OLD_STYLE && return $a->as_HTML;
	$a;
}

sub _to_textfield {
	my ($self, $col) = @_;
 	# pjs added default	
	my $val;
	if (ref $self) { 
		$val = $self->$col; 
	}
	else { 
		$val = eval {$self->column_default($col);}; 
	    $val = '' unless defined $val;  
	}

	my $a = HTML::Element->new("input", type => "text", name => $col);
	$a->attr("value" => $val);
	$OLD_STYLE && return $a->as_HTML;
	$a;
}

# pjs 
# -- Rewrote this to be efficient -- no object creation. 
# -- Added option for CDBI classes to specify a limiting clause
# via "has_a_select_limit". 
# -- Added selected argument to set a selected 

sub _to_select {
    my ($self, $col, $hint, $selected) = @_;
	my $has_a_class;
	if (not $col) { # class is making select box of self
		$has_a_class = ref $self || $self;
		$col = $self->primary_column;
	}
	else {
		$has_a_class = $hint || $self->__hasa_rels->{$col}->[0];
	}

	$selected ||= {};
	if (ref $self and my $id = eval { $self->$col->id }) {
		$selected->{$id} = 1;  
	}
	#pjs  Possible sql limiting clause ("WHERE acol = 'X' " or "LIMIT 10", etc) 
	my $select_box_limit = eval { $self->has_a_select_limit->{$col} } || '' ;  

	# Get columns to appear in select box options on forms. 
	# TODO -- there is maybe a good idiom for this.
	my @select_box_cols;
	@select_box_cols = $has_a_class->columns('SelectBox');
	@select_box_cols = $has_a_class->columns('Stringify') 
											unless @select_box_cols;
	@select_box_cols = $has_a_class->_essential 
											unless @select_box_cols;
	unshift @select_box_cols, $has_a_class->columns('Primary'); 
	my $sql = "SELECT " . join( ', ', @select_box_cols) . " FROM " . 
		      $has_a_class->table . " " . $select_box_limit;
	my $opts_data = $self->db_Main->selectall_arrayref($sql); 
	
    my $a = HTML::Element->new("select", name => $col);
    for (@$opts_data) { 
		my $id = shift @$_;
        my $opt = HTML::Element->new("option", value => $id );
        $opt->attr("selected" => "selected") if $selected->{$id}; 
		my $content = eval {$has_a_class->stringify_selectbox($_);} || 
					  join(' ', @$_);
        $opt->push_content( $content );
        $a->push_content($opt);
    }
    $OLD_STYLE && return $a->as_HTML;
    $a;
}

# Makes a readonly input box out of column's value
# Currently object method only
sub _to_readonly {
	my ($self, $col, $val) = @_;
	unless (defined $val) {
		$self->_croak("Cannot call _to_readonly on class without value arg.")
			unless ref $self;
		$val = $self->$col;
	}
	my $a = HTML::Element->new('input', 'type' => 'text', readonly => '1',
		'name' => $col, 'value'=>$val);
	$OLD_STYLE && return $a->as_HTML;
	$a;
}

=head2 _to_enum_select

$sel_box = $self->_to_enum_select($column, "ENUM('Val1','Val2','Val3')");

Returns an enum select box given a column name and an enum string.
NOTE: The Plugin::Type does not return an enum string for mysql enum columns.
This will not work unless you write your own column_type method in your model.

=cut

sub _to_enum_select {
    my ($self, $col, $type) = @_;
	$type =~ /ENUM\((.*?)\)/i;
	(my $enum = $1) =~ s/'//g;
	my @enum_vals = split /\s*,\s*/, $enum;

    my $a = HTML::Element->new("select", name => $col);
    for ( @enum_vals ) { 
        my $sel = HTML::Element->new("option", value => $_); 
        $sel->attr("selected" => "selected") if ref $self 
                                                and eval { $self->$col eq $_ };
        $sel->push_content($_); 
        $a->push_content($sel);
    }
    $OLD_STYLE && return $a->as_HTML;
    $a;
}


=head2 _to_bool_select

  my $sel = $self->_to_bool_select($column, $bool_string);

This  makes select input for boolean column.  You can provide a
bool string of form: Bool('zero','one') and those are used for option
content. Onthervise No and Yes are used.
TODO -- test without bool string.

=cut

sub _to_bool_select {
    my ($self, $col, $type) = @_;
	my @bool_text = ('No', 'Yes');	
	if ($type =~ /BOOL\((.+?)\)/i) {
		(my $bool = $1) =~ s/'//g;
		@bool_text = split /,/, $bool;
	}
	my $one= ref $self ? eval {$self->$col;} : $self->column_default($col);
    my $a = HTML::Element->new("select", name => $col);
    my ($opt0, $opt1) = ( HTML::Element->new("option", value => 0),
						  HTML::Element->new("option", value => 1) ); 
    $opt0->attr("selected" => "selected") if not $one; 
    $opt0->push_content($bool_text[0]); 
    $opt1->attr("selected" => "selected") if $one; 
    $opt1->push_content($bool_text[1]); 
    $a->push_content($opt0, $opt1);
    $OLD_STYLE && return $a->as_HTML;
    $a;
}


=head2 _to_hidden($name, $value)

This makes a hidden html element. Give it a name and value.

=cut
sub _to_hidden {
    my ($self, $name, $val) = @_;
	return HTML::Element->new('input', 'type' => 'hidden', 
	                          'name' => $name, 'value'=>$val
	);
}



=head2 _to_foreign_inputs

$html_els = $class_or_obj->_to_foreign_inputs($accssr, [$fields, $accssr_meta]);

Get inputs for the accessor's class.  Pass an array ref of fields to get
inputs for only those fields. Otherwise display_columns or all columns is used. 
If you have the meta info handy for the accessor you can pass that too.

TODO make AsForm know more about the request like what action we are doing
so it can use edit columns or search_columns

NOTE , this names the foreign inputs is a particular way so they can be
processed with a general routine and so there are not name clashes.

=cut

sub _to_foreign_inputs {
	my ($self, $accssr, $fields, $accssr_meta) = @_;
	if (!$accssr_meta) {
		my $class_meta = $self->meta_info;
		my ($rel_type) = grep { defined $class_meta->{$_}->{$accssr} }
			keys %$class_meta;
		$self->_croak( "No relationship for accessor $accssr") if not $rel_type;
		$accssr_meta = $class_meta->{$rel_type}->{$accssr};
	}

	my $classORobj = ref $self && ref $self->$accssr ? $self->$accssr : $accssr_meta->{foreign_class};
	
	unless ($fields) { 	
		$fields = $classORobj->can('display_columns') ? 
			[$classORobj->display_columns] : [$classORobj->columns];
	}
	
	# Ignore our fkey in them to  prevent infinite recursion 
	my $me 	        = eval {$accssr_meta->{args}{foreign_column}} || '';  
	my $constrained = $accssr_meta->{args}{constraint}; 
	my %inputs;
	foreach ( @$fields ) {
		next if $constrained->{$_} || ($_ eq $me); # don't display constrained
		$inputs{$_} =  $classORobj->to_field($_);
	}

	# Make hidden inputs for constrained columns unless we are editing object
	# TODO -- is this right thing to do?
	unless (ref $classORobj) {
		$inputs{$_} = $classORobj->_to_hidden($_, $constrained->{$_}) 
			foreach ( keys %$constrained );  
	}
	$self->_rename_foreign_input($accssr, \%inputs);
	return \%inputs;
}

=head2 _rename_foreign_input

_rename_foreign_input($html_el_or_hash_of_them); # changes made by reference

Recursively renames the foreign inputs made by to_foreign_inputs so they 
can be processed generically.  The format is "accessor__AsForeign_colname". 

So if an Employee is a Person who has own  Address and you call 

  Employee->to_field("person")  
  
then you will get inputs for Address named like this: 

  person__AsForeign__address__AsForeign__street
  person__AsForeign__address__AsForeign__city
  person__AsForeign__address__AsForeign__state  
  person__AsForeign__address__AsForeign__zip  

And the processor would know to create this address, put the address id in
person address slot, create the person and put the address id in the employee
before creating the employee. 

=cut

sub _rename_foreign_input {
	my ($self, $accssr, $input) = @_;
	if ( ref $input ne 'HASH' ) {
		my $new_name = $accssr . "__AsForeign__" . $input->attr('name');
		$input->attr( name => $new_name );
	}
	else {
		$self->_rename_foreign_input($accssr, $input->{$_}) 
			foreach (keys %$input);
	}
}


# pjs 

=head2 to_select_from_many 


Usage:  $object->to_select_from_many($accessor|$objects_to_select_from, [$element_name], [$options]);

CD->has_many( 'songs' => "Songs" );
... in some nearby piece of code:
my $cd = CD->retrieve($id);
my $select_song_html = $cd->to_select_from_many('songs');
print "<h1>Choose your Favorite song from $cd</h1>";
print $select_song_html.as_XML;
return;

# OR if you only want to select from a group of objects

my @favorites = $cd->favorite_songs;
my $select_from_favorites = $cd->to_select_from_many(\@favorites);

This an object method that makes a select box out of  the objects related to this object by a has_many relationship.  The select box only allows one selection.
The multiple attribute can easily be added if needed to the element returned :
$this_element->attr('multiple', 'multiple');

You can pass an array ref of objects to select from instead of the class accessor name if you already have the objects to select from. 

Also, you can pass the name you want the element to have as a second argument.
The default is the primary key name (as returned by primary_column) of the firstobject that is being selected from.

If related class has a multi column pk, this dies for now.#strange things may happen as primary_column method is used.

=cut


sub to_select_from_many {
    my ($self, $accessor, $elmnt_name) = @_;
	my $objs = ref $accessor eq "ARRAY" ? $accessor : [$self->$accessor];
	my $rel_class = ( @$objs ) ? ref $objs->[0] : 
	                eval{$self->hasmany_class($accessor)}; 

	$elmnt_name = eval {$rel_class->primary_column} ||  "__AF_TSFM_OBJS__" 
		unless $elmnt_name;

	return  _to_select_from_objs($objs, $elmnt_name);
    
}

=head2 _to_select_from_objs($objects, $name, $selected);

Private method to makes a select box of objects passed with name passed. 
Assumes they are same type

=cut
sub _to_select_from_objs {
    my ($objs, $elmnt_name) = @_;
	CGI::Carp::croak("Usage: element name required") unless ($elmnt_name);
#	$elmnt_name ||= eval {$objs->[0]->primary_column};
#	unless ($elmnt_name) {
#		my $num = @$objs;
#		$self->_carp ("Element name arg. not passed and couldn't get element name from object 0. Number of objects in arg are: $num"); 
#		return;
#	}

	my $a = HTML::Element->new("select", name => $elmnt_name);
	for (@$objs) {
		my $opt = HTML::Element->new("option", value => $_->id);
		$opt->push_content($_->stringify_self);
		$a->push_content($opt);
	}
	$OLD_STYLE && return $a->as_HTML;
	$a;
}
	

# pjs EXPERIMENTAL
#  TODO this is crap. I think this will just be a public sub to select many objects from a class. Then you can do thingks like add them to has_many and stuff. 
#
#  not finished
# usage: CD->has_many('songs', 'Song', 'cd_id');
#        my $song_sel_element = $class->_to_select_many('songs', @options);
# @options have same form as a  SQL::Abstract options with exception of
# -HINT element which is the class name if you want to give it. 
# { '-HINT' => $classname, # so you can cheat, or be efficient
#   'logic'=> 'OR',        # default is OR 
#   $limiting_col => $limit_val,
#   $limiting_col2=> $limit_val2,
#   . . . }
#
# 
# make select box for has many. This is a multiple select box (select many) 
# element. If you want to choose between on of the has_many's an object has (
# ie -- a cd has many songs and you want to choose one of the songs from it)
# then pass an additional hash ref of limiting cols and vals. 
# $cd->_to_many_select('songs', {'cd_id' => $cd->id, . . .}
sub _to_select_many {
    my ($self, $accessor, $hint, $where, $order ) = @_;
    my $has_many_class = $hint || $self->hasmany_class($accessor);
	my %selected = ();
	%selected = map { $_->id => 1} $self->$accessor if ref $self;

	my $pk = $has_many_class->primary_column;
	my $a = $self->_to_select($pk, $has_many_class, \%selected, $where, $order); 
	$a->attr('multiple', 'multiple');

	$OLD_STYLE && return $a->as_HTML;
	$a;
}




sub _to_select_old_version {
	my ($self, $col, $hint) = @_;
	my $has_a_class = $hint || $self->__hasa_rels->{$col}->[0];
	my @objs        = $has_a_class->retrieve_all;
	my $a           = HTML::Element->new("select", name => $col);
	for (@objs) {
		my $sel = HTML::Element->new("option", value => $_->id);
		$sel->attr("selected" => "selected")
			if ref $self
			and eval { $_->id eq $self->$col->id };
		$sel->push_content($_->stringify_self);
		$a->push_content($sel);
	}
	$OLD_STYLE && return $a->as_HTML;
	$a;
}



############################ HELPER METHODS ######################
##################################################################

# hasmany_class($accessor) -- stole code from Maypole::Model::CDBI
# Returns class of has_many relationship when given the accessor
sub hasmany_class  {
	my ( $self, $accessor ) = @_;
	$self->_croak("No accessor (2nd arg) passed to hasmany_class")
		unless $accessor;
	my $rel_meta = $self->meta_info('has_many' => $accessor);
	
	my $mapping; 
	if ( $mapping = $rel_meta->{args}->{mapping} and @$mapping ) {
		return $rel_meta->{foreign_class}->meta_info('has_a')->{ $$mapping[0] }->{foreign_class};
	}
	else {
		return $rel_meta->{foreign_class};
	}
}


1;

=head1 ***NEW PJS Stuff**** GETTING SPECIFIC IN MODEL CLASS 

You can tell AsForm some things in your model classes to get custom results. In particular you can have:

=head2 Custom column_type methods

Since much of this modules functionality relies on the subroutine C<column_type>
returning the type string from the database table definition Model classes can
benefit a great deal by writing their own. See example.  This version tries to
call column_type with the model class first. IF your model's column_type returns
undef or it has no such method it falls back on 
C<&Class::DBI::Plugin::Type::column_type> which is database independent but not
fully functional yet. For full functionality make a custom C<column_type> method
in your base model class and override it in subclasses at will. Some \
Class::DBI::* drivers such as Class::DBI::mysql have mostly functional ones.  

With a column_type sub you can set bool options for users , make select boxes 
for ordinary columns (by lying and returning an enum('blah', 'blh') string for a
column, get correct types for is_a inherited columns, optimize , and maybe more.

=head2 Appropriate elements for columns inherited from an is_a relationship

At least you have the power to get them by making column_type work.

=head2 Select box specifications for has_a columns.

You can specify columns to be selected for a select box's options 
 for a class by :

	__Package__->columns('SelectBox' => qw/col1 col2/);

If you don't, 'Stringify' columns are used if they exist and lastly 'Essential'
columns. The 'Primary' column is always the option value. This means don't 
include it in the 'SelectBox' columns unless you want it in the option content. 

You can limit rows selected for the select box with a has_a_select_limit sub like so:

	Customer->has_a(pay_plan => "PayPlan");
	Customer->has_a(pick_fromTopFive  => "Movie");
	sub has_a_select_limit { {
	 	pay_plan            => "WHERE is_available = 1", 
	  	pick_fromTopFive    => "ORDER BY rank ASC LIMIT 5" }
	}

If you need complex stringification make a C<stringify_selectbox> sub which 
takes an arrayref. Elements are in order specified in columns('SelectBox') 
or whatever columns list was used. Otherwise, the array is joined on ' '. 

=cut


=head1 CHANGES

Many by Peter Speltz


Version 1.x of this module returned raw HTML instead of
C<HTML::Element> objects, which made it harder to manipulate the
HTML before sending it out. If you depend on the old behaviour, set
C<$Class::DBI::AsForm::OLD_STYLE> to a true value.

=head1 MAINTAINER 

Tony Bowden

=head1 ORIGINAL AUTHOR

Simon Cozens

=head1 BUGS and QUERIES

Please direct all correspondence regarding this module to:
  bug-Class-DBI-AsForm@rt.cpan.org

=head1 COPYRIGHT AND LICENSE

Copyright 2003-2004 by Simon Cozens / Tony Bowden

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

=head1 SEE ALSO

L<Class::DBI>, L<Class::DBI::FromCGI>, L<HTML::Element>.

=cut


