File Coverage

blib/lib/Plack/App/Directory/Template.pm
Criterion Covered Total %
statement 81 105 77.1
branch 25 46 54.3
condition 14 17 82.3
subroutine 15 18 83.3
pod 2 3 66.6
total 137 189 72.4


line stmt bran cond sub pod time code
1 4     4   346502 use strict;
  4         9  
  4         128  
2 4     4   19 use warnings;
  4         8  
  4         208  
3             package Plack::App::Directory::Template;
4             #ABSTRACT: Serve static files from document root with directory index template
5             our $VERSION = '0.27'; #VERSION
6 4     4   63 use v5.10;
  4         15  
  4         183  
7              
8 4     4   18 use parent qw(Plack::App::Directory);
  4         7  
  4         23  
9              
10 4     4   243431 use Plack::Middleware::TemplateToolkit;
  4         292654  
  4         245  
11 4     4   39 use Plack::Util::Accessor qw(filter templates);
  4         10  
  4         18  
12              
13 4     4   3669 use File::ShareDir qw(dist_dir);
  4         29045  
  4         368  
14 4     4   4546 use File::stat;
  4         30965  
  4         33  
15 4     4   325 use DirHandle;
  4         10  
  4         144  
16 4     4   72 use Cwd qw(abs_path);
  4         7  
  4         197  
17 4     4   28 use URI::Escape;
  4         7  
  4         5899  
