]>
Commit | Line | Data |
---|---|---|
0ec4ac8a TC |
1 | package DevHelp::Validate; |
2 | use strict; | |
3 | require Exporter; | |
4 | use vars qw(@EXPORT_OK @ISA); | |
a5e3fc4b | 5 | @EXPORT_OK = qw(dh_validate dh_validate_hash dh_fieldnames dh_configure_fields); |
0ec4ac8a TC |
6 | @ISA = qw(Exporter); |
7 | ||
8 | sub new { | |
9 | my ($class, %opts) = @_; | |
10 | ||
11 | return bless \%opts, $class; | |
12 | } | |
13 | ||
14 | my %built_ins = | |
15 | ( | |
16 | email => | |
17 | { | |
18 | match => qr/^[^\@]+\@[\w.-]+\.\w+$/, | |
19 | error => '$n is not a valid email address', | |
20 | }, | |
21 | phone => | |
22 | { | |
23 | match => qr/\d(?:\D*\d){3}/, | |
24 | error => '$n is not a valid phone number', | |
25 | }, | |
26 | postcode => | |
27 | { | |
28 | match => qr/\d(?:\D*\d){3}/, | |
29 | error => '$n is not a valid post code', | |
30 | }, | |
31 | url => | |
32 | { | |
33 | match => qr!^\w+://[\w-]+(?:\.[\w-]+)+(?::\d+)?!, | |
34 | error => '$n is not a valid URL', | |
35 | }, | |
36 | weburl => | |
37 | { | |
38 | match => qr!^https?://[\w-]+(?:\.[\w-]+)+(?::\d+)?!, | |
39 | error => '$n is not a valid URL, it must start with http:// or https://', | |
40 | }, | |
41 | newbieweburl => | |
42 | { | |
43 | match => qr!^(?:https?://)?[\w-]+(?:\.[\w-]+)+(?::\d+)?!, | |
44 | error => '$n is not a valid URL', | |
45 | }, | |
46 | confirm => | |
47 | { | |
48 | confirm=>'password', | |
49 | }, | |
50 | newconfirm => | |
51 | { | |
52 | newconfirm=>'password', | |
53 | }, | |
54 | required => | |
55 | { | |
56 | required => 1, | |
57 | }, | |
58 | abn => | |
59 | { | |
60 | match => qr/\d(?:\D*\d){7}/, | |
61 | error => '$n is not a valid ABN', | |
62 | }, | |
63 | creditcardnumber => | |
64 | { | |
65 | match => qr/^\D*\d(?:\D*\d){11,15}\D*$/, | |
66 | error => '$n is not a valid credit card number', | |
67 | }, | |
68 | creditcardexpiry => | |
69 | { | |
70 | ccexpiry => 1, | |
71 | }, | |
41e7c841 TC |
72 | creditcardexpirysingle => |
73 | { | |
74 | ccexpirysingle => 1, | |
75 | }, | |
76 | creditcardcvv => | |
77 | { | |
78 | match => qr/^(\d){3,4}$/, | |
79 | error => '$n is the 3 or 4 digit code on the back of your card', | |
80 | }, | |
0ec4ac8a TC |
81 | miaa => |
82 | { | |
83 | match => qr/^\s*\d{1,6}\s*$/, | |
84 | error => 'Not a valid MIAA membership number', | |
85 | }, | |
86 | decimal => | |
87 | { | |
88 | match => qr/^\s*(?:\d+(?:\.\d*)?|\.\d+)\s*$/, | |
89 | error => 'Not a valid number', | |
90 | }, | |
91 | money => | |
92 | { | |
93 | match => qr/^\s*(?:\d+(?:\.\d\d)?|\.\d\d)\s*$/, | |
94 | error => 'Not a valid money amount', | |
95 | }, | |
96 | date => | |
97 | { | |
98 | date => 1, | |
99 | }, | |
100 | birthdate => | |
101 | { | |
102 | date => 1, | |
103 | maxdate => '+0y', | |
104 | maxdatemsg => 'Your $n must be in the past', | |
105 | }, | |
106 | adultbirthdate => | |
107 | { | |
108 | date => 1, | |
109 | maxdate => '-10y', | |
110 | maxdatemsg => 'You must be at least 10 years old...', | |
111 | mindate => '-100y', | |
112 | }, | |
113 | futuredate => | |
114 | { | |
115 | date => 1, | |
116 | mindate => '-1d', | |
117 | mindatemsg => 'The date entered must be in the future', | |
118 | }, | |
119 | natural => | |
120 | { | |
121 | integer => '0-', # 0 or higher | |
122 | }, | |
123 | positiveint => | |
124 | { | |
125 | integer => '1-', # 1 or higher | |
126 | }, | |
829c9ed9 TC |
127 | dh_one_line => |
128 | { | |
129 | nomatch => qr/[\x0D\x0A]/, | |
130 | error => '$n may only contain a single line', | |
131 | } | |
0ec4ac8a TC |
132 | ); |
133 | ||
134 | sub dh_validate { | |
135 | my ($cgi, $errors, $validation, $cfg, $section) = @_; | |
136 | ||
137 | return DevHelp::Validate::CGI->new(cfg=>$cfg, cgi=>$cgi, section=>$section) | |
138 | ->validate($errors, $validation); | |
139 | } | |
140 | ||
141 | sub dh_validate_hash { | |
142 | my ($hash, $errors, $validation, $cfg, $section) = @_; | |
143 | ||
144 | return DevHelp::Validate::Hash->new(cfg=>$cfg, hash=>$hash, section=>$section) | |
145 | ->validate($errors, $validation); | |
146 | } | |
147 | ||
148 | sub validate { | |
149 | my ($self, $errors, $validation) = @_; | |
150 | ||
151 | my $rules = $validation->{rules}; | |
152 | my $fields = $validation->{fields}; | |
153 | my $optional = $validation->{optional}; | |
154 | ||
155 | my %cfg_rules; | |
156 | _get_cfg_fields(\%cfg_rules, $self->{cfg}, $self->{section}, $fields) | |
157 | if $self->{cfg} && $self->{section}; | |
158 | ||
159 | for my $rulename (keys %$rules) { | |
160 | unless (exists $cfg_rules{rules}{$rulename}) { | |
161 | $cfg_rules{rules}{$rulename} = $rules->{$rulename}; | |
162 | } | |
163 | } | |
164 | for my $rulename (keys %built_ins) { | |
165 | unless (exists $cfg_rules{rules}{$rulename}) { | |
166 | $cfg_rules{rules}{$rulename} = $built_ins{$rulename}; | |
167 | } | |
168 | } | |
169 | ||
170 | # merge the supplied fields into the config fields | |
171 | my $cfg_fields = $cfg_rules{fields}; | |
172 | for my $field ( keys %$fields ) { | |
173 | my $src = $fields->{$field}; | |
174 | ||
175 | my $dest = $cfg_fields->{$field} || {}; | |
176 | ||
177 | # the config overrides the software supplied fields | |
178 | for my $override (qw(description required required_error)) { | |
179 | if (defined $src->{$override} && !defined $dest->{$override}) { | |
180 | $dest->{$override} = $src->{$override}; | |
181 | } | |
182 | } | |
183 | ||
cc53c774 | 184 | # but we add rules and required_if |
0ec4ac8a TC |
185 | if ($dest->{rules}) { |
186 | my $rules = $src->{rules}; | |
187 | ||
188 | # make a copy of the rules array if it's supplied that way so | |
189 | # we don't modify someone else's data | |
190 | $rules = ref $rules ? [ @$rules ] : [ split /;/, $rules ]; | |
191 | ||
192 | push @$rules, split /;/, $dest->{rules}; | |
193 | } | |
194 | elsif ($src->{rules}) { | |
195 | $dest->{rules} = $src->{rules}; | |
196 | } | |
cc53c774 TC |
197 | if ($dest->{required_if}) { |
198 | $dest->{required_if} .= ";" . $src->{required_if} if $src->{required_if}; | |
199 | } | |
200 | elsif ($src->{required_if}) { | |
201 | $dest->{required_if} = $src->{required_if}; | |
202 | } | |
0ec4ac8a TC |
203 | |
204 | $cfg_fields->{$field} = $dest if keys %$dest; | |
205 | } | |
206 | for my $field ( keys %$cfg_fields ) { | |
207 | $self->validate_field($field, $cfg_fields->{$field}, $cfg_rules{rules}, | |
208 | $optional, $errors); | |
209 | } | |
210 | ||
211 | !keys %$errors; | |
212 | } | |
213 | ||
214 | sub validate_field { | |
215 | my ($self, $field, $info, $rules, $optional, $errors) = @_; | |
216 | ||
217 | my @data = $self->param($field); | |
218 | ||
219 | my $required = $info->{required}; | |
220 | if (@data && $data[0] !~ /\S/ && $info->{required_if}) { | |
221 | # field is required if any of the named fields are non-blank | |
222 | for my $testfield (split /;/, $info->{required_if}) { | |
223 | my $testvalue = $self->param($testfield); | |
224 | if (defined $testvalue && $testvalue =~ /\S/) { | |
225 | ++$required; | |
226 | last; | |
227 | } | |
228 | } | |
229 | } | |
230 | ||
231 | my $rule_names = $info->{rules}; | |
232 | defined $rule_names or $rule_names = ''; | |
233 | $rule_names = [ split /;/, $rule_names ] unless ref $rule_names; | |
234 | ||
235 | push @$rule_names, 'required' if $required; | |
236 | ||
237 | @$rule_names or return; | |
238 | ||
239 | RULE: for my $rule_name (@$rule_names) { | |
240 | my $rule = $rules->{$rule_name}; | |
241 | unless ($rule) { | |
242 | $rule = $self->_get_cfg_rule($rule_name); | |
243 | if ($rule) { | |
244 | $rules->{$rule_name} = $rule; | |
245 | } | |
246 | else { | |
247 | print STDERR "** Unknown validation rule $rule_name for $field\n"; | |
248 | } | |
249 | } | |
250 | if (!$optional && $rule->{required} && !@data ) { | |
251 | $errors->{$field} = _make_error($field, $info, $rule, | |
252 | $info->{required_error} || | |
253 | $rule->{required_error} || | |
254 | '$n is a required field'); | |
255 | last RULE; | |
256 | } | |
257 | for my $data (@data) { | |
258 | if ($rule->{required} && $data !~ /\S/) { | |
259 | $errors->{$field} = _make_error($field, $info, $rule, | |
260 | $info->{required_error} || | |
261 | $rule->{required_error} || | |
262 | '$n is a required field'); | |
263 | last RULE; | |
264 | } | |
265 | if ($rule->{newconfirm}) { | |
266 | my $other = $self->param($rule->{newconfirm}); | |
267 | if ($other ne '' || $data ne '') { | |
268 | if ($other ne $data) { | |
269 | $errors->{$field} = _make_error($field, $info, $rule, | |
270 | q!$n doesn't match the password!); | |
271 | last RULE; | |
272 | } | |
273 | } | |
274 | } | |
275 | if ($data !~ /\S/ && !$rule->{required}) { | |
276 | next RULE; | |
277 | } | |
278 | if ($rule->{match}) { | |
279 | my $match = $rule->{match}; | |
280 | unless ($data =~ /$match/) { | |
281 | $errors->{$field} = _make_error($field, $info, $rule); | |
282 | last RULE; | |
283 | } | |
284 | } | |
50c3d7f9 | 285 | if (defined $rule->{nomatch}) { |
0ec4ac8a TC |
286 | my $match = $rule->{nomatch}; |
287 | if ($data =~ /$match/) { | |
288 | $errors->{$field} = _make_error($field, $info, $rule); | |
289 | last RULE; | |
290 | } | |
291 | } | |
292 | if ($rule->{integer}) { | |
293 | unless ($data =~ /^\s*([-+]?\d+)s*$/) { | |
294 | $errors->{$field} = _make_error($field, $info, $rule, | |
295 | '$n must be a whole number'); | |
296 | last RULE; | |
297 | } | |
298 | my $num = $1; | |
299 | if (my ($from, $to) = $rule->{integer} =~ /^([+-]?\d+)-([+-]?\d+)$/) { | |
300 | unless ($from <= $num and $num <= $to) { | |
301 | $errors->{$field} = _make_error($field, $info, $rule, | |
302 | $info->{range_error} || | |
303 | $rule->{range_error} || | |
304 | "\$n must be in the range $from to $to"); | |
305 | last RULE; | |
306 | } | |
307 | } | |
308 | elsif (my ($from2) = $rule->{integer} =~ /^([+-]?\d+)-$/) { | |
309 | unless ($from2 <= $num) { | |
310 | $errors->{$field} = _make_error($field, $info, $rule, | |
311 | $info->{range_error} || | |
312 | $rule->{range_error} || | |
313 | "\$n must be $from2 or higher"); | |
314 | last RULE; | |
315 | } | |
316 | } | |
317 | } | |
318 | if ($rule->{date}) { | |
319 | unless ($data =~ m!^\s*(\d+)[-+/](\d+)[-+/](\d+)\s*$!) { | |
320 | $errors->{$field} = _make_error($field, $info, $rule, | |
321 | '$n must be a valid date'); | |
322 | last RULE; | |
323 | } | |
324 | my ($day, $month, $year) = ($1, $2, $3); | |
325 | if ($day < 1 || $day > 31) { | |
326 | $errors->{$field} = _make_error($field, $info, $rule, | |
327 | '$n must be a valid date - day out of range'); | |
328 | last RULE; | |
329 | } | |
330 | if ($month < 1 || $month > 12) { | |
331 | $errors->{$field} = _make_error($field, $info, $rule, | |
332 | '$n must be a valid date - month out of range'); | |
333 | last RULE; | |
334 | } | |
335 | if ($rule->{mindate} || $rule->{maxdate}) { | |
336 | require DevHelp::Date; | |
337 | my $workdate = sprintf("%04d-%02d-%02d", $year, $month, $day); | |
338 | if ($rule->{mindate}) { | |
339 | my $mindate = DevHelp::Date::dh_parse_date_sql($rule->{mindate}); | |
340 | if ($workdate le $mindate) { | |
341 | $errors->{$field} = | |
342 | _make_error($field, $info, $rule, | |
343 | $info->{mindatemsg} || $rule->{mindatemsg} || '$n is too early'); | |
344 | } | |
345 | } | |
346 | if (!$errors->{$field} && $rule->{maxdate}) { | |
347 | my $maxdate = DevHelp::Date::dh_parse_date_sql($rule->{maxdate}); | |
348 | if ($workdate ge $maxdate) { | |
349 | $errors->{$field} = | |
350 | _make_error($field, $info, $rule, | |
351 | $info->{mindatemsg} || $rule->{maxdatemsg} || '$n is too late'); | |
352 | } | |
353 | } | |
354 | } | |
355 | } | |
356 | if ($rule->{confirm}) { | |
357 | my $other = $self->param($rule->{confirm}); | |
358 | unless ($other eq $data) { | |
359 | $errors->{$field} = _make_error($field, $info, $rule, | |
360 | q!$n doesn't match the password!); | |
361 | last RULE; | |
362 | } | |
363 | } | |
364 | if ($rule->{ccexpiry}) { | |
365 | (my $year_field = $field) =~ s/month/year/; | |
366 | ||
367 | unless ($data =~ /^\s*\d+\s*$/) { | |
368 | $errors->{$field} = _make_error($field, $info, $rule, | |
369 | q!$n month isn't a number!); | |
370 | last RULE; | |
371 | } | |
372 | my $year = $self->param($year_field); | |
373 | unless (defined $year && $year =~ /\s*\d+\s*$/) { | |
374 | $errors->{$field} = _make_error($field, $info, $rule, | |
375 | q!$n year isn't a number!); | |
376 | last RULE; | |
377 | } | |
378 | my ($now_year, $now_month) = (localtime)[5, 4]; | |
379 | $now_year += 1900; | |
380 | ++$now_month; | |
381 | if ($year < $now_year || $year == $now_year && $data < $now_month) { | |
382 | $errors->{$field} = _make_error($field, $info, $rule, | |
383 | q!$n is in the past, your card has expired!); | |
384 | last RULE; | |
385 | } | |
386 | } | |
41e7c841 TC |
387 | if ($rule->{ccexpirysingle}) { |
388 | unless ($data =~ m!^\s*(\d+)\s*/\s*(\d+)+\s*$!) { | |
389 | $errors->{$field} = _make_error($field, $info, $rule, | |
390 | q!$n must be in MM/YY format!); | |
391 | last RULE; | |
392 | } | |
393 | my ($month, $year) = ($1, $2); | |
394 | $year += 2000; | |
395 | if ($month < 1 || $month > 12) { | |
396 | $errors->{$field} = _make_error($field, $info, $rule, | |
397 | q!$n month must be between 1 and 12!); | |
398 | last RULE; | |
399 | } | |
400 | my ($now_year, $now_month) = (localtime)[5, 4]; | |
401 | $now_year += 1900; | |
402 | ++$now_month; | |
403 | if ($year < $now_year || $year == $now_year && $month < $now_month) { | |
404 | $errors->{$field} = _make_error($field, $info, $rule, | |
405 | q!$n is in the past, your card has expired!); | |
406 | last RULE; | |
407 | } | |
408 | } | |
0ec4ac8a TC |
409 | } |
410 | } | |
411 | } | |
412 | ||
413 | sub _make_error { | |
414 | my ($field, $info, $rule, $message) = @_; | |
415 | ||
416 | $message ||= $rule->{error} || 'Validation error on field $n'; | |
417 | ||
418 | my $name = $info->{description} || $field; | |
419 | $message =~ s/\$n/$name/g; | |
420 | ||
421 | return $message; | |
422 | } | |
423 | ||
424 | sub _get_cfg_fields { | |
425 | my ($rules, $cfg, $section, $field_hash) = @_; | |
426 | ||
427 | $rules->{rules} = {}; | |
428 | $rules->{fields} = {}; | |
429 | ||
430 | my $cfg_fields = $rules->{fields}; | |
431 | ||
432 | my $fields = $cfg->entry($section, 'fields', ''); | |
433 | my @names = ( split(/,/, $fields), keys %$field_hash ); | |
434 | ||
435 | for my $field (@names) { | |
0f9b108b | 436 | for my $cfg_name (qw(required rules description required_error range_error mindatemsg maxdatemsg)) { |
0ec4ac8a TC |
437 | my $value = $cfg->entry($section, "${field}_$cfg_name"); |
438 | if (defined $value) { | |
439 | $cfg_fields->{$field}{$cfg_name} = $value; | |
440 | } | |
441 | } | |
d44b5da9 TC |
442 | |
443 | my $values = $cfg->entry($section, "${field}_values"); | |
444 | if (defined $values) { | |
445 | my @values; | |
446 | if ($values =~ /;/) { | |
447 | for my $entry (split /;/, $values) { | |
cc53c774 | 448 | if ($entry =~ /^([^=]*)=(.*)$/) { |
d44b5da9 TC |
449 | push @values, [ $1, $2 ]; |
450 | } | |
451 | else { | |
452 | push @values, [ $entry, $entry ]; | |
453 | } | |
454 | } | |
455 | } | |
456 | else { | |
cc53c774 TC |
457 | my $strip; |
458 | if ($values =~ s/:([^:]*)$//) { | |
459 | $strip = $1; | |
460 | } | |
d44b5da9 TC |
461 | my %entries = $cfg->entriesCS($values); |
462 | my @order = $cfg->orderCS($values); | |
463 | ||
464 | my %seen; | |
465 | # we only want the last value in the order | |
466 | @order = reverse grep !$seen{$_}++, reverse @order; | |
467 | @values = map [ $_, $entries{$_} ], @order; | |
cc53c774 TC |
468 | if ($strip) { |
469 | $_->[0] =~ s/^\Q$strip// for @values; | |
470 | } | |
d44b5da9 TC |
471 | } |
472 | $cfg_fields->{$field}{values} = \@values; | |
473 | } | |
0ec4ac8a TC |
474 | } |
475 | } | |
476 | ||
a5e3fc4b TC |
477 | sub dh_configure_fields { |
478 | my ($fields, $cfg, $section) = @_; | |
479 | ||
480 | my %cfg_rules; | |
481 | _get_cfg_fields(\%cfg_rules, $cfg, $section, $fields); | |
482 | ||
483 | # **FIXME** duplicated code | |
484 | my $cfg_fields = $cfg_rules{fields}; | |
485 | for my $field ( keys %$fields ) { | |
486 | my $src = $fields->{$field}; | |
487 | ||
488 | my $dest = $cfg_fields->{$field} || {}; | |
489 | ||
490 | # the config overrides the software supplied fields | |
491 | for my $override (qw(description required required_error range_error mindatemsg maxdatemsg htmltype type width height size)) { | |
492 | if (defined $src->{$override} && !defined $dest->{$override}) { | |
493 | $dest->{$override} = $src->{$override}; | |
494 | } | |
495 | } | |
496 | ||
497 | # but we add rules | |
498 | if ($dest->{rules}) { | |
499 | my $rules = $src->{rules}; | |
500 | ||
501 | # make a copy of the rules array if it's supplied that way so | |
502 | # we don't modify someone else's data | |
503 | $rules = ref $rules ? [ @$rules ] : [ split /;/, $rules ]; | |
504 | ||
505 | push @$rules, split /;/, $dest->{rules}; | |
506 | } | |
507 | elsif ($src->{rules}) { | |
508 | $dest->{rules} = $src->{rules}; | |
509 | } | |
510 | ||
511 | $cfg_fields->{$field} = $dest if keys %$dest; | |
512 | } | |
513 | ||
514 | return $cfg_fields; | |
515 | } | |
516 | ||
0ec4ac8a TC |
517 | sub _get_cfg_rule { |
518 | my ($self, $rulename) = @_; | |
519 | ||
520 | my %rule = $self->{cfg}->entries("Validation Rule $rulename"); | |
521 | ||
522 | keys %rule or return; | |
523 | ||
524 | \%rule; | |
525 | } | |
526 | ||
527 | sub dh_fieldnames { | |
528 | my ($cfg, $section, $fields) = @_; | |
529 | ||
530 | # this needs to be obsoleted now that dh_validate() checks the config | |
531 | ||
532 | for my $field (keys %$fields) { | |
533 | my $desc = $cfg->entry($section, $field); | |
534 | defined $desc and $fields->{$field}{description} = $desc; | |
535 | } | |
536 | } | |
537 | ||
538 | package DevHelp::Validate::CGI; | |
539 | use vars qw(@ISA); | |
540 | @ISA = qw(DevHelp::Validate); | |
541 | ||
542 | sub param { | |
543 | my ($self, $field) = @_; | |
544 | ||
545 | $self->{cgi}->param($field); | |
546 | } | |
547 | ||
548 | package DevHelp::Validate::Hash; | |
549 | use vars qw(@ISA); | |
550 | @ISA = qw(DevHelp::Validate); | |
551 | ||
552 | sub param { | |
553 | my ($self, $field) = @_; | |
554 | ||
555 | my $value = $self->{hash}{$field}; | |
556 | ||
557 | defined $value or return; | |
558 | ||
559 | if (ref $value eq 'ARRAY') { | |
560 | return @$value; | |
561 | } | |
562 | ||
563 | return $value; | |
564 | } | |
565 | ||
566 | 1; | |
567 | ||
568 | __END__ | |
569 | ||
570 | =head1 NAME | |
571 | ||
572 | DevHelp::Validate - handy configurable validation, I hope | |
573 | ||
574 | =head1 SYNOPSIS | |
575 | ||
576 | use DevHelp::Validate qw(dh_validate); | |
577 | ||
578 | dh_validate($cgi, \%errors, \%rules, $cfg) | |
579 | or display_errors(..., \%errors); | |
580 | ||
581 | =head1 DESCRIPTION | |
582 | ||
583 | =head1 RULES PARAMETER | |
584 | ||
585 | The rules parameter is a hash with 2 keys: | |
586 | ||
587 | =over | |
588 | ||
589 | =item fields | |
590 | ||
591 | A hash of field names, for each of which the value is a hash. | |
592 | ||
593 | Each hash can have the following keys: | |
594 | ||
595 | =over | |
596 | ||
597 | =item rules | |
598 | ||
599 | A simple rule name, a ';' separated list of rule names or an array | |
600 | ref. | |
601 | ||
602 | =item description | |
603 | ||
604 | A short description of the field, for use in error messages. | |
605 | ||
606 | =back | |
607 | ||
608 | =item rules | |
609 | ||
610 | A hash of rules. See the rules description under L<CONFIGURED | |
611 | VALIDATON>. | |
612 | ||
613 | =back | |
614 | ||
615 | =head1 CONFIGURED VALIDATION | |
616 | ||
617 | Rules can be configured in the database. | |
618 | ||
619 | For the specified section name, each key is a CGI field name. | |
620 | ||
621 | The values of those keys gives the name of a validation rule, a string | |
622 | id for internationlization of the field description and a default | |
623 | field description, separated by commas. | |
624 | ||
625 | Each validation rule name has a corresponding section, [Validate Rule | |
626 | I<rule-name>], which describes the rule. Rule names can also refer to | |
627 | built-in rules, | |
628 | ||
629 | Values in the validation rule section are: | |
630 | ||
631 | =over | |
632 | ||
633 | =item required | |
634 | ||
635 | If this is non-zero the field is required. | |
636 | ||
637 | =item match | |
638 | ||
639 | If present, this is used as a regular expression the field must match. | |
640 | ||
829c9ed9 TC |
641 | =item nomatch |
642 | ||
643 | If present, this is used as a regular expression the field must not | |
644 | match. | |
645 | ||
0ec4ac8a TC |
646 | =item error |
647 | ||
648 | Message returned as the error if the field fails validation. | |
649 | ||
650 | =item integer | |
651 | ||
652 | If set to 1, simply ensures the value is an integer. | |
653 | ||
654 | If set to a range I<integer>-I<integer> then ensures the value is an | |
655 | integer in that range. | |
656 | ||
657 | =item date | |
658 | ||
659 | If set to 1, simply validates the value as a date. | |
660 | ||
661 | =back | |
662 | ||
829c9ed9 TC |
663 | =head1 BUILT-IN RULES |
664 | ||
665 | =over | |
666 | ||
667 | =item email | |
668 | ||
669 | =item phone | |
670 | ||
671 | =item postcode | |
672 | ||
673 | =item url | |
674 | ||
675 | Matches any valid url, including mailto:, ftp:, etc. No checking of | |
676 | the scheme is done. | |
677 | ||
678 | =item weburl | |
679 | ||
680 | Matches any valid http or https url. | |
681 | ||
682 | =item newbieweburl | |
683 | ||
684 | Matches web URLs with or without "http://" or "https://". | |
685 | ||
686 | =item confirm | |
687 | ||
688 | Treats the given field as a confirmation field for a password field | |
689 | "password". | |
690 | ||
691 | =item newconfirm | |
692 | ||
693 | Treats the given field as a confirmation field for an optional | |
694 | password field "password". | |
695 | ||
696 | =item required | |
697 | ||
698 | The field is required. This should be used where the logic of the | |
699 | code requires a field, since it cannot be overridden by the config | |
700 | file. | |
701 | ||
702 | =item abn | |
703 | ||
704 | Valid Australian Business Number | |
705 | ||
706 | =item creditcardnumber | |
707 | ||
708 | Valid credit card number (no checksum checks are performed). | |
709 | ||
710 | =item creditcardexpiry | |
711 | ||
712 | Treats the field as the month part of a credit card expiry date, with | |
713 | the year field being the field with the same name, but "month" | |
714 | replaced with "year". The date of expiry is required to be in the | |
715 | future. | |
716 | ||
717 | =item miaa | |
718 | ||
719 | Valid MIAA membership number. | |
720 | ||
721 | =item decimal | |
722 | ||
723 | Valid simple decimal number | |
724 | ||
725 | =item money | |
726 | ||
727 | Valid decimal number with 0 or 2 digits after the decimal point. | |
728 | ||
729 | =item date | |
730 | ||
731 | A valid date. Currently dates are limited to Australian format | |
732 | dd/mm/yy or dd/mm/yyyy format dates. | |
733 | ||
734 | =item birthdate | |
735 | ||
736 | A valid date in the past. | |
737 | ||
738 | =item adultbirthdate | |
739 | ||
740 | A valid date at least 10 years in the past, at most 100 years in the | |
741 | past. | |
742 | ||
743 | =item futuredate | |
744 | ||
745 | A valid date in the future. | |
746 | ||
747 | =item natural | |
748 | ||
749 | An integer greater or equal to zero. | |
750 | ||
751 | =item positiveint | |
752 | ||
753 | A positive integer. | |
754 | ||
755 | =item dh_one_line | |
756 | ||
757 | The field may not contain line breaks | |
758 | ||
759 | =back | |
760 | ||
0ec4ac8a TC |
761 | =head1 AUTHOR |
762 | ||
763 | Tony Cook <tony@develop-help.com> | |
764 | ||
765 | =cut |