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