18              
19             sub prepare_app {
20 11     11 1 140093 my $self = shift;
21              
22 11   100     117 $self->{_default_vars} = delete $self->{VARIABLES} // { };
23 11 100       68 $self->{templates} = delete $self->{INCLUDE_PATH} if $self->{INCLUDE_PATH};
24             }
25              
26             sub serve_path {
27 10     10 0 9364 my($self, $env, $dir, $fullpath) = @_;
28              
29 10 50       451 if (-f $dir) {
30 0         0 return $self->SUPER::serve_path($env, $dir, $fullpath);
31             }
32              
33 10 100       87 if (defined $self->{dir_index}) {
34 2         9 my $index_file = "$dir/".$self->{dir_index};
35 2 100       36 if (-f $index_file) {
36 1         15 return $self->SUPER::serve_path($env, $index_file, $fullpath);
37             }
38             }
39              
40 9         33 my $urlpath = $env->{SCRIPT_NAME} . $env->{PATH_INFO};
41              
42 9 50       225 if ($urlpath !~ m{/$}) {
43 0         0 return $self->return_dir_redirect($env);
44             }
45              
46 9         206 $urlpath = join('/', map {uri_escape($_)} split m{/}, $urlpath).'/';
  10         134  
47              
48 9         286 my $dh = DirHandle->new($dir);
49 9         2153 my @children;
50 9         213 while (defined(my $ent = $dh->read)) {
51 36 100 100     1872 next if $ent eq '.' or $ent eq '..';
52 18         77 push @children, $ent;
53             }
54              
55 9         92 my $files = [ ];
56 9         40 my @special = ('.');
57 9 100       36 push @special, '..' if $env->{PATH_INFO} ne '/';
58              
59 9         48 foreach ( @special, sort { $a cmp $b } @children ) {
  9         45  
60 30         176 my $name = $_;
61 30         59 my $file = "$dir/$_";
62 30         150 my $stat = stat($file);
63 30         3898 my $url = $urlpath . uri_escape($_);
64              
65 30         834 my $is_dir = -d $file; # TODO: use Fcntl instead ?
66              
67 30 100 100     332 push @$files, bless {
    100          
    100          
68             name => $is_dir ? "$name/" : $name,
69             url => $is_dir ? "$url/" : $url,
70             mime_type => $is_dir ? 'directory' : ( Plack::MIME->mime_type($file) || 'text/plain' ),
71             stat => $stat,
72             }, 'Plack::App::Directory::Template::File';
73             }
74              
75 9 100       111 $files = [ map { $self->filter->($_) || () } @$files ] if $self->filter;
  24 100       303  
76              
77 9         73 my $default_vars = {
78 9         166 %{ $self->{_default_vars} },
79             path => $env->{PATH_INFO},
80             urlpath => $urlpath,
81             root => abs_path($self->root),
82             dir => abs_path($dir),
83             };
84              
85 9         940 my $tt_vars = $self->template_vars( %$default_vars, files => $files );
86 9 50       34 if ($env->{'tt.vars'}) {
87 0         0 $env->{'tt.vars'}->{$_} = $tt_vars->{$_} for keys %$tt_vars;
88             } else {
89 9         25 $env->{'tt.vars'} = $tt_vars;
90             }
91              
92 9 100 100     38 $env->{'tt.template'} = ref $self->templates ? $self->templates
93             : ($self->{PROCESS} // 'index.html');
94              
95             $self->{tt} //= Plack::Middleware::TemplateToolkit->new(
96             INCLUDE_PATH => $self->templates
97 1         14 // eval { dist_dir('Plack-App-Directory-Template') }
  2         17  
98             // 'share',
99             VARIABLES => $default_vars,
100             request_vars => [qw(scheme base parameters path user)],
101 9   66     170 map { $_ => $self->{$_} } grep { $_ =~ /^[A-Z_]+$/ } keys %$self
  25   50     318  
      66        
102             )->to_app;
103              
104 9         114523 return $self->{tt}->($env);
105             }
106              
107             sub template_vars {
108 9     9 1 53 my ($self, %args) = @_;
109 9         46 return { files => $args{files} };
110             }
111              
112             package Plack::App::Directory::Template::File;
113              
114             our $AUTOLOAD;
115 0     0   0 sub can { $_[0]->{$_[1]}; }
116              
117             sub AUTOLOAD {
118 36     36   145066 my $self = shift;
119 36         59 my $attr = $AUTOLOAD;
120 36         151 $attr =~ s/.*://;
121 36         332 $self->{$attr};
122             }
123              
124             sub permission {
125             ## no critic
126 0 0   0     $_[0]->{stat} ? ($_[0]->{stat}->mode & 07777) : undef;
127             }
128              
129             sub mode_string { # not tested or documented
130 0 0   0     return ' ' unless $_[0]->{stat};
131 0           my $mode = $_[0]->{stat}->mode;
132              
133             # Code copied from File::Stat::Ls by Geo Tiger
134             # See also File::Stat::Bits, File::Stat::Ls, Stat::lsMode, File::Stat::ModeString
135              
136 0           my @perms = qw(--- --x -w- -wx r-- r-x rw- rwx);
137 0           my @ftype = qw(. p c ? d ? b ? - ? l ? s ? ? ?);
138 0           $ftype[0] = '';
139             ## no critic
140 0           my $setids = ($mode & 07000)>>9;
141             ## no critic
142 0           my @permstrs = @perms[($mode&0700)>>6, ($mode&0070)>>3, $mode&0007];
143             ## no critic
144 0           my $ftype = $ftype[($mode & 0170000)>>12];
145            
146 0 0         if ($setids) {
147 0 0         if ($setids & 01) { # Sticky bit
148 0 0         $permstrs[2] =~ s/([-x])$/$1 eq 'x' ? 't' : 'T'/e;
  0            
149             }
150 0 0         if ($setids & 04) { # Setuid bit
151 0 0         $permstrs[0] =~ s/([-x])$/$1 eq 'x' ? 's' : 'S'/e;
  0            
152             }
153 0 0         if ($setids & 02) { # Setgid bit
154 0 0         $permstrs[1] =~ s/([-x])$/$1 eq 'x' ? 's' : 'S'/e;
  0            
155             }
156             }
157              
158 0           join '', $ftype, @permstrs;
159             }
160              
161              
162              
163             1;
164              
165             __END__