]> Dogcows Code - chaz/p5-CGI-Ex/blob - lib/CGI/Ex/Validate.pm
CGI::Ex 2.06
[chaz/p5-CGI-Ex] / lib / CGI / Ex / Validate.pm
1 package CGI::Ex::Validate;
2
3 =head1 NAME
4
5 CGI::Ex::Validate - another form validator - but it does javascript in parallel
6
7 =cut
8
9 ###----------------------------------------------------------------###
10 # Copyright 2006 - Paul Seamons #
11 # Distributed under the Perl Artistic License without warranty #
12 ###----------------------------------------------------------------###
13
14 use strict;
15 use vars qw($VERSION
16 $DEFAULT_EXT
17 %DEFAULT_OPTIONS
18 $JS_URI_PATH
19 $JS_URI_PATH_YAML
20 $JS_URI_PATH_VALIDATE
21 $QR_EXTRA
22 @UNSUPPORTED_BROWSERS
23 );
24
25 $VERSION = '2.06';
26
27 $DEFAULT_EXT = 'val';
28 $QR_EXTRA = qr/^(\w+_error|as_(array|string|hash)_\w+|no_\w+)/;
29 @UNSUPPORTED_BROWSERS = (qr/MSIE\s+5.0\d/i);
30
31 use CGI::Ex::Conf ();
32
33 ###----------------------------------------------------------------###
34
35 sub new {
36 my $class = shift || __PACKAGE__;
37 my $self = (@_ && ref($_[0])) ? shift : {@_};
38
39 ### allow for global defaults
40 foreach (keys %DEFAULT_OPTIONS) {
41 $self->{$_} = $DEFAULT_OPTIONS{$_} if ! exists $self->{$_};
42 }
43
44 return bless $self, $class;
45 }
46
47 ###----------------------------------------------------------------###
48
49 sub cgix {
50 my $self = shift;
51 return $self->{cgix} ||= do {
52 require CGI::Ex;
53 CGI::Ex->new;
54 };
55 }
56
57 ### the main validation routine
58 sub validate {
59 my $self = (! ref($_[0])) ? shift->new # $class->validate
60 : UNIVERSAL::isa($_[0], __PACKAGE__) ? shift # $self->validate
61 : __PACKAGE__->new; # &validate
62 my $form = shift || die "Missing form hash";
63 my $val_hash = shift || die "Missing validation hash";
64 my $what_was_validated = shift; # allow for extra arrayref that stores what was validated
65
66 ### turn the form into a form if it is really a CGI object
67 if (! ref($form)) {
68 die "Invalid form hash or cgi object";
69 } elsif(! UNIVERSAL::isa($form,'HASH')) {
70 local $self->{cgi_object} = $form;
71 $form = $self->cgix->get_form($form);
72 }
73
74 ### get the validation - let get_validation deal with types
75 ### if a ref is not passed - assume it is a filename
76 $val_hash = $self->get_validation($val_hash);
77
78 ### allow for validation passed as single group hash, single group array,
79 ### or array of group hashes or group arrays
80 my @ERRORS = ();
81 my %EXTRA = ();
82 my @USED_GROUPS = ();
83 my $group_order = UNIVERSAL::isa($val_hash,'HASH') ? [$val_hash] : $val_hash;
84 foreach my $group_val (@$group_order) {
85 die "Validation groups must be a hashref" if ! UNIVERSAL::isa($group_val,'HASH');
86 my $title = $group_val->{'group title'};
87 my $validate_if = $group_val->{'group validate_if'};
88
89 ### only validate this group if it is supposed to be checked
90 next if $validate_if && ! $self->check_conditional($form, $validate_if);
91 push @USED_GROUPS, $group_val;
92
93 ### If the validation items were not passed as an arrayref.
94 ### Look for a group order and then fail back to the keys of the group.
95 ### We will keep track of what was added using %found - the keys will
96 ### be the hash signatures of the field_val hashes (ignore the hash internals).
97 my @field_keys;
98 my @group_keys;
99 foreach (sort keys %$group_val) {
100 /^(group|general)\s+(\w+)/ ? push(@group_keys, [$1, $2, $_]) : push(@field_keys, $_);
101 }
102 my $fields = $group_val->{'group fields'};
103 if ($fields) { # if I passed group fields array - use it
104 die "'group fields' must be an arrayref" if ! UNIVERSAL::isa($fields,'ARRAY');
105 } else { # other wise - create our own array
106 my @fields = ();
107 if (my $order = $group_val->{'group order'} || \@field_keys) {
108 die "Validation 'group order' must be an arrayref" if ! UNIVERSAL::isa($order,'ARRAY');
109 foreach my $field (@$order) {
110 my $field_val = exists($group_val->{$field}) ? $group_val->{$field}
111 : ($field eq 'OR') ? 'OR' : die "No element found in group for $field";
112 if (ref $field_val && ! $field_val->{'field'}) {
113 $field_val = { %$field_val, 'field' => $field }; # copy the values to add the key
114 }
115 push @fields, $field_val;
116 }
117 }
118 $fields = \@fields;
119 }
120
121 ### double check which field_vals have been used so far
122 ### add any remaining field_vals from the order
123 ### this is necessary for items that weren't in group fields or group order
124 my %found = map {$_->{'field'} => 1} @$fields;
125 foreach my $field (@field_keys) {
126 next if $found{$field};
127 my $field_val = $group_val->{$field};
128 die "Found a nonhashref value on field $field" if ! UNIVERSAL::isa($field_val, 'HASH');
129 $field_val->{'field'} = $field if ! defined $field_val->{'field'};
130 push @$fields, $field_val;
131 }
132
133 ### Finally we have our arrayref of hashrefs that each have their 'field' key
134 ### now lets do the validation
135 my $found = 1;
136 my @errors = ();
137 my $hold_error; # hold the error for a moment - to allow for an "Or" operation
138 foreach (my $i = 0; $i <= $#$fields; $i ++) {
139 my $ref = $fields->[$i];
140 if (! ref($ref) && $ref eq 'OR') {
141 $i ++ if $found; # if found skip the OR altogether
142 $found = 1; # reset
143 next;
144 }
145 $found = 1;
146 die "Missing field key during normal validation" if ! $ref->{'field'};
147 local $ref->{'was_validated'} = 1;
148 my @err = $self->validate_buddy($form, $ref->{'field'}, $ref);
149 if (delete($ref->{'was_validated'}) && $what_was_validated) {
150 push @$what_was_validated, $ref;
151 }
152
153 ### test the error - if errors occur allow for OR - if OR fails use errors from first fail
154 if (scalar @err) {
155 if ($i < $#$fields && ! ref($fields->[$i + 1]) && $fields->[$i + 1] eq 'OR') {
156 $hold_error = \@err;
157 } else {
158 push @errors, $hold_error ? @$hold_error : @err;
159 $hold_error = undef;
160 }
161 } else {
162 $hold_error = undef;
163 }
164 }
165 push(@errors, @$hold_error) if $hold_error; # allow for final OR to work
166
167 ### add on errors as requested
168 if ($#errors != -1) {
169 push @ERRORS, $title if $title;
170 push @ERRORS, @errors;
171 }
172
173 ### add on general options, and group options if errors in group occurred
174 foreach (@group_keys) {
175 my ($type, $short_key, $full_key) = @$_;
176 next if $type eq 'group' && ($#errors == -1 || $short_key =~ /^(field|order|title)$/);
177 $EXTRA{$short_key} = $group_val->{$full_key};
178 }
179 }
180
181 ### store any extra items from self
182 $EXTRA{$_} = $self->{$_} for grep {/$QR_EXTRA/o} keys %$self;
183
184 ### allow for checking for unused keys
185 if ($EXTRA{no_extra_fields}) {
186 my $which = ($EXTRA{no_extra_fields} =~ /used/i) ? 'used' : 'all';
187 my $ref = ($which eq 'all') ? $val_hash : \@USED_GROUPS;
188 my $keys = $self->get_validation_keys($ref);
189 foreach my $key (sort keys %$form) {
190 next if $keys->{$key};
191 push @ERRORS, [$key, 'no_extra_fields', {}, undef];
192 }
193 }
194
195 ### return what they want
196 if ($#ERRORS != -1) {
197 my $err_obj = $self->new_error(\@ERRORS, \%EXTRA);
198 die $err_obj if $EXTRA{'raise_error'};
199 return $err_obj;
200 } else {
201 return wantarray ? () : undef;
202 }
203 }
204
205 sub new_error {
206 my $self = shift;
207 return CGI::Ex::Validate::Error->new(@_);
208 }
209
210 ### allow for optional validation on groups and on individual items
211 sub check_conditional {
212 my ($self, $form, $ifs, $N_level, $ifs_match) = @_;
213
214 $N_level ||= 0;
215 $N_level ++; # prevent too many recursive checks
216
217 ### can pass a single hash - or an array ref of hashes
218 if (! $ifs) {
219 die "Need reference passed to check_conditional";
220 } elsif (! ref($ifs)) {
221 $ifs = [$ifs];
222 } elsif (UNIVERSAL::isa($ifs,'HASH')) {
223 $ifs = [$ifs];
224 }
225
226 ### run the if options here
227 ### multiple items can be passed - all are required unless OR is used to separate
228 my $found = 1;
229 foreach (my $i = 0; $i <= $#$ifs; $i ++) {
230 my $ref = $ifs->[$i];
231 if (! ref $ref) {
232 if ($ref eq 'OR') {
233 $i ++ if $found; # if found skip the OR altogether
234 $found = 1; # reset
235 next;
236 } else {
237 if ($ref =~ s/^\s*!\s*//) {
238 $ref = {field => $ref, max_in_set => "0 of $ref"};
239 } else {
240 $ref = {field => $ref, required => 1};
241 }
242 }
243 }
244 last if ! $found;
245
246 ### get the field - allow for custom variables based upon a match
247 my $field = $ref->{'field'} || die "Missing field key during validate_if (possibly used a reference to a main hash *foo -> &foo)";
248 $field =~ s/\$(\d+)/defined($ifs_match->[$1]) ? $ifs_match->[$1] : ''/eg if $ifs_match;
249
250 my @err = $self->validate_buddy($form, $field, $ref, $N_level);
251 $found = 0 if scalar @err;
252 }
253 return $found;
254 }
255
256
257 ### this is where the main checking goes on
258 sub validate_buddy {
259 my $self = shift;
260 my ($form, $field, $field_val, $N_level, $ifs_match) = @_;
261 $N_level ||= 0;
262 $N_level ++; # prevent too many recursive checks
263 die "Max dependency level reached $N_level" if $N_level > 10;
264
265 my @errors = ();
266 my $types = [sort keys %$field_val];
267
268 ### allow for not running some tests in the cgi
269 if ($field_val->{'exclude_cgi'}) {
270 delete $field_val->{'was_validated'};
271 return wantarray ? @errors : $#errors + 1;
272 }
273
274 ### allow for field names that contain regular expressions
275 if ($field =~ m/^(!\s*|)m([^\s\w])(.*)\2([eigsmx]*)$/s) {
276 my ($not,$pat,$opt) = ($1,$3,$4);
277 $opt =~ tr/g//d;
278 die "The e option cannot be used on validation keys on field $field" if $opt =~ /e/;
279 foreach my $_field (sort keys %$form) {
280 next if ($not && $_field =~ m/(?$opt:$pat)/) || (! $not && $_field !~ m/(?$opt:$pat)/);
281 my @match = (undef, $1, $2, $3, $4, $5); # limit to the matches
282 push @errors, $self->validate_buddy($form, $_field, $field_val, $N_level, \@match);
283 }
284 return wantarray ? @errors : $#errors + 1;
285 }
286
287 my $values = UNIVERSAL::isa($form->{$field},'ARRAY') ? $form->{$field} : [$form->{$field}];
288 my $n_values = $#$values + 1;
289
290 ### allow for default value
291 if (exists $field_val->{'default'}) {
292 if ($n_values == 0 || ($n_values == 1 && (! defined($values->[0]) || ! length($values->[0])))) {
293 $form->{$field} = $values->[0] = $field_val->{'default'};
294 }
295 }
296
297 ### allow for a few form modifiers
298 my $modified = 0;
299 foreach my $value (@$values) {
300 next if ! defined $value;
301 if (! $field_val->{'do_not_trim'}) { # whitespace
302 $value =~ s/^\s+//;
303 $value =~ s/\s+$//;
304 $modified = 1;
305 }
306 if ($field_val->{'to_upper_case'}) { # uppercase
307 $value = uc($value);
308 $modified = 1;
309 } elsif ($field_val->{'to_lower_case'}) { # lowercase
310 $value = lc($value);
311 $modified = 1;
312 }
313 }
314 # allow for inline specified modifications (ie s/foo/bar/)
315 foreach my $type (grep {/^replace_?\d*$/} @$types) {
316 my $ref = UNIVERSAL::isa($field_val->{$type},'ARRAY') ? $field_val->{$type}
317 : [split(/\s*\|\|\s*/,$field_val->{$type})];
318 foreach my $rx (@$ref) {
319 if ($rx !~ m/^\s*s([^\s\w])(.+)\1(.*)\1([eigsmx]*)$/s) {
320 die "Not sure how to parse that replace ($rx)";
321 }
322 my ($pat, $swap, $opt) = ($2, $3, $4);
323 die "The e option cannot be used in swap on field $field" if $opt =~ /e/;
324 my $global = $opt =~ s/g//g;
325 $swap =~ s/\\n/\n/g;
326 if ($global) {
327 foreach my $value (@$values) {
328 $value =~ s{(?$opt:$pat)}{
329 my @match = (undef, $1, $2, $3, $4, $5, $6); # limit on the number of matches
330 my $copy = $swap;
331 $copy =~ s/\$(\d+)/defined($match[$1]) ? $match[$1] : ""/ge;
332 $modified = 1;
333 $copy; # return of the swap
334 }eg;
335 }
336 }else{
337 foreach my $value (@$values) {
338 $value =~ s{(?$opt:$pat)}{
339 my @match = (undef, $1, $2, $3, $4, $5, $6); # limit on the number of matches
340 my $copy = $swap;
341 $copy =~ s/\$(\d+)/defined($match[$1]) ? $match[$1] : ""/ge;
342 $modified = 1;
343 $copy; # return of the swap
344 }e;
345 }
346 }
347 }
348 }
349 ### put them back into the form if we have modified it
350 if ($modified) {
351 if ($n_values == 1) {
352 $form->{$field} = $values->[0];
353 $self->{cgi_object}->param(-name => $field, -value => $values->[0])
354 if $self->{cgi_object};
355 } else {
356 ### values in @{ $form->{$field} } were modified directly
357 $self->{cgi_object}->param(-name => $field, -value => $values)
358 if $self->{cgi_object};
359 }
360 }
361
362 ### only continue if a validate_if is not present or passes test
363 my $needs_val = 0;
364 my $n_vif = 0;
365 foreach my $type (grep {/^validate_if_?\d*$/} @$types) {
366 $n_vif ++;
367 my $ifs = $field_val->{$type};
368 my $ret = $self->check_conditional($form, $ifs, $N_level, $ifs_match);
369 $needs_val ++ if $ret;
370 }
371 if (! $needs_val && $n_vif) {
372 delete $field_val->{'was_validated'};
373 return wantarray ? @errors : $#errors + 1;
374 }
375
376 ### check for simple existence
377 ### optionally check only if another condition is met
378 my $is_required = $field_val->{'required'} ? 'required' : '';
379 if (! $is_required) {
380 foreach my $type (grep {/^required_if_?\d*$/} @$types) {
381 my $ifs = $field_val->{$type};
382 next if ! $self->check_conditional($form, $ifs, $N_level, $ifs_match);
383 $is_required = $type;
384 last;
385 }
386 }
387 if ($is_required
388 && ($n_values == 0 || ($n_values == 1 && (! defined($values->[0]) || ! length $values->[0])))) {
389 return 1 if ! wantarray;
390 push @errors, [$field, $is_required, $field_val, $ifs_match];
391 return @errors;
392 }
393
394 ### min values check
395 my $n = exists($field_val->{'min_values'}) ? $field_val->{'min_values'} || 0 : 0;
396 if ($n_values < $n) {
397 return 1 if ! wantarray;
398 push @errors, [$field, 'min_values', $field_val, $ifs_match];
399 return @errors;
400 }
401
402 ### max values check
403 $field_val->{'max_values'} = 1 if ! exists $field_val->{'max_values'};
404 $n = $field_val->{'max_values'} || 0;
405 if ($n_values > $n) {
406 return 1 if ! wantarray;
407 push @errors, [$field, 'max_values', $field_val, $ifs_match];
408 return @errors;
409 }
410
411 ### max_in_set and min_in_set checks
412 my @min = grep {/^min_in_set_?\d*$/} @$types;
413 my @max = grep {/^max_in_set_?\d*$/} @$types;
414 foreach ([min => \@min],
415 [max => \@max]) {
416 my ($minmax, $keys) = @$_;
417 foreach my $type (@$keys) {
418 $field_val->{$type} =~ m/^\s*(\d+)(?i:\s*of)?\s+(.+)\s*$/
419 || die "Invalid in_set check $field_val->{$type}";
420 my $n = $1;
421 foreach my $_field (split /[\s,]+/, $2) {
422 my $ref = UNIVERSAL::isa($form->{$_field},'ARRAY') ? $form->{$_field} : [$form->{$_field}];
423 foreach my $_value (@$ref) {
424 $n -- if defined($_value) && length($_value);
425 }
426 }
427 if ( ($minmax eq 'min' && $n > 0)
428 || ($minmax eq 'max' && $n < 0)) {
429 return 1 if ! wantarray;
430 push @errors, [$field, $type, $field_val, $ifs_match];
431 return @errors;
432 }
433 }
434 }
435
436 ### at this point @errors should still be empty
437 my $content_checked; # allow later for possible untainting (only happens if content was checked)
438
439 ### loop on values of field
440 foreach my $value (@$values) {
441
442 ### allow for enum types
443 if (exists $field_val->{'enum'}) {
444 my $ref = ref($field_val->{'enum'}) ? $field_val->{'enum'} : [split(/\s*\|\|\s*/,$field_val->{'enum'})];
445 my $found = 0;
446 foreach (@$ref) {
447 $found = 1 if defined($value) && $_ eq $value;
448 }
449 if (! $found) {
450 return 1 if ! wantarray;
451 push @errors, [$field, 'enum', $field_val, $ifs_match];
452 }
453 $content_checked = 1;
454 }
455
456 ### field equality test
457 foreach my $type (grep {/^equals_?\d*$/} @$types) {
458 my $field2 = $field_val->{$type};
459 my $not = ($field2 =~ s/^!\s*//) ? 1 : 0;
460 my $success = 0;
461 if ($field2 =~ m/^([\"\'])(.*)\1$/) {
462 my $test = $2;
463 $success = (defined($value) && $value eq $test);
464 } elsif (exists($form->{$field2}) && defined($form->{$field2})) {
465 $success = (defined($value) && $value eq $form->{$field2});
466 } elsif (! defined($value)) {
467 $success = 1; # occurs if they are both undefined
468 }
469 if ($not ? $success : ! $success) {
470 return 1 if ! wantarray;
471 push @errors, [$field, $type, $field_val, $ifs_match];
472 }
473 $content_checked = 1;
474 }
475
476 ### length min check
477 if (exists $field_val->{'min_len'}) {
478 my $n = $field_val->{'min_len'};
479 if (! defined($value) || length($value) < $n) {
480 return 1 if ! wantarray;
481 push @errors, [$field, 'min_len', $field_val, $ifs_match];
482 }
483 }
484
485 ### length max check
486 if (exists $field_val->{'max_len'}) {
487 my $n = $field_val->{'max_len'};
488 if (defined($value) && length($value) > $n) {
489 return 1 if ! wantarray;
490 push @errors, [$field, 'max_len', $field_val, $ifs_match];
491 }
492 }
493
494 ### now do match types
495 foreach my $type (grep {/^match_?\d*$/} @$types) {
496 my $ref = UNIVERSAL::isa($field_val->{$type},'ARRAY') ? $field_val->{$type}
497 : UNIVERSAL::isa($field_val->{$type}, 'Regexp') ? [$field_val->{$type}]
498 : [split(/\s*\|\|\s*/,$field_val->{$type})];
499 foreach my $rx (@$ref) {
500 if (UNIVERSAL::isa($rx,'Regexp')) {
501 if (! defined($value) || $value !~ $rx) {
502 push @errors, [$field, $type, $field_val, $ifs_match];
503 }
504 } else {
505 if ($rx !~ m/^(!\s*|)m([^\s\w])(.*)\2([eigsmx]*)$/s) {
506 die "Not sure how to parse that match ($rx)";
507 }
508 my ($not,$pat,$opt) = ($1,$3,$4);
509 $opt =~ tr/g//d;
510 die "The e option cannot be used on validation keys on field $field" if $opt =~ /e/;
511 if ( ( $not && ( defined($value) && $value =~ m/(?$opt:$pat)/))
512 || (! $not && (! defined($value) || $value !~ m/(?$opt:$pat)/))
513 ) {
514 return 1 if ! wantarray;
515 push @errors, [$field, $type, $field_val, $ifs_match];
516 }
517 }
518 }
519 $content_checked = 1;
520 }
521
522 ### allow for comparison checks
523 foreach my $type (grep {/^compare_?\d*$/} @$types) {
524 my $ref = UNIVERSAL::isa($field_val->{$type},'ARRAY') ? $field_val->{$type}
525 : [split(/\s*\|\|\s*/,$field_val->{$type})];
526 foreach my $comp (@$ref) {
527 next if ! $comp;
528 my $test = 0;
529 if ($comp =~ /^\s*(>|<|[><!=]=)\s*([\d\.\-]+)\s*$/) {
530 my $val = $value || 0;
531 $val *= 1;
532 if ($1 eq '>' ) { $test = ($val > $2) }
533 elsif ($1 eq '<' ) { $test = ($val < $2) }
534 elsif ($1 eq '>=') { $test = ($val >= $2) }
535 elsif ($1 eq '<=') { $test = ($val <= $2) }
536 elsif ($1 eq '!=') { $test = ($val != $2) }
537 elsif ($1 eq '==') { $test = ($val == $2) }
538
539 } elsif ($comp =~ /^\s*(eq|ne|gt|ge|lt|le)\s+(.+?)\s*$/) {
540 my $val = defined($value) ? $value : '';
541 my ($op, $value2) = ($1, $2);
542 $value2 =~ s/^([\"\'])(.*)\1$/$2/;
543 if ($op eq 'gt') { $test = ($val gt $value2) }
544 elsif ($op eq 'lt') { $test = ($val lt $value2) }
545 elsif ($op eq 'ge') { $test = ($val ge $value2) }
546 elsif ($op eq 'le') { $test = ($val le $value2) }
547 elsif ($op eq 'ne') { $test = ($val ne $value2) }
548 elsif ($op eq 'eq') { $test = ($val eq $value2) }
549
550 } else {
551 die "Not sure how to compare \"$comp\"";
552 }
553 if (! $test) {
554 return 1 if ! wantarray;
555 push @errors, [$field, $type, $field_val, $ifs_match];
556 }
557 }
558 $content_checked = 1;
559 }
560
561 ### server side sql type
562 foreach my $type (grep {/^sql_?\d*$/} @$types) {
563 my $db_type = $field_val->{"${type}_db_type"};
564 my $dbh = ($db_type) ? $self->{dbhs}->{$db_type} : $self->{dbh};
565 if (! $dbh) {
566 die "Missing dbh for $type type on field $field" . ($db_type ? " and db_type $db_type" : "");
567 } elsif (UNIVERSAL::isa($dbh,'CODE')) {
568 $dbh = &$dbh($field, $self) || die "SQL Coderef did not return a dbh";
569 }
570 my $sql = $field_val->{$type};
571 my @args = ($value) x $sql =~ tr/?//;
572 my $return = $dbh->selectrow_array($sql, {}, @args); # is this right - copied from O::FORMS
573 $field_val->{"${type}_error_if"} = 1 if ! defined $field_val->{"${type}_error_if"};
574 if ( (! $return && $field_val->{"${type}_error_if"})
575 || ($return && ! $field_val->{"${type}_error_if"}) ) {
576 return 1 if ! wantarray;
577 push @errors, [$field, $type, $field_val, $ifs_match];
578 }
579 $content_checked = 1;
580 }
581
582 ### server side custom type
583 foreach my $type (grep {/^custom_?\d*$/} @$types) {
584 my $check = $field_val->{$type};
585 next if UNIVERSAL::isa($check, 'CODE') ? &$check($field, $value, $field_val, $type) : $check;
586 return 1 if ! wantarray;
587 push @errors, [$field, $type, $field_val, $ifs_match];
588 $content_checked = 1;
589 }
590
591 ### do specific type checks
592 foreach my $type (grep {/^type_?\d*$/} @$types) {
593 if (! $self->check_type($value,$field_val->{'type'},$field,$form)){
594 return 1 if ! wantarray;
595 push @errors, [$field, $type, $field_val, $ifs_match];
596 }
597 $content_checked = 1;
598 }
599 }
600
601 ### allow for the data to be "untainted"
602 ### this is only allowable if the user ran some other check for the datatype
603 if ($field_val->{'untaint'} && $#errors == -1) {
604 if (! $content_checked) {
605 push @errors, [$field, 'untaint', $field_val, $ifs_match];
606 } else {
607 ### generic untainter - assuming the other required content_checks did good validation
608 $_ = /(.*)/ ? $1 : die "Couldn't match?" foreach @$values;
609 if ($n_values == 1) {
610 $form->{$field} = $values->[0];
611 $self->{cgi_object}->param(-name => $field, -value => $values->[0])
612 if $self->{cgi_object};
613 } else {
614 ### values in @{ $form->{$field} } were modified directly
615 $self->{cgi_object}->param(-name => $field, -value => $values)
616 if $self->{cgi_object};
617 }
618 }
619 }
620
621 ### all done - time to return
622 return wantarray ? @errors : $#errors + 1;
623 }
624
625 ###----------------------------------------------------------------###
626
627 ### used to validate specific types
628 sub check_type {
629 my $self = shift;
630 my $value = shift;
631 my $type = uc(shift);
632
633 ### do valid email address for our system
634 if ($type eq 'EMAIL') {
635 return 0 if ! $value;
636 my($local_p,$dom) = ($value =~ /^(.+)\@(.+?)$/) ? ($1,$2) : return 0;
637
638 return 0 if length($local_p) > 60;
639 return 0 if length($dom) > 100;
640 return 0 if ! $self->check_type($dom,'DOMAIN') && ! $self->check_type($dom,'IP');
641 return 0 if ! $self->check_type($local_p,'LOCAL_PART');
642
643 ### the "username" portion of an email address
644 } elsif ($type eq 'LOCAL_PART') {
645 return 0 if ! defined($value) || ! length($value);
646 return 0 if $value =~ m/[^a-z0-9.\-!&+]/;
647 return 0 if $value =~ m/^[\.\-]/;
648 return 0 if $value =~ m/[\.\-\&]$/;
649 return 0 if $value =~ m/(\.\-|\-\.|\.\.)/;
650
651 ### standard IP address
652 } elsif ($type eq 'IP') {
653 return 0 if ! $value;
654 return (4 == grep {!/\D/ && $_ < 256} split /\./, $value, 4);
655
656 ### domain name - including tld and subdomains (which are all domains)
657 } elsif ($type eq 'DOMAIN') {
658 return 0 if ! $value;
659 return 0 if $value =~ m/[^a-z0-9.\-]/;
660 return 0 if $value =~ m/^[\.\-]/;
661 return 0 if $value =~ m/(\.\-|\-\.|\.\.)/;
662 return 0 if length($value) > 255;
663 return 0 if $value !~ s/\.([a-z]+)$//;
664
665 my $ext = $1;
666 if ($ext eq 'name') { # .name domains
667 return 0 if $value !~ /^[a-z0-9][a-z0-9\-]{0,62} \. [a-z0-9][a-z0-9\-]{0,62}$/x;
668 } else { # any other domains
669 return 0 if $value !~ /^([a-z0-9][a-z0-9\-]{0,62} \.)* [a-z0-9][a-z0-9\-]{0,62}$/x;
670 }
671
672 ### validate a url
673 } elsif ($type eq 'URL') {
674 return 0 if ! $value;
675 $value =~ s|^https?://([^/]+)||i || return 0;
676 my $dom = $1;
677 return 0 if ! $self->check_type($dom,'DOMAIN') && ! $self->check_type($dom,'IP');
678 return 0 if $value && ! $self->check_type($value,'URI');
679
680 ### validate a uri - the path portion of a request
681 } elsif ($type eq 'URI') {
682 return 0 if ! $value;
683 return 0 if $value =~ m/\s+/;
684
685 } elsif ($type eq 'CC') {
686 return 0 if ! $value;
687 ### validate the number
688 return 0 if $value =~ /[^\d\-\ ]/
689 || length($value) > 16
690 || length($value) < 13;
691
692 ### simple mod10 check
693 $value =~ s/\D//g;
694 my $sum = 0;
695 my $switch = 0;
696 foreach my $digit ( reverse split //, $value ){
697 $switch = 1 if ++ $switch > 2;
698 my $y = $digit * $switch;
699 $y -= 9 if $y > 9;
700 $sum += $y;
701 }
702 return 0 if $sum % 10;
703
704 }
705
706 return 1;
707 }
708
709 ###----------------------------------------------------------------###
710
711 sub get_validation {
712 my $self = shift;
713 my $val = shift;
714 return CGI::Ex::Conf::conf_read($val, {html_key => 'validation', default_ext => $DEFAULT_EXT});
715 }
716
717 ### returns all keys from all groups - even if group has validate_if
718 sub get_validation_keys {
719 my $self = shift;
720 my $val_hash = shift;
721 my $form = shift; # with optional form - will only return keys in validated groups
722 my %keys = ();
723
724 ### if a form was passed - make sure it is a hashref
725 if ($form) {
726 if (! ref($form)) {
727 die "Invalid form hash or cgi object";
728 } elsif(! UNIVERSAL::isa($form,'HASH')) {
729 require CGI::Ex;
730 $form = CGI::Ex->new->get_form($form);
731 }
732 }
733
734 my $refs = $self->get_validation($val_hash);
735 $refs = [$refs] if ! UNIVERSAL::isa($refs,'ARRAY');
736 foreach my $group_val (@$refs) {
737 die "Group found that was not a hashref" if ! UNIVERSAL::isa($group_val, 'HASH');
738
739 ### if form is passed, check to see if the group passed validation
740 if ($form) {
741 my $validate_if = $group_val->{'group validate_if'};
742 next if $validate_if && ! $self->check_conditional($form, $validate_if);
743 }
744
745 if ($group_val->{"group fields"}) {
746 die "Group fields must be an arrayref" if ! UNIVERSAL::isa($group_val->{"group fields"}, 'ARRAY');
747 foreach my $field_val (@{ $group_val->{"group fields"} }) {
748 next if ! ref($field_val) && $field_val eq 'OR';
749 die "Field_val must be a hashref" if ! UNIVERSAL::isa($field_val, 'HASH');
750 my $key = $field_val->{'field'} || die "Missing field key in field_val hashref";
751 $keys{$key} = $field_val->{'name'} || 1;
752 }
753 } elsif ($group_val->{"group order"}) {
754 die "Group order must be an arrayref" if ! UNIVERSAL::isa($group_val->{"group order"}, 'ARRAY');
755 foreach my $key (@{ $group_val->{"group order"} }) {
756 my $field_val = $group_val->{$key};
757 next if ! $field_val && $key eq 'OR';
758 die "Field_val for $key must be a hashref" if ! UNIVERSAL::isa($field_val, 'HASH');
759 $key = $field_val->{'field'} if $field_val->{'field'};
760 $keys{$key} = $field_val->{'name'} || 1;
761 }
762 }
763
764 ### get all others
765 foreach my $key (keys %$group_val) {
766 next if $key =~ /^(general|group)\s/;
767 my $field_val = $group_val->{$key};
768 next if ! UNIVERSAL::isa($field_val, 'HASH');
769 $keys{$key} = $field_val->{'name'} || 1;
770 }
771 }
772
773 return \%keys;
774 }
775
776 ###----------------------------------------------------------------###
777
778 ### spit out a chunk that will do the validation
779 sub generate_js {
780 ### allow for some browsers to not receive the validation js
781 return "<!-- JS validation not supported in this browser $_ -->"
782 if $ENV{'HTTP_USER_AGENT'} && grep {$ENV{'HTTP_USER_AGENT'} =~ $_} @UNSUPPORTED_BROWSERS;
783
784 my $self = shift;
785 my $val_hash = shift || die "Missing validation";
786 my $form_name = shift || die "Missing form name";
787 my $js_uri_path = shift || $JS_URI_PATH;
788 $val_hash = $self->get_validation($val_hash);
789
790 ### store any extra items from self
791 my %EXTRA = ();
792 $EXTRA{"general $_"} = $self->{$_} for grep {/$QR_EXTRA/o} keys %$self; # add 'general' to be used in javascript
793
794 my $js_uri_path_validate = $JS_URI_PATH_VALIDATE || do {
795 die "Missing \$js_uri_path" if ! $js_uri_path;
796 "$js_uri_path/CGI/Ex/validate.js";
797 };
798
799 if (! $self->{'no_jsondump'} && eval { require CGI::Ex::JSONDump }) {
800 my $json = CGI::Ex::JSONDump->new({pretty => 1})->dump($val_hash);
801 return qq{<script src="$js_uri_path_validate"></script>
802 <script>
803 document.validation = $json;
804 if (document.check_form) document.check_form("$form_name");
805 </script>
806 };
807
808 } elsif (! $self->{'no_json'} && eval { require JSON }) {
809 my $json = JSON->new(pretty => 1)->objToJson($val_hash);
810
811 return qq{<script src="$js_uri_path_validate"></script>
812 <script>
813 document.validation = $json;
814 if (document.check_form) document.check_form("$form_name");
815 </script>
816 };
817
818 } elsif (eval { require YAML }) {
819
820 my $str = YAML::Dump((scalar keys %EXTRA) ? (\%EXTRA) : () , $val_hash);
821 $str =~ s/(?<!\\)\\(?=[sSdDwWbB0-9?.*+|\-\^\${}()\[\]])/\\\\/g; # fix some issues with YAML
822 $str =~ s/\n/\\n\\\n/g; # allow for one big string that flows on multiple lines
823 $str =~ s/\"/\\\"/g; # quotify it
824
825 ### get the paths
826 my $js_uri_path_yaml = $JS_URI_PATH_YAML || do {
827 die "Missing \$js_uri_path" if ! $js_uri_path;
828 "$js_uri_path/CGI/Ex/yaml_load.js";
829 };
830
831 ### return the string
832 return qq{<script src="$js_uri_path_yaml"></script>
833 <script src="$js_uri_path_validate"></script>
834 <script>
835 document.validation = "$str";
836 if (document.check_form) document.check_form("$form_name");
837 </script>
838 };
839 } else {
840 return '<!-- no JSON or YAML support found for JS validation -->';
841 }
842 }
843
844 ###----------------------------------------------------------------###
845 ### How to handle errors
846
847 package CGI::Ex::Validate::Error;
848
849 use strict;
850 use overload '""' => \&as_string;
851
852 sub new {
853 my $class = shift || __PACKAGE__;
854 my $errors = shift;
855 my $extra = shift || {};
856 die "Missing or invalid arrayref" if ! UNIVERSAL::isa($errors, 'ARRAY');
857 die "Missing or invalid hashref" if ! UNIVERSAL::isa($extra, 'HASH');
858 return bless {errors => $errors, extra => $extra}, $class;
859 }
860
861 sub as_string {
862 my $self = shift;
863 my $extra = $self->{extra} || {};
864 my $extra2 = shift || {};
865
866 ### allow for formatting
867 my $join = defined($extra2->{as_string_join}) ? $extra2->{as_string_join}
868 : defined($extra->{as_string_join}) ? $extra->{as_string_join}
869 : "\n";
870 my $header = defined($extra2->{as_string_header}) ? $extra2->{as_string_header}
871 : defined($extra->{as_string_header}) ? $extra->{as_string_header} : "";
872 my $footer = defined($extra2->{as_string_footer}) ? $extra2->{as_string_footer}
873 : defined($extra->{as_string_footer}) ? $extra->{as_string_footer} : "";
874
875 return $header . join($join, @{ $self->as_array($extra2) }) . $footer;
876 }
877
878 ### return an array of applicable errors
879 sub as_array {
880 my $self = shift;
881 my $errors = $self->{errors} || die "Missing errors";
882 my $extra = $self->{extra} || {};
883 my $extra2 = shift || {};
884
885 my $title = defined($extra2->{as_array_title}) ? $extra2->{as_array_title}
886 : defined($extra->{as_array_title}) ? $extra->{as_array_title}
887 : "Please correct the following items:";
888
889 ### if there are heading items then we may end up needing a prefix
890 my $has_headings;
891 if ($title) {
892 $has_headings = 1;
893 } else {
894 foreach (@$errors) {
895 next if ref;
896 $has_headings = 1;
897 last;
898 }
899 }
900
901 my $prefix = defined($extra2->{as_array_prefix}) ? $extra2->{as_array_prefix}
902 : defined($extra->{as_array_prefix}) ? $extra->{as_array_prefix}
903 : $has_headings ? ' ' : '';
904
905 ### get the array ready
906 my @array = ();
907 push @array, $title if length $title;
908
909 ### add the errors
910 my %found = ();
911 foreach my $err (@$errors) {
912 if (! ref $err) {
913 push @array, $err;
914 %found = ();
915 } else {
916 my $text = $self->get_error_text($err);
917 next if $found{$text};
918 $found{$text} = 1;
919 push @array, "$prefix$text";
920 }
921 }
922
923 return \@array;
924 }
925
926 ### return a hash of applicable errors
927 sub as_hash {
928 my $self = shift;
929 my $errors = $self->{errors} || die "Missing errors";
930 my $extra = $self->{extra} || {};
931 my $extra2 = shift || {};
932
933 my $suffix = defined($extra2->{as_hash_suffix}) ? $extra2->{as_hash_suffix}
934 : defined($extra->{as_hash_suffix}) ? $extra->{as_hash_suffix} : '_error';
935 my $join = defined($extra2->{as_hash_join}) ? $extra2->{as_hash_join}
936 : defined($extra->{as_hash_join}) ? $extra->{as_hash_join} : '<br />';
937
938 ### now add to the hash
939 my %found = ();
940 my %return = ();
941 foreach my $err (@$errors) {
942 next if ! ref $err;
943
944 my ($field, $type, $field_val, $ifs_match) = @$err;
945 die "Missing field name" if ! $field;
946 if ($field_val->{delegate_error}) {
947 $field = $field_val->{delegate_error};
948 $field =~ s/\$(\d+)/defined($ifs_match->[$1]) ? $ifs_match->[$1] : ''/eg if $ifs_match;
949 }
950
951 my $text = $self->get_error_text($err);
952 next if $found{$field}->{$text};
953 $found{$field}->{$text} = 1;
954
955 $field .= $suffix;
956 $return{$field} ||= [];
957 $return{$field} = [$return{$field}] if ! ref($return{$field});
958 push @{ $return{$field} }, $text;
959 }
960
961 ### allow for elements returned as
962 if ($join) {
963 my $header = defined($extra2->{as_hash_header}) ? $extra2->{as_hash_header}
964 : defined($extra->{as_hash_header}) ? $extra->{as_hash_header} : "";
965 my $footer = defined($extra2->{as_hash_footer}) ? $extra2->{as_hash_footer}
966 : defined($extra->{as_hash_footer}) ? $extra->{as_hash_footer} : "";
967 foreach my $key (keys %return) {
968 $return{$key} = $header . join($join,@{ $return{$key} }) . $footer;
969 }
970 }
971
972 return \%return;
973 }
974
975 ### return a user friendly error message
976 sub get_error_text {
977 my $self = shift;
978 my $err = shift;
979 my $extra = $self->{extra} || {};
980 my ($field, $type, $field_val, $ifs_match) = @$err;
981 my $dig = ($type =~ s/(_?\d+)$//) ? $1 : '';
982 my $type_lc = lc($type);
983
984 ### allow for delegated field names - only used for defaults
985 if ($field_val->{delegate_error}) {
986 $field = $field_val->{delegate_error};
987 $field =~ s/\$(\d+)/defined($ifs_match->[$1]) ? $ifs_match->[$1] : ''/eg if $ifs_match;
988 }
989
990 ### the the name of this thing
991 my $name = $field_val->{'name'} || "The field $field";
992 $name =~ s/\$(\d+)/defined($ifs_match->[$1]) ? $ifs_match->[$1] : ''/eg if $ifs_match;
993
994 ### type can look like "required" or "required2" or "required100023"
995 ### allow for fallback from required100023_error through required_error
996 my @possible_error_keys = ("${type}_error");
997 unshift @possible_error_keys, "${type}${dig}_error" if length($dig);
998
999 ### look in the passed hash or self first
1000 my $return;
1001 foreach my $key (@possible_error_keys){
1002 $return = $field_val->{$key} || $extra->{$key} || next;
1003 $return =~ s/\$(\d+)/defined($ifs_match->[$1]) ? $ifs_match->[$1] : ''/eg if $ifs_match;
1004 $return =~ s/\$field/$field/g;
1005 $return =~ s/\$name/$name/g;
1006 if (my $value = $field_val->{"$type$dig"}) {
1007 $return =~ s/\$value/$value/g if ! ref $value;
1008 }
1009 last;
1010 }
1011
1012 ### set default messages
1013 if (! $return) {
1014 if ($type eq 'required' || $type eq 'required_if') {
1015 $return = "$name is required.";
1016
1017 } elsif ($type eq 'min_values') {
1018 my $n = $field_val->{"min_values${dig}"};
1019 my $values = ($n == 1) ? 'value' : 'values';
1020 $return = "$name had less than $n $values.";
1021
1022 } elsif ($type eq 'max_values') {
1023 my $n = $field_val->{"max_values${dig}"};
1024 my $values = ($n == 1) ? 'value' : 'values';
1025 $return = "$name had more than $n $values.";
1026
1027 } elsif ($type eq 'enum') {
1028 $return = "$name is not in the given list.";
1029
1030 } elsif ($type eq 'equals') {
1031 my $field2 = $field_val->{"equals${dig}"};
1032 my $name2 = $field_val->{"equals${dig}_name"} || "the field $field2";
1033 $name2 =~ s/\$(\d+)/defined($ifs_match->[$1]) ? $ifs_match->[$1] : ''/eg if $ifs_match;
1034 $return = "$name did not equal $name2.";
1035
1036 } elsif ($type eq 'min_len') {
1037 my $n = $field_val->{"min_len${dig}"};
1038 my $char = ($n == 1) ? 'character' : 'characters';
1039 $return = "$name was less than $n $char.";
1040
1041 } elsif ($type eq 'max_len') {
1042 my $n = $field_val->{"max_len${dig}"};
1043 my $char = ($n == 1) ? 'character' : 'characters';
1044 $return = "$name was more than $n $char.";
1045
1046 } elsif ($type eq 'max_in_set') {
1047 my $set = $field_val->{"max_in_set${dig}"};
1048 $return = "Too many fields were chosen from the set ($set)";
1049
1050 } elsif ($type eq 'min_in_set') {
1051 my $set = $field_val->{"min_in_set${dig}"};
1052 $return = "Not enough fields were chosen from the set ($set)";
1053
1054 } elsif ($type eq 'match') {
1055 $return = "$name contains invalid characters.";
1056
1057 } elsif ($type eq 'compare') {
1058 $return = "$name did not fit comparison.";
1059
1060 } elsif ($type eq 'sql') {
1061 $return = "$name did not match sql test.";
1062
1063 } elsif ($type eq 'custom') {
1064 $return = "$name did not match custom test.";
1065
1066 } elsif ($type eq 'type') {
1067 my $_type = $field_val->{"type${dig}"};
1068 $return = "$name did not match type $_type.";
1069
1070 } elsif ($type eq 'untaint') {
1071 $return = "$name cannot be untainted without one of the following checks: enum, equals, match, compare, sql, type, custom";
1072
1073 } elsif ($type eq 'no_extra_fields') {
1074 $return = "$name should not be passed to validate.";
1075 }
1076 }
1077
1078 die "Missing error on field $field for type $type$dig" if ! $return;
1079 return $return;
1080
1081 }
1082
1083 ###----------------------------------------------------------------###
1084
1085 1;
1086
1087
1088 __END__
1089
1090 =head1 SYNOPSIS
1091
1092 use CGI::Ex::Validate;
1093
1094 ### THE SHORT
1095
1096 my $errobj = CGI::Ex::Validate->new->validate($form, $val_hash);
1097
1098 ### THE LONG
1099
1100 my $form = CGI->new;
1101 # OR #
1102 my $form = CGI::Ex->new; # OR CGI::Ex->get_form;
1103 # OR #
1104 my $form = {key1 => 'val1', key2 => 'val2'};
1105
1106
1107 ### simplest
1108 my $val_hash = {
1109 username => {
1110 required => 1,
1111 max_len => 30,
1112 field => 'username',
1113 # field is optional in this case - will use key name
1114 },
1115 email => {
1116 required => 1,
1117 max_len => 100,
1118 },
1119 email2 => {
1120 validate_if => 'email',
1121 equals => 'email',
1122 },
1123 };
1124
1125 ### ordered
1126 my $val_hash = {
1127 'group order' => [qw(username email email2)],
1128 username => {required => 1, max_len => 30},
1129 email => ...,
1130 email2 => ...,
1131 };
1132
1133 ### ordered again
1134 my $val_hash = {
1135 'group fields' => [{
1136 field => 'username', # field is not optional in this case
1137 required => 1,
1138 max_len => 30,
1139 }, {
1140 field => 'email',
1141 required => 1,
1142 max_len => 100,
1143 }, {
1144 field => 'email2',
1145 validate_if => 'email',
1146 equals => 'email',
1147 }],
1148 };
1149
1150
1151 my $vob = CGI::Ex::Validate->new;
1152 my $errobj = $vob->validate($form, $val_hash);
1153 # OR #
1154 my $errobj = $vob->validate($form, "/somefile/somewhere.val"); # import config using yaml file
1155 # OR #
1156 my $errobj = $vob->validate($form, "/somefile/somewhere.pl"); # import config using perl file
1157 # OR #
1158 my $errobj = $vob->validate($form, "--- # a yaml document\n"); # import config using yaml str
1159
1160
1161 if ($errobj) {
1162 my $error_heading = $errobj->as_string; # OR "$errobj";
1163 my $error_list = $errobj->as_array; # ordered list of what when wrong
1164 my $error_hash = $errobj->as_hash; # hash of arrayrefs of errors
1165 } else {
1166 # form passed validation
1167 }
1168
1169 ### will add an error for any form key not found in $val_hash
1170 my $vob = CGI::Ex::Validate->new({no_extra_keys => 1});
1171 my $errobj = $vob->validate($form, $val_hash);
1172
1173 =head1 DESCRIPTION
1174
1175 CGI::Ex::Validate is one of many validation modules. It aims to have
1176 all of the basic data validation functions, avoid adding all of the
1177 millions of possible types, while still giving the capability for the
1178 developer to add their own types.
1179
1180 CGI::Ex::Validate can work in a simple way like all of the other
1181 validators do. However, it also allows for grouping of validation
1182 items and conditional validation of groups or individual items. This
1183 is more in line with the normal validation procedures for a website.
1184
1185 It also has full support for providing the same validation in javascript.
1186 It provides methods for attaching the javascript to existing forms.
1187
1188 =head1 METHODS
1189
1190 =over 4
1191
1192 =item C<new>
1193
1194 Used to instantiate the object. Arguments are either a hash, or hashref,
1195 or nothing at all. Keys of the hash become the keys of the object.
1196
1197 =item C<get_validation>
1198
1199 Given a filename or YAML string will return perl hash. If more than one
1200 group is contained in the file, it will return an arrayref of hashrefs.
1201
1202 my $ref = $self->get_validation($file);
1203
1204 =item C<get_validation_keys>
1205
1206 Given a filename or YAML string or a validation hashref, will return all
1207 of the possible keys found in the validation hash. This can be used to
1208 check to see if extra items have been passed to validate. If a second
1209 argument contains a form hash is passed, get_validation_keys will only
1210 return the keys of groups that were validated.
1211
1212 my $key_hashref = $self->get_validation_keys($val_hash);
1213
1214 The values of the hash are the names of the fields.
1215
1216 =item C<validate>
1217
1218 Arguments are a form hashref or cgi object, a validation hashref or
1219 filename, and an optional what_was_validated arrayref. If a CGI
1220 object is passed, CGI::Ex::get_form will be called on that object to
1221 turn it into a hashref. If a filename is given for the validation,
1222 get_validation will be called on that filename. If the
1223 what_was_validated_arrayref is passed - it will be populated (pushed)
1224 with the field hashes that were actually validated (anything that was
1225 skipped because of validate_if will not be in the array).
1226
1227 If the form passes validation, validate will return undef. If it
1228 fails validation, it will return a CGI::Ex::Validate::Error object.
1229 If the 'raise_error' general option has been set, validate will die
1230 with a CGI::Ex::validate::Error object as the value.
1231
1232 my $err_obj = $self->validate($form, $val_hash);
1233
1234 # OR #
1235
1236 $self->{raise_error} = 1; # raise error can also be listed in the
1237 val_hash eval { $self->validate($form, $val_hash) }; if ($@) { my
1238 $err_obj = $@; }
1239
1240 =item C<generate_js>
1241
1242 Requires JSON or YAML to work properly (see L<JSON> or L<YAML>).
1243
1244 Takes a validation hash, a form name, and an optional javascript uri
1245 path and returns Javascript that can be embedded on a page and will
1246 perform identical validations as the server side. The validation can
1247 be any validation hash (or arrayref of hashes. The form name must be
1248 the name of the form that the validation will act upon - the name is
1249 used to register an onsubmit function. The javascript uri path is
1250 used to embed the locations two external javascript source files.
1251
1252
1253 The javascript uri path is highly dependent upon the server
1254 configuration and therefore must be configured manually. It may be
1255 passed to generate_js, or it may be specified in $JS_URI_PATH. There
1256 are two files included with this module that are needed -
1257 CGI/Ex/yaml_load.js and CGI/Ex/validate.js. When generating the js
1258 code, generate_js will look in $JS_URI_PATH_YAML and
1259 $JS_URI_PATH_VALIDATE. If either of these are not set, generate_js
1260 will default to "$JS_URI_PATH/CGI/Ex/yaml_load.js" and
1261 "$JS_URI_PATH/CGI/Ex/validate.js".
1262
1263 $self->generate_js($val_hash, 'my_form', "/cgi-bin/js")
1264
1265 # would generate something like the following...
1266
1267 <script src="/cgi-bin/js/CGI/Ex/yaml_load.js"></script>
1268 <script src="/cgi-bin/js/CGI/Ex/validate.js"></script>
1269 ... more js follows ...
1270
1271 $CGI::Ex::Validate::JS_URI_PATH = "/stock/js";
1272 $CGI::Ex::Validate::JS_URI_PATH_YAML = "/js/yaml_load.js";
1273 $self->generate_js($val_hash, 'my_form')
1274
1275 # would generate something like the following...
1276
1277 <script src="/js/yaml_load.js"></script>
1278 <script src="/stock/js/CGI/Ex/validate.js"></script>
1279 ... more js follows ...
1280
1281 Referencing yaml_load.js and validate.js can be done in any of
1282 several ways. They can be copied to or symlinked to a fixed location
1283 in the servers html directory. They can also be printed out by a cgi.
1284 The method C<-E<gt>print_js> has been provided in CGI::Ex for printing
1285 js files found in the perl hierarchy. See L<CGI::Ex> for more details.
1286 The $JS_URI_PATH of "/cgi-bin/js" could contain the following:
1287
1288 #!/usr/bin/perl -w
1289
1290 use strict;
1291 use CGI::Ex;
1292
1293 ### path_info should contain something like /CGI/Ex/yaml_load.js
1294 my $info = $ENV{PATH_INFO} || '';
1295 die "Invalid path" if $info !~ m|^(/\w+)+.js$|;
1296 $info =~ s|^/+||;
1297
1298 CGI::Ex->new->print_js($info);
1299 exit;
1300
1301 The print_js method in CGI::Ex is designed to cache the javascript in
1302 the browser (caching is suggested as they are medium sized files).
1303
1304 =item C<-E<gt>cgix>
1305
1306 Returns a CGI::Ex object. Used internally.
1307
1308 =back
1309
1310 =head1 VALIDATION HASH
1311
1312 The validation hash may be passed as a perl a hashref or as a
1313 filename, or as a YAML document string. If it is a filename, it will
1314 be translated into a hash using the %EXT_HANDLER for the extension on
1315 the file. If there is no extension, it will use $DEFAULT_EXT as a
1316 default.
1317
1318 The validation "hash" may also be an arrayref of hashrefs. In this
1319 case, each arrayref is treated as a group and is validated separately.
1320 A group can have a validate_if function that allows for that
1321 particular group to apply only if certain conditions are met.
1322
1323 =head1 GROUPS
1324
1325 Each hashref that is passed as a validation hash is treated as a
1326 group. Keys matching the regex m/^group\s+(\w+)$/ are reserved and
1327 are counted as GROUP OPTIONS. Keys matching the regex m/^general\s+(\w+)$/
1328 are reserved and are counted as GENERAL OPTIONS. Other keys (if
1329 any, should be keys that need validation).
1330
1331 If the GROUP OPTION 'group validate_if' is set, the group will only
1332 be validated if the conditions are met. Any group with out a validate_if
1333 fill be automatically validated.
1334
1335 Each of the items listed in the group will be validated. The
1336 validation order is determined in one of three ways:
1337
1338 =over 4
1339
1340 =item Specify 'group fields' arrayref.
1341
1342 # order will be (username, password, 'm/\w+_foo/', somethingelse)
1343 {
1344 'group title' => "User Information",
1345 'group fields' => [
1346 {field => 'username', required => 1},
1347 {field => 'password', required => 1},
1348 {field => 'm/\w+_foo/', required => 1},
1349 ],
1350 somethingelse => {required => 1},
1351 }
1352
1353 =item Specify 'group order' arrayref.
1354
1355 # order will be (username, password, 'm/\w+_foo/', somethingelse)
1356 {
1357 'group title' => "User Information",
1358 'group order' => [qw(username password), 'm/\w+_foo/'],
1359 username => {required => 1},
1360 password => {required => 1},
1361 'm/\w+_foo/' => {required => 1},
1362 somethingelse => {required => 1},
1363 }
1364
1365 =item Do nothing - use sorted order.
1366
1367 # order will be ('m/\w+_foo/', password, somethingelse, username)
1368 {
1369 'group title' => "User Information",
1370 username => {required => 1},
1371 password => {required => 1},
1372 'm/\w+_foo/' => {required => 1},
1373 somethingelse => {required => 1},
1374 }
1375
1376 =back
1377
1378 Each of the individual field validation hashrefs should contain the
1379 types listed in VALIDATION TYPES.
1380
1381 Optionally the 'group fields' or the 'group order' may contain the
1382 word 'OR' as a special keyword. If the item preceding 'OR' fails
1383 validation the item after 'OR' will be tested instead. If the item
1384 preceding 'OR' passes validation the item after 'OR' will not be
1385 tested.
1386
1387 'group order' => [qw(zip OR postalcode state OR region)],
1388
1389 Each individual validation hashref will operate on the field contained
1390 in the 'field' key. This key may also be a regular expression in the
1391 form of 'm/somepattern/'. If a regular expression is used, all keys
1392 matching that pattern will be validated.
1393
1394 =head1 VALIDATION TYPES
1395
1396 This section lists the available validation types. Multiple instances
1397 of the same type may be used for some validation types by adding a
1398 number to the type (ie match, match2, match232, match_94). Multiple
1399 instances are validated in sorted order. Types that allow multiple
1400 values are:
1401
1402 compare
1403 custom
1404 equals
1405 match
1406 max_in_set
1407 min_in_set
1408 replace
1409 required_if
1410 sql
1411 type
1412 validate_if
1413
1414 =over 4
1415
1416 =item C<validate_if>
1417
1418 If validate_if is specified, the field will only be validated
1419 if the conditions are met. Works in JS.
1420
1421 validate_if => {field => 'name', required => 1, max_len => 30}
1422 # Will only validate if the field "name" is present and is less than 30 chars.
1423
1424 validate_if => 'name',
1425 # SAME as
1426 validate_if => {field => 'name', required => 1},
1427
1428 validate_if => '! name',
1429 # SAME as
1430 validate_if => {field => 'name', max_in_set => '0 of name'},
1431
1432 validate_if => {field => 'country', compare => "eq US"},
1433 # only if country's value is equal to US
1434
1435 validate_if => {field => 'country', compare => "ne US"},
1436 # if country doesn't equal US
1437
1438 validate_if => {field => 'password', match => 'm/^md5\([a-z0-9]{20}\)$/'},
1439 # if password looks like md5(12345678901234567890)
1440
1441 {
1442 field => 'm/^(\w+)_pass/',
1443 validate_if => '$1_user',
1444 required => 1,
1445 }
1446 # will validate foo_pass only if foo_user was present.
1447
1448 The validate_if may also contain an arrayref of validation items. So that
1449 multiple checks can be run. They will be run in order. validate_if will
1450 return true only if all options returned true.
1451
1452 validate_if => ['email', 'phone', 'fax']
1453
1454 Optionally, if validate_if is an arrayref, it may contain the word
1455 'OR' as a special keyword. If the item preceding 'OR' fails validation
1456 the item after 'OR' will be tested instead. If the item preceding 'OR'
1457 passes validation the item after 'OR' will not be tested.
1458
1459 validate_if => [qw(zip OR postalcode)],
1460
1461 =item C<required_if>
1462
1463 Requires the form field if the condition is satisfied. The conditions
1464 available are the same as for validate_if. This is somewhat the same
1465 as saying:
1466
1467 validate_if => 'some_condition',
1468 required => 1
1469
1470 required_if => 'some_condition',
1471
1472 If a regex is used for the field name, the required_if
1473 field will have any match patterns swapped in.
1474
1475 {
1476 field => 'm/^(\w+)_pass/',
1477 required_if => '$1_user',
1478 }
1479
1480 This example would require the "foobar_pass" field to be set
1481 if the "foobar_user" field was passed.
1482
1483 =item C<required>
1484
1485 Requires the form field to have some value. If the field is not present,
1486 no other checks will be run.
1487
1488 =item C<min_values> and C<max_values>
1489
1490 Allows for specifying the maximum number of form elements passed.
1491 max_values defaults to 1 (You must explicitly set it higher
1492 to allow more than one item by any given name).
1493
1494 =item C<min_in_set> and C<max_in_set>
1495
1496 Somewhat like min_values and max_values except that you specify the
1497 fields that participate in the count. Also - entries that are not
1498 defined or do not have length are not counted. An optional "of" can
1499 be placed after the number for human readability.
1500
1501 min_in_set => "2 of foo bar baz",
1502 # two of the fields foo, bar or baz must be set
1503 # same as
1504 min_in_set => "2 foo bar baz",
1505 # same as
1506 min_in_set => "2 OF foo bar baz",
1507
1508 validate_if => {field => 'whatever', max_in_set => '0 of whatever'},
1509 # only run validation if there were zero occurrences of whatever
1510
1511 =item C<enum>
1512
1513 Allows for checking whether an item matches a set of options. In perl
1514 the value may be passed as an arrayref. In the conf or in perl the
1515 value may be passed of the options joined with ||.
1516
1517 {
1518 field => 'password_type',
1519 enum => 'plaintext||crypt||md5', # OR enum => [qw(plaintext crypt md5)],
1520 }
1521
1522 =item C<equals>
1523
1524 Allows for comparison of two form elements. Can have an optional !.
1525
1526 {
1527 field => 'password',
1528 equals => 'password_verify',
1529 },
1530 {
1531 field => 'domain1',
1532 equals => '!domain2', # make sure the fields are not the same
1533 }
1534
1535 =item C<min_len and max_len>
1536
1537 Allows for check on the length of fields
1538
1539 {
1540 field => 'site',
1541 min_len => 4,
1542 max_len => 100,
1543 }
1544
1545 =item C<match>
1546
1547 Allows for regular expression comparison. Multiple matches may
1548 be concatenated with ||. Available in JS.
1549
1550 {
1551 field => 'my_ip',
1552 match => 'm/^\d{1,3}(\.\d{1,3})3$/',
1553 match_2 => '!/^0\./ || !/^192\./',
1554 }
1555
1556 =item C<compare>
1557
1558 Allows for custom comparisons. Available types are
1559 >, <, >=, <=, !=, ==, gt, lt, ge, le, ne, and eq. Comparisons
1560 also work in the JS.
1561
1562 {
1563 field => 'my_number',
1564 match => 'm/^\d+$/',
1565 compare1 => '> 100',
1566 compare2 => '< 255',
1567 compare3 => '!= 150',
1568 }
1569
1570 =item C<sql>
1571
1572 SQL query based - not available in JS. The database handle will be looked
1573 for in the value $self->{dbhs}->{foo} if sql_db_type is set to 'foo',
1574 otherwise it will default to $self->{dbh}. If $self->{dbhs}->{foo} or
1575 $self->{dbh} is a coderef - they will be called and should return a dbh.
1576
1577 {
1578 field => 'username',
1579 sql => 'SELECT COUNT(*) FROM users WHERE username = ?',
1580 sql_error_if => 1, # default is 1 - set to 0 to negate result
1581 # sql_db_type => 'foo', # will look for a dbh under $self->{dbhs}->{foo}
1582 }
1583
1584 =item C<custom>
1585
1586 Custom value - not available in JS. Allows for extra programming types.
1587 May be either a boolean value predetermined before calling validate, or may be
1588 a coderef that will be called during validation. If coderef is called, it will
1589 be passed the field name, the form value for that name, and a reference to the
1590 field validation hash. If the custom type returns false the element fails
1591 validation and an error is added.
1592
1593 {
1594 field => 'username',
1595 custom => sub {
1596 my ($key, $val, $type, $field_val_hash) = @_;
1597 # do something here
1598 return 0;
1599 },
1600 }
1601
1602 =item C<custom_js>
1603
1604 Custom value - only available in JS. Allows for extra programming types.
1605 May be either a boolean value pre-determined before calling validate, or may be
1606 section of javascript that will be eval'ed. The last value (return value) of
1607 the eval'ed javascript will determine if validation passed. A false value indicates
1608 the value did not pass validation. A true value indicates that it did. See
1609 the t/samples/js_validate_3.html page for a sample of usage.
1610
1611 {
1612 field => 'date',
1613 required => 1,
1614 match => 'm|^\d\d\d\d/\d\d/\d\d$|',
1615 match_error => 'Please enter date in YYYY/MM/DD format',
1616 custom_js => "
1617 var t=new Date();
1618 var y=t.getYear()+1900;
1619 var m=t.getMonth() + 1;
1620 var d=t.getDate();
1621 if (m<10) m = '0'+m;
1622 if (d<10) d = '0'+d;
1623 (value > ''+y+'/'+m+'/'+d) ? 1 : 0;
1624 ",
1625 custom_js_error => 'The date was not greater than today.',
1626 }
1627
1628 =item C<type>
1629
1630 Allows for more strict type checking. Currently supported types
1631 include CC (credit card). Other types will be added upon request provided
1632 we can add a perl and a javascript version.
1633
1634 {
1635 field => 'credit_card',
1636 type => 'CC',
1637 }
1638
1639 =back
1640
1641 =head1 SPECIAL VALIDATION TYPES
1642
1643 =over 4
1644
1645 =item C<field>
1646
1647 Specify which field to work on. Key may be a regex in the form 'm/\w+_user/'.
1648 This key is required if 'group fields' is used or if validate_if or required_if
1649 are used. It can optionally be used with other types to specify a different form
1650 element to operate on. On errors, if a non-default error is found, $field
1651 will be swapped with the value found in field.
1652
1653 The field name may also be a regular expression in the
1654 form of 'm/somepattern/'. If a regular expression is used, all keys
1655 matching that pattern will be validated.
1656
1657 =item C<name>
1658
1659 Name to use for errors. If a name is not specified, default errors will use
1660 "The field $field" as the name. If a non-default error is found, $name
1661 will be swapped with this name.
1662
1663 =item C<delegate_error>
1664
1665 This option allows for any errors generated on a field to delegate to
1666 a different field. If the field name was a regex, any patterns will
1667 be swapped into the delegate_error value. This option is generally only
1668 useful with the as_hash method of the error object (for inline errors).
1669
1670 {
1671 field => 'zip',
1672 match => 'm/^\d{5}/',
1673 },
1674 {
1675 field => 'zip_plus4',
1676 match => 'm/^\d{4}/',
1677 delegate_error => 'zip',
1678 },
1679 {
1680 field => 'm/^(id_[\d+])_user$/',
1681 delegate_error => '$1',
1682 },
1683
1684 =item C<exclude_js>
1685
1686 This allows the cgi to do checking while keeping the checks from
1687 being run in JavaScript
1688
1689 {
1690 field => 'cgi_var',
1691 required => 1,
1692 exclude_js => 1,
1693 }
1694
1695 =item C<exclude_cgi>
1696
1697 This allows the js to do checking while keeping the checks from
1698 being run in the cgi
1699
1700 {
1701 field => 'js_var',
1702 required => 1,
1703 exclude_cgi => 1,
1704 }
1705
1706 =back
1707
1708 =head1 MODIFYING VALIDATION TYPES
1709
1710 The following types will modify the form value before it is processed.
1711 They work in both the perl and in javascript as well. The javascript
1712 version changes the actual value in the form on appropriate form types.
1713
1714 =over 4
1715
1716 =item C<do_not_trim>
1717
1718 By default, validate will trim leading and trailing whitespace
1719 from submitted values. Set do_not_trim to 1 to allow it to
1720 not trim.
1721
1722 {field => 'foo', do_not_trim => 1}
1723
1724 =item C<replace>
1725
1726 Pass a swap pattern to change the actual value of the form.
1727 Any perl regex can be passed but it is suggested that javascript
1728 compatible regexes are used to make generate_js possible.
1729
1730 {field => 'foo', replace => 's/(\d{3})(\d{3})(\d{3})/($1) $2-$3/'}
1731
1732 =item C<default>
1733
1734 Set item to default value if there is no existing value (undefined
1735 or zero length string).
1736
1737 {field => 'country', default => 'EN'}
1738
1739 =item C<to_upper_case> and C<to_lower_case>
1740
1741 Do what they say they do.
1742
1743 =item C<untaint>
1744
1745 Requires that the validated field has been also checked with
1746 an enum, equals, match, compare, custom, or type check. If the
1747 field has been checked and there are no errors - the field is "untainted."
1748
1749 This is for use in conjunction with perl's -T switch.
1750
1751 =back
1752
1753 =head1 ERROR OBJECT
1754
1755 Failed validation results in an error an error object created via the
1756 new_error method. The default error class is CGI::Ex::Validate::Error.
1757
1758 The error object has several methods for determining what the errors were.
1759
1760 =over 4
1761
1762 =item C<as_array>
1763
1764 Returns an array or arrayref (depending on scalar context) of errors that
1765 occurred in the order that they occurred. Individual groups may have a heading
1766 and the entire validation will have a heading (the default heading can be changed
1767 via the 'as_array_title' general option). Each error that occurred is a separate
1768 item and are pre-pended with 'as_array_prefix' (which is a general option - default
1769 is ' '). The as_array_ options may also be set via a hashref passed to as_array.
1770 as_array_title defaults to 'Please correct the following items:'.
1771
1772 ### if this returns the following
1773 my $array = $err_obj->as_array;
1774 # $array looks like
1775 # ['Please correct the following items:', ' error1', ' error2']
1776
1777 ### then this would return the following
1778 my $array = $err_obj->as_array({
1779 as_array_prefix => ' - ',
1780 as_array_title => 'Something went wrong:',
1781 });
1782 # $array looks like
1783 # ['Something went wrong:', ' - error1', ' - error2']
1784
1785 =item C<as_string>
1786
1787 Returns values of as_array joined with a newline. This method is used as
1788 the stringification for the error object. Values of as_array are joined with
1789 'as_string_join' which defaults to "\n". If 'as_string_header' is set, it will
1790 be pre-pended onto the error string. If 'as_string_footer' is set, it will be
1791 appended onto the error string.
1792
1793 ### if this returns the following
1794 my $string = $err_obj->as_string;
1795 # $string looks like
1796 # "Please correct the following items:\n error1\n error2"
1797
1798 ### then this would return the following
1799 my $string = $err_obj->as_string({
1800 as_array_prefix => ' - ',
1801 as_array_title => 'Something went wrong:',
1802 as_string_join => '<br />',
1803 as_string_header => '<span class="error">',
1804 as_string_footer => '</span>',
1805 });
1806 # $string looks like
1807 # '<span class="error">Something went wrong:<br /> - error1<br /> - error2</span>'
1808
1809 =item C<as_hash>
1810
1811 Returns a hash or hashref (depending on scalar context) of errors that
1812 occurred. Each key is the field name of the form that failed validation with
1813 'as_hash_suffix' added on as a suffix. as_hash_suffix is available as a general option
1814 and may also be passed in via a hashref as the only argument to as_hash.
1815 The default value is '_error'. The values of the hash are arrayrefs of errors
1816 that occurred to that form element.
1817
1818 By default as_hash will return the values of the hash as arrayrefs (a list of the errors
1819 that occurred to that key). It is possible to also return the values as strings.
1820 Three options are available for formatting: 'as_hash_header' which will be pre-pended
1821 onto the error string, 'as_hash_footer' which will be appended, and 'as_hash_join' which
1822 will be used to join the arrayref. The only argument required to force the
1823 stringification is 'as_hash_join'.
1824
1825 ### if this returns the following
1826 my $hash = $err_obj->as_hash;
1827 # $hash looks like
1828 # {key1_error => ['error1', 'error2']}
1829
1830 ### then this would return the following
1831 my $hash = $err_obj->as_hash({
1832 as_hash_suffix => '_foo',
1833 as_hash_join => '<br />',
1834 as_hash_header => '<span class="error">'
1835 as_hash_footer => '</span>'
1836 });
1837 # $hash looks like
1838 # {key1_foo => '<span class="error">error1<br />error2</span>'}
1839
1840 =back
1841
1842 =head1 GROUP OPTIONS
1843
1844 Any key in a validation hash matching the pattern m/^group\s+(\w+)$/
1845 is considered a group option. The current know options are:
1846
1847 =over 4
1848
1849 =item C<'group title'>
1850
1851 Used as a group section heading when as_array or as_string is called
1852 by the error object.
1853
1854 =item C<'group order'>
1855
1856 Order in which to validate key/value pairs of group.
1857
1858 =item C<'group fields'>
1859
1860 Arrayref of validation items to validate.
1861
1862 =item C<'group validate_if'>
1863
1864 Conditions that will be checked to see if the group should be validated.
1865 If no validate_if option is found, the group will be validated.
1866
1867 =back
1868
1869 =head1 GENERAL OPTIONS
1870
1871 Any key in a validation hash matching the pattern m/^general\s+(\w+)$/
1872 is considered a general option. General options will also be looked
1873 for in the Validate object ($self) and can be set when instantiating
1874 the object ($self->{raise_error} is equivalent to
1875 $valhash->{'general raise_error'}). The current know options are:
1876
1877 General options may be set in any group using the syntax:
1878
1879 'general general_option_name' => 'general_option_value'
1880
1881 They will only be set if the group's validate_if is successful or
1882 if the group does not have a validate_if. It is also possible to set
1883 a "group general" option using the following syntax:
1884
1885 'group general_option_name' => 'general_option_value'
1886
1887 These items will only be set if the group fails validation.
1888 If a group has a validate_if block and passes validation, the group
1889 items will not be used. This is so that a failed section can have
1890 its own settings. Note though that the last option found will be
1891 used and that items set in $self override those set in the validation
1892 hash.
1893
1894 Options may also be set globally before calling validate by
1895 populating the %DEFAULT_OPTIONS global hash.
1896
1897 =over 4
1898
1899 =item C<'general raise_error'>
1900
1901 If raise_error is true, any call to validate that fails validation
1902 will die with an error object as the value.
1903
1904 =item C<'general no_extra_fields'>
1905
1906 If no_extra_fields is true, validate will add errors for any field found
1907 in form that does not have a field_val hashref in the validation hash.
1908 Default is false. If no_extra_fields is set to 'used', it will check for
1909 any keys that were not in a group that was validated.
1910
1911 An important exception to this is that field_val hashrefs or field names listed
1912 in a validate_if or required_if statement will not be included. You must
1913 have an explicit entry for each key.
1914
1915 =item C<'general \w+_error'>
1916
1917 These items allow for an override of the default errors.
1918
1919 'general required_error' => '$name is really required',
1920 'general max_len_error' => '$name must be shorter than $value characters',
1921 # OR #
1922 my $self = CGI::Ex::Validate->new({
1923 max_len_error => '$name must be shorter than $value characters',
1924 });
1925
1926 =item C<'general as_array_title'>
1927
1928 Used as the section title for all errors that occur, when as_array
1929 or as_string is called by the error object.
1930
1931 =item C<'general as_array_prefix'>
1932
1933 Used as prefix to individual errors that occur, when as_array
1934 or as_string is called by the error object. Each individual error
1935 will be prefixed with this string. Headings will not be prefixed.
1936 Default is ' '.
1937
1938 =item C<'general as_string_join'>
1939
1940 When as_string is called, the values from as_array will be joined with
1941 as_string_join. Default value is "\n".
1942
1943 =item C<'general as_string_header'>
1944
1945 If set, will be pre-pended onto the string when as_string is called.
1946
1947 =item C<'general as_string_footer'>
1948
1949 If set, will be pre-pended onto the string when as_string is called.
1950
1951 =item C<'general as_hash_suffix'>
1952
1953 Added on to key names during the call to as_hash. Default is '_error'.
1954
1955 =item C<'general as_hash_join'>
1956
1957 By default, as_hash will return hashref values that are errors joined with
1958 the default as_hash_join value of <br />. It can also return values that are
1959 arrayrefs of the errors. This can be done by setting as_hash_join to a non-true value
1960 (for example '')
1961
1962 =item C<'general as_hash_header'>
1963
1964 If as_hash_join has been set to a true value, as_hash_header may be set to
1965 a string that will be pre-pended on to the error string.
1966
1967 =item C<'general as_hash_footer'>
1968
1969 If as_hash_join has been set to a true value, as_hash_footer may be set to
1970 a string that will be postpended on to the error string.
1971
1972 =item C<'general no_inline'>
1973
1974 If set to true, the javascript validation will not attempt to generate inline
1975 errors. Default is true. Inline errors are independent of confirm and alert
1976 errors.
1977
1978 =item C<'general no_confirm'>
1979
1980 If set to true, the javascript validation will try to use an alert instead
1981 of a confirm to inform the user of errors. Alert and confirm are independent
1982 or inline errors. Default is false.
1983
1984 =item C<'general no_alert'>
1985
1986 If set to true, the javascript validation will not show an alert box
1987 when errors occur. Default is false. This option only comes into
1988 play if no_confirm is also set. This option is independent of inline
1989 errors. Although it is possible to turn off all errors by setting
1990 no_inline, no_confirm, and no_alert all to 1, it is suggested that at
1991 least one of the error reporting facilities is left on.
1992
1993 =back
1994
1995 It is possible to have a group that contains nothing but general options.
1996
1997 my $val_hash = [
1998 {'general error_title' => 'The following things went wrong',
1999 'general error_prefix' => ' - ',
2000 'general raise_error' => 1,
2001 'general name_suffix' => '_foo_error',
2002 'general required_error' => '$name is required',
2003 },
2004 {'group title' => 'User Information',
2005 username => {required => 1},
2006 email => {required => 1},
2007 password => {required => 1},
2008 },
2009 ];
2010
2011 =head1 JAVASCRIPT
2012
2013 CGI::Ex::Validate provides for having duplicate validation on the
2014 client side as on the server side. Errors can be shown in any
2015 combination of inline and confirm, inline and alert, inline only,
2016 confirm only, alert only, and none. These combinations are controlled
2017 by the general options no_inline, no_confirm, and no_alert.
2018 Javascript validation can be generated for a page using the
2019 C<-E<gt>generate_js> Method of CGI::Ex::Validate. It is also possible
2020 to store the validation inline with the html. This can be done by
2021 giving each of the elements to be validated an attribute called
2022 "validation", or by setting a global javascript variable called
2023 "document.validation" or "var validation". An html file containing this
2024 validation will be read in using CGI::Ex::Conf::read_handler_html.
2025
2026 All inline html validation must be written in yaml.
2027
2028 It is anticipated that the html will contain something like either of the
2029 following examples:
2030
2031 <script src="/cgi-bin/js/CGI/Ex/yaml_load.js"></script>
2032 <script src="/cgi-bin/js/CGI/Ex/validate.js"></script>
2033 <script>
2034 // \n\ allows all browsers to view this as a single string
2035 document.validation = "\n\
2036 general no_confirm: 1\n\
2037 general no_alert: 1\n\
2038 group order: [username, password]\n\
2039 username:\n\
2040 required: 1\n\
2041 max_len: 20\n\
2042 password:\n\
2043 required: 1\n\
2044 max_len: 30\n\
2045 ";
2046 if (document.check_form) document.check_form('my_form_name');
2047 </script>
2048
2049 Alternately we can use element attributes:
2050
2051 <form name="my_form_name">
2052
2053 Username: <input type=text size=20 name=username validation="
2054 required: 1
2055 max_len: 20
2056 "><br>
2057 <span class=error id=username_error>[% username_error %]</span><br>
2058
2059 Password: <input type=text size=20 name=password validation="
2060 required: 1
2061 max_len: 30
2062 "><br>
2063 <span class=error id=password_error>[% password_error %]</span><br>
2064
2065 <input type=submit>
2066
2067 </form>
2068
2069 <script src="/cgi-bin/js/CGI/Ex/yaml_load.js"></script>
2070 <script src="/cgi-bin/js/CGI/Ex/validate.js"></script>
2071 <script>
2072 if (document.check_form) document.check_form('my_form_name');
2073 </script>
2074
2075 The read_handler_html from CGI::Ex::Conf will find either of these
2076 types of validation.
2077
2078 If inline errors are asked for, each error that occurs will attempt
2079 to find an html element with its name as the id. For example, if
2080 the field "username" failed validation and created a "username_error",
2081 the javascript would set the html of <span id="username_error"></span>
2082 to the error message.
2083
2084 It is suggested to use something like the following so that you can
2085 have inline javascript validation as well as report validation errors
2086 from the server side as well.
2087
2088 <span class=error id=password_error>[% password_error %]</span><br>
2089
2090 If the javascript fails for some reason, the form should still be able
2091 to submit as normal (fail gracefully).
2092
2093 If the confirm option is used, the errors will be displayed to the user.
2094 If they choose OK they will be able to try and fix the errors. If they
2095 choose cancel, the form will submit anyway and will rely on the server
2096 to do the validation. This is for fail safety to make sure that if the
2097 javascript didn't validate correctly, the user can still submit the data.
2098
2099 =head1 THANKS
2100
2101 Thanks to Eamon Daly for providing bug fixes for bugs in validate.js
2102 caused by HTML::Prototype.
2103
2104 =head1 AUTHOR
2105
2106 Paul Seamons
2107
2108 =head1 LICENSE
2109
2110 This module may be distributed under the same terms as Perl itself.
2111
2112 =cut
2113
2114
This page took 0.174022 seconds and 4 git commands to generate.