The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
Changes 036
META.json 34
META.yml 1415
Makefile.PL 12
lib/jQuery/File/Upload.pm 31136
5 files changed (This is a version diff) 49193
@@ -78,3 +78,39 @@ Revision history for jQuery-File-Upload
 0.19
 
 	removed duplicate entry for thumbnail_prex in docs
+
+0.20
+
+	Fixed docs for <gt> by author name
+
+0.21
+
+	Fixed docs for <gt> by author name
+
+
+0.22
+
+	Updated deleteType, deleteUrl, and thumbnailUrl for new jQuery File Upload
+
+
+0.23
+
+	Updated delete response for new expected JSON
+
+0.24
+
+
+0.25
+	added size function for file size
+
+0.26
+	fixed issue in generate_output where url was not set
+
+0.27
+	filenames now generated using Data::GUID. pre_post and pre_delete can now return errors.
+
+0.28
+	Fixed "user_eror" typo for delete in handle_request
+
+0.29
+	Added reject_file_types
@@ -4,7 +4,7 @@
       "Adam Hopkins <srchulo@cpan.org>"
    ],
    "dynamic_config" : 1,
-   "generated_by" : "ExtUtils::MakeMaker version 6.6302, CPAN::Meta::Converter version 2.120921",
+   "generated_by" : "ExtUtils::MakeMaker version 6.98, CPAN::Meta::Converter version 2.141170",
    "license" : [
       "unknown"
    ],
@@ -34,8 +34,9 @@
          "requires" : {
             "CGI" : "0",
             "Cwd" : "0",
-            "Digest::MD5" : "0",
+            "Data::GUID" : "0",
             "Image::Magick" : "0",
+            "JSON" : "0",
             "JSON::XS" : "0",
             "Net::SSH2" : "0",
             "Net::SSH2::SFTP" : "0",
@@ -45,5 +46,5 @@
       }
    },
    "release_status" : "stable",
-   "version" : "0.20"
+   "version" : "0.29"
 }
@@ -3,28 +3,29 @@ abstract: 'Server-side solution for the L<jQuery File Upload|https://github.com/
 author:
   - 'Adam Hopkins <srchulo@cpan.org>'
 build_requires:
-  Test::More: 0
+  Test::More: '0'
 configure_requires:
-  ExtUtils::MakeMaker: 0
+  ExtUtils::MakeMaker: '0'
 dynamic_config: 1
-generated_by: 'ExtUtils::MakeMaker version 6.6302, CPAN::Meta::Converter version 2.120921'
+generated_by: 'ExtUtils::MakeMaker version 6.98, CPAN::Meta::Converter version 2.141170'
 license: unknown
 meta-spec:
   url: http://module-build.sourceforge.net/META-spec-v1.4.html
-  version: 1.4
+  version: '1.4'
 name: jQuery-File-Upload
 no_index:
   directory:
     - t
     - inc
 requires:
