Skip to content

Instantly share code, notes, and snippets.

/upload.diff Secret

Created June 25, 2015 16:58
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save anonymous/4ac3f6efbfac749be151 to your computer and use it in GitHub Desktop.
Save anonymous/4ac3f6efbfac749be151 to your computer and use it in GitHub Desktop.
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