File Coverage

blib/lib/CGI/Application/Plugin/AJAXUpload.pm
Criterion Covered Total %
statement 45 98 45.9
branch 2 28 7.1
condition 2 7 28.5
subroutine 13 17 76.4
pod 3 3 100.0
total 65 153 42.4


line stmt bran cond sub pod time code
1             package CGI::Application::Plugin::AJAXUpload;
2              
3 2     2   131544 use warnings;
  2         5  
  2         64  
4 2     2   9 use strict;
  2         3  
  2         54  
5 2     2   8 use Carp;
  2         7  
  2         138  
6 2     2   8 use base qw(Exporter);
  2         3  
  2         159  
7 2     2   10 use vars qw(@EXPORT);
  2         4  
  2         101  
8 2     2   5460 use Perl6::Slurp;
  2         4067  
  2         12  
9 2     2   2039 use Readonly;
  2         7217  
  2         1311  
10 2     2   2384 use Data::FormValidator;
  2         68219  
  2         159  
11              
12             @EXPORT = qw(
13             ajax_upload_httpdocs
14             ajax_upload_setup
15             ajax_upload_default_profile
16             _ajax_upload_rm
17             _ajax_upload_compile_messages
18             );
19              
20 2     2   2033 use version; our $VERSION = qv('0.0.3');
  2         4147  
  2         11  
21              
22             # Module implementation here
23              
24             Readonly my $FIELD_NAME => 'file';
25             Readonly my $MAX_UPLOAD => 512*1024;
26              
27             sub ajax_upload_httpdocs {
28 1     1 1 2407 my $self = shift;
29 1         5 my $httpdocs = shift;
30 1 50       6 if ($httpdocs) {
31 1         4 $self->{__CAP__AJAXUPLOAD_HTTPDOCS} = $httpdocs;
32 1         4 return;
33             }
34 0         0 return $self->{__CAP__AJAXUPLOAD_HTTPDOCS};
35             }
36              
37             sub ajax_upload_setup {
38 1     1 1 24 my $self = shift;
39 1         3 my %args = @_;
40              
41 1   50     10 my $upload_subdir = $args{upload_subdir} || '/img/uploads';
42 1         2 my $dfv_profile = $args{dfv_profile};
43 1 50       7 if (!$dfv_profile) {
44 1         5 $dfv_profile = $self->ajax_upload_default_profile();
45             }
46 1   50     9 my $run_mode = $args{run_mode} || 'ajax_upload_rm';
47              
48             $self->run_modes(
49             $run_mode => sub {
50 0     0   0 my $c = shift;
51 0         0 $c->header_props(
52             -type=>'text/javascript',
53             -encoding=>'utf-8',
54             -charset=>'utf-8'
55             );
56 0         0 my $r = eval {
57 0         0 $c->_ajax_upload_rm($upload_subdir, $dfv_profile);
58             };
59 0 0       0 if ($@) {
60 0         0 carp $@;
61 0         0 return $c->to_json({status=> 'Internal Error'});
62             }
63 0         0 return $r;
64             }
65 1         15 );
66              
67 1         25 return;
68             }
69              
70             sub _ajax_upload_rm {
71 2     2   2822 use autodie qw(open close);
  2         36077  
  2         16  
72 0     0   0 my $self = shift;
73 0         0 my $upload_subdir = shift;
74 0         0 my $dfv_profile = shift;
75 0         0 my $httpdocs_dir = $self->ajax_upload_httpdocs;
76              
77 0 0       0 return $self->to_json({status => 'No document root specified'})
78             if not defined $httpdocs_dir;
79              
80 0         0 my $full_upload_dir = "$httpdocs_dir/$upload_subdir";
81 0         0 my $query = $self->query;
82              
83 0         0 my $lightweight_fh = $query->upload('file');
84 0 0       0 return $self->to_json({status=>'No file handle obtained'})
85             if !defined $lightweight_fh;
86            
87 0         0 my $fh = $lightweight_fh->handle;
88 0 0       0 return $self->to_json({status => 'No file handle promoted'})
89             if not $fh;
90              
91 0         0 my $value = slurp $fh;
92 0         0 close $fh;
93 0         0 my $filename = $query->param('file');
94 0         0 my $info = $query->uploadInfo($filename);
95 0 0       0 return $self->to_json({status => 'No file name obtained'})
96             if not $filename;
97 0         0 $filename = "$filename"; # force $filename to be a strict string
98              
99 0         0 my $mime_type = 'text/plain';
100 0 0 0     0 if ($info and exists $info->{'Content-Type'}) {
101 0         0 $mime_type = $info->{'Content-Type'};
102             }
103            
104 0         0 my $data = {
105             value => $value,
106             file_name => $filename,
107             mime_type => $mime_type,
108             data_size => length $value,
109             };
110 0         0 my $results = Data::FormValidator->check($data, $dfv_profile);
111 0 0       0 return $self->_ajax_upload_compile_messages($results->msgs)
112             if ! $results->success;
113              
114 0         0 $value = $results->valid('value');
115 0         0 $filename = $results->valid('file_name');
116              
117 0 0       0 if ($query->param('validate')) {
118              
119 0 0       0 return $self->to_json({status => 'Document root is not a directory'})
120             if not -d $httpdocs_dir;
121              
122 0 0       0 return $self->to_json({status => 'Upload folder is not a directory'})
123             if not -d $full_upload_dir;
124              
125 0 0       0 return $self->to_json({status => 'Upload folder is not writeable'})
126             if not -w $full_upload_dir;
127            
128 0 0       0 return $self->to_json({status => 'No data uploaded'})
129             if not $value;
130              
131             }
132              
133 0         0 open $fh, '>', "$full_upload_dir/$filename";
134 0         0 print {$fh} $value;
  0         0  
135 0         0 close $fh;
136              
137 0         0 return $self->to_json({
138             status=>'UPLOADED',
139             image_url=>"$upload_subdir/$filename"
140             });
141             }
142              
143             sub _ajax_upload_compile_messages {
144 0     0   0 my $self = shift;
145 0         0 my $msgs = shift;
146 0         0 my $text = '';
147 0         0 foreach my $key (keys %$msgs) {
148 0         0 $text .= "$key: $msgs->{$key}, ";
149             }
150 0         0 return $self->to_json({status=>$text});
151             }
152              
153             sub ajax_upload_default_profile {
154             return {
155             required=>[qw(value file_name mime_type data_size)],
156             untaint_all_constraints=>1,
157             constraint_methods => {
158             value=>qr{\A.+\z}xms,
159             file_name=>qr/^[\w\.\-\_]{1,30}$/,
160             data_size=>sub {
161 0     0   0 my ($dfv, $val) = @_;
162 0         0 $dfv->set_current_constraint_name('data_size');
163 0         0 return $val < $MAX_UPLOAD;
164             },
165 1     1 1 30 mime_type=>qr{
166             \A
167             image/
168             (?:
169             jpeg|png|gif
170             )
171             \z
172             }xms,
173             },
174             msgs => {
175             format => '%s',
176             },
177             };
178             }
179              
180             1; # Magic true value required at end of module
181             __END__