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