1package jQuery::File::Upload;
2
3use 5.008008;
4use strict;
5#use warnings;
6
7use CGI;
8use JSON::XS;
9use JSON;
10use Net::SSH2;
11use Net::SSH2::SFTP;
12use Image::Magick;
13use Cwd 'abs_path';
14use URI;
15use Data::GUID;
16
17#use LWP::UserAgent;
18#use LWP::Protocol::https;
19
20our $VERSION = '0.30';
21
22my %errors =  (
23	'_validate_max_file_size' => 'File is too big',
24	'_validate_min_file_size' => 'File is too small',
25	'_validate_accept_file_types' => 'Filetype not allowed',
26	'_validate_reject_file_types' => 'Filetype not allowed',
27	'_validate_max_number_of_files' => 'Maximum number of files exceeded',
28	'_validate_max_width' => 'Image exceeds maximum width',
29	'_validate_min_width' => 'Image requires a minimum width',
30	'_validate_max_height' => 'Image exceeds maximum height',
31	'_validate_min_height' => 'Image requires a minimum height'
32);
33
34#GETTERS/SETTERS
35sub new {
36    my $invocant = shift;
37    my $class   = ref($invocant) || $invocant;
38    my $self = {
39		field_name => 'files[]',
40		ctx => undef,
41		cgi	=> undef,
42		thumbnail_width => 80,
43		thumbnail_height => 80,
44		thumbnail_quality => 70,
45		thumbnail_format => 'jpg',
46		thumbnail_density => undef,
47		format => 'jpg',
48		quality => 70,
49
50		process_images => 1,
51		thumbnail_filename => undef,
52		thumbnail_prefix => 'thumb_',
53		thumbnail_postfix => '',
54		filename => undef,
55		client_filename => undef,
56		show_client_filename => 1,
57		use_client_filename => undef,
58		filename_salt => '',
59		copy_file => 0,
60		script_url => undef,
61		tmp_dir => '/tmp',
62		should_delete => 1,
63
64		absolute_filename => undef,
65		absolute_thumbnail_filename => undef,
66
67		delete_params => [],
68
69		upload_dir => undef,
70		thumbnail_upload_dir => undef,
71		upload_url_base => undef,
72		thumbnail_url_base => undef,
73		relative_url_path => '/files',
74		thumbnail_relative_url_path => undef,
75		relative_to_host => undef,
76		delete_url => undef,
77
78		data => {},
79
80		#callbacks
81		post_delete => sub {},
82		post_post => sub {},
83		post_get => sub {},
84
85		#pre calls
86		pre_delete => sub {},
87		pre_post => sub {},
88		pre_get => sub {},
89
90		#scp/rcp login info
91		scp => [],
92
93		#user validation specifications
94		max_file_size => undef,
95		min_file_size => 1,
96		accept_file_types => [],
97		reject_file_types => [],
98		require_image => undef,
99		max_width => undef,
100		max_height => undef,
101		min_width => 1,
102		min_height => 1,
103		max_number_of_files => undef,
104
105		#not to be used by users
106		output => undef,
107		handle => undef,
108		tmp_filename => undef,
109		fh => undef,
110		error => undef,
111		upload => undef,
112		file_type => undef,
113		is_image => undef,
114		image_magick => undef,
115		width => undef,
116		height => undef,
117		num_files_in_dir => undef,
118		user_error => undef,
119        @_,                 # Override previous attributes
120    };
121    return bless $self, $class;
122}
123
124sub upload_dir {
125	my $self = shift;
126
127  if (@_) {
128 		$self->{upload_dir} = shift;
129  }
130
131	#set upload_dir to directory of this script if not provided
132	if(!(defined $self->{upload_dir})) {
133		$self->{upload_dir} = abs_path($0);
134		$self->{upload_dir} =~ s/(.*)\/.*/$1/;
135		$self->{upload_dir} .= '/files';
136	}
137
138	return $self->{upload_dir};
139}
140
141sub thumbnail_upload_dir {
142	my $self = shift;
143
144  if (@_) {
145	  $self->{thumbnail_upload_dir} = shift;
146  }
147
148	#set upload_dir to directory of this script if not provided
149	if(!(defined $self->{thumbnail_upload_dir})) {
150			$self->{thumbnail_upload_dir} = $self->upload_dir;
151	}
152
153	return $self->{thumbnail_upload_dir};
154}
155
156sub upload_url_base {
157	my $self = shift;
158
159  if (@_) {
160  	$self->{upload_url_base} = shift;
161  }
162
163	if(!(defined $self->{upload_url_base})) {
164		$self->{upload_url_base} = $self->_url_base . $self->relative_url_path;
165	}
166
167	return $self->{upload_url_base};
168}
169
170sub _url_base {
171	my $self = shift;
172	my $url;
173
174	if($self->relative_to_host) {
175		$url = $self->{uri}->scheme . '://' . $self->{uri}->host;
176	}
177	else {
178		$url = $self->script_url;
179		$url =~ s/(.*)\/.*/$1/;
180	}
181
182	return $url;
183}
184
185sub thumbnail_url_base {
186	my $self = shift;
187
188	if (@_) {
189 	 $self->{thumbnail_url_base} = shift;
190  }
191
192	if(!(defined $self->{thumbnail_url_base})) {
193		if(defined $self->thumbnail_relative_url_path) {
194			$self->{thumbnail_url_base} = $self->_url_base . $self->thumbnail_relative_url_path;
195		}
196		else {
197			$self->{thumbnail_url_base} = $self->upload_url_base;
198		}
199	}
200
201	return $self->{thumbnail_url_base};
202}
203
204
205sub relative_url_path {
206	my $self = shift;
207
208	if(@_) {
209		$self->{relative_url_path} = shift;
210	}
211
212	return $self->{relative_url_path};
213}
214
215sub thumbnail_relative_url_path {
216	my $self = shift;
217
218	if(@_) {
219		$self->{thumbnail_relative_url_path} = shift;
220	}
221
222	return $self->{thumbnail_relative_url_path};
223}
224
225sub relative_to_host {
226	my $self = shift;
227
228	if(@_) {
229		$self->{relative_to_host} = shift;
230	}
231
232	return $self->{relative_to_host};
233}
234
235
236
237sub field_name {
238	my $self = shift;
239
240    if (@_) {
241        $self->{field_name} = shift;
242    }
243
244	return $self->{field_name};
245}
246
247sub ctx {
248	my $self = shift;
249
250    if (@_) {
251        $self->{ctx} = shift;
252    }
253
254	return $self->{ctx};
255}
256
257sub cgi {
258	my $self = shift;
259
260    if (@_) {
261	    $self->{cgi} = shift;
262    }
263	$self->{cgi} = CGI->new unless defined $self->{cgi};
264
265	return $self->{cgi};
266}
267
268sub should_delete {
269	my $self = shift;
270
271    if (@_) {
272        $self->{should_delete} = shift;
273    }
274
275	return $self->{should_delete};
276}
277
278sub scp {
279	my $self = shift;
280
281    if (@_) {
282        $self->{scp} = shift;
283    }
284
285	return $self->{scp};
286}
287
288sub max_file_size {
289	my $self = shift;
290
291    if (@_) {
292        $self->{max_file_size} = shift;
293    }
294
295	return $self->{max_file_size};
296}
297
298sub min_file_size {
299	my $self = shift;
300
301    if (@_) {
302        $self->{min_file_size} = shift;
303    }
304
305	return $self->{min_file_size};
306}
307
308sub accept_file_types {
309	my $self = shift;
310
311  if (@_) {
312	my $a_ref = shift;
313	die "accept_file_types must be an array ref" unless UNIVERSAL::isa($a_ref,'ARRAY');
314   	$self->{accept_file_types} = $a_ref;
315  }
316
317	if(scalar(@{$self->{accept_file_types}}) == 0 and $self->require_image) {
318		$self->{accept_file_types} = ['image/jpeg','image/jpg','image/png','image/gif'];
319	}
320
321	return $self->{accept_file_types};
322}
323
324sub reject_file_types {
325	my $self = shift;
326
327	if (@_) {
328		my $a_ref = shift;
329		die "reject_file_types must be an array ref" unless UNIVERSAL::isa($a_ref,'ARRAY');
330		$self->{reject_file_types} = $a_ref;
331	}
332
333	return $self->{reject_file_types};
334}
335
336sub require_image {
337	my $self = shift;
338
339  if (@_) {
340   	$self->{require_image} = shift;
341  }
342
343	return $self->{require_image};
344}
345
346sub delete_params {
347	my $self = shift;
348
349    if (@_) {
350		my $a_ref = shift;
351		die "delete_params must be an array ref" unless UNIVERSAL::isa($a_ref,'ARRAY');
352        $self->{delete_params} = $a_ref;
353    }
354
355	return $self->{delete_params};
356}
357
358sub delete_url {
359	my $self = shift;
360
361	if(@_) {
362		$self->{delete_url} = shift;
363	}
364
365	return $self->{delete_url};
366}
367
368sub thumbnail_width {
369	my $self = shift;
370
371    if (@_) {
372        $self->{thumbnail_width} = shift;
373    }
374
375	return $self->{thumbnail_width};
376}
377
378sub thumbnail_height {
379	my $self = shift;
380
381    if (@_) {
382        $self->{thumbnail_height} = shift;
383    }
384
385	return $self->{thumbnail_height};
386}
387
388sub thumbnail_quality {
389	my $self = shift;
390
391    if (@_) {
392        $self->{thumbnail_quality} = shift;
393    }
394
395	return $self->{thumbnail_quality};
396}
397
398sub thumbnail_format {
399	my $self = shift;
400
401    if (@_) {
402        $self->{thumbnail_format} = shift;
403    }
404
405	return $self->{thumbnail_format};
406}
407
408sub thumbnail_density {
409	my $self = shift;
410
411    if (@_) {
412        $self->{thumbnail_density} = shift;
413    }
414
415	return $self->{thumbnail_density};
416}
417
418sub thumbnail_prefix {
419	my $self = shift;
420
421    if (@_) {
422        $self->{thumbnail_prefix} = shift;
423    }
424
425	return $self->{thumbnail_prefix};
426}
427
428sub thumbnail_postfix {
429	my $self = shift;
430
431    if (@_) {
432        $self->{thumbnail_postfix} = shift;
433    }
434
435	return $self->{thumbnail_postfix};
436}
437
438sub thumbnail_final_width {
439	my $self = shift;
440
441	if(@_) {
442		$self->{thumbnail_final_width} = shift;
443	}
444
445	return $self->{thumbnail_final_width};
446}
447
448sub thumbnail_final_height {
449	my $self = shift;
450
451	if(@_) {
452		$self->{thumbnail_final_height} = shift;
453	}
454
455	return $self->{thumbnail_final_height};
456}
457
458sub quality {
459	my $self = shift;
460
461    if (@_) {
462        $self->{quality} = shift;
463    }
464
465	return $self->{quality};
466}
467
468sub format {
469	my $self = shift;
470
471    if (@_) {
472        $self->{format} = shift;
473    }
474
475	return $self->{format};
476}
477
478sub final_width {
479	my $self = shift;
480
481	if(@_) {
482		$self->{final_width} = shift;
483	}
484
485	return $self->{final_width};
486}
487
488sub final_height {
489	my $self = shift;
490
491	if(@_) {
492		$self->{final_height} = shift;
493	}
494
495	return $self->{final_height};
496}
497
498sub max_width {
499	my $self = shift;
500
501    if (@_) {
502        $self->{max_width} = shift;
503    }
504
505	return $self->{max_width};
506}
507
508sub max_height {
509	my $self = shift;
510
511    if (@_) {
512        $self->{max_height} = shift;
513    }
514
515	return $self->{max_height};
516}
517
518sub min_width {
519	my $self = shift;
520
521    if (@_) {
522        $self->{min_width} = shift;
523    }
524
525	return $self->{min_width};
526}
527
528sub min_height {
529	my $self = shift;
530
531    if (@_) {
532        $self->{min_height} = shift;
533    }
534
535	return $self->{min_height};
536}
537
538sub max_number_of_files {
539	my $self = shift;
540
541    if (@_) {
542        $self->{max_number_of_files} = shift;
543    }
544
545	return $self->{max_number_of_files};
546}
547
548sub filename {
549	my $self = shift;
550
551    if (@_) {
552        $self->{filename} = shift;
553    }
554
555	return $self->{filename};
556}
557
558sub absolute_filename {
559	my $self = shift;
560
561    if (@_) {
562        $self->{absolute_filename} = shift;
563    }
564
565	return $self->{absolute_filename};
566}
567
568sub thumbnail_filename {
569	my $self = shift;
570
571    if (@_) {
572        $self->{thumbnail_filename} = shift;
573    }
574
575	return $self->{thumbnail_filename};
576}
577
578sub absolute_thumbnail_filename {
579	my $self = shift;
580
581    if (@_) {
582        $self->{absolute_thumbnail_filename} = shift;
583    }
584
585	return $self->{absolute_thumbnail_filename};
586}
587
588sub client_filename {
589	my $self = shift;
590
591    if (@_) {
592        $self->{client_filename} = shift;
593    }
594
595	return $self->{client_filename};
596}
597
598sub show_client_filename {
599	my $self = shift;
600
601    if (@_) {
602        $self->{show_client_filename} = shift;
603    }
604
605	return $self->{show_client_filename};
606}
607
608sub use_client_filename {
609	my $self = shift;
610
611    if (@_) {
612        $self->{use_client_filename} = shift;
613    }
614
615	return $self->{use_client_filename};
616}
617
618sub filename_salt {
619	my $self = shift;
620
621    if (@_) {
622        $self->{filename_salt} = shift;
623    }
624
625	return $self->{filename_salt};
626}
627
628sub tmp_dir {
629	my $self = shift;
630
631    if (@_) {
632        $self->{tmp_dir} = shift;
633    }
634
635	return $self->{tmp_dir};
636}
637
638sub script_url {
639	my $self = shift;
640
641    if (@_) {
642        $self->{script_url} = shift;
643    }
644
645	if(!(defined $self->{script_url})) {
646		if(defined $self->ctx) {
647			$self->{script_url} = $self->ctx->request->uri;
648		}
649		else {
650			$self->{script_url} = $ENV{SCRIPT_URI};
651		}
652	}
653
654	return $self->{script_url};
655}
656
657sub data {
658	my $self = shift;
659
660	if(@_) {
661		$self->{data} = shift;
662	}
663
664	return $self->{data};
665}
666
667sub process_images {
668	my $self = shift;
669
670    if (@_) {
671        $self->{process_images} = shift;
672    }
673
674	return $self->{process_images};
675}
676
677sub copy_file {
678	my $self = shift;
679
680    if (@_) {
681        $self->{copy_file} = shift;
682    }
683
684	return $self->{copy_file};
685}
686
687#GETTERS
688sub output { shift->{output} }
689sub url { shift->{url} }
690sub thumbnail_url { shift->{thumbnail_url} }
691sub is_image { shift->{is_image} }
692sub size { shift->{file_size} }
693
694#OTHER METHODS
695sub print_response {
696	my $self = shift;
697
698	my $content_type = 'text/plain';
699	if(defined $self->ctx) {
700
701		#thanks to Lukas Rampa for this suggestion
702  		if ($self->ctx->req->headers->header('Accept') =~ qr(application/json) ) {
703	  		$content_type = 'application/json';
704  		}
705
706		$self->ctx->stash->{current_view} = '';
707		$self->ctx->res->content_type("$content_type; charset=utf-8");
708		$self->ctx->res->body($self->output . ""); #concatenate "" for when there is no output
709	}
710	else {
711		print "Content-type: $content_type\n\n";
712		print $self->output;
713	}
714}
715
716sub handle_request {
717	my $self = shift;
718	my ($print) = @_;
719
720	my $method = $self->_get_request_method;
721
722	if($method eq 'GET') {
723		&{$self->pre_get}($self);
724		&{$self->post_get}($self);
725	}
726	elsif($method eq 'PATCH' or $method eq 'POST' or $method eq 'PUT') {
727		$self->{user_error} = &{$self->pre_post}($self);
728		unless($self->{user_error}) {
729			$self->_post;
730			&{$self->post_post}($self);
731		}
732		else { $self->_generate_output }
733	}
734	elsif($method eq 'DELETE') {
735		$self->{user_error} = &{$self->pre_delete}($self); #even though we may not delete, we should give user option to still run code
736		if(not $self->{user_error} and $self->should_delete) {
737			$self->_delete;
738			&{$self->post_delete}($self);
739		}
740		else { $self->_generate_output }
741	}
742	else {
743		$self->_set_status(405);
744	}
745
746	$self->print_response if $print;
747	$self->_clear;
748}
749
750sub generate_output {
751	my $self = shift;
752	my ($arr_ref) = @_;
753
754	#necessary if we are going to use _url_base via thumbnail_url_base and upload_url_base
755	$self->_set_uri;
756
757	my @arr;
758	for(@$arr_ref) {
759		my %h;
760		die "Must provide a filename in generate_output" unless exists $_->{filename};
761		die "Must provide a size in generate_output" unless exists $_->{size};
762		$self->{is_image} = $self->process_images && $_->{image} eq 'y' ? 1 : 0;
763		$h{size} = $_->{size};
764		$h{error} = $_->{error};
765
766		if(exists $_->{'name'}) {
767			$h{name} = $_->{name}
768		}
769		else {
770			$h{name} = $_->{filename};
771		}
772
773		if($_->{filename}) {
774			$self->filename($_->{filename});
775		}
776
777		if(exists $_->{thumbnail_filename}) {
778			$self->thumbnail_filename($_->{thumbnail_filename});
779		}
780		else {
781			my $no_ext = $self->_no_ext;
782			$self->thumbnail_filename($self->thumbnail_prefix . $no_ext . $self->thumbnail_postfix . '.' . $self->thumbnail_format);
783		}
784
785		$self->_set_urls;
786		$h{url} = $_->{url} eq '' ? $self->url : $_->{url};
787		$h{thumbnailUrl} = $_->{thumbnailUrl} eq '' ? $self->thumbnail_url : $_->{thumbnailUrl};
788
789		$h{deleteUrl} = $_->{'deleteUrl'} eq '' ? $self->_delete_url($_->{delete_params}) : $_->{'deleteUrl'};
790		$h{deleteType} = 'DELETE';
791		push @arr, \%h;
792
793		#reset for the next time around
794		$self->delete_url('');
795	}
796
797	#they should provide image=y or image=n if image
798	my $json = JSON::XS->new->ascii->pretty->allow_nonref;
799	$self->{output} = $json->encode({files => \@arr});
800}
801
802sub _no_ext {
803	my $self = shift;
804	$self->filename($_->{filename});
805	my ($no_ext) = $self->filename =~ qr/(.*)\.(.*)/;
806	return $no_ext;
807}
808
809#PRE/POST METHODS
810sub pre_delete {
811	my $self = shift;
812
813    if (@_) {
814        $self->{pre_delete} = shift;
815    }
816
817	return $self->{pre_delete};
818}
819
820sub post_delete {
821	my $self = shift;
822
823    if (@_) {
824        $self->{post_delete} = shift;
825    }
826
827	return $self->{post_delete};
828}
829
830sub pre_post {
831	my $self = shift;
832
833    if (@_) {
834        $self->{pre_post} = shift;
835    }
836
837	return $self->{pre_post};
838}
839
840sub post_post {
841	my $self = shift;
842
843    if (@_) {
844        $self->{post_post} = shift;
845    }
846
847	return $self->{post_post};
848}
849
850sub pre_get {
851	my $self = shift;
852
853    if (@_) {
854        $self->{pre_get} = shift;
855    }
856
857	return $self->{pre_get};
858}
859
860sub post_get {
861	my $self = shift;
862
863    if (@_) {
864        $self->{post_get} = shift;
865    }
866
867	return $self->{post_get};
868}
869
870sub _clear {
871	my $self = shift;
872
873	#clear cgi object so we get a new one for new request
874	$self->{cgi} = undef;
875	$self->{handle} = undef;
876	$self->{tmp_filename} = undef;
877	$self->{upload} = undef;
878	$self->{fh} = undef;
879	$self->{file_size} = undef;
880	$self->{error} = undef;
881	$self->{file_type} = undef;
882	$self->{is_image} = 0;
883	$self->{width} = undef;
884	$self->{height} = undef;
885	$self->{num_files_in_dir} = undef;
886	$self->{output} = undef;
887	$self->{client_filename} = undef;
888	$self->{tmp_thumb_path} = undef;
889	$self->{tmp_file_path} = undef;
890	$self->{user_error} = undef;
891}
892
893sub _post {
894	my $self = shift;
895
896	if($self->_prepare_file_attrs and $self->_validate_file) {
897		if($self->is_image) {
898			$self->_create_thumbnail;
899			$self->_create_tmp_image
900		}
901		$self->_save;
902	}
903
904	#delete temporary files
905	if($self->is_image) {
906		unlink ($self->{tmp_thumb_path}, $self->{tmp_file_path});
907	}
908
909	#generate json output
910	$self->_generate_output;
911}
912
913sub _generate_output {
914	my $self = shift;
915
916	my $method = $self->_get_request_method;
917	my $obj;
918
919	if($method eq 'POST') {
920		my %hash;
921		unless($self->{user_error}) {
922			$hash{'url'} = $self->url;
923			$hash{'thumbnailUrl'} = $self->thumbnail_url;
924			$hash{'deleteUrl'} = $self->_delete_url;
925			$hash{'deleteType'} = 'DELETE';
926			$hash{error} = $self->_generate_error;
927		}
928		else {
929			$self->_prepare_file_basics;
930			$hash{error} = $self->{user_error};
931		}
932
933		$hash{'name'} = $self->show_client_filename ? $self->client_filename . "" : $self->filename;
934		$hash{'size'} = $self->{file_size};
935		$obj->{files} = [\%hash];
936	}
937	elsif($method eq 'DELETE') {
938		unless($self->{user_error}) {
939			$obj->{$self->_get_param('filename')} = JSON::true;
940		}
941		else {
942			$obj->{error} = $self->{user_error};
943		}
944	}
945
946	my $json = JSON::XS->new->ascii->pretty->allow_nonref;
947	$self->{output} = $json->encode($obj);
948}
949
950sub _delete {
951	my $self = shift;
952
953	my $filename = $self->_get_param('filename');
954	my $thumbnail_filename = $self->_get_param('thumbnail_filename');
955	my $image_yn = $self->_get_param('image');
956
957	if(@{$self->scp}) {
958		for(@{$self->scp}) {
959
960			my $ssh2 = $self->_auth_user($_);
961			$_->{thumbnail_upload_dir} = $_->{upload_dir} if $_->{thumbnail_upload_dir} eq '';
962
963			my $sftp = $ssh2->sftp;
964			$sftp->unlink($_->{upload_dir} . '/' . $filename);
965			$sftp->unlink($_->{thumbnail_upload_dir} . '/' . $thumbnail_filename) if $image_yn eq 'y';
966		}
967	}
968	else {
969		my $no_ext = $self->_no_ext;
970		unlink $self->upload_dir . '/' . $filename;
971		unlink($self->thumbnail_upload_dir . '/' . $thumbnail_filename) if $image_yn eq 'y';
972	}
973
974	$self->_generate_output;
975}
976
977sub _get_param {
978	my $self = shift;
979	my ($param) = @_;
980
981	if(defined $self->ctx) {
982		return $self->ctx->req->params->{$param};
983	}
984	else {
985		return $self->cgi->param($param);
986	}
987}
988
989sub _delete_url {
990	my $self = shift;
991	return if $self->delete_url ne '';
992	my ($delete_params) = @_;
993
994	my $url = $self->script_url;
995	my $uri = $self->{uri}->clone;
996
997	my $image_yn = $self->is_image ? 'y' : 'n';
998
999	unless(defined $delete_params and scalar(@$delete_params)) {
1000		$delete_params = [];
1001	}
1002
1003	push @$delete_params, @{$self->delete_params} if @{$self->delete_params};
1004	push @$delete_params, ('filename',$self->filename,'image',$image_yn);
1005	push @$delete_params, ('thumbnail_filename',$self->thumbnail_filename) if $self->is_image;
1006
1007	$uri->query_form($delete_params);
1008
1009	$self->delete_url($uri->as_string);
1010
1011	return $self->delete_url;
1012}
1013
1014sub _script_url {
1015	my $self = shift;
1016
1017	if(defined $self->ctx) {
1018		return $self->ctx->request->uri;
1019	}
1020	else {
1021		return $ENV{'SCRIPT_URI'};
1022	}
1023}
1024
1025sub _prepare_file_attrs {
1026	my $self = shift;
1027
1028	#ORDER MATTERS
1029	return unless $self->_prepare_file_basics;
1030	$self->_set_tmp_filename;
1031	$self->_set_file_type;
1032	$self->_set_is_image;
1033	$self->_set_filename;
1034	$self->_set_absolute_filenames;
1035	$self->_set_image_magick;
1036	$self->_set_width;
1037	$self->_set_height;
1038	$self->_set_num_files_in_dir;
1039	$self->_set_uri;
1040	$self->_set_urls;
1041
1042	return 1;
1043}
1044
1045sub _prepare_file_basics {
1046	my ($self) = @_;
1047
1048	return undef unless $self->_set_upload_obj;
1049	$self->_set_fh;
1050	$self->_set_file_size;
1051	$self->_set_client_filename;
1052
1053	return 1;
1054}
1055
1056sub _set_urls {
1057	my $self = shift;
1058
1059	if($self->is_image) {
1060		$self->{thumbnail_url} = $self->thumbnail_url_base . '/' . $self->thumbnail_filename;
1061	}
1062	$self->{url} = $self->upload_url_base . '/' . $self->filename;
1063}
1064
1065sub _set_uri {
1066	my $self = shift;
1067	#if catalyst, use URI already made?
1068	if(defined $self->ctx) {
1069		$self->{uri} = $self->ctx->req->uri;
1070	}
1071	else {
1072		$self->{uri} = URI->new($self->script_url);
1073	}
1074}
1075
1076sub _generate_error {
1077	my $self = shift;
1078	return undef unless defined $self->{error} and @{$self->{error}};
1079
1080	my $restrictions = join ',', @{$self->{error}->[1]};
1081	return $errors{$self->{error}->[0]} . " Restriction: $restrictions Provided: " . $self->{error}->[2];
1082}
1083
1084sub _validate_file {
1085	my $self = shift;
1086	return undef unless
1087	$self->_validate_max_file_size and
1088	$self->_validate_min_file_size and
1089	$self->_validate_accept_file_types and
1090	$self->_validate_reject_file_types and
1091	$self->_validate_max_width and
1092	$self->_validate_min_width and
1093	$self->_validate_max_height and
1094	$self->_validate_min_height and
1095	$self->_validate_max_number_of_files;
1096
1097	return 1;
1098}
1099
1100sub _save {
1101	my $self = shift;
1102
1103	if(@{$self->scp}) {
1104		$self->_save_scp;
1105	}
1106	else {
1107		$self->_save_local;
1108	}
1109}
1110
1111sub _save_scp {
1112	my $self = shift;
1113
1114	for(@{$self->scp}) {
1115		die "Must provide a host to scp" if $_->{host} eq '';
1116
1117		$_->{thumbnail_upload_dir} = $_->{upload_dir} if $_->{thumbnail_upload_dir} eq '';
1118
1119		my $path = $_->{upload_dir} . '/' . $self->filename;
1120		my $thumb_path = $_->{thumbnail_upload_dir} . '/' . $self->thumbnail_filename;
1121
1122		if(($_->{user} ne '' and $_->{public_key} ne '' and $_->{private_key} ne '') or ($_->{user} ne '' and $_->{password} ne '')) {
1123			my $ssh2 = $self->_auth_user($_);
1124
1125			#if it is an image, scp both file and thumbnail
1126			if($self->is_image) {
1127				$ssh2->scp_put($self->{tmp_file_path}, $path);
1128				$ssh2->scp_put($self->{tmp_thumb_path}, $thumb_path);
1129			}
1130			else {
1131				$ssh2->scp_put($self->{tmp_filename}, $path);
1132			}
1133
1134			$ssh2->disconnect;
1135		}
1136		else {
1137			die "Must provide a user and password or user and identity file for connecting to host";
1138		}
1139
1140	}
1141}
1142
1143sub _auth_user {
1144	my $self = shift;
1145	my ($auth) = @_;
1146
1147	my $ssh2 = Net::SSH2->new;
1148
1149	$ssh2->connect($auth->{host}) or die $!;
1150
1151	#authenticate
1152	if($auth->{user} ne '' and $auth->{public_key} ne '' and $auth->{private_key} ne '') {
1153		$ssh2->auth_publickey($auth->{user},$auth->{public_key},$auth->{private_key});
1154	}
1155	else {
1156		$ssh2->auth_password($auth->{user},$auth->{password});
1157	}
1158
1159	unless($ssh2->auth_ok) {
1160		die "error authenticating with remote server";
1161	}
1162
1163	die "upload directory must be provided with scp hash" if $auth->{upload_dir} eq '';
1164
1165	return $ssh2;
1166}
1167
1168sub _save_local {
1169	my $self = shift;
1170
1171	#if image
1172	if($self->is_image) {
1173		rename $self->{tmp_file_path}, $self->absolute_filename;
1174		rename $self->{tmp_thumb_path}, $self->absolute_thumbnail_filename;
1175	}
1176	#if non-image with catalyst
1177	elsif(defined $self->ctx) {
1178		if ($self->copy_file) {
1179			$self->{upload}->copy_to($self->absolute_filename);
1180		} else {
1181			$self->{upload}->link_to($self->absolute_filename);
1182		}
1183	}
1184	#if non-image with regular CGI perl
1185	else {
1186		my $io_handle = $self->{fh}->handle;
1187
1188		my $buffer;
1189		open (OUTFILE,'>', $self->absolute_filename);
1190		while (my $bytesread = $io_handle->read($buffer,1024)) {
1191			print OUTFILE $buffer;
1192		}
1193
1194		close OUTFILE;
1195	}
1196}
1197
1198sub _validate_max_file_size {
1199	my $self = shift;
1200	return 1 unless $self->max_file_size;
1201
1202	if($self->{file_size} > $self->max_file_size) {
1203		$self->{error} = ['_validate_max_file_size',[$self->max_file_size],$self->{file_size}];
1204		return undef;
1205	}
1206	else {
1207		return 1;
1208	}
1209}
1210
1211sub _validate_min_file_size {
1212	my $self = shift;
1213	return 1 unless $self->min_file_size;
1214
1215	if($self->{file_size} < $self->min_file_size) {
1216		$self->{error} = ['_validate_min_file_size',[$self->min_file_size],$self->{file_size}];
1217		return undef;
1218	}
1219	else {
1220		return 1;
1221	}
1222}
1223
1224sub _validate_accept_file_types {
1225	my $self = shift;
1226
1227	#if accept_file_types is empty, we except all types
1228	#so return true
1229	return 1 unless @{$self->accept_file_types};
1230
1231	if(grep { $_ eq $self->{file_type} } @{$self->{accept_file_types}}) {
1232		return 1;
1233	}
1234	else {
1235		my $types = join ",", @{$self->accept_file_types};
1236		$self->{error} = ['_validate_accept_file_types',[$types],$self->{file_type}];
1237		return undef;
1238	}
1239}
1240
1241sub _validate_reject_file_types {
1242	my $self = shift;
1243
1244	#if reject_file_types is empty, we except all types
1245	#so return true
1246	return 1 unless @{$self->reject_file_types};
1247
1248	unless(grep { $_ eq $self->{file_type} } @{$self->{reject_file_types}}) {
1249		return 1;
1250	}
1251	else {
1252		my $types = join ",", @{$self->reject_file_types};
1253		$self->{error} = ['_validate_reject_file_types',[$types],$self->{file_type}];
1254		return undef;
1255	}
1256}
1257
1258sub _validate_max_width {
1259	my $self = shift;
1260	return 1 unless $self->is_image;
1261
1262	#if set to undef, there's no max_width
1263	return 1 unless $self->max_width;
1264
1265	if($self->{width} > $self->max_width) {
1266		$self->{error} = ['_validate_max_width',[$self->max_width],$self->{width}];
1267		return undef;
1268	}
1269	else {
1270		return 1;
1271	}
1272}
1273
1274sub _validate_min_width {
1275	my $self = shift;
1276	return 1 unless $self->is_image;
1277
1278	#if set to undef, there's no min_width
1279	return 1 unless $self->min_width;
1280
1281	if($self->{width} < $self->min_width) {
1282		$self->{error} = ['_validate_min_width',[$self->min_width],$self->{width}];
1283		return undef;
1284	}
1285	else {
1286		return 1;
1287	}
1288}
1289
1290sub _validate_max_height {
1291	my $self = shift;
1292	return 1 unless $self->is_image;
1293
1294	#if set to undef, there's no max_height
1295	return 1 unless $self->max_height;
1296
1297	if($self->{height} > $self->max_height) {
1298		$self->{error} = ['_validate_max_height',[$self->max_height],$self->{height}];
1299		return undef;
1300	}
1301	else {
1302		return 1;
1303	}
1304}
1305
1306sub _validate_min_height {
1307	my $self = shift;
1308	return 1 unless $self->is_image;
1309
1310	#if set to undef, there's no max_height
1311	return 1 unless $self->min_height;
1312
1313	if($self->{height} < $self->min_height) {
1314		$self->{error} = ['_validate_min_height',[$self->min_height],$self->{height}];
1315		return undef;
1316	}
1317	else {
1318		return 1;
1319	}
1320}
1321
1322sub _validate_max_number_of_files {
1323	my $self = shift;
1324	return 1 unless $self->max_number_of_files;
1325
1326	if($self->{num_files_in_dir} > $self->max_number_of_files) {
1327		$self->{error} = ['_validate_max_number_of_files',[$self->max_number_of_files],$self->{num_files_in_dir}];
1328		return undef;
1329	}
1330	else {
1331		return 1;
1332	}
1333}
1334
1335sub _set_file_size {
1336	my $self = shift;
1337
1338	if(defined $self->ctx) {
1339		$self->{file_size} = $self->{upload}->size;
1340	}
1341	else {
1342		$self->{file_size} = -s $self->{upload};
1343	}
1344
1345	return $self->{file_size};
1346}
1347
1348sub _set_client_filename {
1349	my $self = shift;
1350	return if defined $self->client_filename;
1351
1352	if(defined $self->ctx) {
1353		$self->client_filename($self->{upload}->filename);
1354	}
1355	else {
1356		$self->client_filename($self->cgi->param($self->field_name));
1357	}
1358
1359	return $self->client_filename;
1360}
1361
1362sub _set_filename {
1363	my $self = shift;
1364	return if defined $self->filename;
1365
1366	if($self->use_client_filename) {
1367		$self->filename($self->client_filename);
1368	}
1369	else {
1370		my $filename = Data::GUID->new->as_string . $self->filename_salt;
1371		$self->thumbnail_filename($self->thumbnail_prefix . $filename . $self->thumbnail_postfix . '.' . $self->thumbnail_format) unless $self->thumbnail_filename;
1372
1373		if($self->is_image) {
1374			$filename .= '.' . $self->format;
1375		}
1376		else {
1377			#add extension if present
1378			if($self->client_filename =~ qr/.*\.(.*)/) {
1379				$filename .= '.' . $1;
1380			}
1381		}
1382		$self->filename($filename) unless $self->filename;
1383	}
1384
1385	return $self->filename;
1386}
1387
1388sub _set_absolute_filenames {
1389	my $self = shift;
1390
1391	$self->absolute_filename($self->upload_dir . '/' . $self->filename) unless $self->absolute_filename;
1392	$self->absolute_thumbnail_filename($self->thumbnail_upload_dir . '/' . $self->thumbnail_filename) unless $self->absolute_thumbnail_filename;
1393}
1394
1395sub _set_file_type {
1396	my $self = shift;
1397
1398	if(defined $self->ctx) {
1399		$self->{file_type} = $self->{upload}->type;
1400	}
1401	else {
1402		$self->{file_type} = $self->cgi->uploadInfo($self->client_filename)->{'Content-Type'};
1403	}
1404
1405	return $self->{file_type};
1406}
1407
1408sub _set_is_image {
1409	my $self = shift;
1410
1411	if($self->process_images and ($self->process_images and ($self->{file_type} eq 'image/jpeg' or $self->{file_type} eq 'image/jpg' or $self->{file_type} eq 'image/png' or $self->{file_type} eq 'image/gif'))) {
1412		$self->{is_image} = 1;
1413	}
1414	else {
1415		$self->{is_image} = 0;
1416	}
1417
1418	return $self->is_image;
1419}
1420
1421sub _set_image_magick {
1422	my $self = shift;
1423	return unless $self->is_image;
1424
1425	#if used in persistent setting, don't recreate object
1426	$self->{image_magick} = Image::Magick->new unless defined $self->{image_magick};
1427
1428	$self->{image_magick}->Read(file => $self->{fh});
1429
1430	return $self->{image_magick};
1431}
1432
1433sub _set_width {
1434	my $self = shift;
1435	return unless $self->is_image;
1436
1437	$self->{width} = $self->{image_magick}->Get('width');
1438}
1439
1440sub _set_height {
1441	my $self = shift;
1442	return unless $self->is_image;
1443
1444	$self->{height} = $self->{image_magick}->Get('height');
1445}
1446
1447sub _set_tmp_filename {
1448	my $self = shift;
1449
1450	my $tmp_filename;
1451	if(defined $self->ctx) {
1452		$self->{tmp_filename} = $self->{upload}->tempname;
1453	}
1454	else {
1455		$self->{tmp_filename} = $self->cgi->tmpFileName($self->client_filename);
1456	}
1457}
1458
1459sub _set_upload_obj {
1460	my $self = shift;
1461
1462	if(defined $self->ctx) {
1463		$self->{upload} = $self->ctx->request->upload($self->field_name);
1464	}
1465	else {
1466		$self->{upload} = $self->cgi->upload($self->field_name);
1467	}
1468
1469	return defined $self->{upload};
1470}
1471
1472sub _set_fh {
1473	my $self = shift;
1474
1475	if(defined $self->ctx) {
1476		$self->{fh} = $self->{upload}->fh;
1477	}
1478	else {
1479		$self->{fh} = $self->{upload};
1480	}
1481
1482	return $self->{fh};
1483}
1484
1485sub _set_num_files_in_dir {
1486	my $self = shift;
1487	return unless $self->max_number_of_files;
1488
1489	#DO SCP VERSION
1490	if(@{$self->{scp}}) {
1491		my $max = 0;
1492		for(@{$self->{scp}}) {
1493			my $ssh2 = $self->_auth_user($_);
1494			my $chan = $ssh2->channel();
1495			$chan->exec('ls -rt ' . $_->{upload_dir} . ' | wc -l');
1496			my $buffer;
1497			$chan->read($buffer,1024);
1498			($self->{num_files_in_dir}) = $buffer =~ qr/(\d+)/;
1499			$max = $self->{num_files_in_dir} if $self->{num_files_in_dir} > $max;
1500		}
1501
1502		#set to maximum of hosts because we know if one's over that's too many
1503		$self->{num_files_in_dir} = $max;
1504	}
1505	else {
1506		my $dir = $self->upload_dir;
1507		my @files = <$dir/*>;
1508	   	$self->{num_files_in_dir} = @files;
1509	}
1510
1511	return $self->{num_files_in_dir};
1512}
1513
1514sub _get_request_method {
1515	my $self = shift;
1516
1517	my $method = '';
1518	if(defined $self->ctx) {
1519		$method = $self->ctx->req->method;
1520	}
1521	else {
1522		$method = $self->cgi->request_method;
1523	}
1524
1525	return $method;
1526}
1527
1528sub _set_status {
1529	my $self = shift;
1530	my ($response) = @_;
1531
1532	if(defined $self->ctx) {
1533		$self->ctx->response->status($response);
1534	}
1535	else {
1536		print $self->cgi->header(-status=>$response);
1537	}
1538}
1539
1540sub _set_header {
1541	my $self = shift;
1542	my ($key,$val) = @_;
1543
1544	if(defined $self->ctx) {
1545		$self->ctx->response->header($key => $val);
1546	}
1547	else {
1548		print $self->cgi->header($key,$val);
1549	}
1550}
1551
1552sub _create_thumbnail {
1553  my $self = shift;
1554
1555  my $im = $self->{image_magick}->Clone;
1556
1557	#thumb is added at beginning of tmp_thumb_path as to not clash with the original image file path
1558  my $output  = $self->{tmp_thumb_path} = $self->tmp_dir . '/thumb_' . $self->thumbnail_filename;
1559  my $width   = $self->thumbnail_width;
1560  my $height  = $self->thumbnail_height;
1561
1562  my $density = $self->thumbnail_density || $width . "x" . $height;
1563  my $quality = $self->thumbnail_quality;
1564  my $format  = $self->thumbnail_format;
1565
1566  # source image dimensions
1567  my ($o_width, $o_height) = $im->Get('width','height');
1568
1569  # calculate image dimensions required to fit onto thumbnail
1570  my ($t_width, $t_height, $ratio);
1571  # wider than tall (seems to work...) needs testing
1572  if( $o_width > $o_height ){
1573    $ratio = $o_width / $o_height;
1574    $t_width = $width;
1575    $t_height = $width / $ratio;
1576
1577    # still won't fit, find the smallest size.
1578    while($t_height > $height){
1579      $t_height -= $ratio;
1580      $t_width -= 1;
1581    }
1582  }
1583  # taller than wide
1584  elsif( $o_height > $o_width ){
1585    $ratio = $o_height / $o_width;
1586    $t_height = $height;
1587    $t_width = $height / $ratio;
1588
1589    # still won't fit, find the smallest size.
1590    while($t_width > $width){
1591      $t_width -= $ratio;
1592      $t_height -= 1;
1593    }
1594  }
1595  # square (fixed suggested by Philip Munt phil@savvyshopper.net.au)
1596  elsif( $o_width == $o_height){
1597    $ratio = 1;
1598    $t_height = $width;
1599    $t_width  = $width;
1600     while (($t_width > $width) or ($t_height > $height)){
1601       $t_width -= 1;
1602       $t_height -= 1;
1603     }
1604  }
1605
1606  # Create thumbnail
1607  if( defined $im ){
1608    $im->Resize( width => $t_width, height => $t_height );
1609    $im->Set( quality => $quality );
1610    $im->Set( density => $density );
1611
1612	$self->final_width($t_width);
1613	$self->final_height($t_height);
1614
1615	$im->Write("$format:$output");
1616  }
1617}
1618
1619sub _create_tmp_image {
1620  my $self = shift;
1621  my $im = $self->{image_magick};
1622
1623	#main_ is added as to not clash with thumbnail tmp path if thumbnail_prefix = '' and they have the same name
1624  my $output  = $self->{tmp_file_path} = $self->tmp_dir . '/main_' . $self->filename;
1625  my $quality = $self->thumbnail_quality;
1626  my $format  = $self->thumbnail_format;
1627
1628  if( defined $im ){
1629    $im->Set( quality => $quality );
1630
1631	$im->Write("$format:$output");
1632
1633	$self->final_width($im->Get('width'));
1634	$self->final_height($im->Get('height'));
1635  }
1636}
1637
1638#sub _save_cloud {
1639#	my $self = shift;
1640#	my $io_handle = $self->{fh}->handle;
1641#
1642#	#IF IS IMAGE, MUST UPLOAD BOTH IMAGES
1643#
1644#  my $s_contents;
1645#	while (my $bytesread = $io_handle->read($buffer,1024)) {
1646#		print OUTFILE $buffer;
1647##	}
1648#
1649#
1650##   while(<FILE>)
1651#    {
1652#     $s_contents .= $_;
1653##    }
1654#
1655##   ### we will call this resource whatever comes after the last /
1656#    my $s_resourceName;
1657#
1658##    if($param->{'path'} =~ /^.*\/(.*)$/)
1659#    {
1660#     $s_resourceName = $1;
1661##    }
1662#    else
1663#    {
1664#     return('fail', "could not parse path: $param->{'path'}");
1665##    }
1666#
1667#    ### should we pass these vars ... or look them up?
1668#   my $s_user = '';
1669#    my $s_key = '';
1670##    my $s_cdn_uri ='';
1671#
1672#    my $ua = LWP::UserAgent->new;
1673#   my $req = HTTP::Request->new(GET => 'https://auth.api.rackspacecloud.com/v1.0');
1674##    $req->header('X-Auth-User' => $s_user);
1675#    $req->header('X-Auth-Key' => $s_key);
1676#
1677##    my $res = $ua->request($req);
1678#
1679#   if ($res->is_success)
1680##    {
1681#      my $s_url = $res->header('X-Storage-Url') . "/container/" . $s_resourceName;
1682#
1683##      my $reqPUT = HTTP::Request->new(PUT => $s_url);
1684#      $reqPUT->header('X-Auth-Token' => $res->header('X-Auth-Token'));
1685#
1686##      $reqPUT->content( $s_contents );
1687#
1688#     my $resPUT = $ua->request($reqPUT);
1689##
1690#      if($resPUT->is_success)
1691#     {
1692##        my $s_returnURI = $s_cdn_uri . "/" . $s_resourceName;
1693#        return('pass','passed afaict', $s_returnURI);
1694#     }
1695##      else
1696#      {
1697#       my $s_temp = $resPUT->as_string;
1698#        $s_temp =~ s/'/\\'/g;
1699##        return('fail',"PUT failed with response:$s_temp")
1700#      }
1701#   }
1702##    else
1703#    {
1704#     my $s_temp = $res->as_string;
1705##      $s_temp =~ s/'/\\'/g;
1706#      return('fail',"failed with response:$s_temp")
1707#   }
1708##  }
1709#  else
1710# {
1711##    return("fail","sorry no file found at $param->{'path'}");
1712#  }
1713#}
1714#
1715##sub _delete_cloud {
1716#	my $self    = shift;
1717#  my $request = HTTP::Request->new( 'DELETE', $self->_url,
1718#Q					        [ 'X-Auth-Token' => $self->cloudfiles->token ] );
1719#	my $response = $self->cloudfiles->_request($request);
1720#  confess 'Object ' . $self->name . ' not found' if $response->code == 404;
1721#  confess 'Unknown error' if $response->code != 204;
1722#}
1723
1724# Preloaded methods go here.
1725
17261;
1727__END__
1728# Below is stub documentation for your module. You'd better edit it!
1729
1730=head1 NAME
1731
1732jQuery::File::Upload - Server-side solution for the L<jQuery File Upload|https://github.com/blueimp/jQuery-File-Upload/> plugin.
1733
1734=head1 SYNOPSIS
1735
1736  use jQuery::File::Upload;
1737
1738  #simplest implementation
1739  my $j_fu = jQuery::File::Upload->new;
1740  $j_fu->handle_request;
1741  $j_fu->print_response;
1742
1743  #alternatively you can call $j_fu->handle_request(1) and this will call print_response for you.
1744  my $j_fu = jQuery::File::Upload->new;
1745  $j_fu->handle_request(1);
1746
1747The above example is the simplest one possible, however it assumes a lot of defaults.
1748
1749=head2 Assumptions
1750
1751=over 4
1752
1753=item default upload directory
1754
1755It is assumed that your files are being uploaded to the current directory of the script that's running, plus '/files'.
1756So if your script is in /home/user/public_html, your files will be uploaded to /home/user/public_html/files.
1757
1758=item default url
1759
1760It is also assumed that the files will be hosted one directory above the running script, plus '/files'. So if the
1761script is located at the url http://www.mydomain.com/upload.cgi, then all files will be assumed to be at the url
1762http://www.mydomain.com/files/file_name.
1763
1764=item default filename
1765
1766Uploaded files are given a name at run time unless you specifically set the filename in the jQuery::File::Upload object.
1767
1768=item same server upload
1769
1770By default, jQuery::File::Upload also assumes that you're meaning to have the uploaded file uploaded to the same server
1771that the script is running on. jQuery::File::Upload also has the ability to SCP files to remote servers, but this is not the default.
1772
1773=item CGI environment
1774
1775By default, jQuery::File::Upload assumes that the script is running in a regular CGI-type environment. However, jQuery::File::Upload
1776also has the ability to work with L<Catalyst> by being passed the context object.
1777
1778=item all files
1779
1780This implementation accepts all types of files.
1781
1782=back
1783
1784=head2 A more complicated example
1785
1786  use jQuery::File::Upload;
1787
1788  my $j_fu = jQuery::File::Upload->new(
1789		scp => [{
1790			user => 'user', #remote user
1791			public_key => '/home/user/.ssh/id_rsa.pub',	#also possible to use password instead of keys
1792			private_key => '/home/user/.ssh/id_rsa',
1793			host => 'mydomain.com',
1794			upload_dir => '/var/www/html/files', #directory that files will be uploaded to
1795		}],
1796
1797		#user validation specifications
1798		max_file_size => 5242880, #max file size is 5mb
1799		min_file_size => 1,
1800		accept_file_types => ['image/jpeg','image/png','image/gif','text/html'], #uploads restricted to these filetypes
1801		max_width => 40000, #max width for images
1802		max_height => 50000, #max height for images
1803		min_width => 1,
1804		min_height => 1,
1805		max_number_of_files => 40, #maximum number of files we can have in our upload directory
1806	  );
1807
1808
1809  $j_fu->handle_request;
1810  $j_fu->print_response;
1811
1812=head2 Getting fancy
1813
1814  use jQuery::File::Upload;
1815
1816  my $j_fu = jQuery::File::Upload->new(
1817			pre_get => sub {
1818				my $j = shift; #jQuery::File::Upload object for current request
1819				#any code in here will be executed before any get requests are handled
1820				#jQuery File Upload makes Get request when the page first loads, so this
1821				#can be useful for prefilling jQuery File Upload with files if you're using
1822				#jQuery File upload with saved data to view/delete/upload more
1823
1824				#generate starting files for jQuery File Upload
1825				$j->generate_output(
1826					[
1827						{
1828							size => 500000,
1829							filename =>	'my_image.jpeg',
1830							image => 'y', #need to let jQuery::File::Upload know this is an image
1831   								      #or else thumbnails won't be deleted
1832						},
1833						{
1834							size => 500000,
1835							filename =>	'my_other_image.jpeg',
1836							image => 'y',
1837						},
1838					]
1839				);
1840
1841  				#The above makes assumptions yet again. It generates the url based on the defaults, unless
1842  				#you provide below the upload_url_base.
1843			},
1844			pre_delete => sub {
1845				my $j = shift;
1846
1847				#here you can do something with the information in the params of the delete_url
1848				#you can set your own meaningful delete_params (see below)
1849				#NOTE: delete_urls always have the 'filename' param, so that might be enough for your needs
1850				my $id = param->('id')
1851
1852				#DELETE FROM table WHERE id=$id
1853				#etc.
1854			},
1855			post_post => sub {
1856				my $j = shift;
1857				#do some stuff here after post (image is uploaded)
1858				#possibly save information about image in a database to keep track of it?
1859				#you can call any methods now to get useful info:
1860
1861				#INSERT INTO table (name,is_image,width,height) VALUES($j->filename,$j->is_image,$j->final_width,$j->final_height)
1862				#etc
1863			},
1864			delete_params => ['key1','val1','key2','val2'],
1865                                                            #this will add these key value pairs as
1866                                                            #params on the delete_url that is generated
1867                                                            #for each image. This could be useful if you
1868                                                            #kept track of these files in a database and wanted to
1869                                                            #delete them from the database when they are deleted.
1870                                                            #then delete_params could be ['id',unique_db_identifier]
1871                                                            #then you could check for the param 'id' in pre_delete
1872                                                            #and delete the file from your DB
1873  		);
1874
1875
1876  #N.B. All of the above can also be set with the getter/setter methods
1877
1878  $j_fu->handle_request(1); #when passed a one, will call print_response for you
1879
1880=head1 DESCRIPTION
1881
1882jQuery::File::Upload makes integrating server-side with the L<jQuery File Upload|https://github.com/blueimp/jQuery-File-Upload/> plugin simple.
1883It provides many features, such as:
1884
1885=over 4
1886
1887=item 1
1888
1889the ability to SCP file uploads to remote servers
1890
1891=item 2
1892
1893the ability to provide your own functions to add to how each request is handled before the request and after the request
1894
1895=item 3
1896
1897options to validate the uploaded files server-side
1898
1899=item 4
1900
1901automatically generates thumbnails if the file is an image
1902
1903=item 5
1904
1905see below for everything you can do with jQuery::File::Upload
1906
1907=back
1908
1909The location of the script should be where L<jQuery File Upload|https://github.com/blueimp/jQuery-File-Upload/> is
1910told to upload to.
1911
1912=head1 METHODS
1913
1914=head2 Getters/Setters
1915
1916=head3 new
1917
1918Any of the below getters/setters can be passed into new as options.
1919
1920  my $j_fu = jQuery::File::Upload->new(option=>val,option2=>val2,...);
1921
1922=head3 upload_dir
1923
1924  $j_fu->upload_dir('/home/user/public_html/files');
1925
1926Sets the upload directory if saving files locally. Should not end with a slash.
1927The default is the current directory of the running script with '/files' added to
1928the end:
1929
1930  /home/user/public_html/upload.cgi
1931
1932yields:
1933
1934  /home/user/public_html/files
1935
1936When using jQuery::File::Upload under normal CGI, it should have
1937no problem generating this default upload directory if that's what you want.
1938However, if you are using L<Catalyst>, depending on how you're running L<Catalyst>
1939(i.e. mod_perl, fastcgi, etc.) the generated default might be kind of strange.
1940So if you are using L<Catalyst> and you want to upload to the same server
1941that jQuery::File::Upload is running on, it's best to just manually set this.
1942Make sure that the user running your script can write to the directory you
1943specify.
1944
1945=head3 thumbnail_upload_dir
1946
1947  $j_fu->thumbnail_upload_dir('/home/user/public_html/files/thumbs');
1948
1949This can be used to set the upload directory form thumbnails. The default
1950is L<upload_dir|/"upload_dir">. If you change this, that will make thumbnails
1951have a different base url than L<upload_url_base|/"upload_url_base">. Make
1952sure to change L<thumbnail_url_base|/"thumbnail_url_base"> to match this accordingly.
1953If you would like images and thumbnails to have the same name but just be in
1954different directories, make sure you set L<thumbnail_prefix|/"thumbnail_prefix">
1955to ''. This should not end with a slash.
1956Make sure that the user running your script can write to the directory you
1957specify.
1958
1959=head3 upload_url_base
1960
1961  $j_fu->upload_url_base('http://www.mydomain.com/files');
1962
1963Sets the url base for files. Should not end with a slash.
1964The default is the current directory of the running script
1965with '/files' added to the end:
1966
1967  http://www.mydomain.com/upload.cgi
1968
1969yields:
1970
1971  http://www.mydomain.com/files
1972
1973Which means that a file url would look like this:
1974
1975  http://www.mydomain.com/files/file.txt
1976
1977=head3 thumbnail_url_base
1978
1979  $j_fu->thumbnail_url_base('http://www.mydomain.com/files/thumbs');
1980
1981Sets the url base for thumbnails. Should not end with a slash.
1982The default is L<upload_url_base|/"upload_url_base">.
1983Resulting thumbnail urls would look like:
1984
1985  http://www.mydomain.com/files/thumbs/thumb_image.jpg
1986
1987However, if L<thumbnail_relative_url_base|/"thumbnail_relative_url_base">
1988is set, the default will be the current url with the thumbnail
1989relative base at the end.
1990
1991=head3 relative_url_path
1992
1993  $j_fu->relative_url_path('/files');
1994
1995This sets the relative url path for your files relative to the directory
1996your script is currently running in. For example:
1997
1998  http://www.mydomain.com/upload.cgi
1999
2000yields:
2001
2002  http://www.mydomain.com/files
2003
2004and then all files will go after /files. The default for this is /files,
2005which is why upload_url_base has the default /files at the end. If
2006your location for the images is not relative, i.e. it is located
2007at a different domain, then just set L<upload_url_base|/"upload_url_base">
2008to get the url_base you want. There should not be
2009a slash at the end.
2010
2011=head3 thumbnail_relative_url_path
2012
2013  $j_fu->thumbnail_relative_url_path('/files/thumbs');
2014
2015This sets the thumbnail relative url path for your files relative to the directory
2016your script is currently running in. For example:
2017
2018  http://www.mydomain.com/upload.cgi
2019
2020yields:
2021
2022  http://www.mydomain.com/files/thumbs
2023
2024and then all thumbnails will go after /files/thumbs. The default for this is nothing,
2025so then the thumbnail_url will just fall back on whatever the value of
2026L<upload_url_base|/"upload_url_base"> is.
2027If your location for thumbnail images is not relative, i.e. it is located
2028at a different domain, then just set L<thumbnail_url_base|/"thumbnail_url_base">
2029to get the url_base you want. There should not be
2030a slash at the end.
2031
2032=head3 relative_to_host
2033
2034  $j_fu->relative_to_host(1);
2035
2036If set to 1, this will make L<relative_url_path|/"relative_url_path"> and
2037L<thumbnail_relative_url_path|/"thumbnail_relative_url_path"> be relative to
2038the host of the script url. For example:
2039
2040  http://www.mydomain.com/folder/upload.cgi
2041
2042With a L<relative_url_path|/"relative_url_path"> '/files' would yield:
2043
2044  http://www.mydomain.com/files
2045
2046Whereas by default L<relative_url_path|/"relative_url_path"> and
2047L<thumbnail_relative_url_path|/"thumbnail_relative_url_path"> are
2048relative to the folder the upload script is running in.
2049
2050If you use this option, make sure to set L<upload_dir|/"upload_dir">
2051(and/or L<thumbnail_upload_dir|/"thumbnail_upload_dir"> if necessary)
2052since jQuery::File::Upload can no longer do a relative path
2053for saving the file.
2054
2055Default is undef.
2056
2057=head3 field_name
2058
2059  $j_fu->field_name('files[]');
2060
2061This is the name of the jQuery File Uploader client side.
2062The default is files[], as this is the jQuery File Upload
2063plugin's default.
2064
2065=head3 ctx
2066
2067  $j_fu->ctx($c);
2068
2069This is meant to set the L<Catalyst> context object if you are using
2070this plugin with L<Catalyst>. The default is to not use this.
2071
2072=head3 cgi
2073
2074  $j_fu->cgi(CGI->new);
2075
2076This should be used mostly internally by jQuery::File::Upload
2077(assuming you haven't passed in ctx).
2078It is just the CGI object that the module uses, however if you already
2079have one you could pass it in.
2080
2081=head3 should_delete
2082
2083  $j_fu->should_delete(1)
2084
2085This is used to decide whether to actually delete the files when jQuery::File::Upload
2086receives a DELETE request. The default is to delete, however this could be useful
2087if you wanted to maybe just mark the field as deleted in your database (using L<pre_delete|/"pre_delete">)
2088and then actually physically
2089remove it with your own clean up script later. The benefit to this could be that
2090if you are SCPing the files to a remote server, perhaps issuing the remote commands
2091to delete these files is something that seems to costly to you.
2092
2093=head3 scp
2094
2095  $j_fu->scp([{
2096			host => 'media.mydomain.com',
2097			user => 'user',
2098		  	public_key => '/home/user/.ssh/id_rsa.pub',
2099		  	private_key => '/home/user/.ssh/id_rsa',
2100			password => 'pass', #if keys are present, you do not need password
2101			upload_dir => '/my/remote/dir',
2102		}]);
2103
2104This method takes in an arrayref of hashrefs, where each hashref is a remote host you would like to SCP the files to.
2105SCPing the uploaded files to remote hosts could be useful if say you hosted your images on a different server
2106than the one doing the uploading.
2107
2108=head4 SCP OPTIONS
2109
2110=over 4
2111
2112=item
2113
2114host (REQUIRED) - the remote host you want to scp the files to, i.e. 127.0.0.1 or media.mydomain.com
2115
2116=item
2117
2118user (REQUIRED) - used to identify the user to remote server
2119
2120=item
2121
2122public_key & private_key - used to make secure connection. Not needed if password is given.
2123
2124=item
2125
2126password - used along with user to authenticate with remote server. Not needed if keys are supplied.
2127
2128=item
2129
2130upload_dir (REQUIRED) - the directory you want to scp to on the remote server. Should not end with a slash
2131
2132=item
2133
2134thumbnail_upload_dir - Will default to upload_dir. You only need to provide this if your thumbnails are stored in a different directory than regular images. Should not end with a slash
2135
2136=back
2137
2138You can check L<Net::SSH2> for more information on connecting to the remote server.
2139
2140=head3 max_file_size
2141
2142  $j_fu->max_file_size(1024);
2143
2144Sets the max file size in bytes. By default there is no max file size.
2145
2146=head3 min_file_size
2147
2148  $j_fu->min_file_size(1);
2149
2150Sets the minimum file size in bytes. Default minimum is 1 byte. to disable a minimum file size, you can set this to undef or 0.
2151
2152=head3 accept_file_types
2153
2154  $j_fu->accept_file_types(['image/jpeg','image/png','image/gif','text/html']);
2155
2156Sets what file types are allowed to be uploaded. By default, all file types are allowed.
2157File types should be in the format of the Content-Type header sent on requests.
2158
2159=head3 reject_file_types
2160
2161  #None of these types are allowed.
2162  $j_fu->reject_file_types(['image/jpeg','image/png','image/gif','text/html']);
2163
2164Sets what file types are NOT allowed to be uploaded. By default, all file types are allowed.
2165File types should be in the format of the Content-Type header sent on requests.
2166
2167=head3 require_image
2168
2169  $j_fu->require_image(1);
2170
2171If set to 1, it requires that all uploads must be an image. Setting this is equivalent
2172to calling:
2173
2174  $j_fu->accept_file_types(['image/jpeg','image/jpg','image/png','image/gif']);
2175
2176Default is undef.
2177
2178=head3 delete_params
2179
2180  $j_fu->delete_params(['key1','val1','key2','val2']);
2181
2182Sets the keys and values of the params added to the delete_url.
2183This can be useful when used with L<pre_delete|/"pre_delete">,
2184because if you are keeping track of these files in a database,
2185you can add unique identifiers to the params so that in L<pre_delete|/"pre_delete">
2186you can get these unique identifiers and use them to remove or edit the file
2187in the databse. By default filename will also be a param unless you
2188set the delete_url manually.
2189
2190=head3 delete_url
2191
2192  $j_fu->delete_url('http://www.mydomain.com/upload.cgi?filename=file.jpg');
2193
2194This can be used to set the delete_url that will be requested when
2195a user deletes a file. However, it is recommended that you do not
2196set this manually and rather use L<delete_params|/"delete_params">
2197if you want to add your own params to the delete_url.
2198
2199=head3 thumbnail_width
2200
2201  $j_fu->thumbnail_width(80);
2202
2203This sets the width for the thumbnail that will be created if the
2204file is an image. Default is 80.
2205
2206=head3 thumbnail_height
2207
2208  $j_fu->thumbnail_height(80);
2209
2210This sets the height for the thumbnail that will be created if the
2211file is an image. Default is 80.
2212
2213=head3 thumbnail_quality
2214
2215  $j_fu->thumbnail_quality(70);
2216
2217This sets the quality of the thumbnail image. Default is 70 and it
2218can be on a scale of 0-100. See L<Image::Magick> for more information.
2219
2220=head3 thumbnail_format
2221
2222  $j_fu->thumbnail_format('jpg');
2223
2224Sets the format for the generated thumbnail. Can be jpg, png, or gif.
2225See L<Image::Magick> for more information. Defaults to jpg.
2226
2227=head3 thumbnail_density
2228
2229  $j_fu->thumbnail_density('80x80');
2230
2231Sets the density for the generated thumbnail. Default is width x height.
2232See L<Image::Magick> for more information.
2233
2234=head3 thumbnail_prefix
2235
2236  $j_fu->thumbnail_prefix('thumb_');
2237
2238Added before the image filename to create the thumbnail unique filename.
2239Default is 'thumb_'.
2240
2241=head3 thumbnail_postfix
2242
2243  $j_fu->thumbnail_postfix('_thumb');
2244
2245Added after the image filename to create the thumbnail unique filename.
2246Default is ''.
2247
2248=head3 thumbnail_final_width
2249
2250  my $final_width = $j_fu->thumbnail_final_width;
2251
2252Because the thumbnails are scaled proportionally, the thumbnail width
2253may not be what you orignally suggested. This gets you the final width.
2254
2255=head3 thumbnail_final_height
2256
2257  my $final_height = $j_fu->thumbnail_final_height;
2258
2259Because the thumbnails are scaled proportionally, the thumbnail height
2260may not be what you orignally suggested. This gets you the final height.
2261
2262=head3 quality
2263
2264  $j_fu->quality(70);
2265
2266This sets the quality of the uploaded image. Default is 70 and it
2267can be on a scale of 0-100. See L<Image::Magick> for more information.
2268
2269=head3 process_images
2270
2271  $j_fu->process_images(1);
2272
2273Create thumbnails for uploaded image files when set to 1. When set to undef, L<jQuery::File::Upload> will skip creating
2274thumbnails even when the uploaded file is an image. Images are simply treated like any other file. The default is 1.
2275
2276=head3 format
2277
2278  $j_fu->format('jpg');
2279
2280Sets the format for the generated thumbnail. Can be jpg,png, or gif.
2281See L<Image::Magick> for more information. Defaults to jpg.
2282
2283=head3 final_width
2284
2285  my $final_width = $j_fu->final_width;
2286
2287Returns the final width of the uploaded image.
2288
2289=head3 final_height
2290
2291  my $final_height = $j_fu->final_height;
2292
2293Returns the final height of the uploaded image.
2294
2295=head3 max_width
2296
2297  $j_fu->max_width(10000);
2298
2299Sets the maximum width of uploaded images. Will return an error to browser if not
2300valid. Default is any width.
2301
2302=head3 max_height
2303
2304  $j_fu->max_height(10000);
2305
2306Sets the maximum height of uploaded images. Will return an error to browser if not
2307valid. Default is any height.
2308
2309=head3 min_width
2310
2311  $j_fu->min_width(10000);
2312
2313Sets the minimum width of uploaded images. Will return an error to browser if not
2314valid. Default is 1.
2315
2316=head3 min_height
2317
2318  $j_fu->min_height(10000);
2319
2320Sets the minimum height of uploaded images. Will return an error to browser if not
2321valid. Default is 1.
2322
2323=head3 max_number_of_files
2324
2325  $j_fu->max_number_of_files(20);
2326
2327Sets the maximum number of files the upload directory can contain. Returns an error
2328to the browser if number is reached. Default is any number of files. If you have
2329listed multiple remote directories, the maximum file count out of all of these directories
2330is what will be used.
2331
2332=head3 filename
2333
2334  my $filename = $j_fu->filename;
2335
2336Returns the resulting filename after processing the request.
2337
2338  $j_fu->filename('my_name.txt');
2339
2340You can also set the filename to use for this request before you call
2341L<handle_request|/"handle_request">. However, unless you're sure
2342that you are going to give the file a unique name, you should
2343just let jQuery::File::Upload generate the filename. Please note
2344that if you choose your own filename, you do have to manually set
2345L<thumbnail_filename|/"thumbnail_filename">
2346
2347=head3 absolute_filename
2348
2349  my $absolute_filename = $j_fu->absolute_filename;
2350
2351Returns the absolute filename of the file on the server.
2352You can also set this manually if you would like, or jQuery::File::Upload
2353will generate it for you.
2354
2355=head3 thumbnail_filename
2356
2357  $j_fu->filename('my_name.txt');
2358
2359You can also set the thumbnail_filename to use for this request before you call
2360L<handle_request|/"handle_request">. However, unless you're sure
2361that you are going to give the file a unique name, you should
2362just let jQuery::File::Upload generate the filename.
2363
2364=head3 absolute_thumbnail_filename
2365
2366  my $absolute_filename = $j_fu->absolute_thumbnail_filename;
2367
2368Returns the absolute filename of the thumbnail image on the server.
2369You can also set this manually if you would like, or jQuery::File::Upload
2370will generate it for you.
2371
2372=head3 client_filename
2373
2374  my $client_filename = $j_fu->client_filename;
2375
2376Returns the filename of the file as it was named by the user.
2377
2378=head3 show_client_filename
2379
2380  $j_fu->show_client_filename(1);
2381
2382This can be used to set whether jQuery::File::Upload shows the user the name
2383of the file as it looked when they uploaded, or the new name of the file.
2384When set to true, the user will see the file as it was named on their computer.
2385The default is true, and this is recommended because typically the user's
2386filename will look better than the unique one that jQuery::File::Upload generates
2387for you.
2388
2389=head3 use_client_filename
2390
2391  $j_fu->use_client_filename(0);
2392
2393If this is set to true, jQuery::File::Upload will use
2394the user's name for the file when saving it. However, this
2395is not recommended because the user could have two files named
2396the same thing that could overwrite one another, and same scenario
2397between two different users. It is best to let jQuery::File::Upload
2398generate the filenames to save with because these are much more
2399likely to be unique. Another reason not to use client filenames
2400is that it is possible that they could have invalid characters in them
2401such as spaces which will prevent a url from loading.
2402
2403=head3 filename_salt
2404
2405  $j_fu->filename_salt('_i_love_the_circus');
2406
2407Anything added here will be appended to the end of the filename.
2408This is meant to be used if you want to guarantee uniqueness of image
2409names, i.e. you could use a user id at the end to greatly lessen the chance
2410of duplicate filenames. Default is nothing.
2411
2412=head3 copy_file
2413
2414  $j_fu->copy_file(undef);
2415
2416Performs a copy instead of a link from the temporary directory to the upload directory.
2417This might be useful if you are using Windows share that can't handle links.
2418The default is undef and thus L<jQuery::File::Upload> will use links.
2419
2420=head3 tmp_dir
2421
2422  $j_fu->tmp_dir('/tmp');
2423
2424The provided directory will be used to store temporary files such as images.
2425Make sure that the user the script is running under has permission to create
2426and write to files in the tmp_dir. Also, there should be no slash at the end.
2427Default is /tmp.
2428
2429=head3 script_url
2430
2431  $j_fu->script_url('http://www.mydomain.com/upload.cgi');
2432
2433This can be used to set the url of the script that jQuery::File::Upload is
2434running under. jQuery::File::Upload then uses this value to generate
2435other parts of the output. jQuery::File::Upload in most cases is able
2436to figure this out on its own, however if you are experiencing issues
2437with things such as url generation, try setting this manually.
2438
2439=head3 data
2440
2441  $j_fu->data({
2442            dbh => $dbh,
2443            my_var = $var,
2444            arr = [],
2445            self => $self, #maybe useful for Catalyst
2446        });
2447
2448This method can be populated with whatever you like. Its purpose is
2449if you need to get access to other data in one of your
2450L</PRE/POST REQUEST METHODS>. This way you
2451can access any outside data you need by calling L<data|/"data"> on
2452the jQuery::File::Upload object that you are passed. However, keep in mind
2453that if you are using L<Catalyst>, you will have access to the context
2454object via the jQuery::File::Upload object that is passed in, and this
2455would be an equally good place to store/retrieve data that you need.
2456
2457=head2 JUST GETTERS
2458
2459=head3 output
2460
2461  my $output = $j_fu->output;
2462
2463Returns the JSON output that will be printed to the browser.
2464Unless you really feel you need the JSON, it's usually just easier to
2465call L<print_response|/"print_response"> as this prints out
2466the header and the JSON for you (or alternatively call L<handle_response|/"handle_response>
2467and pass it a 1 so that it will call L<print_response|/"print_response"> for you.
2468
2469=head3 url
2470
2471  my $file_url = $j_fu->url;
2472
2473This returns the resulting url of the file.
2474
2475=head3 thumbnail_url
2476
2477  my $thumbnail_url = $j_fu->thumbnail_url;
2478
2479This returns the resulting thumbnail url of the image.
2480
2481=head3 is_image
2482
2483  my $is_image = $j_fu->is_image;
2484
2485Returns whether or not the uploaded file was an image.
2486This should be called after handle_request or in
2487L<post_post|/"post_post">.
2488
2489=head3 size
2490
2491  my $size = $j_fu->size;
2492
2493Returns the size of the uploaded file.
2494This should be called after handle_request or in
2495L<post_post|/"post_post">.
2496
2497
2498=head2 OTHER METHODS
2499
2500=head3 print_response
2501
2502  $j_fu->print_response;
2503
2504Should be called after L<handle_request|/"handle_request">.
2505Prints out header and JSON back to browser. Called for
2506convenience by L<handle_request|/"handle_request"> if
2507L<handle_request|/"handle_request"> is passed a 1.
2508
2509=head3 handle_request
2510
2511  $j_fu->handle_request;
2512
2513Called to handle one of 'GET','POST', or 'DELETE' requests.
2514If passed a 1, will also call L<print_response|/"print_response">
2515after it's finished.
2516
2517=head3 generate_output
2518
2519  $j_fu->generate_output([{
2520			image => 'y', #or 'n'
2521			filename => 'my_cool_pic.jpg',
2522			size => 1024,
2523		  }]);
2524
2525This should be used in conjuction with L<pre_get|/"pre_get">
2526to populate jQuery File Upload with files on page load. It takes in
2527an arrayref of hashrefs, where each hashref is a file. After this
2528method is called, you will need to call L<print_response|/"print_response">
2529or L<handle_request|/"handle_request"> with a 1 to print out the JSON.
2530
2531=head4 GENERATE_OUTPUT OPTIONS
2532
2533=over 4
2534
2535=item
2536
2537filename (REQUIRED) - name of the file
2538
2539=item
2540
2541size (REQUIRED) - size in bytes
2542
2543=item
2544
2545image - 'y' or 'n'. Necessary if file is image and you would like thumbnail to be deleted with file. Also, needed if you want thumbnail to be displayed by jQuery File Upload
2546
2547=item
2548
2549name - name that will be displayed to client as the filename. If not provided, defaults to filename. Can be
2550used well with client_filename to make filename's look prettier client-side.
2551
2552=item
2553
2554thumbnail_filename - filename for thumbnail. jQuery::File::Upload will generate the thumbnail_filename based
2555on the filename and other factors (such as L<upload_url_base|/"upload_url_base">) if you don't set this.
2556
2557=item
2558
2559url - url used for file. If not provided, will be generated with filename and other defaults.
2560
2561=item
2562
2563thumbnail_url - url used for thumbnail. If not provided, will be generated with other defaults.
2564
2565=item
2566
2567delete_url - url that will be called by L<jQuery File Upload|https://github.com/blueimp/jQuery-File-Upload/> to
2568delete the file. It's better to just let jQuery::File::Upload generate this and use L<delete_params|/"delete_params">
2569if you want to set your own parameters for the delete url.
2570
2571=item
2572
2573delete_params - The format of this is just like L<delete_params|/"delete_params">. It takes [key,value] pairs.
2574Any values here will be added in addition to any global L<delete_params|/"delete_params"> that you set.
2575
2576=item
2577
2578error - can be used to supply an error for a file (although I don't really know why you would use this...)
2579
2580=back
2581
2582Note that jQuery::File::Upload will generate urls and such
2583based upon things given here (like filename) and other
2584options such as L<upload_url_base|/"upload_url_base">.
2585
2586=head2 PRE/POST REQUEST METHODS
2587
2588N.B. The following functions are all passed a jQuery::File::Upload object. And they
2589can be passed into L<new|/"new"> as options.
2590
2591Also, note that since all of these user-defined methods are passed the jQuery::File::Upload object,
2592if you are using L<Catalyst> you can just call the L<ctx|/"ctx"> method to get anything
2593stored via your context object. For L<Catalyst> users, this makes this a practical (and possibly better)
2594alternative to the provided L<data|/"data"> method.
2595
2596=head3 pre_delete
2597
2598  $j_fu->pre_delete(sub { my $j_fu = shift });
2599
2600or
2601
2602  $j_fu->pre_delete(\&mysub);
2603
2604pre_delete will be called before a delete request is handled.
2605This can be useful if you want to mark a file as deleted in your
2606database. Also, you can use this along with L<delete_params|/"delete_params">
2607to set unique identifiers (such as an id for the file or the primary key) so that you can
2608find the file in your database easier to perform whatever operations
2609you want to on it. B<Note:> This will be called even if
2610L<should_delete|/"should_delete"> is set to false.
2611If your pre_delete returns a value, this will be interpreted as an error
2612message and the delete call will be terminated and will return the error.
2613For example:
2614
2615  $j_fu->pre_delete(sub {
2616    return 'You cannot delete this file.'; #file will not be deleted
2617  });
2618
2619=head3 post_delete
2620
2621  $j_fu->post_delete(sub { my $j_fu = shift });
2622
2623or
2624
2625  $j_fu->post_delete(\&mysub);
2626
2627post_delete will be called after a delete request is handled.
2628B<Note:> This will not be called if
2629L<should_delete|/"should_delete"> is set to false.
2630
2631=head3 pre_post
2632
2633  $j_fu->pre_post(sub { my $j_fu = shift });
2634
2635or
2636
2637  $j_fu->pre_post(\&mysub);
2638
2639pre_post will be called before a post request is handled.
2640POST requests are what happen when jQuery File Upload uploads your file.
2641If your pre_post returns a value, this will be interpreted as an error
2642message and the post call will be terminated and will return the error.
2643For example:
2644
2645  $j_fu->pre_post(sub {
2646    return 'You have too many files.'; #file will not be uploaded
2647  });
2648
2649=head3 post_post
2650
2651  $j_fu->post_post(sub { my $j_fu = shift });
2652
2653or
2654
2655  $j_fu->post_post(\&mysub);
2656
2657post_post will be called after a post request is handled.
2658This can be useful if you want to keep track of the file
2659that was just uploaded by recording it in a database.
2660You can use the jQuery::File::Upload object that
2661is passed in to get information about the file that you would like
2662to store in the databse. Later on you can use this stored
2663information about the files to prepopulate a jQuery File Upload
2664form with files you already have by preloading the form by using
2665L<pre_get|/"pre_get">.
2666
2667=head3 pre_get
2668
2669  $j_fu->pre_get(sub { my $j_fu = shift });
2670
2671or
2672
2673  $j_fu->pre_get(\&mysub);
2674
2675pre_get will be called before a get request is handled.
2676Get requests happen on page load to see if there are any
2677files to prepopulate the form with. This method can
2678be useful to prepopulate the jQuery File Upload form
2679by combining saved information about the files you want to
2680load and using L<generate_output|/"generate_output"> to
2681prepare the output that you would like to send to the
2682jQuery File Upload form.
2683
2684=head3 post_get
2685
2686  $j_fu->post_get(sub { my $j_fu = shift });
2687
2688or
2689
2690  $j_fu->post_get(\&mysub);
2691
2692post_get will be called after a get request is handled.
2693
2694=head2 EXPORT
2695
2696None by default.
2697
2698=head1 Catalyst Performance - Persistent jQuery::File::Upload
2699
2700A jQuery::File::Upload object shouldn't be too expensive to create, however
2701if you'd like to only create the object once you could create it as an
2702L<Moose> attribute to the class:
2703
2704  use jQuery::File::Upload;
2705  has 'j_uf' => (isa => 'jQuery::File::Upload', is => 'rw',
2706		  lazy => 0, default => sub { jQuery::File::Upload->new } );
2707
2708However, if you do this it is possible that you could run into issues
2709with values of the jQuery::File::Upload object that were not cleared
2710messing with the current request. The _clear method is called before
2711every L<handle_request|/"handle_request"> which clears the values of
2712the jQuery::File::Upload object, but it's possible I may have
2713missed something.
2714
2715=head1 SEE ALSO
2716
2717=over 4
2718
2719=item
2720
2721L<CGI>
2722
2723=item
2724
2725L<JSON::XS>
2726
2727=item
2728
2729L<Net::SSH2>
2730
2731=item
2732
2733L<Net::SSH2::SFTP>
2734
2735=item
2736
2737L<Image::Magick>
2738
2739=item
2740
2741L<Cwd>
2742
2743=item
2744
2745L<Digest::MD5>
2746
2747=item
2748
2749L<URI>
2750
2751=item
2752
2753L<jQuery File Upload|https://github.com/blueimp/jQuery-File-Upload/>
2754
2755=back
2756
2757=head1 AUTHOR
2758
2759Adam Hopkins, E<lt>srchulo@cpan.org>
2760
2761=head1 Bugs
2762
2763I haven't tested this too thoroughly beyond my needs, so it is possible
2764that I have missed something. If I have, please feel free to submit a bug
2765to the bug tracker, and you can send me an email letting me know that you
2766submitted a bug if you want me to see it sooner :)
2767
2768=head1 COPYRIGHT AND LICENSE
2769
2770Copyright (C) 2013 by Adam Hopkins
2771
2772This library is free software; you can redistribute it and/or modify
2773it under the same terms as Perl itself, either Perl version 5.8.8 or,
2774at your option, any later version of Perl 5 you may have available.
2775
2776=cut
2777