-  CGI: 0
-  Cwd: 0
-  Digest::MD5: 0
-  Image::Magick: 0
-  JSON::XS: 0
-  Net::SSH2: 0
-  Net::SSH2::SFTP: 0
-  URI: 0
-  perl: 5.006
-version: 0.20
+  CGI: '0'
+  Cwd: '0'
+  Data::GUID: '0'
+  Image::Magick: '0'
+  JSON: '0'
+  JSON::XS: '0'
+  Net::SSH2: '0'
+  Net::SSH2::SFTP: '0'
+  URI: '0'
+  perl: '5.006'
+version: '0.29'
@@ -20,12 +20,13 @@ WriteMakefile(
     PREREQ_PM => {
         'CGI' => 0,
 		'JSON::XS' => 0,
+		'JSON' => 0,
 		'Net::SSH2' => 0,
 		'Net::SSH2::SFTP' => 0,
 		'Image::Magick' => 0,
 		'Cwd' => 0,
-		'Digest::MD5' => 0,
 		'URI' => 0,
+		'Data::GUID' => 0,
     },
     dist  => { COMPRESS => 'gzip -9f', SUFFIX => 'gz', },
     clean => { FILES => 'jQuery-File-Upload-*' },
@@ -2,26 +2,28 @@ package jQuery::File::Upload;
 
 use 5.008008;
 use strict;
-use warnings;
+#use warnings;
 
 use CGI;
 use JSON::XS;
+use JSON;
 use Net::SSH2;
 use Net::SSH2::SFTP;
 use Image::Magick;
 use Cwd 'abs_path';
-use Digest::MD5 qw(md5_hex);
 use URI;
+use Data::GUID;
 
 #use LWP::UserAgent;
 #use LWP::Protocol::https;
 
-our $VERSION = '0.20';
+our $VERSION = '0.29';
 
 my %errors =  (
 	'_validate_max_file_size' => 'File is too big',
 	'_validate_min_file_size' => 'File is too small',
 	'_validate_accept_file_types' => 'Filetype not allowed',
+	'_validate_reject_file_types' => 'Filetype not allowed',
 	'_validate_max_number_of_files' => 'Maximum number of files exceeded',
 	'_validate_max_width' => 'Image exceeds maximum width',
 	'_validate_min_width' => 'Image requires a minimum width',
@@ -90,6 +92,7 @@ sub new {
 		max_file_size => undef,
 		min_file_size => 1,
 		accept_file_types => [],
+		reject_file_types => [],
 		require_image => undef,
 		max_width => undef,
 		max_height => undef,
@@ -110,6 +113,7 @@ sub new {
 		width => undef,
 		height => undef,
 		num_files_in_dir => undef,
+		user_error => undef,
         @_,                 # Override previous attributes
     };
     return bless $self, $class;
@@ -303,8 +307,8 @@ sub accept_file_types {
 	my $self = shift;
 	     
   if (@_) {
-		my $a_ref = shift;
-		die "accept_file_types must be an array ref" unless UNIVERSAL::isa($a_ref,'ARRAY');
+	my $a_ref = shift;
+	die "accept_file_types must be an array ref" unless UNIVERSAL::isa($a_ref,'ARRAY');
    	$self->{accept_file_types} = $a_ref;
   }
 
@@ -315,6 +319,18 @@ sub accept_file_types {
 	return $self->{accept_file_types};
 }
 
+sub reject_file_types { 
+	my $self = shift;
+	     
+	if (@_) {
+		my $a_ref = shift;
+		die "reject_file_types must be an array ref" unless UNIVERSAL::isa($a_ref,'ARRAY');
+		$self->{reject_file_types} = $a_ref;
+	}
+
+	return $self->{reject_file_types};
+}
+
 sub require_image { 
 	my $self = shift;
 	     
@@ -651,6 +667,7 @@ sub output { shift->{output} }
 sub url { shift->{url} }
 sub thumbnail_url { shift->{thumbnail_url} }
 sub is_image { shift->{is_image} }
+sub size { shift->{file_size} }
 
 #OTHER METHODS
 sub print_response { 
@@ -685,16 +702,20 @@ sub handle_request {
 		&{$self->post_get}($self);
 	}
 	elsif($method eq 'PATCH' or $method eq 'POST' or $method eq 'PUT') { 
-		&{$self->pre_post}($self);
-		$self->_post;
-		&{$self->post_post}($self);
+		$self->{user_error} = &{$self->pre_post}($self);
+		unless($self->{user_error}) {
+			$self->_post;
+			&{$self->post_post}($self);
+		}
+		else { $self->_generate_output }
 	}
 	elsif($method eq 'DELETE') { 
-		&{$self->pre_delete}($self); #even though we may not delete, we should give user option to still run code
-		if($self->should_delete) {
+		$self->{user_error} = &{$self->pre_delete}($self); #even though we may not delete, we should give user option to still run code
+		if(not $self->{user_error} and $self->should_delete) {
 			$self->_delete;
 			&{$self->post_delete}($self);
 		}
+		else { $self->_generate_output }
 	}
 	else { 
 		$self->_set_status(405);
@@ -727,6 +748,10 @@ sub generate_output {
 			$h{name} = $_->{filename};
 		}
 
+		if($_->{filename}) { 
+			$self->filename($_->{filename});
+		}
+
 		if(exists $_->{thumbnail_filename}) {
 			$self->thumbnail_filename($_->{thumbnail_filename});
 		}
@@ -737,10 +762,10 @@ sub generate_output {
 
 		$self->_set_urls;
 		$h{url} = $_->{url} eq '' ? $self->url : $_->{url};
-		$h{thumbnail_url} = $_->{thumbnail_url} eq '' ? $self->thumbnail_url : $_->{thumbnail_url};
+		$h{thumbnailUrl} = $_->{thumbnailUrl} eq '' ? $self->thumbnail_url : $_->{thumbnailUrl};
 
-		$h{delete_url} = $_->{'delete_url'} eq '' ? $self->_delete_url($_->{delete_params}) : $_->{'delete_url'};
-		$h{delete_type} = 'DELETE';
+		$h{deleteUrl} = $_->{'deleteUrl'} eq '' ? $self->_delete_url($_->{delete_params}) : $_->{'deleteUrl'};
+		$h{deleteType} = 'DELETE';
 		push @arr, \%h;
 
 		#reset for the next time around
@@ -755,7 +780,7 @@ sub generate_output {
 sub _no_ext { 
 	my $self = shift;
 	$self->filename($_->{filename});
-	my ($no_ext) = $self->filename =~ /(.*)\.(.*)/;
+	my ($no_ext) = $self->filename =~ qr/(.*)\.(.*)/;
 	return $no_ext;
 }
 
@@ -840,6 +865,7 @@ sub _clear {
 	$self->{client_filename} = undef;
 	$self->{tmp_thumb_path} = undef;
 	$self->{tmp_file_path} = undef;
+	$self->{user_error} = undef;
 }
 
 sub _post { 
@@ -865,18 +891,38 @@ sub _post {
 sub _generate_output { 
 	my $self = shift;
   	
-  my %hash;
-	$hash{'name'} = $self->show_client_filename ? $self->client_filename . "" : $self->filename;
-  $hash{'size'} = $self->{file_size};
-  $hash{'url'} = $self->url;
-  $hash{'thumbnail_url'} = $self->thumbnail_url;
-  $hash{'delete_url'} = $self->_delete_url;
-  $hash{'delete_type'} = 'DELETE';
+	my $method = $self->_get_request_method;
+	my $obj;
+
+	if($method eq 'POST') {
+		my %hash;
+		unless($self->{user_error}) {
+			$hash{'url'} = $self->url;
+			$hash{'thumbnailUrl'} = $self->thumbnail_url;
+			$hash{'deleteUrl'} = $self->_delete_url;
+			$hash{'deleteType'} = 'DELETE';
+			$hash{error} = $self->_generate_error;
+		}
+		else { 
+			$self->_prepare_file_basics;
+			$hash{error} = $self->{user_error};
+		}
 
-	$hash{'error'} = $self->_generate_error;
+		$hash{'name'} = $self->show_client_filename ? $self->client_filename . "" : $self->filename;
+		$hash{'size'} = $self->{file_size};
+		$obj->{files} = [\%hash];
+	}
+	elsif($method eq 'DELETE') { 
+		unless($self->{user_error}) {
+			$obj->{$self->_get_param('filename')} = JSON::true;
+		}
+		else { 
+			$obj->{error} = $self->{user_error};
+		}
+	}
 
 	my $json = JSON::XS->new->ascii->pretty->allow_nonref;
-	$self->{output} = $json->encode({files => [\%hash]});
+	$self->{output} = $json->encode($obj);
 }
 
 sub _delete { 
@@ -902,6 +948,8 @@ sub _delete {
 		unlink $self->upload_dir . '/' . $filename;
 		unlink($self->thumbnail_upload_dir . '/' . $thumbnail_filename) if $image_yn eq 'y';
 	}
+
+	$self->_generate_output;
 }
 
 sub _get_param { 
@@ -956,10 +1004,7 @@ sub _prepare_file_attrs {
 	my $self = shift;
 
 	#ORDER MATTERS
-	return undef unless $self->_set_upload_obj;
-	$self->_set_fh;
-	$self->_set_file_size;
-	$self->_set_client_filename;
+	return unless $self->_prepare_file_basics;
 	$self->_set_tmp_filename;
 	$self->_set_file_type;
 	$self->_set_is_image;
@@ -975,6 +1020,17 @@ sub _prepare_file_attrs {
 	return 1;
 }
 
+sub _prepare_file_basics { 
+	my ($self) = @_;
+
+	return undef unless $self->_set_upload_obj;
+	$self->_set_fh;
+	$self->_set_file_size;
+	$self->_set_client_filename;
+
+	return 1;
+}
+
 sub _set_urls { 
 	my $self = shift;
 
@@ -1009,6 +1065,7 @@ sub _validate_file {
 	$self->_validate_max_file_size and
 	$self->_validate_min_file_size and
 	$self->_validate_accept_file_types and
+	$self->_validate_reject_file_types and
 	$self->_validate_max_width and
 	$self->_validate_min_width and
 	$self->_validate_max_height and
@@ -1155,6 +1212,23 @@ sub _validate_accept_file_types {
 	}
 }
 
+sub _validate_reject_file_types { 
+	my $self = shift;
+
+	#if reject_file_types is empty, we except all types
+	#so return true
+	return 1 unless @{$self->reject_file_types};
+
+	unless(grep { $_ eq $self->{file_type} } @{$self->{reject_file_types}}) { 
+		return 1;
+	}
+	else { 
+		my $types = join ",", @{$self->reject_file_types};
+		$self->{error} = ['_validate_reject_file_types',[$types],$self->{file_type}];
+		return undef;	
+	}
+}
+
 sub _validate_max_width { 
 	my $self = shift;
 	return 1 unless $self->is_image;
@@ -1267,7 +1341,7 @@ sub _set_filename {
 		$self->filename($self->client_filename);
 	}
 	else { 
-		my $filename = md5_hex($self->client_filename . time() . int(rand(1000))) . time() . $self->filename_salt;
+		my $filename = Data::GUID->new->as_string . $self->filename_salt;
 		$self->thumbnail_filename($self->thumbnail_prefix . $filename . $self->thumbnail_postfix . '.' . $self->thumbnail_format) unless $self->thumbnail_filename;
 
 		if($self->is_image) { 
@@ -1275,7 +1349,7 @@ sub _set_filename {
 		}
 		else { 
 			#add extension if present
-			if($self->client_filename =~ /.*\.(.*)/) {
+			if($self->client_filename =~ qr/.*\.(.*)/) {
 				$filename .= '.' . $1;
 			}
 		}
@@ -1395,7 +1469,7 @@ sub _set_num_files_in_dir {
 			$chan->exec('ls -rt ' . $_->{upload_dir} . ' | wc -l');
 			my $buffer;
 			$chan->read($buffer,1024);
-			($self->{num_files_in_dir}) = $buffer =~ /(\d+)/;
+			($self->{num_files_in_dir}) = $buffer =~ qr/(\d+)/;
 			$max = $self->{num_files_in_dir} if $self->{num_files_in_dir} > $max;
 		}
 		
@@ -2056,6 +2130,14 @@ Sets the minimum file size in bytes. Default minimum is 1 byte. to disable a min
 Sets what file types are allowed to be uploaded. By default, all file types are allowed. 
 File types should be in the format of the Content-Type header sent on requests.
 
+=head3 reject_file_types
+
+  #None of these types are allowed.
+  $j_fu->reject_file_types(['image/jpeg','image/png','image/gif','text/html']);
+
+Sets what file types are NOT allowed to be uploaded. By default, all file types are allowed. 
+File types should be in the format of the Content-Type header sent on requests.
+
 =head3 require_image
 
   $j_fu->require_image(1);
@@ -2324,7 +2406,7 @@ with things such as url generation, try setting this manually.
 
 This method can be populated with whatever you like. Its purpose is
 if you need to get access to other data in one of your
-L<pre/post request|/"PRE/POST REQUEST METHODS">. This way you
+L</PRE/POST REQUEST METHODS>. This way you
 can access any outside data you need by calling L<data|/"data"> on
 the jQuery::File::Upload object that you are passed. However, keep in mind
 that if you are using L<Catalyst>, you will have access to the context
@@ -2363,6 +2445,15 @@ Returns whether or not the uploaded file was an image.
 This should be called after handle_request or in
 L<post_post|/"post_post">.
 
+=head3 size
+
+  my $size = $j_fu->size;
+
+Returns the size of the uploaded file.
+This should be called after handle_request or in
+L<post_post|/"post_post">.
+
+
 =head2 OTHER METHODS
 
 =head3 print_response
@@ -2476,6 +2567,13 @@ to set unique identifiers (such as an id for the file or the primary key) so tha
 find the file in your database easier to perform whatever operations
 you want to on it. B<Note:> This will be called even if
 L<should_delete|/"should_delete"> is set to false.
+If your pre_delete returns a value, this will be interpreted as an error
+message and the delete call will be terminated and will return the error.
+For example:
+
+  $j_fu->pre_delete(sub { 
+    return 'You cannot delete this file.'; #file will not be deleted
+  });
 
 =head3 post_delete
 
@@ -2499,6 +2597,13 @@ or
 
 pre_post will be called before a post request is handled.
 POST requests are what happen when jQuery File Upload uploads your file.
+If your pre_post returns a value, this will be interpreted as an error
+message and the post call will be terminated and will return the error.
+For example:
+
+  $j_fu->pre_post(sub { 
+    return 'You have too many files.'; #file will not be uploaded
+  });
 
 =head3 post_post