1#============================================================= -*-Perl-*- 2# 3# Template::Plugin::File 4# 5# DESCRIPTION 6# Plugin for encapsulating information about a system file. 7# 8# AUTHOR 9# Originally written by Michael Stevens <michael@etla.org> as the 10# Directory plugin, then mutilated by Andy Wardley <abw@kfs.org> 11# into separate File and Directory plugins, with some additional 12# code for working with views, etc. 13# 14# COPYRIGHT 15# Copyright 2000-2007 Michael Stevens, Andy Wardley. 16# 17# This module is free software; you can redistribute it and/or 18# modify it under the same terms as Perl itself. 19# 20#============================================================================ 21 22package Template::Plugin::File; 23 24use strict; 25use warnings; 26use Cwd; 27use File::Spec; 28use File::Basename; 29use base 'Template::Plugin'; 30 31our $VERSION = 2.71; 32 33our @STAT_KEYS = qw( dev ino mode nlink uid gid rdev size 34 atime mtime ctime blksize blocks ); 35 36 37#------------------------------------------------------------------------ 38# new($context, $file, \%config) 39# 40# Create a new File object. Takes the pathname of the file as 41# the argument following the context and an optional 42# hash reference of configuration parameters. 43#------------------------------------------------------------------------ 44 45sub new { 46 my $config = ref($_[-1]) eq 'HASH' ? pop(@_) : { }; 47 my ($class, $context, $path) = @_; 48 my ($root, $home, @stat, $abs); 49 50 return $class->throw('no file specified') 51 unless defined $path and length $path; 52 53 # path, dir, name, root, home 54 55 if (File::Spec->file_name_is_absolute($path)) { 56 $root = ''; 57 } 58 elsif (($root = $config->{ root })) { 59 # strip any trailing '/' from root 60 $root =~ s[/$][]; 61 } 62 else { 63 $root = ''; 64 } 65 66 my ($name, $dir, $ext) = fileparse($path, '\.\w+'); 67 # fixup various items 68 $dir =~ s[/$][]; 69 $dir = '' if $dir eq '.'; 70 $name = $name . $ext; 71 $ext =~ s/^\.//g; 72 73 my @fields = File::Spec->splitdir($dir); 74 shift @fields if @fields && ! length $fields[0]; 75 $home = join('/', ('..') x @fields); 76 $abs = File::Spec->catfile($root ? $root : (), $path); 77 78 my $self = { 79 path => $path, 80 name => $name, 81 root => $root, 82 home => $home, 83 dir => $dir, 84 ext => $ext, 85 abs => $abs, 86 user => '', 87 group => '', 88 isdir => '', 89 stat => defined $config->{ stat } 90 ? $config->{ stat } 91 : ! $config->{ nostat }, 92 map { ($_ => '') } @STAT_KEYS, 93 }; 94 95 if ($self->{ stat }) { 96 (@stat = stat( $abs )) 97 || return $class->throw("$abs: $!"); 98 99 @$self{ @STAT_KEYS } = @stat; 100 101 unless ($config->{ noid }) { 102 $self->{ user } = eval { getpwuid( $self->{ uid }) || $self->{ uid } }; 103 $self->{ group } = eval { getgrgid( $self->{ gid }) || $self->{ gid } }; 104 } 105 $self->{ isdir } = -d $abs; 106 } 107 108 bless $self, $class; 109} 110 111 112#------------------------------------------------------------------------- 113# rel($file) 114# 115# Generate a relative filename for some other file relative to this one. 116#------------------------------------------------------------------------ 117 118sub rel { 119 my ($self, $path) = @_; 120 $path = $path->{ path } if ref $path eq ref $self; # assumes same root 121 return $path if $path =~ m[^/]; 122 return $path unless $self->{ home }; 123 return $self->{ home } . '/' . $path; 124} 125 126 127#------------------------------------------------------------------------ 128# present($view) 129# 130# Present self to a Template::View. 131#------------------------------------------------------------------------ 132 133sub present { 134 my ($self, $view) = @_; 135 $view->view_file($self); 136} 137 138 139sub throw { 140 my ($self, $error) = @_; 141 die (Template::Exception->new('File', $error)); 142} 143 1441; 145 146__END__ 147 148=head1 NAME 149 150Template::Plugin::File - Plugin providing information about files 151 152=head1 SYNOPSIS 153 154 [% USE File(filepath) %] 155 [% File.path %] # full path 156 [% File.name %] # filename 157 [% File.dir %] # directory 158 159=head1 DESCRIPTION 160 161This plugin provides an abstraction of a file. It can be used to 162fetch details about files from the file system, or to represent abstract 163files (e.g. when creating an index page) that may or may not exist on 164a file system. 165 166A file name or path should be specified as a constructor argument. e.g. 167 168 [% USE File('foo.html') %] 169 [% USE File('foo/bar/baz.html') %] 170 [% USE File('/foo/bar/baz.html') %] 171 172The file should exist on the current file system (unless C<nostat> 173option set, see below) as an absolute file when specified with as 174leading 'C</>' as per 'C</foo/bar/baz.html>', or otherwise as one relative 175to the current working directory. The constructor performs a C<stat()> 176on the file and makes the 13 elements returned available as the plugin 177items: 178 179 dev ino mode nlink uid gid rdev size 180 atime mtime ctime blksize blocks 181 182e.g. 183 184 [% USE File('/foo/bar/baz.html') %] 185 186 [% File.mtime %] 187 [% File.mode %] 188 ... 189 190In addition, the C<user> and C<group> items are set to contain the user 191and group names as returned by calls to C<getpwuid()> and C<getgrgid()> for 192the file C<uid> and C<gid> elements, respectively. On Win32 platforms 193on which C<getpwuid()> and C<getgrid()> are not available, these values are 194undefined. 195 196 [% USE File('/tmp/foo.html') %] 197 [% File.uid %] # e.g. 500 198 [% File.user %] # e.g. abw 199 200This user/group lookup can be disabled by setting the C<noid> option. 201 202 [% USE File('/tmp/foo.html', noid=1) %] 203 [% File.uid %] # e.g. 500 204 [% File.user %] # nothing 205 206The C<isdir> flag will be set if the file is a directory. 207 208 [% USE File('/tmp') %] 209 [% File.isdir %] # 1 210 211If the C<stat()> on the file fails (e.g. file doesn't exists, bad 212permission, etc) then the constructor will throw a C<File> exception. 213This can be caught within a C<TRY...CATCH> block. 214 215 [% TRY %] 216 [% USE File('/tmp/myfile') %] 217 File exists! 218 [% CATCH File %] 219 File error: [% error.info %] 220 [% END %] 221 222Note the capitalisation of the exception type, 'C<File>', to indicate an 223error thrown by the C<File> plugin, to distinguish it from a regular 224C<file> exception thrown by the Template Toolkit. 225 226Note that the C<File> plugin can also be referenced by the lower case 227name 'C<file>'. However, exceptions are always thrown of the C<File> 228type, regardless of the capitalisation of the plugin named used. 229 230 [% USE file('foo.html') %] 231 [% file.mtime %] 232 233As with any other Template Toolkit plugin, an alternate name can be 234specified for the object created. 235 236 [% USE foo = file('foo.html') %] 237 [% foo.mtime %] 238 239The C<nostat> option can be specified to prevent the plugin constructor 240from performing a C<stat()> on the file specified. In this case, the 241file does not have to exist in the file system, no attempt will be made 242to verify that it does, and no error will be thrown if it doesn't. 243The entries for the items usually returned by C<stat()> will be set 244empty. 245 246 [% USE file('/some/where/over/the/rainbow.html', nostat=1) 247 [% file.mtime %] # nothing 248 249=head1 METHODS 250 251All C<File> plugins, regardless of the C<nostat> option, have set a number 252of items relating to the original path specified. 253 254=head2 path 255 256The full, original file path specified to the constructor. 257 258 [% USE file('/foo/bar.html') %] 259 [% file.path %] # /foo/bar.html 260 261=head2 name 262 263The name of the file without any leading directories. 264 265 [% USE file('/foo/bar.html') %] 266 [% file.name %] # bar.html 267 268=head2 dir 269 270The directory element of the path with the filename removed. 271 272 [% USE file('/foo/bar.html') %] 273 [% file.name %] # /foo 274 275=head2 ext 276 277The file extension, if any, appearing at the end of the path following 278a 'C<.>' (not included in the extension). 279 280 [% USE file('/foo/bar.html') %] 281 [% file.ext %] # html 282 283=head2 home 284 285This contains a string of the form 'C<../..>' to represent the upward path 286from a file to its root directory. 287 288 [% USE file('bar.html') %] 289 [% file.home %] # nothing 290 291 [% USE file('foo/bar.html') %] 292 [% file.home %] # .. 293 294 [% USE file('foo/bar/baz.html') %] 295 [% file.home %] # ../.. 296 297=head2 root 298 299The C<root> item can be specified as a constructor argument, indicating 300a root directory in which the named file resides. This is otherwise 301set empty. 302 303 [% USE file('foo/bar.html', root='/tmp') %] 304 [% file.root %] # /tmp 305 306=head2 abs 307 308This returns the absolute file path by constructing a path from the 309C<root> and C<path> options. 310 311 [% USE file('foo/bar.html', root='/tmp') %] 312 [% file.path %] # foo/bar.html 313 [% file.root %] # /tmp 314 [% file.abs %] # /tmp/foo/bar.html 315 316=head2 rel(path) 317 318This returns a relative path from the current file to another path specified 319as an argument. It is constructed by appending the path to the 'C<home>' 320item. 321 322 [% USE file('foo/bar/baz.html') %] 323 [% file.rel('wiz/waz.html') %] # ../../wiz/waz.html 324 325=head1 EXAMPLES 326 327 [% USE file('/foo/bar/baz.html') %] 328 329 [% file.path %] # /foo/bar/baz.html 330 [% file.dir %] # /foo/bar 331 [% file.name %] # baz.html 332 [% file.home %] # ../.. 333 [% file.root %] # '' 334 [% file.abs %] # /foo/bar/baz.html 335 [% file.ext %] # html 336 [% file.mtime %] # 987654321 337 [% file.atime %] # 987654321 338 [% file.uid %] # 500 339 [% file.user %] # abw 340 341 [% USE file('foo.html') %] 342 343 [% file.path %] # foo.html 344 [% file.dir %] # '' 345 [% file.name %] # foo.html 346 [% file.root %] # '' 347 [% file.home %] # '' 348 [% file.abs %] # foo.html 349 350 [% USE file('foo/bar/baz.html') %] 351 352 [% file.path %] # foo/bar/baz.html 353 [% file.dir %] # foo/bar 354 [% file.name %] # baz.html 355 [% file.root %] # '' 356 [% file.home %] # ../.. 357 [% file.abs %] # foo/bar/baz.html 358 359 [% USE file('foo/bar/baz.html', root='/tmp') %] 360 361 [% file.path %] # foo/bar/baz.html 362 [% file.dir %] # foo/bar 363 [% file.name %] # baz.html 364 [% file.root %] # /tmp 365 [% file.home %] # ../.. 366 [% file.abs %] # /tmp/foo/bar/baz.html 367 368 # calculate other file paths relative to this file and its root 369 [% USE file('foo/bar/baz.html', root => '/tmp/tt2') %] 370 371 [% file.path('baz/qux.html') %] # ../../baz/qux.html 372 [% file.dir('wiz/woz.html') %] # ../../wiz/woz.html 373 374=head1 AUTHORS 375 376Michael Stevens wrote the original C<Directory> plugin on which this is based. 377Andy Wardley split it into separate C<File> and C<Directory> plugins, added 378some extra code and documentation for C<VIEW> support, and made a few other 379minor tweaks. 380 381=head1 COPYRIGHT 382 383Copyright 2000-2007 Michael Stevens, Andy Wardley. 384 385This module is free software; you can redistribute it and/or 386modify it under the same terms as Perl itself. 387 388=head1 SEE ALSO 389 390L<Template::Plugin>, L<Template::Plugin::Directory>, L<Template::View> 391 392