-
-
Save anonymous/4ac3f6efbfac749be151 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
diff --git a/lib/Mojolicious/Controller.pm b/lib/Mojolicious/Controller.pm | |
index 44c8bc2..b23e640 100644 | |
--- a/lib/Mojolicious/Controller.pm | |
+++ b/lib/Mojolicious/Controller.pm | |
@@ -341,6 +341,7 @@ sub validation { | |
my $header = $req->headers->header('X-CSRF-Token'); | |
my $hash = $req->params->to_hash; | |
$hash->{csrf_token} //= $header if $token && $header; | |
+ $hash->{$_} = $req->every_upload($_) for map { $_->name } @{$req->uploads}; | |
my $validation = $self->app->validator->validation->input($hash); | |
return $stash->{'mojo.validation'} = $validation->csrf_token($token); | |
} | |
@@ -916,11 +917,11 @@ to inherit query parameters from the current request. | |
my $validation = $c->validation; | |
Get L<Mojolicious::Validator::Validation> object for current request to | |
-validate C<GET> and C<POST> parameters extracted from the query string and | |
-C<application/x-www-form-urlencoded> or C<multipart/form-data> message body. | |
-Parts of the request body need to be loaded into memory to parse C<POST> | |
-parameters, so you have to make sure it is not excessively large, there's a | |
-16MB limit by default. | |
+validate file uploads as well as C<GET> and C<POST> parameters extracted from | |
+the query string and C<application/x-www-form-urlencoded> or | |
+C<multipart/form-data> message body. Parts of the request body need to be loaded | |
+into memory to parse C<POST> parameters, so you have to make sure it is not | |
+excessively large, there's a 16MB limit by default. | |
my $validation = $c->validation; | |
$validation->required('title')->size(3, 50); | |
diff --git a/lib/Mojolicious/Plugin/TagHelpers.pm b/lib/Mojolicious/Plugin/TagHelpers.pm | |
index 0e4b435..c2f449d 100644 | |
--- a/lib/Mojolicious/Plugin/TagHelpers.pm | |
+++ b/lib/Mojolicious/Plugin/TagHelpers.pm | |
@@ -16,17 +16,16 @@ sub register { | |
my @helpers = ( | |
qw(csrf_field form_for hidden_field javascript label_for link_to), | |
- qw(password_field select_field stylesheet submit_button tag_with_error), | |
- qw(text_area) | |
+ qw(select_field stylesheet submit_button tag_with_error text_area) | |
); | |
$app->helper($_ => __PACKAGE__->can("_$_")) for @helpers; | |
$app->helper(check_box => | |
sub { _input(shift, shift, value => shift, @_, type => 'checkbox') }); | |
- $app->helper(file_field => | |
- sub { shift; _tag('input', name => shift, @_, type => 'file') }); | |
+ $app->helper(file_field => sub { _empty_field('file', @_) }); | |
$app->helper(image => sub { _tag('img', src => shift->url_for(shift), @_) }); | |
$app->helper(input_tag => sub { _input(@_) }); | |
+ $app->helper(password_field => sub { _empty_field('password', @_) }); | |
$app->helper(radio_button => | |
sub { _input(shift, shift, value => shift, @_, type => 'radio') }); | |
@@ -39,6 +38,11 @@ sub _csrf_field { | |
return _hidden_field($c, csrf_token => $c->helpers->csrf_token, @_); | |
} | |
+sub _empty_field { | |
+ my ($type, $c, $name) = (shift, shift, shift); | |
+ return _validation($c, $name, 'input', name => $name, @_, type => $type); | |
+} | |
+ | |
sub _form_for { | |
my ($c, @url) = (shift, shift); | |
push @url, shift if ref $_[0] eq 'HASH'; | |
@@ -119,12 +123,6 @@ sub _option { | |
return _tag('option', %attrs, @$pair[2 .. $#$pair], $pair->[0]); | |
} | |
-sub _password_field { | |
- my ($c, $name) = (shift, shift); | |
- return _validation($c, $name, 'input', name => $name, @_, | |
- type => 'password'); | |
-} | |
- | |
sub _select_field { | |
my ($c, $name, $options, %attrs) = (shift, shift, shift, @_); | |
diff --git a/lib/Mojolicious/Validator.pm b/lib/Mojolicious/Validator.pm | |
index 124eae7..9fd84d5 100644 | |
--- a/lib/Mojolicious/Validator.pm | |
+++ b/lib/Mojolicious/Validator.pm | |
@@ -4,7 +4,13 @@ use Mojo::Base -base; | |
use Mojolicious::Validator::Validation; | |
has checks => sub { | |
- {equal_to => \&_equal_to, in => \&_in, like => \&_like, size => \&_size}; | |
+ { | |
+ equal_to => \&_equal_to, | |
+ file => sub { !ref $_[2] || !$_[2]->isa('Mojo::Upload') }, | |
+ in => \&_in, | |
+ like => sub { $_[2] !~ $_[3] }, | |
+ size => \&_size | |
+ }; | |
}; | |
sub add_check { $_[0]->checks->{$_[1]} = $_[2] and return $_[0] } | |
@@ -25,8 +31,6 @@ sub _in { | |
return 1; | |
} | |
-sub _like { $_[2] !~ $_[3] } | |
- | |
sub _size { | |
my ($validation, $name, $value, $min, $max) = @_; | |
my $len = length $value; | |
@@ -39,7 +43,7 @@ sub _size { | |
=head1 NAME | |
-Mojolicious::Validator - Validate parameter | |
+Mojolicious::Validator - Validate values | |
=head1 SYNOPSIS | |
@@ -53,7 +57,7 @@ Mojolicious::Validator - Validate parameter | |
=head1 DESCRIPTION | |
-L<Mojolicious::Validator> validates parameters for L<Mojolicious>. | |
+L<Mojolicious::Validator> validates values for L<Mojolicious>. | |
=head1 CHECKS | |
@@ -65,6 +69,12 @@ These validation checks are available by default. | |
Value needs to be equal to the value of another field. | |
+=head2 file | |
+ | |
+ $validation->file; | |
+ | |
+Value needs to be a L<Mojo::Upload> object, representing a file upload. | |
+ | |
=head2 in | |
$validation->in(qw(foo bar baz)); | |
diff --git a/lib/Mojolicious/Validator/Validation.pm b/lib/Mojolicious/Validator/Validation.pm | |
index 0ce067b..ac1c410 100644 | |
--- a/lib/Mojolicious/Validator/Validation.pm | |
+++ b/lib/Mojolicious/Validator/Validation.pm | |
@@ -191,9 +191,9 @@ array reference. | |
my $names = $validation->failed; | |
-Return a list of all names for parameters that failed validation. | |
+Return a list of all names for values that failed validation. | |
- # Names of all parameters that failed | |
+ # Names of all values that failed | |
say for @{$validation->failed}; | |
=head2 has_data | |
@@ -227,17 +227,16 @@ Change validation L</"topic">. | |
my $value = $validation->param('foo'); | |
-Access validated parameters. If there are multiple values sharing the same | |
-name, and you want to access more than just the last one, you can use | |
-L</"every_param">. | |
+Access validated values. If there are multiple values sharing the same name, and | |
+you want to access more than just the last one, you can use L</"every_param">. | |
=head2 passed | |
my $names = $validation->passed; | |
-Return a list of all names for parameters that passed validation. | |
+Return a list of all names for values that passed validation. | |
- # Names of all parameters that passed | |
+ # Names of all values that passed | |
say for @{$validation->passed}; | |
=head2 required | |
diff --git a/t/mojolicious/validation_lite_app.t b/t/mojolicious/validation_lite_app.t | |
index aa3ddd2..7267e73 100644 | |
--- a/t/mojolicious/validation_lite_app.t | |
+++ b/t/mojolicious/validation_lite_app.t | |
@@ -3,6 +3,7 @@ use Mojo::Base -strict; | |
BEGIN { $ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll' } | |
use Test::More; | |
+use Mojo::Upload; | |
use Mojolicious::Lite; | |
use Test::Mojo; | |
@@ -21,6 +22,13 @@ any '/' => sub { | |
$validation->optional('yada')->two; | |
} => 'index'; | |
+any '/upload' => sub { | |
+ my $c = shift; | |
+ my $validation = $c->validation; | |
+ return $c->render unless $validation->has_data; | |
+ $validation->required('foo')->file; | |
+}; | |
+ | |
any '/forgery' => sub { | |
my $c = shift; | |
my $validation = $c->validation; | |
@@ -73,6 +81,23 @@ ok $validation->has_error, 'has error'; | |
is_deeply $validation->error('yada'), [qw(equal_to 1 foo)], 'right error'; | |
is_deeply $validation->failed, [qw(baz yada)], 'right names'; | |
+# File | |
+$validation = $t->app->validation->input( | |
+ { | |
+ foo => Mojo::Upload->new, | |
+ bar => [Mojo::Upload->new, Mojo::Upload->new], | |
+ baz => [Mojo::Upload->new, 'test'] | |
+ } | |
+); | |
+ok $validation->required('foo')->file->is_valid, 'valid'; | |
+ok $validation->required('bar')->file->is_valid, 'valid'; | |
+ok $validation->required('baz')->is_valid, 'valid'; | |
+ok !$validation->has_error, 'no error'; | |
+ok !$validation->file->is_valid, 'not valid'; | |
+ok $validation->has_error, 'has error'; | |
+is_deeply $validation->error('baz'), [qw(file 1)], 'right error'; | |
+is_deeply $validation->failed, ['baz'], 'right names'; | |
+ | |
# In | |
$validation = $t->app->validation->input( | |
{foo => [qw(bar whatever)], baz => [qw(yada ohoh)]}); | |
@@ -195,6 +220,29 @@ $t->post_ok('/' => form => {foo => 'no'})->status_is(200) | |
->element_count_is('.field-with-error', 2) | |
->element_count_is('.field-with-error', 2, 'with description'); | |
+# Successful file upload | |
+$t->post_ok( | |
+ '/upload' => form => {foo => {content => 'bar', filename => 'test.txt'}}) | |
+ ->element_exists_not('.field-with-error'); | |
+ | |
+# Successful file upload (multiple files) | |
+$t->post_ok( | |
+ '/upload' => form => { | |
+ foo => [ | |
+ {content => 'One', filename => 'one.txt'}, | |
+ {content => 'Two', filename => 'two.txt'} | |
+ ] | |
+ } | |
+)->element_exists_not('.field-with-error'); | |
+ | |
+# Failed file upload | |
+$t->post_ok('/upload' => form => {foo => 'bar'}) | |
+ ->element_exists('.field-with-error'); | |
+ | |
+# Failed file upload (multiple files) | |
+$t->post_ok('/upload' => form => {foo => ['one', 'two']}) | |
+ ->element_exists('.field-with-error'); | |
+ | |
# Missing CSRF token | |
$t->get_ok('/forgery' => form => {foo => 'bar'})->status_is(200) | |
->content_like(qr/Wrong or missing CSRF token!/) | |
@@ -268,6 +316,12 @@ __DATA__ | |
%= password_field 'yada' | |
% end | |
+@@ upload.html.ep | |
+%= form_for upload => begin | |
+ %= file_field 'foo' | |
+ %= submit_button | |
+% end | |
+ | |
@@ forgery.html.ep | |
%= form_for forgery => begin | |
%= 'Wrong or missing CSRF token!' if validation->has_error('csrf_token') |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment