File Coverage

blib/lib/Mojolicious/Plugin/Directory.pm
Criterion Covered Total %
statement 89 89 100.0
branch 29 32 90.6
condition 16 24 66.6
subroutine 15 15 100.0
pod 1 6 16.6
total 150 166 90.3


line stmt bran cond sub pod time code
1             package Mojolicious::Plugin::Directory;
2 8     8   19823 use strict;
  8         13  
  8         234  
3 8     8   28 use warnings;
  8         8  
  8         308  
4             our $VERSION = '0.11';
5              
6 8     8   27 use Cwd ();
  8         12  
  8         102  
7 8     8   651 use Encode ();
  8         7220  
  8         113  
8 8     8   3262 use DirHandle;
  8         3996  
  8         211  
9 8     8   605 use Mojo::Base qw{ Mojolicious::Plugin };
  8         5459  
  8         42  
10 8     8   2302 use Mojolicious::Types;
  8         493  
  8         51  
11 8     8   611 use Mojo::JSON qw(encode_json);
  8         32398  
  8         7659  
12              
13             # Stolen from Plack::App::Direcotry
14             my $dir_page = <<'PAGE';
15            
16             Index of <%= $cur_path %>
17            
18            
25            
26            

Index of <%= $cur_path %>

27            
28            
29            
30             Name
31             Size
32             Type
33             Last Modified
34            
35             % for my $file (@$files) {
36            
<%== $file->{name} %><%= $file->{size} %><%= $file->{type} %><%= $file->{mtime} %>
37             % }
38            
39            
40            
41             PAGE
42              
43             my $types = Mojolicious::Types->new;
44              
45             sub register {
46 7     7 1 219 my $self = shift;
47 7         10 my ( $app, $args ) = @_;
48              
49 7   66     66 my $root = Mojo::Home->new( $args->{root} || Cwd::getcwd );
50 7         457 my $handler = $args->{handler};
51 7         10 my $index = $args->{dir_index};
52 7   100     34 my $auto_index = $args->{auto_index} // 1;
53 7         11 my $json = $args->{json};
54 7 100       25 $dir_page = $args->{dir_page} if ( $args->{dir_page} );
55              
56             $app->hook(
57             before_dispatch => sub {
58 20     20   136461 my $c = shift;
59 20 100       85 return render_file( $c, $root, $handler ) if ( -f $root->to_string() );
60 18         563 my $path = $root->rel_dir( Mojo::Util::url_unescape( $c->req->url->path ) );
61 18 100       1315 if ( -f $path ) {
    50          
62 9         28 render_file( $c, $path, $handler );
63             }
64             elsif ( -d $path ) {
65 9 100 66     36 if ( $index && ( my $index_path = locate_index( $index, $path ) ) ) {
66 1         3 return render_file( $c, $index_path, $handler );
67             }
68              
69 8 100       38 render_indexes( $c, $path, $json ) unless not $auto_index;
70             }
71             },
72 7         55 );
73 7         128 return $app;
74             }
75              
76             sub locate_index {
77 1   50 1 0 3 my $index = shift || return;
78 1   33     2 my $dir = shift || Cwd::getcwd;
79 1         5 my $root = Mojo::Home->new($dir);
80 1 50       22 $index = ( ref $index eq 'ARRAY' ) ? $index : ["$index"];
81 1         2 for (@$index) {
82 1         3 my $path = $root->rel_file($_);
83 1 50       21 return $path if ( -e $path );
84             }
85             }
86              
87             sub render_file {
88 12     12 0 62 my $c = shift;
89 12         14 my $path = shift;
90 12         13 my $handler = shift;
91 12 100       56 $handler->( $c, $path ) if ( ref $handler eq 'CODE' );
92 12 100       2393 return if ( $c->tx->res->code );
93 3         28 my $data = Mojo::Util::slurp($path);
94 3   50     157 $c->render( data => $data, format => get_ext($path) || 'txt' );
95             }
96              
97             sub render_indexes {
98 7     7 0 9 my $c = shift;
99 7         9 my $dir = shift;
100 7         8 my $json = shift;
101              
102 7 100       19 my @files =
103             ( $c->req->url->path eq '/' )
104             ? ()
105             : ( { url => '../', name => 'Parent Directory', size => '', type => '', mtime => '' } );
106 7         213 my $children = list_files($dir);
107              
108 7         216 my $cur_path = Encode::decode_utf8( Mojo::Util::url_unescape( $c->req->url->path ) );
109 7         455 for my $basename ( sort { $a cmp $b } @$children ) {
  161         179  
110 69         95 my $file = "$dir/$basename";
111 69         144 my $url = Mojo::Path->new($cur_path)->trailing_slash(0);
112 69         2498 push @{ $url->parts }, $basename;
  69         113  
113              
114 69         894 my $is_dir = -d $file;
115 69         163 my @stat = stat _;
116 69 100       119 if ($is_dir) {
117 11         15 $basename .= '/';
118 11         21 $url->trailing_slash(1);
119             }
120              
121 69 100 100     164 my $mime_type =
122             $is_dir
123             ? 'directory'
124             : ( $types->type( get_ext($file) || 'txt' ) || 'text/plain' );
125 69         714 my $mtime = Mojo::Date->new( $stat[9] )->to_string();
126              
127 69   50     1990 push @files, {
128             url => $url,
129             name => $basename,
130             size => $stat[7] || 0,
131             type => $mime_type,
132             mtime => $mtime,
133             };
134             }
135              
136 7         30 my $any = { inline => $dir_page, files => \@files, cur_path => $cur_path };
137 7 100       12 if ($json) {
138 2         9 $c->respond_to(
139             json => { json => encode_json(\@files) },
140             any => $any,
141             );
142             }
143             else {
144 5         25 $c->render( %$any );
145             }
146             }
147              
148             sub get_ext {
149 61 100   61 0 260 $_[0] =~ /\.([0-9a-zA-Z]+)$/ || return;
150 56         261 return lc $1;
151             }
152              
153             sub list_files {
154 7   50 7 0 20 my $dir = shift || return [];
155 7         33 my $dh = DirHandle->new($dir);
156 7         293 my @children;
157 7         19 while ( defined( my $ent = $dh->read ) ) {
158 83 100 100     1100 next if $ent eq '.' or $ent eq '..';
159 69         97 push @children, Encode::decode_utf8($ent);
160             }
161 7         72 return [ @children ];
162             }
163              
164             1;
165              
166             __END__