]> git.decadent.org.uk Git - maypole.git/blob - lib/Maypole/Model/CDBI/AsForm.pm
6ca422a0c8bdea4088f6f4a28647be8a5a667c60
[maypole.git] / lib / Maypole / Model / CDBI / AsForm.pm
1 package Maypole::Model::CDBI::AsForm;
2
3 #TODO -- 
4 # lots of doc
5 # _to_select_or_create  -- select input stays
6 # _to_create_or_select  -- create input trumps
7
8
9 # TESTED and Works --
10 #  has_many select -- $obj->to_field($has_many_col);   # select one form many
11 #                  -- $class->to_field($has_many_col); # foreign inputs  
12 #  $class->search_inputs; /
13
14
15 use strict;
16 use warnings;
17
18 use base 'Exporter';
19 use Data::Dumper;
20 use Class::DBI::Plugin::Type ();
21 use HTML::Element;
22 use Carp qw/cluck/;
23
24 our $OLD_STYLE = 0;
25 # pjs  --  Added new methods to @EXPORT 
26 our @EXPORT = 
27         qw( 
28                 to_cgi to_field  make_element_foreign search_inputs unselect_element
29                 _field_from_how _field_from_relationship _field_from_column
30                 _to_textarea _to_textfield _to_select  _select_guts
31                 _to_foreign_inputs _to_enum_select _to_bool_select
32                 _to_hidden _to_link_hidden _rename_foreign_input _to_readonly
33                 _options_from_objects _options_from_arrays _options_from_hashes 
34                 _options_from_array _options_from_hash 
35     );
36                                 
37 our $VERSION = '.10'; 
38
39 =head1 NAME
40
41 Maypole::Model:CDBI::AsForm - Produce HTML form elements for database columns
42
43 =head1 SYNOPSIS
44
45     package Music::CD;
46     use Maypole::Model::CDBI::AsForm;
47     use base 'Class::DBI';
48     use CGI;
49     ...
50
51     sub create_or_edit {
52         my $self = shift;
53         my %cgi_field = $self->to_cgi;
54         return start_form,
55                (map { "<b>$_</b>: ". $cgi_field{$_}->as_HTML." <br>" } 
56                     $class->Columns),
57                end_form;
58     }
59
60 # Example of has_many select
61 package Job;
62 __PACKAGE__->has_a('job_employer' => 'Employer');
63 __PACKAGE__->has_a('contact'  => 'Contact')
64
65 package Contact;
66 __PACKAGE__->has_a('cont_employer' => 'Employer');
67 __PACKAGE__->has_many('jobs'  => 'Job',
68         { join => { job_employer => 'cont_employer' },
69           constraint => { 'finshed' => 0  },
70           order_by   => "created ASC",
71         }
72 );
73
74 package Employer;
75 __PACKAGE__->has_many('jobs'  => 'Job',);
76 __PACKAGE__->has_many('contacts'  => 'Contact',
77             order_by => 'name DESC',
78 );
79
80
81   # Choose some jobs to add to a contact (has multiple attribute).
82   my $job_sel = Contact->to_field('jobs'); # Uses constraint and order by
83     
84
85   # Choose a job from $contact->jobs 
86   my $job_sel = $contact->to_field('jobs');
87     
88
89
90 =head1 DESCRIPTION
91
92 This module helps to generate HTML forms for creating new database rows
93 or editing existing rows. It maps column names in a database table to
94 HTML form elements which fit the schema. Large text fields are turned
95 into textareas, and fields with a has-a relationship to other
96 C<Class::DBI> tables are turned into select drop-downs populated with
97 objects from the joined class.
98
99
100 =head1 ARGUMENTS HASH
101
102 This provides a convenient way to tweak AsForm's behavior in exceptional or 
103 not so exceptional instances. Below describes the arguments hash and 
104 example usages. 
105
106
107   $beer->to_field($col, $how, $args); 
108   $beer->to_field($col, $args);
109
110 Not all _to_* methods pay attention to all arguments. For example, '_to_textfield' does not look in $args->{'items'} at all.
111  
112 =over
113
114 =item name -- the name the element will have , this trumps the derived name.
115
116   $beer->to_field('brewery', 'readonly', {
117                 name => 'brewery_id'
118   });
119   
120 =item value -- the initial value the element will have, trumps derived value
121
122   $beer->to_field('brewery', 'textfield', { 
123                 name => 'brewery_id', value => $beer->brewery,
124                 # however, no need to set value since $beer is object
125   });
126  
127 =item items -- array of items generally used to make select box options
128
129 Can be array of objects, hashes, arrays, or strings, or just a hash.
130
131    # Rate a beer
132    $beer->to_field(rating =>  select => {
133                 items => [1 , 2, 3, 4, 5],
134    });
135  
136    # Select a Brewery to visit in the UK
137    Brewery->to_field(brewery_id => {
138                 items => [ Brewery->search_like(location => 'UK') ],
139    });
140
141   # Make a select for a boolean field
142   $Pub->to_field('open' , { items => [ {'Open' => 1, 'Closed' => 0 } ] }); 
143
144 =item selected -- something representing which item is selected in a select box
145
146    $beer->to_field('brewery', {
147                 selected => $beer->brewery, # again not necessary since caller is obj.
148    });
149
150 Can be an simple scalar id, an object, or an array of either
151
152 =item class -- the class for which the input being made for field pertains to.
153
154 This in almost always derived in cases where it may be difficult to derive, --
155    # Select beers to serve on handpump
156    Pub->to_field(handpumps => select => {
157                 class => 'Beer', order_by => 'name ASC', multiple => 1,
158         });
159
160 =item column_type -- a string representing column type
161    
162   $pub->to_field('open', 'bool_select', {
163                 column_type => "bool('Closed', 'Open'),
164   });
165
166 =item column_nullable -- flag saying if column is nullable or not
167
168 Generally this can be set to get or not get a null/empty option added to
169 a select box.  AsForm attempts to call "$class->column_nullable" to set this
170 and it defaults to true if there is no shuch method.
171   
172   $beer->to_field('brewery', { column_nullable => 1 });    
173
174 =item r or request  -- the mapyole request object 
175
176 =item uri -- uri for a link , used in methods such as _to_link_hidden
177
178  $beer->to_field('brewery', 'link_hidden', 
179       {r => $r, uri => 'www.maypole.perl.org/brewery/view/'.$beer->brewery}); 
180  # an html link that is also a hidden input to the object. R is required to
181  # make the uri  unless you  pass a  uri
182
183 =item order_by, constraint, join
184
185 These are used in making select boxes. order_by is a simple order by clause
186 and constraint and join are hashes used to limit the rows selected. The
187 difference is that join uses methods of the object and constraint uses 
188 static values. You can also specify these in the relationship arguments.
189
190   BeerDB::LondonBeer->has_a('brewery', 'BeerDB::Brewery', 
191            order_by     => 'brewery_name ASC',
192            constraint   => {location  => 'London'},
193            'join'       => {'brewery_tablecolumn  => 'beer_obj_column'}, 
194           );
195            
196 =item no_hidden_constraints -- 
197
198 Tell AsForm not to make hidden inputs for relationship constraints. It does
199 this  sometimes when making foreign inputs . 
200
201 =back
202
203 =head2 to_cgi
204
205   $self->to_cgi([@columns, $args]); 
206
207 This returns a hash mapping all the column names to HTML::Element objects 
208 representing form widgets.  It takes two opitonal arguments -- a list of 
209 columns and a hashref of hashes of arguments for each column.  If called with an object like for editing, the inputs will have the object's values.
210
211   $self->to_cgi(); # uses $self->columns;  # most used
212   $self->to_cgi(qw/brewery style rating/); # sometimes
213   # and on rare occassions this is desireable if you have a lot of fields
214   # and dont want to call to_field a bunch of times just to tweak one or 
215   # two of them.
216   $self->to_cgi(@cols, {brewery => {  
217                                      how => 'textfield' # too big for select 
218                                                                    }, 
219                         style   => { 
220                                                              column_nullable => 0, 
221                                                              how => 'select', 
222                                                                      items => ['Ale', 'Lager']
223                                                                    }
224                                                 }
225
226 =cut
227
228 sub to_cgi {
229         my ($class, @columns) = @_; # pjs -- added columns arg
230         my $args = {};
231         if (not @columns) {
232                 @columns = $class->columns; 
233         }
234         else {
235                 if ( ref $columns[-1] eq 'HASH' ) { $args = pop @columns; }
236         }
237         map { $_ => $class->to_field($_, $args->{$_}) } @columns;
238 }
239
240 =head2 to_field($field [, $how][, $args])
241
242 This maps an individual column to a form element. The C<how> argument
243 can be used to force the field type into any you want. It tells AsForm how
244 to make the input ie-- forces it to use the method "_to_$how".
245 If C<how> is specified but the class cannot call the method it maps to,
246 then AsForm will issue a warning and the default input will be made. 
247 You can write your own "_to_$how" methods and AsForm comes with many.
248 See C<HOW Methods>. You can also pass this argument in $args->{how}.
249
250
251 =cut
252
253 sub to_field {
254         my ($self, $field, $how, $args) = @_;
255     if (ref $how)   { $args = $how; $how = ''; }
256         unless ($how)   { $how = $args->{how} || ''; }
257 #warn "In to_field field is $field how is $how. args ar e" . Dumper($args) . " \n";
258
259     #if (ref $field) { $args = $field; $field = '' }
260
261         #use Data::Dumper;
262         #warn "args to_field  are $field, . " . Dumper($how) . " ,  " . Dumper($args);
263         
264
265         return  $self->_field_from_how($field, $how, $args)   || 
266                 $self->_field_from_relationship($field, $args) ||
267                         $self->_field_from_column($field, $args)  ||
268                         $self->_to_textfield($field, $args);
269 }
270
271 =head2 search_inputs
272
273   my $cgi = $class->search_inputs ([$args]); # optional $args
274
275 Returns hash or hashref of search inputs elements for a class making sure the
276 inputs are empty of any initial values.
277 You can specify what columns you want inputs for in
278 $args->{columns} or
279 by the method "search_columns". The default is  "display_columns".
280 If you want to te search on columns in related classes you can do that by
281 specifying a one element hashref in place of the column name where
282 the key is the related "column" (has_a or has_many method for example) and
283 the value is a list ref of columns to search on in the related class.
284
285 Example:
286   sub  BeerDB::Beer::search_columns {
287      return ( 'name' , 'rating', { brewery => [ 'name', 'location'] } );
288   }
289
290   # Now foreign inputs are made for Brewery name and location and the
291   # there will be no name clashing and processing can be automated.
292
293 =cut
294
295
296 sub search_inputs {
297     my ($class, $args) = @_;
298     $class = ref $class || $class;
299     #my $accssr_class = { $class->accessor_classes };
300     my %cgi;
301
302     $args->{columns} ||= $class->can('search_columns') ?[$class->search_columns] : [$class->display_columns];
303
304     foreach my $field ( @{ $args->{columns} } ) {
305                 my $base_args = {
306                         no_hidden_constraints => 1,
307                         column_nullable => 1, # empty option on select boxes
308                         value  => '',
309                 };
310         if ( ref $field eq "HASH" ) { # foreign search fields
311             my ($accssr, $cols)  = each %$field;
312                         $base_args->{columns} = $cols;
313             unless (  @$cols ) {
314                 # default to search fields for related
315                 #$cols =  $accssr_class->{$accssr}->search_columns;
316                 die ("$class search_fields error: Must specify at least one column to search in the foreign object named '$accssr'");
317             }
318             my $fcgi  = $class->to_field($accssr, 'foreign_inputs', $base_args);
319
320             # unset the default values for a select box
321             foreach (keys %$fcgi) {
322                         my $el = $fcgi->{$_};
323                                 if ($el->tag eq 'select') {
324                                         
325                                         $class->unselect_element($el);
326                                         my ($first, @content) = $el->content_list;
327                                         my @fc = $first->content_list;
328                                         my $val = $first ? $first->attr('value') : undef;  
329                                         if ($first and (@fc > 0 or (defined $val and $val ne '')) ) { # something ( $first->attr('value') ne '' or 
330                                                       
331                                            #(defined $first->attr('value') or $first->attr('value') ne ''))  
332                                            # push an empty option on stactk
333                                            $el->unshift_content(HTML::Element->new('option'));
334                                     }
335                                 }
336                                         
337             }
338             $cgi{$accssr} = $fcgi;
339                         delete $base_args->{columns};
340                 }
341         else {
342             $cgi{$field} = $class->to_field($field, $base_args); #{no_select => $args->{no_select}{$field} });
343                 my $el = $cgi{$field};
344                         if ($el->tag eq 'select') {
345                                 $class->unselect_element($el);
346                                 my ($first, @content) = $el->content_list;
347                                 if ($first and $first->content_list) { # something 
348                                            #(defined $first->attr('value') or $first->attr('value') ne ''))  
349                                            # push an empty option on stactk
350                                            $el->unshift_content(HTML::Element->new('option'));
351                                 }
352                         }
353         }
354     }
355     return \%cgi;
356 }
357
358
359
360
361 =head2 unselect_element
362
363   unselect any selected elements in a HTML::Element select list widget
364
365 =cut
366 sub unselect_element {
367    my ($self, $el) = @_;
368    #unless (ref $el eq 'HTML::Element') {
369    #$self->_croak ('Need an HTML::Element to unselect. You gave a ' . Dumper($el)); }
370    if ($el->tag eq 'select') {
371        foreach my $opt ($el->content_list) {
372            $opt->attr('selected', undef) if $opt->attr('selected');
373        }
374    }
375 }
376
377 =head2 _field_from_how($field, $how,$args)
378
379 Returns an input element based the "how" parameter or nothing at all.
380 Override at will. 
381
382 =cut
383
384 sub _field_from_how {
385         my ($self, $field, $how, $args) = @_;
386         return unless $how;
387         $args ||= {};
388         no strict 'refs';
389         my $meth = "_to_$how";
390         if (not $self->can($meth)) { 
391                 warn "Class can not $meth";
392                 return;
393         }
394         return $self->$meth($field, $args); 
395         return;
396 }
397
398 =head2 _field_from_relationship($field, $args)
399
400 Returns an input based on the relationship associated with the field or nothing.
401 Override at will.
402
403 For has_a it will give select box
404
405 =cut
406
407 sub _field_from_relationship {
408         my ($self, $field, $args) = @_;
409         return unless $field;
410         my $rel_meta = $self->related_meta('r',$field) || return; 
411         my $rel_name = $rel_meta->{name};
412         #my $meta = $self->meta_info;
413         #grep{ defined $meta->{$_}{$field} } keys %$meta;
414         my $fclass = $rel_meta->foreign_class;
415         my $fclass_is_cdbi = $fclass ? $fclass->isa('Class::DBI') : 0;
416
417         # maybe has_a select 
418         if ($rel_meta->{name} eq 'has_a' and $fclass_is_cdbi) {
419             # This condictions allows for trumping of the has_a args
420                 if  (not $rel_meta->{args}{no_select} and not $args->{no_select}) 
421                 {
422                 $args->{class} = $fclass;
423                 return  $self->_to_select($field, $args);
424                 }
425                 return;
426         }
427         # maybe has many select
428         if ($rel_meta->{name} eq 'has_many' and $fclass_is_cdbi and ref $self) {
429             # This condictions allows for trumping of the has_a args
430                 if  (not $rel_meta->{args}{no_select} and not $args->{no_select}) 
431                 {
432                 $args->{class} = $fclass;
433                         my @itms = $self->$field; # need list not iterator
434                         $args->{items} = \@itms;
435                 return  $self->_to_select($field, $args);
436                 }
437                 return;
438         }
439
440                 
441         
442         #NOOO!  maybe select from has_many 
443 #       if ($rel_type eq 'has_many' and ref $self) {
444 #               $args->{items} ||= [$self->$field];
445 #               # arg name || fclass pk name || field
446 #               if (not $args->{name}) {
447 #                       $args->{name} =  eval{$fclass->primary_column->name} || $field; 
448 #               }
449 #       return  $self->_to_select($field, $args);
450 #       }
451 #
452         # maybe foreign inputs 
453         my %local_cols = map { $_ => 1 } $self->columns; # includes is_a cols
454         if ($fclass_is_cdbi and (not $local_cols{$field} or $rel_name eq 'has_own'))
455         {
456                 $args->{related_meta} = $rel_meta; # suspect faster to set these args 
457                 return $self->_to_foreign_inputs($field, $args);
458         }
459         return;
460 }
461                         
462 =head2 _field_from_column($field, $args)
463
464 Returns an input based on the column's characteristics, namely type, or nothing.
465 Override at will.
466
467 =cut
468
469 sub _field_from_column {
470         my ($self, $field, $args) = @_;
471         return unless $field;
472         my $class = ref $self || $self;
473         # Get column type       
474     unless ($args->{column_type}) { 
475         if ($class->can('column_type')) {
476                         $args->{column_type} = $class->column_type($field);
477                 }       
478                 else {
479                 # Right, have some of this
480                 eval "package $class; Class::DBI::Plugin::Type->import()";
481                 $args->{column_type} = $class->column_type($field);
482                 }
483     }
484     my $type = $args->{column_type};
485
486         return $self->_to_textfield($field, $args)
487                 if $type  and $type =~ /^(VAR)?CHAR/i;  #common type
488         return $self->_to_textarea($field, $args)
489                 if $type and $type =~ /^(TEXT|BLOB)$/i;
490         return $self->_to_enum_select($field, $args)  
491                 if $type and  $type =~ /^ENUM\((.*?)\)$/i; 
492         return $self->_to_bool_select($field, $args)
493                 if $type and  $type =~ /^BOOL/i; 
494         return $self->_to_readonly($field, $args)
495             if $type and $type =~ /^readonly$/i;
496         return;
497 }
498
499
500 sub _to_textarea {
501         my ($self, $col, $args) = @_;
502         # pjs added default     
503     $args ||= {};
504     my $val =  $args->{value}; 
505     
506     unless (defined $val) {
507         if (ref $self) {
508                         $val = $self->$col; 
509                 }
510                 else { 
511                         $val = eval {$self->column_default($col);}; 
512                 $val = '' unless defined $val;  
513                 }
514         }
515     my ($rows, $cols) = _box($val);
516     $rows = $args->{rows} if $args->{rows};
517     $cols = $args->{cols} if $args->{cols};;
518     my $name = $args->{name} || $col; 
519         my $a =
520                 HTML::Element->new("textarea", name => $name, rows => $rows, cols => $cols);
521         $a->push_content($val);
522         $OLD_STYLE && return $a->as_HTML;
523         $a;
524 }
525
526 sub _to_textfield {
527     my ($self, $col, $args ) = @_;
528     use Carp qw/confess/;
529     confess "No col passed to _to_textfield" unless $col;
530     $args ||= {};
531     my $val  = $args->{value}; 
532     my $name = $args->{name} || $col; 
533
534     unless (defined $val) {
535         if (ref $self) {
536             # Case where column inflates.
537             # Input would get stringification which could be not good.
538             #  as in the case of Time::Piece objects
539             $val = $self->can($col) ? $self->$col : ''; # in case it is a virtual column
540             if (ref $val) {
541                                 if (my $meta = $self->related_meta('',$col)) {
542                         if (my $code = $meta->{args}{deflate4edit} || $meta->{args}{deflate} ) {
543                         $val  = ref $code ? &$code($val) : $val->$code;
544                                         }
545                                         elsif ( $val->isa('Class::DBI') ) {
546                                             $val  = $val->id;
547                                         }
548                                         else { 
549                                                 #warn "No deflate4edit code defined for $val of type " . 
550                                              #ref $val . ". Using the stringified value in textfield..";
551                                         }
552                 }
553                                 else {
554                                         $val  = $val->id if $val->isa("Class::DBI"); 
555                }
556                 }
557                         
558         }
559         else {
560                 $val = eval {$self->column_default($col);};
561                 $val = '' unless defined $val;
562         }
563     }
564         my $a;
565         # THIS If section is neccessary or you end up with "value" for a vaiue
566         # if val is 
567         $val = '' unless defined $val; 
568         $a = HTML::Element->new("input", type => "text", name => $name, value =>$val);
569     $OLD_STYLE && return $a->as_HTML;
570     $a;
571 }
572
573
574 # Too expensive version -- TODO
575 #sub _to_select {
576 #       my ($self, $col, $hint) = @_;
577 #       my $fclass = $hint || $self->__hasa_rels->{$col}->[0];
578 #       my @objs        = $fclass->retrieve_all;
579 #       my $a           = HTML::Element->new("select", name => $col);
580 #       for (@objs) {
581 #               my $sel = HTML::Element->new("option", value => $_->id);
582 #               $sel->attr("selected" => "selected")
583 #                       if ref $self
584 #                       and eval { $_->id eq $self->$col->id };
585 #               $sel->push_content($_->stringify_self);
586 #               $a->push_content($sel);
587 #       }
588 #       $OLD_STYLE && return $a->as_HTML;
589 #       $a;
590 #}
591
592
593
594 # pjs 
595 # -- Rewrote this to be efficient -- no object creation. 
596 # -- Added option for CDBI classes to specify a limiting clause
597 # via "has_a_select_limit". 
598 # -- Added selected argument to set a selected 
599
600 =head2 recognized arguments
601  
602   selected => $object|$id,
603   name     => $name,
604   value    => $value,
605   where    => SQL 'WHERE' clause,
606   order_by => SQL 'ORDER BY' clause,
607   limit    => SQL 'LIMIT' clause,
608   items    => [ @items_of_same_type_to_select_from ],
609   class => $class_we_are_selecting_from
610   stringify => $stringify_coderef|$method_name
611   
612   
613
614
615 # select box requirements
616 # 1. a select box for objecs of a has_a related class -- DONE 
617 =head2  1. a select box out of a has_a or has_many related class.
618   # For has_a the default behavior is to make a select box of every element in 
619   # related class and you choose one. 
620   #Or explicitly you can create one and pass options like where and order
621   BeerDB::Beer->to_field('brewery','select', {where => "location = 'Germany'");
622   
623   # For has_many the default is to get a multiple select box with all objects.
624   # If called as an object method, the objects existing ones will be selected. 
625   Brewery::BeerDB->to_field('beers','select', {where => "rating > 5"}); 
626   
627
628 =head2  2. a select box for objects of arbitrary class -- say BeerDB::Beer for fun. 
629   # general 
630   BeerDB::Beer->to_field('', 'select', $options)
631
632   BeerDB::Beer->to_field('', 'select'); # Select box of all the rows in class
633                                   # with PK as ID, $Class->to_field() same.
634   BeerDB::Beer->to_field('','select',{ where => "rating > 3 AND class like 'Ale'", order_by => 'rating DESC, beer_id ASC' , limit => 10});
635   # specify exact where clause 
636
637 =head2 3. If you already have a list of objects to select from  -- 
638
639   BeerDB:;Beer->to_field($col, 'select' , {items => $objects});
640
641 # 3. a select box for arbitrary set of objects 
642  # Pass array ref of objects as first arg rather than field 
643  $any_class_or_obj->to_field([BeerDB::Beer->search(favorite => 1)], 'select',);
644  
645
646 =cut
647
648 sub _to_select {
649     my ($self, $col, $args) = @_;
650     $args ||= {};
651         # Do we have items already ? Go no further. 
652     if ($args->{items} and ref $args->{items}) {  
653         my $a = $self->_select_guts($col,  $args);
654         $OLD_STYLE && return $a->as_HTML;
655                 if ($args->{multiple}) { $a->attr('multiple', 'multiple');}
656                 return $a;
657         }
658
659         # Else what are we making a select box out of ?  
660         # No Column parameter --  means making a select box of args->class or self 
661     # Using all rows from class's table
662     if (not $col) { 
663                 unless ($args->{class}) {
664                 $args->{class} = ref $self || $self;
665                         # object selected if called with one
666             $args->{selected} = { $self->id => 1} 
667                                 if not $args->{selected} and ref $self;
668                 }
669         $col = $args->{class}->primary_column;
670     }
671     # Related Class maybe ? 
672     elsif (my $rel_meta =  $self->related_meta('r:)', $col) ) {
673         $args->{class} = $rel_meta->{foreign_class};
674         # related objects pre selected if object
675                                 
676                 # "Has many" -- Issues:
677                 # 1) want to select one  or many from list if self is an object
678                 # Thats about all we can do really, 
679                 # 2) except for mapping which is TODO and  would 
680                 # do something like add to and take away from list of permissions for
681                 # example.
682
683                 # Hasmany select one from list if ref self
684                 if ($rel_meta->{name} =~ /has_many/i and ref $self) {
685                     my @itms =  $self->$col; # need list not iterator
686                         $args->{items} = \@itms;
687                         my $a = $self->_select_guts($col,  $args);
688                     $OLD_STYLE && return $a->as_HTML;
689                     return $a;
690                 }
691                 else {
692                         $args->{selected} ||= [ $self->$col ] if  ref $self; 
693                         #warn "selected is " . Dumper($args->{selected});
694                         my $c = $rel_meta->{args}{constraint} || {};
695                         my $j = $rel_meta->{args}{join} || {};
696                         my @join ; 
697                         if (ref $self) {
698                                 @join   =  map { $_ ." = ". $self->_attr($_) } keys %$j; 
699                         }
700                         my @constr= map { "$_ = '$c->{$_}'"} keys %$c; 
701                         $args->{where}    ||= join (' AND ', (@join, @constr));
702                         $args->{order_by} ||= $rel_meta->{args}{order_by};
703                         $args->{limit}    ||= $rel_meta->{args}{limit};
704                 }
705                         
706     }
707     # We could say :Col is name and we are selecting  out of class arg.
708         # DIE for now
709         else {
710                 #$args->{name} = $col;
711                 die "Usage _to_select. $col not related to any class to select from. ";
712                 
713     }
714                 
715     # Set arguments 
716         unless ( defined  $args->{column_nullable} ) {
717             $args->{column_nullable} = $self->can('column_nullable') ?
718                          $self->column_nullable($col) : 1;
719         }
720
721         # Get items to select from
722     $args->{items} = _select_items($args);
723         #use Data::Dumper;
724         #warn "Just got items. They are  " . Dumper($args->{items});
725
726         # Make select HTML element
727         $a = $self->_select_guts($col, $args);
728
729         if ($args->{multiple}) {$a->attr('multiple', 'multiple');}
730
731         # Return 
732     $OLD_STYLE && return $a->as_HTML;
733     $a;
734
735 }
736
737
738 ##############
739 # Function # 
740 # #############
741 # returns the intersection of list refs a and b
742 sub _list_intersect {
743         my ($a, $b) = @_;
744         my %isect; my %union;
745     foreach my $e (@$a, @$b) { $union{$e}++ && $isect{$e}++ }
746         return  %isect;
747 }
748 ############
749 # FUNCTION #
750 ############
751 # Get Items 
752 sub _select_items { 
753         my $args = shift;
754         my $fclass = $args->{class};
755     my @disp_cols = @{$args->{columns} || []};
756     @disp_cols = $fclass->columns('SelectBox') unless @disp_cols;
757     @disp_cols = $fclass->columns('Stringify')unless @disp_cols;
758     @disp_cols = $fclass->_essential unless @disp_cols;
759         unshift @disp_cols,  $fclass->columns('Primary');
760         #my %isect = _list_intersect(\@pks, \@disp_cols);
761         #foreach (@pks) { push @sel_cols, $_ unless $isect{$_}; } 
762     #push @sel_cols, @disp_cols;                
763
764         #warn "in select items. args are : " . Dumper($args);
765         my $distinct = '';
766         if ($args->{'distinct'}) {
767         $distinct = 'DISTINCT ';
768         }
769
770     my $sql = "SELECT $distinct" . join( ', ', @disp_cols) . 
771                   " FROM " . $fclass->table;
772
773         $sql .= " WHERE " . $args->{where}   if $args->{where};
774         $sql .= " ORDER BY " . $args->{order_by} if $args->{order_by};
775         $sql .= " LIMIT " . $args->{limit} if $args->{limit};
776         #warn "_select_items sql is : $sql";
777
778         return $fclass->db_Main->selectall_arrayref($sql);
779
780 }
781
782
783 # Makes a readonly input box out of column's value
784 # No args makes object to readonly
785 sub _to_readonly {
786     my ($self, $col, $args) = @_;
787     my $val = $args->{value};
788     if (not defined $val ) { # object to readonly
789         $self->_croak("AsForm: To readonly field called as class method without a value") unless ref $self; 
790         $val = $self->id;
791         $col = $self->primary_column;
792     }
793     my $a = HTML::Element->new('input', 'type' => 'text', readonly => '1',
794         'name' => $col, 'value'=>$val);
795         $OLD_STYLE && return $a->as_HTML;
796     $a;
797 }
798
799
800 =head2 _to_enum_select
801
802 $sel_box = $self->_to_enum_select($column, "ENUM('Val1','Val2','Val3')");
803
804 Returns an enum select box given a column name and an enum string.
805 NOTE: The Plugin::Type does not return an enum string for mysql enum columns.
806 This will not work unless you write your own column_type method in your model.
807
808 =cut
809
810 sub _to_enum_select {
811     my ($self, $col, $args) = @_;
812         my $type = $args->{column_type};
813     $type =~ /ENUM\((.*?)\)/i;
814     (my $enum = $1) =~ s/'//g;
815     my @enum_vals = split /\s*,\s*/, $enum;
816
817     # determine which is pre selected --
818     # if obj, the value is , otherwise use column_default which is the first
819     # value in the enum list unless it has been overridden
820     my $selected = eval { $self->$col  };
821     $selected = eval{$self->column_default($col)} unless defined $selected;
822     $selected = $enum_vals[0]               unless defined $selected;
823
824     my $a = HTML::Element->new("select", name => $col);
825     for ( @enum_vals ) {
826         my $sel = HTML::Element->new("option", value => $_);
827         $sel->attr("selected" => "selected") if $_ eq $selected ;
828         $sel->push_content($_);
829         $a->push_content($sel);
830     }
831     $OLD_STYLE && return $a->as_HTML;
832     $a;
833 }
834
835
836 =head2 _to_bool_select
837
838   my $sel = $self->_to_bool_select($column, $bool_string);
839
840 This  makes select input for boolean column.  You can provide a
841 bool string of form: Bool('zero','one') and those are used for option
842 content. Onthervise No and Yes are used.
843 TODO -- test without bool string.
844
845 =cut
846
847 # TCODO fix this mess with args
848 sub _to_bool_select {
849     my ($self, $col, $args) = @_;
850         my $type = $args->{column_type};
851         my @bool_text = ('No', 'Yes');  
852         if ($type =~ /BOOL\((.+?)\)/i) {
853                 (my $bool = $1) =~ s/'//g;
854                 @bool_text = split /,/, $bool;
855         }
856
857         # get selectedod 
858         
859         my $selected = $args->{value} if defined $args->{value};
860         $selected = $args->{selected} unless defined $selected;
861         $selected =  ref $self ? eval {$self->$col;} : $self->column_default($col)
862                 unless (defined $selected);
863
864     my $a = HTML::Element->new("select", name => $col);
865     if ($args->{column_nullable} || $args->{value} eq '') {
866                 my $null =  HTML::Element->new("option");
867                 $null->attr('selected', 'selected') if  $args->{value} eq '';
868             $a->push_content( $null ); 
869         }
870            
871     my ($opt0, $opt1) = ( HTML::Element->new("option", value => 0),
872                                                   HTML::Element->new("option", value => 1) ); 
873     $opt0->push_content($bool_text[0]); 
874     $opt1->push_content($bool_text[1]); 
875         unless ($selected eq '') { 
876         $opt0->attr("selected" => "selected") if not $selected; 
877         $opt1->attr("selected" => "selected") if $selected; 
878         }
879     $a->push_content($opt0, $opt1);
880     $OLD_STYLE && return $a->as_HTML;
881     $a;
882 }
883
884
885 =head2 _to_hidden($col, $args)
886
887 This makes a hidden html element. Give it a name and value or if name is
888 a ref it will use the PK name and value of the object.
889
890 =cut
891
892 sub _to_hidden {
893     my ($self, $name, $val) = @_;
894     my $args = {};
895     my $obj;
896     if (ref $name and $name->isa("Class::DBI")) {
897        $obj = $name;
898        $name= ($obj->primary_columns)[0]->name;
899     }
900     if (ref $val) {
901                 $args = $val;
902         $val = $args->{value};
903         $name = $args->{name} if $args->{name};
904     }
905     elsif (not $name ) { # hidding object caller
906         $self->_croak("No object available in _to_hidden") unless ref $self;
907         $name = ($self->primary_column)[0]->name;
908         $val  = $self->id;
909     }
910     return HTML::Element->new('input', 'type' => 'hidden',
911                               'name' => $name, 'value'=>$val
912     );
913 }
914
915 =head2 _to_link_hidden($col, $args) 
916
917 Makes a link with a hidden input with the id of $obj as the value and name.
918 Name defaults to the objects primary key. The object defaults to self.
919
920 =cut
921
922 sub _to_link_hidden {
923     my ($self, $accessor, $args) = @_;
924     my $r =  eval {$self->controller} || $args->{r} || '';
925     my $uri = $args->{uri} || '';
926    use Data::Dumper;
927     $self->_croak("_to_link_hidden cant get uri. No  Maypole Request class (\$r) or uri arg. Need one or other.")
928         unless $r;
929     my ($obj, $name);
930     if (ref $self) { # hidding linking self
931          $obj  = $self;
932          $name = $args->{name} || $obj->primary_column->name;
933     }
934     elsif ($obj = $args->{items}->[0]) {
935         $name = $args->{name} || $accessor || $obj->primary_column->name; 
936                 # TODO use meta data above maybe
937     }
938     else {           # hiding linking related object with id in args
939         $obj  = $self->related_class($r, $accessor)->retrieve($args->{id});
940         $name = $args->{name} || $accessor ; #$obj->primary_column->name;
941                 # TODO use meta data above maybe
942     }
943     $self->_croak("_to_link_hidden has no object") unless ref $obj;
944     my $href =  $uri || $r->config->{uri_base} . "/". $obj->table."/view/".$obj->id;
945     my $a = HTML::Element->new('a', 'href' => $href);
946     $a->push_content("$obj");
947     $a->push_content($self->_to_hidden($name, $obj->id));
948         $OLD_STYLE && return $a->as_HTML;
949     $a;
950 }
951
952 =head2 _to_foreign_inputs
953
954 $html_els = $class_or_obj->_to_foreign_inputs($accssr, [$fields, $accssr_meta]);
955
956 Get inputs for the accessor's class.  Pass an array ref of fields to get
957 inputs for only those fields. Otherwise display_columns or all columns is used. 
958 If you have the meta info handy for the accessor you can pass that too.
959
960 TODO make AsForm know more about the request like what action we are doing
961 so it can use edit columns or search_columns
962
963 NOTE , this names the foreign inputs is a particular way so they can be
964 processed with a general routine and so there are not name clashes.
965
966 args -
967 related_meta -- if you have this, great, othervise it will determine or die
968 columns  -- list of columns to make inputs for 
969
970 =cut
971
972 sub _to_foreign_inputs {
973         my ($self, $accssr, $args) = @_;
974         my $rel_meta = $args->{related_meta} || $self->related_meta('r',$accssr); 
975         my $fields              = $args->{columns};
976         if (!$rel_meta) {
977                 $self->_croak( "No relationship for accessor $accssr");
978         }
979
980         my $rel_type = $rel_meta->{name};
981         my $classORobj = ref $self && ref $self->$accssr ? $self->$accssr : $rel_meta->{foreign_class};
982         
983         unless ($fields) {      
984                 $fields = $classORobj->can('display_columns') ? 
985                         [$classORobj->display_columns] : [$classORobj->columns];
986         }
987         
988         # Ignore our fkey in them to  prevent infinite recursion 
989         my $me          = eval {$rel_meta->{args}{foreign_column}} || '';  
990         my $constrained = $rel_meta->{args}{constraint}; 
991         my %inputs;
992         foreach ( @$fields ) {
993                 next if $constrained->{$_} || ($_ eq $me); # don't display constrained
994                 $inputs{$_} =  $classORobj->to_field($_);
995         }
996
997         # Make hidden inputs for constrained columns unless we are editing object
998         # TODO -- is this right thing to do?
999         unless (ref $classORobj || $args->{no_hidden_constraints}) {
1000                 $inputs{$_} = $classORobj->_to_hidden($_, $constrained->{$_}) 
1001                         foreach ( keys %$constrained );  
1002         }
1003         $self->_rename_foreign_input($accssr, \%inputs);
1004         return \%inputs;
1005 }
1006
1007
1008 =head2 _hash_selected
1009
1010 Method to make sense out of the "selected" argument which can be in a number
1011 of formats perhaps.  It returns a hashref with the the values of options to be
1012 as the keys. 
1013
1014 Below handles these formats for the "selected" slot in the arguments hash:
1015   Object (with id method)
1016   Scalar (assumes it is value)
1017   Array ref *OF* objects, arrays of data (0 elmnt used), hashes of data
1018     (id key used), and simple scalars.
1019     
1020
1021 =cut 
1022  
1023 ############
1024 # FUNCTION #
1025 ############
1026 sub _hash_selected {
1027         my ($args) = shift;
1028         my $selected = $args->{value} || $args->{selected};
1029         #warn "**** SELECTED is $selected ****";
1030         my $type = ref $selected;
1031     return $selected unless $selected and $type ne 'HASH'; 
1032         #warn "Selected dump : " . Dumper($selected);
1033         # Single Object 
1034     if ($type and $type ne 'ARRAY') {
1035            my $id = $selected->id;
1036            $id =~ s/^0*//;
1037        return  {$id => 1};
1038     }
1039     # Single Scalar id 
1040         elsif (not $type) {
1041                 return { $selected => 1}; 
1042         }
1043         
1044
1045         # Array of objs, arrays, hashes, or just scalalrs. 
1046         elsif ($type eq 'ARRAY') {
1047                 my %hashed;
1048                 my $ltype = ref $selected->[0];
1049                 # Objects
1050                 if ($ltype and $ltype ne 'ARRAY')  {
1051                         %hashed = map { $_->id  => 1 } @$selected;
1052         }
1053                 # Arrays of data with id first 
1054             elsif ($ltype and $ltype eq 'ARRAY') {
1055                         %hashed = map { $_->[0]  => 1 } @$selected; 
1056                 }
1057                 # Hashes using pk or id key
1058                 elsif ($ltype and $ltype eq 'HASH') {
1059                         my $pk = $args->{class}->primary_column || 'id';
1060                         %hashed = map { $_->{$pk}  => 1 } @$selected; 
1061                 }
1062                 # Just Scalars
1063         else { 
1064                         %hashed = map { $_  => 1 } @$selected; 
1065                 }
1066                 return \%hashed;
1067         }
1068         else { warn "AsForm Could not hash the selected argument: $selected"; }
1069
1070                 
1071
1072
1073
1074 =head2 _select_guts 
1075
1076 Internal api  method to make the actual select box form elements.
1077
1078 3 types of lists making for -- 
1079   Hash, Array, 
1080   Array of CDBI objects.
1081   Array of scalars , 
1082   Array or  Array refs with cols from class,
1083   Array of hashes 
1084
1085 =cut
1086
1087
1088
1089 sub _select_guts {
1090     my ($self, $col, $args) = @_; #$nullable, $selected_id, $values) = @_;
1091
1092     #$args->{stringify} ||=  'stringify_selectbox';
1093
1094     $args->{selected} = _hash_selected($args) if defined $args->{selected};
1095         warn "*** Dumpe of selected " . Dumper( $args->{selected} );
1096         my $name = $args->{name} || $col;
1097     my $a = HTML::Element->new('select', name => $name);
1098         $a->attr( %{$args->{attr}} ) if $args->{attr};
1099     
1100     if ($args->{column_nullable}) {
1101                 my $null_element = HTML::Element->new('option', value => '');
1102         $null_element->attr(selected => 'selected')
1103                 if ($args->{selected}{'null'});
1104         $a->push_content($null_element);
1105     }
1106
1107         my $items = $args->{items};
1108     my $type = ref $items;
1109         my $proto = eval { ref $items->[0]; } || "";
1110         my $optgroups = $args->{optgroups} || '';
1111         
1112         # Array of hashes, one for each optgroup
1113         if ($optgroups) {
1114                 my $i = 0;
1115                 foreach (@$optgroups) {
1116                         my $ogrp=  HTML::Element->new('optgroup', label => $_);
1117                         $ogrp->push_content($self->_options_from_hash($items->[$i], $args));
1118                         $a->push_content($ogrp);
1119                         $i++;
1120                 }
1121         }               
1122     # Single Hash
1123     elsif ($type eq 'HASH') {
1124         $a->push_content($self->_options_from_hash($items, $args));
1125     }
1126     # Single Array
1127     elsif ( $type eq 'ARRAY' and not ref $items->[0] ) {
1128         $a->push_content($self->_options_from_array($items, $args));
1129     }
1130     # Array of Objects
1131     elsif( $type eq 'ARRAY' and $proto !~ /ARRAY|HASH/i ) {
1132         # make select  of objects
1133         $a->push_content($self->_options_from_objects($items, $args));
1134     }
1135     # Array of Arrays
1136     elsif ( $type eq 'ARRAY' and $proto eq 'ARRAY' ) {
1137         $a->push_content($self->_options_from_arrays($items, $args));
1138     }
1139     # Array of Hashes
1140     elsif ( $type eq 'ARRAY' and $proto eq 'HASH' ) {
1141         $a->push_content($self->_options_from_hashes($items, $args));
1142     }
1143     else {
1144         die "You passed a weird type of data structure to me. Here it is: " .
1145         Dumper($items );
1146     }
1147
1148     return $a;
1149
1150
1151 }
1152
1153 =head2 _options_from_objects ( $objects, $args);
1154
1155 Private method to makes a options out of  objects. It attempts to call each
1156 objects stringify method specified in $args->{stringify} as the content. Otherwise the default stringification prevails.
1157
1158 *Note only  single primary keys supported
1159
1160 =cut
1161 sub _options_from_objects {
1162     my ($self, $items, $args) = @_;
1163         my $selected = $args->{selected} || {};
1164         my $stringify = $args->{stringify} || '';
1165     my @res;
1166         for (@$items) {
1167                 my $id = $_->id;
1168                 my $opt = HTML::Element->new("option", value => $id);
1169                 $id =~ s/^0*//; # leading zeros no good in hash key
1170                 $opt->attr(selected => "selected") if $selected->{$id}; 
1171                 my $content = $stringify ? $_->stringify :  "$_";
1172                 $opt->push_content($content);
1173                 push @res, $opt; 
1174         }
1175     return @res;
1176 }
1177
1178 sub _options_from_arrays {
1179     my ($self, $items, $args) = @_;
1180         my $selected = $args->{selected} || {};
1181     my @res;
1182         my $class = $args->{class} || '';
1183         my $stringify = $args->{stringify} || '';
1184         for my $item (@$items) {
1185             my @pks; # for future multiple key support
1186             push @pks, shift @$item foreach $class->columns('Primary');
1187                 my $id = $pks[0];
1188                 $id =~ s/^0+//;  # In case zerofill is on .
1189                 my $val = defined $id ? $id : '';
1190                 my $opt = HTML::Element->new("option", value =>$val);
1191                 $opt->attr(selected => "selected") if $selected->{$id};
1192                 
1193                 my $content = ($class and $stringify and $class->can($stringify)) ? 
1194                               $class->$stringify($_) : 
1195                                   join( '/', map { $_ if $_; }@{$item} );
1196                 $opt->push_content( $content );
1197         push @res, $opt; 
1198     }
1199     return @res;
1200 }
1201
1202
1203 sub _options_from_array {
1204     my ($self, $items, $args) = @_;
1205     my $selected = $args->{selected} || {};
1206     my @res;
1207     for (@$items) {
1208                 my $val = defined $_ ? $_ : '';
1209         my $opt = HTML::Element->new("option", value => $val);
1210         #$opt->attr(selected => "selected") if $selected =~/^$id$/;
1211         $opt->attr(selected => "selected") if $selected->{$_};
1212         $opt->push_content( $_ );
1213         push @res, $opt;
1214     }
1215     return @res;
1216 }
1217
1218 sub _options_from_hash {
1219     my ($self, $items, $args) = @_;
1220     my $selected = $args->{selected} || {};
1221     my @res;
1222
1223     my @values = values %$items;
1224     # hash Key is the option content  and the hash value is option value
1225     for (sort keys %$items) {
1226                 my $val = defined $items->{$_} ? $items->{$_} : '';
1227         my $opt = HTML::Element->new("option", value => $val);
1228         #$opt->attr(selected => "selected") if $selected =~/^$id$/;
1229         $opt->attr(selected => "selected") if $selected->{$items->{$_}};
1230         $opt->push_content( $_ );
1231         push @res, $opt;
1232     }
1233     return @res;
1234 }
1235
1236
1237 sub _options_from_hashes {
1238     my ($self, $items, $args) = @_;
1239         my $selected = $args->{selected} || {};
1240         my $pk = eval {$args->{class}->primary_column} || 'id';
1241         my $fclass = $args->{class} || '';
1242         my $stringify = $args->{stringify} || '';
1243         my @res;
1244         for (@$items) {
1245                 my $val = defined $_->{$pk} ? $_->{$pk} : '';
1246                 my $opt = HTML::Element->new("option", value => $val);
1247                 $opt->attr(selected => "selected") if $selected->{$val};
1248                 my $content = ($fclass and $stringify and $fclass->can($stringify)) ? 
1249                               $fclass->$stringify($_) : 
1250                                   join(' ', @$_);
1251                 $opt->push_content( $content );
1252         push @res, $opt; 
1253     }
1254         return @res;
1255 }
1256
1257 # TODO -- Maybe
1258 #sub _to_select_or_create {
1259 #       my ($self, $col, $args) = @_;
1260 #       $args->{name} ||= $col;
1261 #       my $select = $self->to_field($col, 'select', $args);
1262 #       $args->{name} = "create_" . $args->{name};
1263 #       my $create = $self->to_field($col, 'foreign_inputs', $args);
1264 #       $create->{'__select_or_create__'} = 
1265 #               $self->to_field('__select_or_create__',{ name => '__select_or_create__' , value => 1 } );
1266 #       return ($select, $create);
1267 #}
1268         
1269
1270 # checkboxes: if no data in hand (ie called as class method), replace
1271 # with a radio button, in order to allow this field to be left
1272 # unspecified in search / add forms.
1273
1274 # Not tested
1275 # TODO  --  make this general checkboxse
1276
1277 #
1278 sub _to_checkbox {
1279     my ($self, $col, $args) = @_;
1280     my $nullable = eval {self->column_nullable($col)} || 0; 
1281     return $self->_to_radio($col) if !ref($self) || $nullable;
1282     my $value = $self->$col;
1283     my $a = HTML::Element->new("input", type=> "checkbox", name => $col);
1284     $a->attr("checked" => 'true') if $value eq 'Y';
1285     return $a;
1286 }
1287
1288
1289 # TODO  -- make this general radio butons
1290 #
1291 sub _to_radio {
1292     my ($self, $col) = @_;
1293     my $value = ref $self && $self->$col || '';
1294     my $nullable = eval {self->column_nullable($col)} || 0; 
1295     my $a = HTML::Element->new("span");
1296     my $ry = HTML::Element->new("input", type=> "radio", name=>$col, value=>'Y' );
1297     my $rn = HTML::Element->new("input", type=> "radio", name=>$col, value=>'N' );
1298     my $ru = HTML::Element->new("input", type=> "radio", name=>$col, value=>'' ) if $nullable;
1299     $ry->push_content('Yes'); $rn->push_content('No');
1300     $ru->push_content('n/a') if $nullable;
1301     if ($value eq 'Y') { $ry->attr("checked" => 'true') }
1302     elsif ($value eq 'N') { $rn->attr("checked" => 'true') }
1303     elsif ($nullable) { $ru->attr("checked" => 'true') }
1304     $a->push_content($ry, $rn);
1305     $a->push_content($ru) if $nullable;
1306     return $a;
1307 }
1308
1309
1310
1311 ############################ HELPER METHODS ######################
1312 ##################################################################
1313
1314 =head2 _rename_foreign_input
1315
1316 _rename_foreign_input($html_el_or_hash_of_them); # changes made by reference
1317
1318 Recursively renames the foreign inputs made by _to_foreign_inputs so they 
1319 can be processed generically.  The format is "accessor__AsForeign_colname". 
1320
1321 So if an Employee is a Person who has_own  Address and you call 
1322
1323   Employee->to_field("person")  
1324   
1325 then you will get inputs for the Person as well as their Address (by default,
1326 override _field_from_relationship to change logic) named like this: 
1327
1328   person__AsForeign__address__AsForeign__street
1329   person__AsForeign__address__AsForeign__city
1330   person__AsForeign__address__AsForeign__state  
1331   person__AsForeign__address__AsForeign__zip  
1332
1333 And the processor would know to create this address, put the address id in
1334 person->address data slot, create the person and put the person id in the employee->person data slot and then create the employee with that data.
1335
1336 Overriede make_element_foreign to change how you want a foreign param labeled.
1337
1338 =head2 make_element_foreign
1339
1340   $class->make_element_foreign($accessor, $element);
1341   
1342 Makes an HTML::Element type object foreign elemen representing the 
1343 class's accessor.  (IE this in an input element for $class->accessor :) )
1344
1345 =cut
1346
1347 sub make_element_foreign {
1348         my ($self, $accssr, $element)  = @_;
1349         $element->attr( name => $accssr . "__AsForeign__" . $element->attr('name'));
1350 }
1351
1352
1353
1354 sub _rename_foreign_input {
1355         my ($self, $accssr, $element) = @_;
1356         if ( ref $element ne 'HASH' ) {
1357         #       my $new_name = $accssr . "__AsForeign__" . $input->attr('name');
1358                 $self->make_element_foreign($accssr, $element);
1359         }
1360         else {
1361                 $self->_rename_foreign_input($accssr, $element->{$_}) 
1362                         foreach (keys %$element);
1363         }
1364 }
1365 =head2 _box($value) 
1366
1367 This functions computes the dimensions of a textarea based on the value 
1368 or the defaults.
1369
1370 =cut
1371
1372 our ($min_rows, $max_rows, $min_cols, $max_cols) = (2 => 50, 20 => 100);
1373 sub _box
1374 {
1375     my $text = shift;
1376     if ($text) {
1377         my @rows = split /^/, $text;
1378         my $cols = $min_cols;
1379         my $chars = 0;
1380         for (@rows) {
1381             my $len = length $_;
1382             $chars += $len;
1383             $cols = $len if $len > $cols;
1384             $cols = $max_cols if $cols > $max_cols;
1385         }
1386         my $rows = @rows;
1387         $rows = int($chars/$cols) + 1 if $chars/$cols > $rows;
1388         $rows = $min_rows if $rows < $min_rows;
1389         $rows = $max_rows if $rows > $max_rows;
1390         ($rows, $cols)
1391     }
1392     else { ($min_rows, $min_cols) }
1393 }
1394
1395
1396 1;
1397
1398
1399 =head1 CHANGES
1400
1401 =head1 MAINTAINER 
1402
1403 Maypole Developers
1404
1405 =head1 AUTHORS
1406
1407 Peter Speltz, Aaron Trevena 
1408
1409 =head1 AUTHORS EMERITUS
1410
1411 Simon Cozens, Tony Bowden
1412
1413 =head1 TODO
1414
1415   Documenting 
1416   Testing - lots
1417   chekbox generalization
1418   radio generalization
1419   select work
1420   Make link_hidden use standard make_url stuff when it gets in Maypole
1421   How do you tell AF --" I want a has_many select box for this every time so,
1422      when you call "to_field($this_hasmany)" you get a select box
1423
1424 =head1 BUGS and QUERIES
1425
1426 Please direct all correspondence regarding this module to:
1427  Maypole list. 
1428
1429 =head1 COPYRIGHT AND LICENSE
1430
1431 Copyright 2003-2004 by Simon Cozens / Tony Bowden
1432
1433 This library is free software; you can redistribute it and/or modify
1434 it under the same terms as Perl itself.
1435
1436 =head1 SEE ALSO
1437
1438 L<Class::DBI>, L<Class::DBI::FromCGI>, L<HTML::Element>.
1439
1440 =cut
1441