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