File Coverage

blib/lib/XAO/DO/Web/FilloutForm.pm
Criterion Covered Total %
statement 54 433 12.4
branch 15 332 4.5
condition 4 270 1.4
subroutine 7 27 25.9
pod 11 17 64.7
total 91 1079 8.4


line stmt bran cond sub pod time code
1             =head1 NAME
2              
3             XAO::DO::Web::FilloutForm - support for HTML forms
4              
5             =head1 DESCRIPTION
6              
7             Fill out form object. Helps to create fill-out forms for registration
8             and so on. Checks that parameters are Ok and then displays either form
9             or thanks.
10              
11             Must be overriden with something which will put 'fields' parameter
12             into $self. Format is as array of hash references reference of the
13             following structure:
14              
15             [ { name => field name,
16             required => 0 || 1,
17             style => selection || text || textarea || email || phone ||
18             integer || dollars || real,
19             maxlength => maximum length,
20             minlength => minimum length,
21             param => name of parameter for form substitution,
22             text => description of parameter,
23             },
24             { ... }
25             ]
26              
27             If you do not care in what order fields are checked you can also
28             supply 'fields' as a hash reference:
29              
30             { name => {
31             required => 0 || 1,
32             style => selection || text || textarea || email || phone ||
33             integer || dollars || real,
34             maxlength => maximum length,
35             minlength => minimum length,
36             param => name of parameter for form substitution,
37             text => description of parameter,
38             },
39             name1 => { ... }
40             }
41              
42             When form filled out "form_ok" method is called, which must be
43             overridden in inherited object to do something good with
44             results. Alternatively reference to subroutine can be given through
45             'setup' method. This is suitable for using FilloutForm object without
46             overriding it.
47              
48             Displays form with PARAM.VALUE set to value, PARAM.NAME - to name,
49             PARAM.TEXT - to text, PARAM.REQUIRED to the 0/1 required flag, and
50             PARAM.HTML - to piece of HTML code if applicable (Country selection for
51             example).
52              
53             =head1 METHODS
54              
55             =over
56              
57             =cut
58              
59             ###############################################################################
60             package XAO::DO::Web::FilloutForm;
61 1     1   920 use strict;
  1         2  
  1         34  
62 1     1   6 use XAO::Utils qw(:args :debug :html);
  1         2  
  1         289  
63 1     1   8 use XAO::Errors qw(XAO::DO::Web::FilloutForm);
  1         2  
  1         7  
64 1     1   420 use base XAO::Objects->load(objname => 'Web::Page');
  1         3  
  1         5  
