File Coverage

blib/lib/Mojolicious/Plugin/StaticShare/Controller.pm
Criterion Covered Total %
statement 75 209 35.8
branch 25 142 17.6
condition 20 117 17.0
subroutine 11 24 45.8
pod 0 8 0.0
total 131 500 26.2


line stmt bran cond sub pod time code
1             package Mojolicious::Plugin::StaticShare::Controller;
2 1     1   14545 use Mojo::Base 'Mojolicious::Controller';
  1         4  
  1         8  
3 1     1   294 use Mojo::File qw(path);
  1         34  
  1         76  
4 1     1   518 use HTTP::AcceptLanguage;
  1         2470  
  1         32  
5 1     1   8 use Mojo::Path;
  1         3  
  1         7  
6 1     1   37 use Mojo::Util qw ( decode encode url_unescape xml_escape);#
  1         2  
  1         76  
7 1     1   525 use Time::Piece;# module replaces the standard localtime and gmtime functions with implementations that return objects
  1         7380  
  1         6  
8             #~ use Mojo::Asset::File;
9              
10             has plugin => sub { shift->stash('plugin') };
11             has public_uploads => sub { shift->plugin->public_uploads };
12             has is_admin => sub {
13             my $c = shift;
14             $c->plugin->is_admin($c);
15             };
16              
17             sub get {
18 1     1 0 420 my ($c) = @_;
19            
20 1         15 $c->_stash();
21            
22             return $c->not_found
23 1 50 33     68 if !$c->is_admin && grep {/^\./} @{$c->stash('url_path')->parts};
  0         0  
  1         33  
24            
25 1 50       74 if ($c->plugin->access) {
26 0 0       0 my $access = ref $c->plugin->access eq 'CODE' ? $c->plugin->access->($c) : $c->plugin->access;
27            
28 0 0       0 return $c->not_found
29             unless $access;
30            
31 0 0       0 return $c->render(%$access)
32             if ref $access eq 'HASH';
33             }
34            
35 1 50 33     8 if ($c->is_admin && $c->param('admin')) {
36             # Temporary Redirect
37 0         0 $c->res->code(307);
38 0         0 return $c->redirect_to($c->req->url->to_abs->path);
39             }
40              
41 1         20 my $file_path = $c->stash('file_path');
42            
43 1 50       25 return $c->dir($file_path)
44             if -d $file_path;
45 0 0       0 return $c->file($file_path)
46             if -f $file_path;
47              
48 0         0 $c->not_found;
49             }
50              
51             sub post {
52 0     0 0 0 my ($c) = @_;
53 0         0 $c->_stash();
54            
55 0         0 my $file_path = $c->stash('file_path');
56 0         0 my $url_path = $c->stash('url_path');
57            
58 0 0       0 $c->app->log->debug($file_path, $url_path->to_route, $c->dumper($c->req->params->to_hash))
59             if $c->plugin->debug;
60            
61 0 0 0     0 if (my $dir = $c->param('dir')) {
    0          
    0          
    0          
62 0 0       0 return $c->new_dir($file_path, $dir)
63             if $c->is_admin;
64 0         0 return $c->render(json=>{error=>$c->i18n('you cant create dir')});
65             } elsif (my $rename = $c->param('rename')) {
66 0 0       0 return $c->rename($file_path, $rename)
67             if $c->is_admin;
68 0         0 return $c->render(json=>{error=>$c->i18n('you cant rename')});
69             } elsif (my $delete = $c->param('delete[]') && $c->every_param('delete[]')) {
70 0 0       0 return $c->delete($file_path, $delete)
71             if $c->is_admin;
72 0         0 return $c->render(json=>{error=>$c->i18n('you cant delete')});
73             } elsif (defined (my $edit = $c->param('edit'))) {
74 0 0       0 return $c->_edit($file_path, $edit)
75             if $c->is_admin;
76 0         0 return $c->render(json=>{error=>$c->i18n('you cant edit')});
77             }
78            
79             return $c->render(json=>{error=>$c->i18n('target directory not found')})
80 0 0 0     0 if !$c->is_admin && grep {/^\./} @{$c->stash('url_path')->parts};
  0         0  
  0         0  
81            
82 0 0 0     0 return $c->render(json=>{error=>$c->i18n('you cant upload')})
83             unless $c->is_admin || $c->public_uploads;
84            
85 0   0     0 my $name = url_unescape($c->param('name') || '');#$file->filename
86 0         0 utf8::upgrade($name);
87             #~ return $c->render(json=>{error=>$c->i18n('Provide the name of upload file')})
88             #~ unless $name =~ /\S/i;
89            
90 0 0 0     0 return $c->render(json=>{error=>$c->i18n('Cant open target directory')})
91             unless !$name || -w $file_path;
92             #~ $c->req->max_message_size(0);
93             # Check file size
94 0 0       0 return $c->render(json=>{error=>$c->i18n('upload is too big')}, status=>417)
95             if $c->req->is_limit_exceeded;
96              
97 0 0       0 my $file = $c->req->upload('file')
98             or return $c->render(json=>{error=>$c->i18n('Where is your upload file?')});
99              
100 0 0       0 my $to = $name ? $file_path->child($name) : $file_path;
101            
102 0 0 0     0 return $c->render(json=>{error=>$c->i18n('path is not a directory')})
103             unless !$name || -d $file_path;
104 0 0 0     0 return $c->render(json=>{error=>$c->i18n('path is a directory')})
105             if !$name && -d $to;
106            
107 0 0 0     0 return $c->render(json=>{error=>$c->i18n('file already exists')})
108             if !$c->param('replace') && -e $to;
109            
110 0 0       0 eval { $file->asset->move_to($to) }
  0         0  
111             or return $c->render(json=>{error=>$@ =~ /(.+) at /});
112            
113            
114 0 0       0 $url_path->merge($name)
115             if $name;
116            
117 0         0 $c->render(json=>{ok=> $url_path->trailing_slash(0)->to_route});
118             }
119              
120             # TODO DELETE request
121             sub delete {
122             my ($c) = @_;
123             $c->_stash();
124            
125             my $file_path = $c->stash('file_path');
126             my $url_path = $c->stash('url_path');
127            
128             $c->app->log->debug($file_path, $url_path->to_route, $c->dumper($c->req->params->to_hash))
129             if $c->plugin->debug;
130            
131             }
132              
133             sub _stash {
134 1     1   4 my ($c) = @_;
135              
136 1   50     7 my $lang = HTTP::AcceptLanguage->new($c->req->headers->accept_language || 'en;q=0.5');
137 1         189 $c->stash('language' => $lang);
138            
139 1         27 my $pth = Mojo::Path->new($c->stash('pth'))->leading_slash(0)->trailing_slash(0);
140 1 50       153 $pth = $pth->trailing_slash(1)->merge('.'.$c->stash('format'))
141             if $c->stash('format');
142 1         15 $c->stash('pth' => $pth);
143 1         30 my $url_path = $c->plugin->root_url->clone->merge($pth)->trailing_slash(1);
144 1         303 $c->stash('url_path' => $url_path);
145             #~ $c->stash('file_path' => $c->plugin->root_dir->clone->merge($c->stash('pth')));
146 1         21 $c->stash('file_path' => path(decode('UTF-8', url_unescape($c->plugin->root_dir->clone->merge($pth)))));
147 1         769 $c->stash('title' => $c->i18n('Share')." ".$url_path->to_route);
148             }
149              
150              
151             sub dir {
152 1     1 0 36 my ($c, $path) = @_;
153            
154 1 50 50     5 return $c->not_found
155             if ($c->plugin->render_dir // '') eq 0;
156            
157 1         25 my $ex = Mojo::Exception->new($c->i18n(qq{Cant open directory}));
158 1 50 0     24 opendir(my $dir, $path)
159             or return $c->render_maybe('Mojolicious-Plugin-StaticShare/exception', format=>'html', handler=>'ep', status=>500, exception=>$ex)
160             || $c->reply->exception($ex);
161            
162 1         51 my $files = $c->stash('files' => [])->stash('files');
163 1         40 my $dirs = $c->stash('dirs' => [])->stash('dirs');
164            
165 1         55 while (readdir $dir) {
166             next
167 17 100 100     1621 if $_ eq '.' || $_ eq '..';
168             next
169 15 50 33     54 if !$c->is_admin && /^\./;
170            
171 15         132 my $child = $path->child(decode('UTF-8', $_));
172            
173 15 100 50     511 push @$dirs, decode('UTF-8', $_)
      66        
174             and next
175             if -d $child && -r _;
176            
177             next
178 11 50       239 unless -f _;
179            
180 11         37 my @stat = stat $child;
181            
182 11   100     201 push @$files, {
183             name => decode('UTF-8', $_),
184             size => $stat[7] || 0,
185             #~ type => $c->plugin->mime->type( (/\.([0-9a-zA-Z]+)$/)[0] || 'txt' ) || 'application/octet-stream',
186             mtime => decode('UTF-8', localtime( $stat[9] )->strftime), #->to_datetime, #to_string(),
187             #~ mode=> $stat[2] & 07777, #-r _,
188             };
189             }
190 1         144 closedir $dir;
191            
192 1 50       7 for my $index ($c->plugin->dir_index ? @{$c->plugin->dir_index} : ()) {
  1         19  
193 3         70 my $file = $path->child($index);
194             next
195 3 100       58 unless -f $file;
196            
197 1 50 0     21 $c->_stash_markdown($file)
      0        
198             and $c->stash(index=>$index)
199             and last
200             if $index =~ $c->plugin->re_markdown;
201            
202 1 50 33     5 $c->_stash_pod($file)
      50        
203             and $c->stash(index=>$index)
204             and last
205             if $index =~ $c->plugin->re_pod;
206              
207             }
208            
209 1 0       4382 return $c->render(ref $c->plugin->render_dir ? %{$c->plugin->render_dir} : $c->plugin->render_dir,)
  0 50       0  
210             if $c->plugin->render_dir;
211            
212             $c->render_maybe("Mojolicious-Plugin-StaticShare/$_/dir", format=>'html', handler=>'ep',)
213             and return
214 1   50     20 for $c->stash('language')->languages;
215            
216 1         4887 return $c->render('Mojolicious-Plugin-StaticShare/dir', format=>'html', handler=>'ep',);
217              
218            
219             #~ $c->render_maybe('Mojolicious-Plugin-StaticShare/exception', format=>'html', handler=>'ep', status=>500,exception=>Mojo::Exception->new(qq{Template rendering for dir content not found}))
220             #~ or $c->reply->exception();
221             }
222              
223             sub new_dir {
224 0     0 0 0 my ($c, $path, $dir) = @_;
225            
226 0         0 my $edir = url_unescape($dir);
227 0         0 utf8::upgrade($edir);
228            
229 0 0       0 return $c->render(json=>{error=>$c->i18n('provide the name of new directory')})
230             unless $edir =~ /\S/;
231            
232 0         0 my $to = $path->child($edir);#
233            
234 0 0       0 return $c->render(json=>{error=>$c->i18n('dir or file exists')})
235             if -e $to;
236            
237 0         0 $to->make_path;
238            
239 0         0 $c->render(json=>{ok=> $c->stash('url_path')->clone->merge($dir)->trailing_slash(1)->to_route});
240            
241             }
242              
243             sub rename {
244 0     0 0 0 my ($c, $path, $rename) = @_;
245            
246 0         0 my $ename = url_unescape($rename);
247 0         0 utf8::upgrade($ename);
248            
249 0 0       0 return $c->render(json=>{error=>$c->i18n('provide new name')})
250             unless $ename =~ /\S/;
251            
252 0         0 my $to = $path->sibling($ename);
253            
254 0 0       0 return $c->render(json=>{error=>$c->i18n('dir or file exists')})
255             if -e $to;
256            
257 0 0       0 my $move = eval { $path->move_to($to) }
  0         0  
258             or return $c->render(json=>{error=>$@ =~ /(.+) at /});
259            
260 0         0 $c->render(json=>{ok=> $c->stash('url_path')->trailing_slash(0)->to_dir->merge($rename)->to_route});
261            
262             }
263              
264             sub delete {
265 0     0 0 0 my ($c, $path, $delete) = @_;
266 0         0 my @delete = ();
267 0         0 for (@$delete) {
268 0         0 my $del = url_unescape($_);
269 0         0 utf8::upgrade($del);
270 0 0 0     0 push @delete, $c->i18n('provide the name of deleted dir')
271             and next
272             unless $del =~ /\S/;
273 0         0 my $d = $path->sibling($del);
274 0 0 0     0 push @delete, $c->i18n('dir or file does not exists')
275             and next
276             unless -e $d;
277 0 0       0 push @delete, eval {$d->remove_tree()} ? 1 : 0,
  0         0  
278             }
279            
280 0         0 $c->render(json=>{ok=>\@delete});
281             }
282              
283             sub file {
284 0     0 0 0 my ($c, $path) = @_;
285            
286 0         0 my $ex = Mojo::Exception->new($c->i18n(qq{Permission denied}));
287 0 0 0     0 return $c->render_maybe('Mojolicious-Plugin-StaticShare/exception', format=>'html', handler=>'ep', status=>500,exception=>$ex)
288             || $c->reply->exception($ex)
289             unless -r $path;
290            
291 0 0 0     0 return $c->_edit($path)
292             if $c->is_admin && $c->param('edit');
293            
294 0         0 my $filename = $path->basename;
295            
296 0 0 0     0 return $c->_markdown($path)
      0        
      0        
297             unless ($c->plugin->render_markdown || '') eq 0 || $c->param('attachment') || $filename !~ $c->plugin->re_markdown;
298            
299 0 0 0     0 return $c->_pod($path)
      0        
      0        
300             unless ($c->plugin->render_pod || '') eq 0 || $c->param('attachment') || $filename !~ $c->plugin->re_pod;
301            
302 0 0 0     0 my $asset = $c->_html($path)
303             unless $c->param('attachment') || $filename !~ $c->plugin->re_html;
304            
305 0 0       0 $c->res->headers->content_disposition($c->param('attachment') ? "attachment; filename=$filename;" : "inline");
306 0   0     0 my $type =$c->plugin->mime->type( ( $path =~ /\.([0-9a-zA-Z]+)$/)[0] || 'txt' ) || $c->plugin->mime->type('txt');#'application/octet-stream';
307 0         0 $c->res->headers->content_type($type);
308 0   0     0 $c->reply->asset($asset || Mojo::Asset::File->new(path => $path));
309             }
310              
311             sub _html {# disable scripts inside html
312 0     0   0 my ($c, $path) = @_;
313 0         0 my $file = Mojo::Asset::File->new(path => $path);
314 0         0 my $content = $file->slurp;
315 0         0 my $dom = Mojo::DOM->new($content);
316 0 0       0 $dom->find('script')->each(\&_sanitize_script)->size
317             or return $file;
318            
319 0         0 my $asset = Mojo::Asset::Memory->new;
320 0         0 $asset->add_chunk($dom)->mtime($file->mtime);
321 0         0 return $asset;
322             }
323              
324             sub _markdown {# file
325 0     0   0 my ($c, $path) = @_;
326              
327 0         0 my $ex = Mojo::Exception->new($c->i18n(qq{Please install or verify markdown module (default to Text::Markdown::Hoedown) with markdown(\$str) sub or parse(\$str) method}));
328              
329 0 0 0     0 $c->_stash_markdown($path)
330             or return $c->render_maybe('Mojolicious-Plugin-StaticShare/exception', format=>'html', handler=>'ep', status=>500,exception=>$ex)
331             || $c->reply->exception($ex);
332            
333             return $c->plugin->render_markdown
334 0 0       0 ? $c->render(ref $c->plugin->render_markdown ? %{$c->plugin->render_markdown} : $c->plugin->render_markdown,)
  0 0       0  
335             : $c->render('Mojolicious-Plugin-StaticShare/markdown', format=>'html', handler=>'ep',);
336             }
337              
338             my $layout_re = qr|^(?:\s*%+\s*layouts/(.+)[;\s]+)|;
339              
340             sub _stash_markdown {
341 0     0   0 my ($c, $path) = @_;
342 0 0       0 my $md = $c->plugin->markdown
343             or return; # not installed
344 0         0 my $content = decode('UTF-8', $path->slurp);
345 0         0 $content =~ s|$layout_re|$c->_layout_index($1)|e;#) {# || $content =~ s/(?:%\s*layout\s+"([^"]+)";?)//m
  0         0  
346 0         0 my $dom = Mojo::DOM->new($md->parse($content));
347 0         0 $dom->find('script')->each(\&_sanitize_script);
348 0         0 $dom->find('*')->each(\&_dom_attrs);
349 0   0     0 $c->stash(markdown => $dom->at('body') || $dom);
350             #~ my $filename = $path->basename;
351             #~ $c->stash('title'=>$filename);
352             }
353              
354             my $layout_ext_re = qr|\.([^/\.;\s]+)?\.?([^/\.;\s]+)?$|; # .html.ep
355              
356             sub _layout_index {# from regexp execute
357 0     0   0 my ($c, @match) = @_;#
358 0         0 $match[0] =~ s|[;\s]+$||;
359             #~ utf8::encode($match[0]);
360 0 0       0 push @match, $1, $2
361             if $match[0] =~ s|$layout_ext_re||;
362 0   0     0 my $found = $c->app->renderer->template_path({
      0        
363             template => "layouts/$match[0]",
364             format => $match[1] || 'html',
365             handler => $match[2] || 'ep',
366             });
367 0 0 0     0 $c->layout($match[0])#encode('UTF-8', $match[0]))
368             and return ''
369             if $found;
370 0         0 my $err = "layout [$match[0]].$match[1].$match[2] not found";
371 0         0 $c->app->log->error("$err", "\t app->renderer->paths: ", @{$c->app->renderer->paths});
  0         0  
372 0         0 return "
$err
";
373             }
374              
375             sub _sanitize_script {# for markdown
376 0     0   0 my $el = shift;
377 0         0 my $text = xml_escape $el->text;
378 0         0 $el->replace("$text");
379             }
380              
381             sub _dom_attrs {# for markdown
382             # translate ^{...} to id, style, class attributes
383             # берем только первый child и он должен быть текстом
384 155     155   9811 my $el = shift;
385 155 100       388 my $text = $el->text
386             or return;
387 138         4638 my $child1 = $el->child_nodes->first;
388 138         11097 my $parent = $child1->parent;
389             return
390 138 100 33     6558 unless $parent && $parent->type eq 'tag' && $child1->type eq 'text';
      66        
391 135         3293 my $content = $child1->content;
392 135 50       3405 if ($content =~ s|^(?:\s*\{([^\}]+)\}\s*)||) {
393 0         0 my $attrs = $1;
394 0         0 utf8::upgrade($attrs);
395             # styles
396 0         0 $parent->{style} .= " $1"
397             while $attrs =~ s|(\S+\s*:\s*[^;]+;)||;
398             # id
399 0         0 $parent->{id} = $1
400             while $attrs =~ s|#(\S+)||;
401             # classes
402 0         0 $parent->{class} .= " $1"
403             while $attrs =~ s|\.?([^\.\s]+)||;
404 0         0 $child1->content($content);# replace
405             }
406             }
407              
408             sub _pod {# file
409 0     0   0 my ($c, $path) = @_;
410              
411 0 0       0 $c->_stash_pod($path)
412             or return;# $c->render_maybe('Mojolicious-Plugin-StaticShare/exception', format=>'html', handler=>'ep', status=>500,exception=>$ex)
413             #~ || $c->reply->exception($ex);
414            
415             return $c->plugin->render_pod
416 0 0       0 ? $c->render(ref $c->plugin->render_pod ? %{$c->plugin->render_pod} : $c->plugin->render_pod,)
  0 0       0  
417             : $c->render('Mojolicious-Plugin-StaticShare/pod', format=>'html', handler=>'ep',);
418            
419             }
420              
421             sub _stash_pod {
422 1     1   4 my ($c, $path) = @_;
423             return
424 1 50       11 unless $c->app->renderer->helpers->{'pod_to_html'};
425              
426 1         28 my $content = decode('UTF-8', $path->slurp);
427 1         380 $content =~ s|$layout_re|$c->_layout_index($1)|e;#) {# || $content =~ s/(?:%\s*layout\s+"([^"]+)";?)//m
  0         0  
428 1         14 my $dom = Mojo::DOM->new($c->pod_to_html($content));
429 1         22175 $dom->find('script')->each(\&_sanitize_script);
430 1         4603 $dom->find('*')->each(\&_dom_attrs);
431 1   33     52 $c->stash(pod => $dom->at('body') || $dom);
432             #~ my $filename = $path->basename;
433             #~ $c->stash('title'=>$filename);
434             }
435              
436             sub not_found {
437 0     0 0   my $c = shift;
438 0 0         $c->render_maybe('Mojolicious-Plugin-StaticShare/not_found', format=>'html', handler=>'ep', status=>404,)
439             or $c->reply->not_found;
440            
441             };
442              
443             sub _edit {
444 0     0     my ($c, $path, $edit) = @_;
445 0 0         unless (defined $edit) {# get
446 0           $c->stash('edit'=> decode('UTF-8', $path->slurp));
447 0           $c->stash('title' => $c->i18n('Edit')." ".$c->stash('url_path')->to_route);
448 0           return $c->render('Mojolicious-Plugin-StaticShare/edit', format=>'html', handler=>'ep',);
449             }
450            
451             # save
452 0           $path->spurt(encode('UTF-8', $edit));
453 0           $c->render(json=>{ok=>$path->to_string});
454             }
455              
456             1;
457              
458             =pod
459              
460             =encoding utf8
461              
462             Доброго всем
463              
464             =head1 Mojolicious::Plugin::StaticShare::Controller
465              
466             ¡ ¡ ¡ ALL GLORY TO GLORIA ! ! !
467              
468             =head1 NAME
469              
470             Mojolicious::Plugin::StaticShare::Controller - is an internal controller for L.
471              
472              
473             =cut