File Coverage

lib/HTML/ValidationRules/Legacy.pm
Criterion Covered Total %
statement 87 94 92.5
branch 53 64 82.8
condition 21 28 75.0
subroutine 10 10 100.0
pod 2 2 100.0
total 173 198 87.3


line stmt bran cond sub pod time code
1             package HTML::ValidationRules::Legacy;
2 3     3   28331 use strict;
  3         6  
  3         107  
3 3     3   11 use warnings;
  3         3  
  3         78  
4 3     3   501 use Mojo::Base 'Exporter';
  3         7426  
  3         28  
5 3     3   1042 use Mojo::JSON;
  3         43686  
  3         126  
6 3     3   16 use Mojo::Util qw{decode};
  3         2  
  3         145  
7 3     3   1156 use Mojo::Parameters;
  3         3360  
  3         23  
8 3     3   107 use Scalar::Util qw(blessed);
  3         6  
  3         4670  
9              
10             our @EXPORT_OK = qw(extract validate),
11              
12             our $TERM_PROPERTIES = 'properties';
13             our $TERM_REQUIRED = 'required';
14             our $TERM_MAXLENGTH = 'maxLength';
15             our $TERM_MIN_LENGTH = 'minLength';
16             our $TERM_OPTIONS = 'options';
17             our $TERM_PATTERN = 'pattern';
18             our $TERM_MIN = 'maximam';
19             our $TERM_MAX = 'minimum';
20             our $TERM_TYPE = 'type';
21             our $TERM_ADD_PROPS = 'additionalProperties';
22             our $TERM_NUMBER = 'number';
23              
24             sub extract {
25 17     17 1 19 my ($form, $charset) = @_;
26 17         20 my $props = {};
27 17         15 my @required;
28            
29 17 50       35 if (! ref $form) {
30 0 0       0 $form = Mojo::DOM->new($charset ? decode($charset, $form) : $form);
31             }
32            
33             $form->find("*[name]")->each(sub {
34 40     40   8548 my $tag = shift;
35 40   100     76 my $type = $tag->attr('type') || '';
36 40         1318 my $name = $tag->attr('name');
37 40   100     1307 $props->{$name} ||= {};
38            
39 40 100       57 if (grep {$_ eq $type} qw{hidden checkbox radio submit image}) {
  200         308  
40 24         23 push(@{$props->{$name}->{$TERM_OPTIONS}}, $tag->attr('value'));
  24         75  
41             }
42            
43 40 100       722 if ($tag->tag eq 'select') {
44             $tag->find('option')->each(sub {
45 6         901 push(@{$props->{$name}->{$TERM_OPTIONS}}, shift->attr('value'));
  6         21  
46 2         67 });
47             }
48            
49 40 100       1255 if ($type eq 'number') {
50 1         11 $props->{$name}->{$TERM_TYPE} = $TERM_NUMBER;
51 1 50       5 if (my $val = $tag->attr->{min}) {
52 1         42 $props->{$name}->{$TERM_MIN} = $val;
53             }
54 1 50       3 if (my $val = $tag->attr->{max}) {
55 1         40 $props->{$name}->{$TERM_MAX} = $val;
56             }
57             }
58            
59 40 100       84 if (! exists $tag->attr->{disabled}) {
60 33 100 66     1134 if ($type ne 'submit' && $type ne 'image' && $type ne 'checkbox' &&
      100        
      100        
      66        
61             ($type ne 'radio' || exists $tag->attr->{checked})) {
62 19         94 $props->{$name}->{$TERM_REQUIRED} = Mojo::JSON->true;
63             }
64             }
65            
66 40 100       546 if (exists $tag->attr->{maxlength}) {
67 2   100     90 $props->{$name}->{$TERM_MAXLENGTH} = $tag->attr->{maxlength} || 0;
68             }
69            
70 40 100       1207 if (exists $tag->attr->{required}) {
71 2         50 $props->{$name}->{$TERM_MIN_LENGTH} = 1;
72             }
73            
74 40 100       1046 if (exists $tag->attr->{pattern}) {
75 1         24 $props->{$name}->{$TERM_PATTERN} = $tag->attr->{pattern};
76             }
77 17         50 });
78            
79             return {
80 17         923 $TERM_PROPERTIES => $props,
81             $TERM_ADD_PROPS => Mojo::JSON->false,
82             };
83             }
84              
85             sub validate {
86 44     44 1 382 my ($schema, $params, $charset) = @_;
87            
88 44 50 33     480 if (! (blessed($params) && $params->isa('Mojo::Parameters'))) {
89 0         0 my $wrapper = Mojo::Parameters->new;
90 0         0 $wrapper->charset($charset);
91 0 0 0     0 if (blessed($params) && $params->isa('Hash::MultiValue')) {
92 0         0 $wrapper->append($params->flatten);
93             } else {
94 0         0 $wrapper->append($params);
95             }
96 0         0 $params = $wrapper;
97             }
98            
99 44         117 my $props = $schema->{$TERM_PROPERTIES};
100            
101 44 50       223 if (! $schema->{$TERM_ADD_PROPS}) {
102 44         293 for my $name (@{$params->names}) {
  44         145  
103 69 100       1535 return "Field $name is injected" if (! $props->{$name});
104             }
105             }
106            
107 42         383 for my $name (keys %$props) {
108 81         210 my @params = grep {defined $_} $params->param($name);
  81         1586  
109            
110 81 100 100     399 if (($props->{$name}->{$TERM_REQUIRED} || '') eq Mojo::JSON->true) {
111 51 100       760 return "Field $name is required" if (! scalar @params);
112             }
113            
114 78 100       447 if (my $allowed = $props->{$name}->{$TERM_OPTIONS}) {
115 42         74 for my $given (@params) {
116 55         174 return "Field $name has been tampered"
117 24 100       44 if (! grep {$_ eq $given} @$allowed);
118             }
119             }
120 71 100       231 if (exists $props->{$name}->{$TERM_MAXLENGTH}) {
121 5         9 for my $given (@params) {
122 5 100       34 return "Field $name is too long"
123             if (length($given) > $props->{$name}->{$TERM_MAXLENGTH});
124             }
125             }
126 69 100       183 if (defined $props->{$name}->{$TERM_MIN_LENGTH}) {
127 2         4 for my $given (@params) {
128 2 100       21 return "Field $name cannot be empty"
129             if (length($given) < $props->{$name}->{$TERM_MIN_LENGTH});
130             }
131             }
132 68 100       203 if (my $pattern = $props->{$name}->{$TERM_PATTERN}) {
133 5         12 for my $given (@params) {
134 5 100       66 return "Field $name not match pattern"
135             if ($given !~ /\A$pattern\Z/);
136             }
137             }
138 64 100 100     351 if (($props->{$name}->{$TERM_TYPE} || '') eq $TERM_NUMBER) {
139 6         17 for my $given (@params) {
140 6 100       68 return "Field $name not match pattern"
141             if ($given !~ /\A[\d\+\-\.]+\Z/);
142 5 50       23 if (my $min = $props->{$name}->{$TERM_MIN}) {
143 5 100       28 return "Field $name too low" if ($given < $min);
144             }
145 4 50       19 if (my $max = $props->{$name}->{$TERM_MAX}) {
146 4 100       35 return "Field $name too great" if ($given > $max);
147             }
148             }
149             }
150             }
151 21         185 return;
152             }
153              
154             1;
155              
156             __END__