65              
66             our $VERSION='2.029';
67              
68             sub setup ($%);
69             sub field_desc ($$;$);
70             sub field_names ($);
71             sub display ($;%);
72             sub form_ok ($%);
73             sub form_phase ($);
74             sub check_form ($%);
75             sub pre_check_form ($%);
76             sub countries_list ();
77             sub us_continental_states_list ();
78             sub us_states_list ();
79             sub cc_list ($);
80             sub cc_validate ($%);
81             sub calculate_year ($$);
82              
83             ###############################################################################
84              
85             =item new (%)
86              
87             Overrided new method for those who prefer to use inheritance style.
88              
89             =cut
90              
91             sub new ($%) {
92 1     1 1 19 my $proto=shift;
93 1   33     6 my $class=ref($proto) || $proto;
94 1         5 my $args=get_args(\@_);
95 1         19 my $self=$proto->SUPER::new($args);
96              
97             # Setting up fields if required
98             #
99             $self->setup_fields(fields => $args->{'fields'},
100 1 50       70 values => $args->{'values'}) if $args->{'fields'};
101              
102             # Done
103             #
104 1         4 $self;
105             }
106              
107              
108             ###############################################################################
109              
110             # Setting object up for use as embedded form checker from other
111             # non-derived objects.
112             #
113             # Arguments are:
114             # fields => fields descriptions
115             # values => values for fields, unless this is set all values
116             # are cleaned
117             # extra_data => reference to any data, subroutines will then be
118             # able to access it.
119             # form_ok => form_ok subroutine reference (mandatory)
120             # pre_check_form => pre_check_form subroutine reference
121             # check_form => check_form subroutine reference
122             # submit_name => name of the submit button
123             # keep_form => display form template even when the form is complete
124             #
125             # Call to this subroutine is not required from derived objects, use
126             # method overriding instead when possible!
127             #
128              
129             sub setup ($%) {
130 0     0 0 0 my $self=shift;
131 0         0 my $args=get_args(\@_);
132              
133             # Fields and values
134             #
135             $self->setup_fields(
136             fields => $args->{'fields'},
137 0         0 values => $args->{'values'},
138             );
139              
140             # Handlers and special data:
141             # extra_data - passed to handlers as is.
142             # submit_name - name of submit button for pre-filled forms (change form).
143             # dont_sanitize - don't remove <> from CGI input
144             #
145 0         0 my @names=qw(extra_data submit_name form_ok pre_check_form check_form keep_form dont_sanitize);
146 0         0 @{$self}{@names}=@{$args}{@names};
  0         0  
  0         0  
147              
148 0   0     0 my $values=$args->{'values'} || {};
149 0         0 foreach my $fdata (@{$self->{'fields'}}) {
  0         0  
150 0         0 $fdata->{'value'}=$values->{$fdata->{'name'}};
151             }
152             }
153              
154             ###############################################################################
155              
156             =item setup_fields (%)
157              
158             Copying fields descriptions. We copy entire structure here because it
159             could be persistent and we do not want original data to be modified.
160              
161             =cut
162              
163             sub setup_fields ($%) {
164 0     0 1 0 my $self=shift;
165 0         0 my $args=get_args(\@_);
166 0         0 my $fields=$args->{'fields'};
167 0 0 0     0 return unless $fields && ref($fields);
168              
169 0         0 my $values=$args->{'values'};
170 0         0 my @copy;
171 0 0       0 foreach my $fdata (ref($fields) eq 'ARRAY' ? @{$fields}
  0         0  
172 0         0 : keys %{$fields}) {
173 0         0 my $name;
174 0 0       0 if(! ref($fdata)) {
175 0         0 $name=$fdata;
176 0         0 $fdata=$fields->{$name};
177 0         0 $fdata->{'name'}=$name;
178             }
179             else {
180 0         0 $name=$fdata->{'name'};
181             }
182              
183 0         0 my %cd;
184 0         0 @cd{keys %{$fdata}}=values %{$fdata};
  0         0  
  0         0  
185 0 0 0     0 $cd{'value'}=$values->{$name} if $values && $values->{$name};
186 0         0 push(@copy,\%cd);
187             }
188              
189 0         0 $self->{'fields'}=\@copy;
190             }
191              
192             ###############################################################################
193              
194             =item display (%)
195              
196             Displaying the form.
197              
198             =cut
199              
200             sub display ($;%) {
201 0     0 1 0 my $self=shift;
202 0         0 my $args=get_args(\@_);
203 0         0 my $cgi=$self->cgi;
204 0         0 my $fields=$self->{'fields'};
205 0 0       0 $fields || throw XAO::E::DO::Web::FilloutForm
206             "display - has not set fields for FilloutForm";
207 0         0 my $phase=$self->{'phase'}=$args->{'phase'};
208 0 0       0 $self->{'submit_name'}=$args->{'submit_name'} if $args->{'submit_name'};
209              
210             # Checking the type of fields argument we have - hash or
211             # array? Converting to array if it is a hash.
212             #
213 0 0       0 if(ref($fields) eq 'HASH') {
214 0         0 my @newf;
215 0         0 foreach my $name (keys %{$fields}) {
  0         0  
216 0         0 $fields->{$name}->{'name'}=$name;
217 0         0 push @newf,$fields->{$name};
218             }
219 0         0 $self->{'fields'}=$fields=\@newf;
220             }
221              
222             # Pre-checking form with external overridable function.
223             #
224 0         0 $self->pre_check_form($args);
225              
226             # Displayable object
227             #
228 0         0 my $obj=$self->object;
229              
230             # Special parameter named 'submit_name' contains submit button name
231             # and used for pre-filled forms - these forms usually already have
232             # valid data and we need some way to know when the form was really
233             # checked and corrected by user.
234             #
235 0         0 my $have_cgivalues=0;
236 0         0 my $have_submit=1;
237 0 0       0 if($self->{'submit_name'}) {
238             $have_submit=($cgi->param($self->{'submit_name'}) ||
239             $cgi->param($self->{'submit_name'}.'.x') ||
240 0 0 0     0 $cgi->param($self->{'submit_name'}.'.y')
241             ) ? 1 : 0;
242 0         0 $have_cgivalues=$have_submit;
243             }
244              
245             # First checking all parameters and collecting mistakes into errstr.
246             #
247             # Also creating hash with parameters for form diplaying while we are
248             # going through fields anyway.
249             #
250 0         0 my $errstr;
251             my %formparams;
252              
253 0   0     0 my $dont_sanitize=$self->{'dont_sanitize'} || $args->{'dont_sanitize'};
254              
255 0         0 foreach my $fdata (@{$fields}) {
  0         0  
256 0         0 my $name=$fdata->{'name'};
257 0   0     0 my $param=$fdata->{'param'} || uc($name);
258              
259 0         0 my $cgivalue=$cgi->param($name);
260 0 0       0 $have_cgivalues++ if defined($cgivalue);
261              
262             # Unless we have a 'dont_sanitize' argument we remove angle
263             # brackets to prevent XSS attacks.
264             #
265 0 0 0     0 if(defined $cgivalue && !$dont_sanitize) {
266 0         0 $cgivalue=~s/[<>]/ /sg;
267             }
268              
269             # Checking form phase for multi-phased forms if required.
270             #
271 0 0 0     0 next if defined($fdata->{'phase'}) && $phase<$fdata->{'phase'};
272              
273 0         0 my $value=$fdata->{'newvalue'};
274 0 0       0 $value=$cgivalue unless defined($value);
275 0 0       0 if(!$have_cgivalues) {
276 0 0       0 $value=$fdata->{'value'} unless defined($value);
277 0 0       0 $value=$fdata->{'default'} unless defined($value);
278             }
279              
280             # Empty value is the same as undefined. Spaces are trimmed from the
281             # beginning and the end of the string.
282             #
283 0 0       0 $value="" unless defined $value;
284 0         0 $value=~s/^\s*(.*?)\s*$/$1/g;
285              
286             # Various checks depending on field style.
287             #
288 0         0 my $newerr;
289 0   0     0 my $style=$fdata->{'style'} || $fdata->{'type'} ||
290             throw $self "display - no style or type in field '$name'";
291 0 0 0     0 if(!length($value) && $fdata->{'required'}) {
    0 0        
    0 0        
    0 0        
    0 0        
    0 0        
    0 0        
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
292 0         0 $newerr=$self->Tx('Required field!');
293             }
294             elsif($fdata->{'maxlength'} && length($value) > $fdata->{'maxlength'}) {
295 0         0 $newerr=$self->Tx('Value is too long!');
296             }
297             elsif($fdata->{'minlength'} && length($value) &&
298             length($value) < $fdata->{'minlength'}) {
299 0         0 $newerr=$self->Tx("Value is too short!");
300             }
301             elsif($style eq 'text') {
302             # No checks for text
303             }
304             elsif($style eq 'textarea') {
305             # No checks for textarea
306             }
307             elsif($style eq 'file') {
308 0 0       0 if(!$value) {
309 0         0 $newerr=$self->Tx("No filename given");
310             }
311             }
312             elsif($style eq 'email') {
313 0 0 0     0 if(length($value) && $value !~ /^[\w\.\+\/\$\%\&\`{}'=-]+\@([a-z0-9-]+\.)+[a-z]+$/i) {
314 0         0 $newerr=$self->Tx("Value is not in the form of user\@host.domain!");
315             }
316             }
317             elsif($style eq 'usphone') {
318 0 0       0 $fdata->{'maxlength'}=15 unless $fdata->{'maxlength'};
319 0 0       0 if(length($value)) {
320 0         0 $value =~ s/\D//g;
321 0 0       0 if(length($value) == 7) {
    0          
    0          
322 0         0 $newerr=$self->Tx("Needs area code!");
323             }
324             elsif(length($value) == 11) {
325 0 0       0 if(substr($value,0,1) ne '1') {
326 0         0 $newerr=$self->Tx("Must be a US phone!");
327             }
328             }
329             elsif(length($value) != 10) {
330 0         0 $newerr=$self->Tx("Does not look like a right phone!");
331             }
332             else {
333 0         0 $value=~s/^.?(...)(...)(....)/($1) $2-$3/;
334             }
335             }
336             }
337             elsif($style eq 'phone') { # +99 (123) 456-78-90 x 123
338 0 0       0 $fdata->{'maxlength'}=30 unless $fdata->{'maxlength'};
339 0 0       0 if(length($value)) {
340 0         0 my ($p,$e)=split(/[a-zA-Z]+/,$value);
341              
342 0         0 $p=~s/\D//g;
343 0   0     0 $e||='';
344 0         0 $e=~s/\D//g;
345              
346 0 0       0 if(length($p)<10) {
    0          
    0          
347 0         0 $newerr=$self->Tx("Needs area code!");
348             }
349             elsif(length($p)==10) {
350 0         0 $p='1' . $p;
351             }
352             elsif(length($p)>13) {
353 0         0 $newerr=$self->Tx("Too many digits!");
354             }
355              
356 0 0       0 if(!$newerr) {
357 0         0 ($value=$p)=~s/^(.+)(...)(...)(....)$/+$1 ($2) $3-$4/;
358 0 0       0 $value.=" ext. $e" if $e;
359             }
360             }
361             }
362             elsif($style eq 'int' || $style eq 'integer' || $style eq 'number') {
363 0 0       0 if(length($value)) {
364 0 0       0 if($value =~ /^-?[\d,']+$/) {
365 0         0 $value=~s/[,']+//g;
366 0 0 0     0 if(defined($fdata->{'minvalue'}) && $value<$fdata->{'minvalue'}) {
367             $newerr=$self->Tx("Value is less than {{min}}",
368 0         0 { min => $fdata->{'minvalue'} });
369             }
370 0 0 0     0 if(defined($fdata->{'maxvalue'}) && $value>$fdata->{'maxvalue'}) {
371             $newerr=$self->Tx("Value is greater than {{max}}",
372 0         0 { max => $fdata->{'maxvalue'} });
373             }
374             }
375             else {
376 0         0 $newerr=$self->Tx("Is not an integer!");
377             }
378             }
379             }
380             elsif($style eq 'real') {
381 0 0       0 if(length($value)) {
382 0 0       0 if($value =~ /^-?[\d,'\.]+$/) {
383 0         0 $value=~s/[,']+//g;
384 0 0 0     0 if(defined($fdata->{'minvalue'}) && $value<$fdata->{'minvalue'}) {
385             $newerr=$self->Tx("Value is less than {{min}}",
386 0         0 { min => $fdata->{'minvalue'} });
387             }
388 0 0 0     0 if(defined($fdata->{'maxvalue'}) && $value>$fdata->{'maxvalue'}) {
389             $newerr=$self->Tx("Value is greater than {{max}}",
390 0         0 { max => $fdata->{'maxvalue'} });
391             }
392             }
393             else {
394 0         0 $newerr=$self->Tx("Is not a number!");
395             }
396             }
397             }
398             elsif($style eq 'password') {
399 0 0 0     0 if(length($value) && $fdata->{'pair'} &&
      0        
400             $value ne $cgi->param($fdata->{'pair'})) {
401 0         0 $newerr=$self->Tx("Does not match the copy!");
402             }
403             }
404             elsif($style eq 'country') {
405 0         0 my @cl=$self->countries_list();
406 0         0 my $match=0;
407 0         0 foreach my $c (@cl) {
408 0         0 $match=lc($c) eq lc($value);
409 0 0       0 last if $match;
410             }
411 0 0 0     0 if(length($value) && !$match) {
412 0         0 $newerr=$self->Tx("Unknown country");
413             }
414             }
415             elsif($style eq 'usstate' || $style eq 'uscontst') {
416 0 0       0 my @cl=$style eq 'usstate' ? $self->us_states_list()
417             : $self->us_continental_states_list();
418 0         0 my $match=0;
419 0   0     0 my $sv=substr($value || '',0,2);
420 0         0 foreach my $c (@cl) {
421 0         0 $match=lc(substr($c,0,2)) eq lc($sv);
422 0 0       0 last if $match;
423             }
424 0 0 0     0 if(length($value) && !$match) {
425 0         0 $newerr=$self->Tx("Unknown state");
426             }
427             }
428             elsif($style eq 'cctype') {
429 0         0 my @cl=$self->cc_list();
430 0         0 my $match=0;
431 0         0 foreach my $c (@cl) {
432 0         0 $match=lc($c) eq lc($value);
433 0 0       0 last if $match;
434             }
435 0 0 0     0 if(length($value) && !$match) {
436 0         0 $newerr=$self->Tx("Unknown credit card type");
437             }
438             }
439             elsif($style eq 'ccnum') {
440 0 0       0 if(length($value)) {
441 0 0       0 my $type=$fdata->{'pair'} ? $cgi->param($fdata->{'pair'}) : '';
442 0         0 $newerr=$self->cc_validate(type => $type, number => $value, validated => \$value);
443             }
444             }
445             elsif($style eq 'month') {
446 0 0       0 if(length($value)) {
447 0         0 $value=int($value);
448 0 0 0     0 if($value<1 || $value>12) {
449 0         0 $newerr=$self->Tx('Invalid month!');
450             }
451             }
452             }
453             elsif($style eq 'year') {
454 0 0 0     0 if($fdata->{'minyear'} && $fdata->{'maxyear'}) {
    0          
455 0         0 my $minyear=$self->calculate_year($fdata->{'minyear'});
456 0         0 my $maxyear=$self->calculate_year($fdata->{'maxyear'});
457 0 0       0 if(length($value)) {
458 0         0 $value=$self->calculate_year($value);
459 0 0       0 if($value<$minyear) {
    0          
460 0         0 $newerr=$self->Tx("Must be after {{year}}",
461             { year => $minyear });
462             }
463             elsif($value>$maxyear) {
464 0         0 $newerr=$self->Tx("Must be before {{year}}",
465             { year => $maxyear });
466             }
467             }
468             }
469             elsif(length($value)) {
470 0         0 $value=$self->calculate_year($value);
471 0 0 0     0 if($value<1900 || $value>2099) {
472 0         0 $newerr=$self->Tx('Invalid year!');
473             }
474             }
475             }
476             elsif($style eq 'checkbox') {
477              
478             # If checkbox is not checked we don't get any info about it
479             # in the cgi parameters. So we have to take a guess if the
480             # form was generally filled in, but we have an unchecked
481             # checkbox or this is the first display and form was not
482             # submitted yet.
483             #
484 0 0       0 if($have_cgivalues) {
485 0 0       0 $value=(defined $fdata->{'newvalue'} ? $fdata->{'newvalue'} : $cgivalue) ? 1 : 0;
    0          
486             }
487             else {
488 0 0       0 $value=(defined($fdata->{'value'}) ? $fdata->{'value'} : $fdata->{'default'}) ? 1 : 0;
    0          
489             }
490             }
491             elsif($style eq 'selection') {
492 0 0       0 if(length($value)) {
493 0         0 my $opt=$fdata->{'options'};
494 0 0       0 if(ref($opt) eq 'HASH') {
    0          
495 0 0       0 if(!defined $opt->{$value}) {
496 0         0 $newerr=$self->Tx('Bad option value!');
497             }
498             }
499             elsif(ref($opt) eq 'ARRAY') {
500 0         0 my $found;
501 0         0 for(my $i=0; $i<@$opt; $i+=2) {
502 0 0       0 next unless defined($opt->[$i+1]);
503 0 0       0 if($opt->[$i] eq $value) {
504 0         0 $found=1;
505 0         0 last;
506             }
507             }
508 0 0       0 if(!$found) {
509 0         0 $newerr=$self->Tx('Bad option value!');
510             }
511             }
512             else {
513 0         0 $newerr=$self->Tx('Unknown data in options!');
514             }
515             }
516             }
517             else {
518 0         0 $self->throw("display - unknown style '$style'");
519             }
520              
521             # If the form is not filled at all we empty the errstr
522             #
523 0 0 0     0 if($newerr && (!$have_submit || !$have_cgivalues)) {
      0        
524 0         0 $newerr = '';
525             }
526              
527             # Adding error description to the list if there was an
528             # error. Storing value otherwise.
529             #
530 0 0       0 if($newerr) {
531 0   0     0 $errstr.=($fdata->{'text'} || $name) . ": " . $newerr . "
\n";
532 0         0 $fdata->{'errstr'}=$newerr;
533             }
534             else {
535 0         0 $fdata->{'value'}=$value;
536             }
537              
538 0         0 $fdata->{'rawvalue'}=$value;
539              
540             # Filling formparams hash
541             #
542 0 0       0 $formparams{"$param.VALUE"}=defined($value) ? $value : "";
543 0   0     0 $formparams{"$param.TEXT"}=$fdata->{'text'} || $name;
544 0         0 $formparams{"$param.NAME"}=$name;
545 0 0       0 $formparams{"$param.REQUIRED"}=$fdata->{'required'} ? 1 : 0;
546 0   0     0 $formparams{"$param.SIZE"}=$fdata->{'size'} || 30;
547 0   0     0 $formparams{"$param.ROWS"}=$fdata->{'rows'} || 1;
548 0   0     0 $formparams{"$param.MAXLENGTH"}=$fdata->{'maxlength'} || 100;
549 0   0     0 $formparams{"$param.MINLENGTH"}=$fdata->{'minlength'} || 0;
550 0   0     0 $formparams{"$param.ERRSTR"}=$fdata->{'errstr'} || '';
551             }
552              
553             # Checking content for general compatibility by overriden
554             # method. Called only if data are basicly good.
555             #
556 0 0 0     0 if($have_submit && $have_cgivalues && !$errstr) {
      0        
557 0         0 my @rc=$self->check_form(merge_refs($args,\%formparams));
558 0 0       0 if(@rc<2) {
    0          
559 0   0     0 $formparams{"ERRSTR.CHECK_FORM"}=$errstr=($rc[0] || '');
560             }
561             elsif(scalar(@rc)%2 == 0) {
562 0         0 for(my $i=0; $i<@rc; $i+=2) {
563 0   0     0 my $e=($rc[$i] || '');
564 0 0       0 next unless $e;
565 0         0 my $fname=$rc[$i+1];
566 0 0       0 if($fname) {
567 0         0 my $fdata=$self->field_desc($fname);
568 0   0     0 my $param=$fdata->{'param'} || uc($fdata->{'name'});
569              
570 0 0       0 if($fdata->{'errstr'}) {
571 0 0       0 $fdata->{'errstr'}.=($fdata->{'errstr'} =~ /\.\s*$/ ? ' ' : '; ') . $e;
572 0         0 $formparams{"$param.ERRSTR"}=$fdata->{'errstr'};
573             }
574             else {
575 0         0 $fdata->{'errstr'}=$formparams{"$param.ERRSTR"}=$e;
576             }
577              
578 0 0       0 $errstr.="\n
" if $errstr;
579 0         0 $errstr.=$e;
580             }
581             else {
582 0 0       0 $errstr.="\n
" if $errstr;
583 0 0       0 $formparams{'ERRSTR.CHECK_FORM'}.="\n
" if $errstr;
584 0         0 $errstr.=$e;
585 0         0 $formparams{'ERRSTR.CHECK_FORM'}.=$e;
586             }
587             }
588             }
589             else {
590 0         0 throw $self "display - wrong number of results (".join('|',@rc).")";
591             }
592             }
593 0   0     0 $formparams{"ERRSTR.CHECK_FORM"}||='';
594              
595             # If the form is not filled at all we remove errstr's from
596             # individual fields.
597             #
598 0 0 0     0 if(!$have_submit || !$have_cgivalues) {
599 0         0 $errstr='';
600 0         0 foreach my $fdata (@{$fields}) {
  0         0  
601 0   0     0 my $param=$fdata->{'param'} || uc($fdata->{'name'});
602 0         0 $formparams{"$param.ERRSTR"}='';
603             }
604             }
605              
606             # Building form element HTML values. Need to do this after error
607             # checking, because the element HTML might be reliant on error
608             # checking status.
609             #
610 0         0 foreach my $fdata (@{$fields}) {
  0         0  
611 0         0 my $name=$fdata->{'name'};
612 0   0     0 my $param=$fdata->{'param'} || uc($name);
613              
614 0         0 my $value=$fdata->{'rawvalue'};
615              
616 0   0     0 my $style=$fdata->{'style'} || $fdata->{'type'} ||
617             throw $self "- no style or type in field '$name'";
618              
619 0         0 my $seloptions;
620             my $selcompare;
621 0 0 0     0 if($style eq 'country') {
    0 0        
    0 0        
    0 0        
    0 0        
    0 0        
    0 0        
    0 0        
    0 0        
    0 0        
    0 0        
      0        
      0        
622 0         0 my @cl=$self->countries_list();
623             $seloptions=[
624             '' => 'Select Country',
625 0         0 (map { $_ => $_ } @cl),
  0         0  
626             ];
627 0     0   0 $selcompare=sub { return uc($_[0]) eq uc($_[1]) };
  0         0  
628             }
629             elsif($style eq 'usstate' || $style eq 'uscontst') {
630 0 0       0 my @cl=$style eq 'usstate' ? $self->us_states_list()
631             : $self->us_continental_states_list();
632             $seloptions=[
633             '' => 'Select State',
634 0         0 (map { uc(substr($_,0,2)) => $_ } @cl),
  0         0  
635             ];
636 0     0   0 $selcompare=sub { return uc($_[0]) eq uc($_[1]) };
  0         0  
637             }
638             elsif($style eq 'cctype') {
639 0         0 my @cl=$self->cc_list();
640             $seloptions=[
641             '' => 'Select Card Type',
642 0         0 (map { $_ => $_ } @cl),
  0         0  
643             ];
644 0     0   0 $selcompare=sub { return uc($_[0]) eq uc($_[1]) };
  0         0  
645             }
646             elsif($style eq 'month') {
647 0         0 my @cl=qw(January February March April May June July
648             August September October November December);
649             $seloptions=[
650             '' => 'Select Month',
651 0         0 (map { sprintf('%02u',$_) => sprintf('%02u - %s',$_,$cl[$_-1]) } (1..12)),
  0         0  
652             ];
653 0   0 0   0 $selcompare=sub { return defined $_[0] && length $_[0] && length $_[1] && $_[0] == $_[1] };
  0         0  
654             }
655             elsif($style eq 'year' && !$fdata->{'maxlength'} && $fdata->{'minyear'} && $fdata->{'maxyear'}) {
656 0         0 my $minyear=$self->calculate_year($fdata->{'minyear'});
657 0         0 my $maxyear=$self->calculate_year($fdata->{'maxyear'});
658             $seloptions=[
659             '' => 'Select Year',
660 0         0 (map { sprintf('%04u',$_) => sprintf('%04u',$_) } ($minyear..$maxyear)),
  0         0  
661             ];
662 0   0 0   0 $selcompare=sub { return defined $_[0] && length $_[0] && length $_[1] && $_[0] == $_[1] };
  0         0  
663             }
664             elsif($style eq 'checkbox') {
665             $fdata->{'html'}=$obj->expand(
666             path => '/bits/fillout-form/html-checkbox',
667             NAME => $name,
668             VALUE => $fdata->{'value'} || '',
669             CHECKED => $value ? ' checked ' : '',
670             HTMLID => $fdata->{'htmlid'} || $name,
671 0 0 0     0 ERRSTR => $fdata->{'errstr'} // '',
      0        
      0        
672             );
673             }
674             elsif($style eq 'selection') {
675 0   0     0 $seloptions=$fdata->{'options'} ||
676             $self->throw("display - no 'options' for '$name' selection");
677             }
678             elsif($style eq 'text' || $style eq 'phone' || $style eq 'usphone' ||
679             $style eq 'ccnum' || $style eq 'email' || $style eq 'year' ||
680             $style eq 'number' || $style eq 'int' || $style eq 'integer' ||
681             $style eq 'real' ) {
682             $fdata->{'html'}=$obj->expand(
683             path => '/bits/fillout-form/html-text',
684             NAME => $name,
685             VALUE => $value // '',
686             MAXLENGTH => $fdata->{'maxlength'} || 100,
687             SIZE => $fdata->{'size'} || 30,
688 0   0     0 ERRSTR => $fdata->{'errstr'} // '',
      0        
      0        
      0        
689             );
690             }
691             elsif($style eq 'textarea') {
692             $fdata->{'html'}=$obj->expand(
693             path => '/bits/fillout-form/html-textarea',
694             NAME => $name,
695             VALUE => $value // '',
696             SIZE => $fdata->{'size'} || 30,
697             ROWS => $fdata->{'rows'} || 8,
698 0   0     0 ERRSTR => $fdata->{'errstr'} // '',
      0        
      0        
      0        
699             );
700             }
701             elsif($style eq 'file') {
702             $fdata->{'html'}=$obj->expand(
703             path => '/bits/fillout-form/html-file',
704             NAME => $name,
705             SIZE => $fdata->{'size'} || 30,
706 0   0     0 ERRSTR => $fdata->{'errstr'} // '',
      0        
707             );
708             }
709             elsif($style eq 'password') {
710             $fdata->{'html'}=$obj->expand(
711             path => '/bits/fillout-form/html-password',
712             NAME => $name,
713             VALUE => $value // '',
714             MAXLENGTH => $fdata->{'maxlength'} || 100,
715             SIZE => $fdata->{'size'} || 30,
716 0   0     0 ERRSTR => $fdata->{'errstr'} // '',
      0        
      0        
      0        
717             );
718             }
719              
720             # Various selection fields above just set seloptions for uniform build.
721             #
722 0 0       0 if($seloptions) {
723 0         0 my $has_empty;
724             my $used_selected;
725 0         0 my $html='';
726             my $html_sub=sub {
727 0     0   0 my ($v,$t)=@_;
728 0 0 0     0 $has_empty=1 if !defined($v) || !length($v);
729 0 0       0 return unless defined($t);
730 0         0 my $sel='';
731 0 0       0 if(!$used_selected) {
732 0 0       0 my $equal=$selcompare ? $selcompare->($v,$value) : ($v eq $value);
733 0 0       0 if($equal) {
734 0         0 $sel=' selected';
735 0         0 $used_selected=1;
736             }
737             }
738 0         0 $html.=$obj->expand(
739             path => '/bits/fillout-form/html-select-option',
740             NAME => $name,
741             VALUE => $v,
742             TEXT => $t,
743             SELECTED=> $sel,
744             );
745 0 0       0 $formparams{"$param.RV_CURRENT_$v"}=$sel ? 1 : 0;
746 0         0 $formparams{"$param.RV_VALUE_$v"}=$v;
747 0         0 $formparams{"$param.RV_TEXT_$v"}=$t;
748 0         0 };
749              
750 0 0       0 if(ref($seloptions) eq 'HASH') {
    0          
751 0         0 foreach my $v (sort { $seloptions->{$a} cmp $seloptions->{$b} } keys %$seloptions) {
  0         0  
752 0         0 &{$html_sub}($v,$seloptions->{$v});
  0         0  
753             }
754             }
755             elsif(ref($seloptions) eq 'ARRAY') {
756 0         0 for(my $i=0; $i<@$seloptions; $i+=2) {
757 0         0 &{$html_sub}($seloptions->[$i],$seloptions->[$i+1]);
  0         0  
758             }
759             }
760             else {
761 0         0 throw $self "Unknown data type in 'options' name=$name";
762             };
763              
764             # We do not display 'Please select' if there is an empty
765             # value in the list, we assume that that empty value is a
766             # prompt of some sort.
767             #
768             # If there is no need for empty value and no need for a
769             # prompt -- use ('' => undef) as an indicator of that.
770             #
771 0         0 $formparams{"$param.HTML_OPTIONS"}=$html;
772              
773             # For compatibility with older code this is not included in
774             # HTML_OPTIONS
775             #
776 0 0       0 if(!$has_empty) {
777 0         0 $html='
778             t2ht($self->Tx('Please select')) .
779             ''.$html;
780             }
781              
782             # Final code
783             #
784             $fdata->{'html'}=$obj->expand(
785             path => '/bits/fillout-form/html-select',
786             NAME => $name,
787             VALUE => $value // '',
788             OPTIONS => $html,
789 0   0     0 ERRSTR => $fdata->{'errstr'},
790             );
791             }
792              
793 0   0     0 $formparams{"$param.HTML"}=$fdata->{'html'} || "";
794             }
795              
796             # If there were errors then displaying the form. We also display
797             # the form here if it is not yet filled out and if it is, but we we
798             # asked to keep displaying it using 'keep_form' setup parameter.
799             #
800 0         0 my $keep_form=$self->{'keep_form'};
801 0 0 0     0 if(!$have_submit || !$have_cgivalues || $errstr || $keep_form) {
      0        
      0        
802 0         0 my $eh;
803             my $et;
804 0 0 0     0 if($errstr && $have_cgivalues) {
805             $eh=$obj->expand(
806             path => '/bits/fillout-form/errstr',
807             ERRSTR => $errstr,
808 0         0 'ERRSTR.CHECK_FORM' => $formparams{"ERRSTR.CHECK_FORM"},
809             );
810 0         0 $et=$errstr;
811              
812             }
813             $obj->display($args,\%formparams,{
814             path => $args->{'form.path'},
815 0   0     0 template => $args->{'form.template'},
      0        
816             ERRSTR => $et || '',
817             'ERRSTR.HTML' => $eh || '',
818             });
819 0 0 0     0 return unless $keep_form && !$errstr && $have_cgivalues && $have_submit;
      0        
      0        
820             }
821              
822             # Our form is correct!
823             #
824 0         0 $self->form_ok(merge_refs($args,\%formparams));
825             }
826              
827             ###############################################################################
828              
829             =item field_desc ($)
830              
831             Returns field description by name. This is the correct way to get to the
832             value of a field from check_form() or form_ok() methods.
833              
834             If the optional second parameter set to true then on failure to find the
835             field the method will return undef instead of throwing an error.
836              
837             =cut
838              
839             sub field_desc ($$;$) {
840 0     0 1 0 my ($self,$name,$soft_failure)=@_;
841              
842 0   0     0 my $fields=$self->{'fields'} ||
843             throw $self "field_desc - has not set fields for FilloutForm";
844              
845 0 0       0 if(ref($fields) eq 'ARRAY') {
846 0         0 foreach my $fdata (@{$fields}) {
  0         0  
847 0 0       0 return $fdata if $fdata->{'name'} eq $name;
848             }
849             }
850             else {
851 0 0       0 return $fields->{$name} if $fields->{$name};
852             }
853              
854 0 0       0 return undef if $soft_failure;
855              
856 0         0 throw $self "field_desc - unknown field '$name' referred";
857             }
858              
859             ###############################################################################
860              
861             =item field_names ($)
862              
863             Returns field a list of all field names in the current form.
864              
865             =cut
866              
867             sub field_names ($) {
868 0     0 1 0 my $self=shift;
869              
870 0   0     0 my $fields=$self->{'fields'} ||
871             throw $self "field_names - has not set fields for FilloutForm";
872              
873 0 0       0 if(ref($fields) eq 'ARRAY') {
874 0         0 return map { $_->{'name'} } @$fields;
  0         0  
875             }
876             else {
877 0         0 return map { $_->{'name'} } keys %$fields;
  0         0  
878             }
879             }
880              
881             ###############################################################################
882              
883             =item form_ok
884              
885             Default handler for filled out form. Must be overriden!
886              
887             =cut
888              
889             sub form_ok ($%) {
890 0     0 1 0 my $self=shift;
891 0 0       0 if($self->{'form_ok'}) {
892             my $na=merge_refs(get_args(\@_),{
893 0         0 extra_data => $self->{'extra_data'},
894             });
895 0         0 return &{$self->{'form_ok'}}($self,$na);
  0         0  
896             }
897 0         0 throw $self 'form_ok - must be overriden in derived class or using form_ok parameter';
898             }
899              
900             ##
901             # High-level form content check. Should be overriden for real checks.
902             # Returns '' if there were no error or error text otherwise.
903             #
904             sub check_form ($%) {
905 0     0 0 0 my $self=shift;
906 0 0       0 if($self->{'check_form'}) {
907 0         0 my %na=%{get_args(\@_)};
  0         0  
908 0         0 $na{'extra_data'}=$self->{'extra_data'};
909 0         0 return &{$self->{'check_form'}}($self,\%na);
  0         0  
910             }
911 0         0 '';
912             }
913              
914             ###############################################################################
915              
916             =item pre_check_form (%)
917              
918             Pre-checking form. May be used if some values are calculated or copied
919             from another and should be checked later.
920              
921             Should stuff generated values into {'newvalue'} parameter.
922              
923             =cut
924              
925             sub pre_check_form ($%) {
926 0     0 1 0 my $self=shift;
927 0 0       0 if($self->{'pre_check_form'}) {
928 0         0 my $na=get_args(\@_);
929 0         0 $na->{'extra_data'}=$self->{'extra_data'};
930 0         0 return &{$self->{'pre_check_form'}}($self,$na);
  0         0  
931             }
932             }
933              
934             ###############################################################################
935              
936             =item countries_list ()
937              
938             Returns list of countries for selection. May be overriden if site
939             needs only a fraction of that.
940              
941             =cut
942              
943             sub countries_list () {
944 0     0 1 0 split(/\n/,<<'END_OF_LIST');
945             United States
946             Afghanistan
947             Albania
948             Algeria
949             American Samoa
950             Andorra
951             Angola
952             Anguilla
953             Antarctica
954             Antigua
955             Antilles
956             Arab Emirates
957             Argentina
958             Armenia
959             Aruba
960             Australia
961             Austria
962             Azerbaidjan
963             Bahamas
964             Bahrain
965             Bangladesh
966             Barbados
967             Barbuda
968             Belarus
969             Belgium
970             Belize
971             Benin
972             Bermuda
973             Bhutan
974             Bolivia
975             Bosnia Herz.
976             Botswana
977             Bouvet Isl.
978             Brazil
979             Brunei Dar.
980             Bulgaria
981             Burkina Faso
982             Burundi
983             C. African Rep.
984             Cambodia
985             Cameroon
986             Cambodia
987             Cameroon
988             Canada
989             Cape Verde
990             Cayman Islands
991             Chad
992             Chile
993             China
994             Christmas Isl.
995             Cocos Islands
996             Colombia
997             Comoros
998             Congo
999             Cook Islands
1000             Costa Rica
1001             Croatia
1002             Cuba
1003             Cyprus
1004             Czech Republic
1005             Denmark
1006             Djibouti
1007             Dominica
1008             Dominican Rep.
1009             East Timor
1010             Ecuador
1011             Egypt
1012             England
1013             El Salvador
1014             Equat. Guinea
1015             Eritrea
1016             Estonia
1017             Ethiopia
1018             Falkland Isl.
1019             Faroe Islands
1020             Fiji
1021             Finland
1022             Former Czech.
1023             Former USSR
1024             France
1025             French Guyana
1026             French S. Terr.
1027             Gabon
1028             Gambia
1029             Georgia
1030             Germany
1031             Ghana
1032             Gibraltar
1033             Great Britain
1034             Greece
1035             Greenland
1036             Grenada
1037             Guadeloupe
1038             Grenada
1039             Guadeloupe
1040             Guam (USA)
1041             Guatemala
1042             Guinea
1043             Guinea Bissau
1044             Guyana
1045             Haiti
1046             Heard/McDonald
1047             Honduras
1048             Hong Kong
1049             Hungary
1050             Iceland
1051             India
1052             Indonesia
1053             Iran
1054             Iraq
1055             Ireland
1056             Israel
1057             Italy
1058             Ivory Coast
1059             Jamaica
1060             Japan
1061             Jordan
1062             Kazakhstan
1063             Kenya
1064             Kiribati
1065             Kuwait
1066             Kyrgyzstan
1067             Laos
1068             Latvia
1069             Lebanon
1070             Lesotho
1071             Liberia
1072             Libya
1073             Liechtenstein
1074             Lithuania
1075             Luxembourg
1076             Macau
1077             Macedonia
1078             Madagascar
1079             Malawi
1080             Malaysia
1081             Maldives
1082             Mali
1083             Malta
1084             Marshall Isl.
1085             Martinique
1086             Mauritania
1087             Mauritius
1088             Mayotte
1089             Mexico
1090             Mayotte
1091             Mexico
1092             Micronesia
1093             Moldavia
1094             Monaco
1095             Mongolia
1096             Montserrat
1097             Morocco
1098             Mozambique
1099             Myanmar
1100             N. Mariana Isl.
1101             Namibia
1102             Nauru
1103             Nepal
1104             Netherlands
1105             Neutral Zone
1106             New Caledonia
1107             New Zealand
1108             Nicaragua
1109             Niger
1110             Nigeria
1111             Niue
1112             Norfolk Island
1113             Northern Ireland
1114             North Korea
1115             Norway
1116             Oman
1117             Pakistan
1118             Palau
1119             Panama
1120             Papua New Guinea
1121             Paraguay
1122             Peru
1123             Philippines
1124             Pitcairn Isl.
1125             Poland
1126             Polynesia
1127             Portugal
1128             Puerto Rico
1129             Qatar
1130             Reunion
1131             Romania
1132             Russia
1133             Rwanda
1134             Samoa
1135             San Marino
1136             Saudi Arabia
1137             Scotland
1138             Senegal
1139             Seychelles
1140             Sierra Leone
1141             Singapore
1142             Sierra Leone
1143             Singapore
1144             Slovak Rep.
1145             Slovenia
1146             Solomon Isl.
1147             Somalia
1148             South Africa
1149             South Korea
1150             Spain
1151             Sri Lanka
1152             St Helena
1153             St Lucia
1154             St Pierre
1155             St Tome
1156             St Vincent
1157             Sudan
1158             Suriname
1159             Swaziland
1160             Sweden
1161             Switzerland
1162             Syrian Arab Republic
1163             Tadjikistan
1164             Taiwan
1165             Tanzania
1166             Thailand
1167             Tobago
1168             Togo
1169             Tokelau
1170             Tonga
1171             Trinidad & Tobago
1172             Tunisia
1173             Turopaque
1174             Turkmenistan
1175             Turks/Caicos Isl.
1176             Tuvalu
1177             Uganda
1178             Ukraine
1179             Uruguay
1180             Uzbekistan
1181             Vanuatu
1182             Vatican City
1183             Venezuela
1184             Vietnam
1185             Virg.Isl. (UK)
1186             Virg.Isl. (US)
1187             Wales
1188             Western Sahara
1189             Yemen
1190             Yugoslavia
1191             Zaire
1192             Zambia
1193             Zimbabwe
1194             END_OF_LIST
1195             }
1196              
1197             ###############################################################################
1198              
1199             =item us_continental_states_list ()
1200              
1201             Returns list of US continental states for selection. May be overriden
1202             if site needs only a fraction of that.
1203              
1204             =cut
1205              
1206             sub us_continental_states_list () {
1207 0     0 1 0 my $self=shift;
1208 0         0 my @list;
1209 0         0 foreach my $st ($self->us_states_list) {
1210 0 0       0 next if $st =~ /^AK/;
1211 0 0       0 next if $st =~ /^AS/;
1212 0 0       0 next if $st =~ /^FM/;
1213 0 0       0 next if $st =~ /^GU/;
1214 0 0       0 next if $st =~ /^HI/;
1215 0 0       0 next if $st =~ /^MH/;
1216 0 0       0 next if $st =~ /^MP/;
1217 0 0       0 next if $st =~ /^VI/;
1218 0         0 push(@list,$st);
1219             }
1220 0         0 @list;
1221             }
1222              
1223             ###############################################################################
1224              
1225             =item us_states_list ()
1226              
1227             Returns list of US states for selection. May be overriden if site
1228             needs only a fraction of that.
1229              
1230             =cut
1231              
1232             sub us_states_list () {
1233 0     0 1 0 split(/\n/,<<'END_OF_LIST');
1234             AL - Alabama
1235             AK - Alaska
1236             AS - American Samoa
1237             AZ - Arizona
1238             AR - Arkansas
1239             CA - California
1240             CO - Colorado
1241             CT - Connecticut
1242             DE - Delaware
1243             DC - District Of Columbia
1244             FM - Federated States Of Micronesia
1245             FL - Florida
1246             GA - Georgia
1247             GU - Guam
1248             HI - Hawaii
1249             ID - Idaho
1250             IL - Illinois
1251             IN - Indiana
1252             IA - Iowa
1253             KS - Kansas
1254             KY - Kentucky
1255             LA - Louisiana
1256             ME - Maine
1257             MH - Marshall Islands
1258             MD - Maryland
1259             MA - Massachusetts
1260             MI - Michigan
1261             MN - Minnesota
1262             MS - Mississippi
1263             MO - Missouri
1264             MT - Montana
1265             NE - Nebraska
1266             NV - Nevada
1267             NH - New Hampshire
1268             NJ - New Jersey
1269             NM - New Mexico
1270             NY - New York
1271             NC - North Carolina
1272             ND - North Dakota
1273             MP - Northern Mariana Islands
1274             OH - Ohio
1275             OK - Oklahoma
1276             OR - Oregon
1277             PW - Palau
1278             PA - Pennsylvania
1279             PR - Puerto Rico
1280             RI - Rhode Island
1281             SC - South Carolina
1282             SD - South Dakota
1283             TN - Tennessee
1284             TX - Texas
1285             UT - Utah
1286             VT - Vermont
1287             VI - Virgin Islands
1288             VA - Virginia
1289             WA - Washington
1290             WV - West Virginia
1291             WI - Wisconsin
1292             WY - Wyoming
1293             END_OF_LIST
1294             }
1295              
1296             ##
1297             # Returns a list of known Credit Card types. May be overriden. Should be
1298             # consistent with cc_validate.
1299             #
1300             sub cc_list ($) {
1301 0     0 0 0 split(/\n/,<<'END_OF_LIST');
1302             Visa
1303             American Express
1304             MasterCard
1305             Discover
1306             Diner's Club
1307             END_OF_LIST
1308             }
1309              
1310             ###############################################################################
1311              
1312             =item cc_validate (%)
1313              
1314             Returns error text if card number is invalid. Only checksum and
1315             consistence with card type is checked.
1316              
1317             Card number is taken from 'number' argument and card type from 'type'
1318             argument (optionally).
1319              
1320             Will store card number into a scalar reference given by 'validated'
1321             argument, if it exists and the card validates. Will store card type code
1322             into scalar reference given by 'typecode' argument if it exists and the
1323             card validates. Codes are:
1324              
1325             VI -- Visa
1326             AE -- American Express
1327             MC -- Mastercard
1328             DC -- Discover
1329              
1330             =cut
1331              
1332             sub cc_validate ($%) {
1333 4     4 1 228 my $self=shift;
1334 4         10 my $args=get_args(\@_);
1335              
1336 4         51 my $number=$args->{'number'};
1337 4         6 my $type=$args->{'type'};
1338              
1339             # General corrections and checks first.
1340             #
1341 4         21 $number=~s/\D//g;
1342 4 50       12 if(length($number)<13) {
1343 0         0 return $self->Tx('Number is too short!');
1344             }
1345              
1346             # Checksum first
1347             #
1348 4         6 my $sum=0;
1349 4         11 for(my $i=0; $i!=length($number)-1; $i++) {
1350 60         96 my $weight = substr($number, -1 * ($i + 2), 1) * (2 - ($i % 2));
1351 60 100       125 $sum += (($weight < 10) ? $weight : ($weight - 9));
1352             }
1353 4 50       16 if(substr($number,-1) ne (10-$sum%10)%10) {
1354             ### dprint "have ".substr($number,-1)." want ".(10-$sum%10)%10;
1355 0         0 return $self->Tx('Invalid number!');
1356             }
1357              
1358             # Guessing card type.
1359             #
1360 4         6 my $typecode;
1361 4         6 my $realtype='';
1362 4         6 my $reqlen;
1363 4 100       16 if($number =~ /^(?:34|37)/) {
    50          
    0          
    0          
1364 1         2 $realtype='american express';
1365 1         2 $typecode='AE';
1366 1         2 $reqlen=[15];
1367             }
1368             elsif($number =~ /^4/) {
1369 3         6 $realtype='visa';
1370 3         4 $typecode='VI';
1371 3         6 $reqlen=[13,16];
1372             }
1373             elsif($number =~ /^5/) {
1374 0         0 $realtype='master\s?card';
1375 0         0 $typecode='MC';
1376 0         0 $reqlen=[16];
1377             }
1378             elsif($number =~ /^6/) {
1379 0         0 $realtype='discover';
1380 0         0 $typecode='DC';
1381 0         0 $reqlen=[16];
1382             }
1383             else {
1384 0         0 return $self->Tx('Unknown card type!');
1385             }
1386 4 50       10 if($reqlen) {
1387 4 100       7 scalar(grep { length($number)==$_ } @$reqlen) ||
  7         28  
1388             return $self->Tx('Invalid number length!');
1389             }
1390              
1391             # Checking guessed type against the given type.
1392             #
1393 3 50 66     23 if($type && lc($type) !~ $realtype) {
1394 0         0 return $self->Tx('Number does not match card type!');
1395             }
1396              
1397             # Storing values if we were given these references.
1398             #
1399 3 50       10 ${$args->{'validated'}}=$number if $args->{'validated'};
  3         6  
1400 3 50       8 ${$args->{'typecode'}}=$typecode if $args->{'typecode'};
  3         6  
1401              
1402 3         12 return '';
1403             }
1404              
1405             ##
1406             # Calculates year - accepts value, +N, -N.
1407             #
1408             sub calculate_year ($$) {
1409 0     0 0 0 my $self=shift;
1410 0         0 my $year=shift;
1411 0 0       0 if(substr($year,0,1) eq '+') {
    0          
    0          
    0          
1412 0         0 $year=(localtime)[5]+1900+substr($year,1);
1413             }
1414             elsif(substr($year,0,1) eq '-') {
1415 0         0 $year=(localtime)[5]+1900-substr($year,1);
1416             }
1417             elsif($year < 20) {
1418 0         0 $year+=2000;
1419             }
1420             elsif($year < 100) {
1421 0         0 $year+=1900;
1422             }
1423 0         0 $year;
1424             }
1425              
1426             ##
1427             # Returns form phase for multi-page forms. Taken from 'phase' argument
1428             # to 'display' method.
1429             #
1430             sub form_phase ($) {
1431 0     0 0 0 my $self=shift;
1432 0   0     0 return $self->{'phase'} || 1;
1433             }
1434              
1435             ###############################################################################
1436              
1437             sub Tx ($$;$) {
1438 1     1 0 2 my $self=shift;
1439 1         2 my $text=shift;
1440 1   50     5 my $values=shift || { };
1441              
1442 1 50       10 if($self->can('Tx_translate')) {
1443 0         0 $text=$self->Tx_translate($text,$values);
1444             }
1445              
1446 1         3 $text=~s/
1447             \{\{(\w+)\}\}
1448             /
1449 0 0       0 exists $values->{$1} ? $values->{$1} : ''
1450             /xesg;
1451              
1452 1         6 return $text;
1453             }
1454              
1455             ###############################################################################
1456             1;
1457             __END__