CGI-Application-PhotoGallery
view release on metacpan or search on metacpan
lib/CGI/Application/PhotoGallery.pm view on Meta::CPAN
This method will create (if needed) and return a L<Cache::FileCache> object,
=cut
sub cache {
my $self = shift;
unless ( $self->{ _cache } ) {
my %options = ( namespace => $self->param( 'title' ),
directory_umask => 0007 );
$options{ 'cache_root' } = $self->param( 'cache_root' )
if defined $self->param( 'cache_root' );
$options{ 'namespace' } = $self->param( 'cache_namespace' )
if defined $self->param( 'cache_namespace' );
$options{ 'directory_umask' } = $self->param( 'cache_dirumask' )
if defined $self->param( 'cache_dirumask' );
if ( defined $self->param( 'cache_datumask' ) ) {
umask $self->param( 'cache_datumask' );
}
else {
umask 006;
}
$self->{ _cache } = Cache::FileCache->new( \%options );
}
return $self->{ _cache };
}
=head1 RUN MODES
=head2 gallery_index( )
Reads in the contents of your C<photos_dir> and generates an index of photos.
=cut
sub gallery_index {
my $self = shift;
my $types = $self->mime_types;
my $query = $self->query;
my $limit = $self->param( 'preview_thumbs' );
my $photo_dir = $self->param( 'photos_dir' );
my $user_dir = $self->query->param( 'dir' ) || '';
$user_dir =~ s/\.\.//g;
$user_dir =~ s/\/$//;
my $parent = $user_dir;
$parent =~ s{^(.*?)/([^/]+?)/?$}{$1/};
my $directory = $photo_dir . $user_dir;
die "ERROR: File not found." unless -e $directory;
die "ERROR: '$directory' is not a directory" unless -d $directory;
my $output;
my $cache = $self->cache;
my $key = $directory;
my $lastmod = ( stat( $directory ) )[ 9 ];
my $cstamp = "$directory/.cachetime";
if ( $output = $cache->get( $key ) ) {
my $cachetime = $cache->get( $cstamp );
if ( $cachetime && $cachetime == $lastmod ) {
my $reqmod;
if ( my $header = $query->http( 'If-Modified-Since' ) ) {
$reqmod = HTTP::Date::str2time(
( split( /;/, $header, 2 ) )[ 0 ] );
if ( $reqmod && $reqmod == $lastmod ) {
$self->header_props( { -status => '304 Not Modified' } );
return;
}
}
$self->header_add(
{ -last_modified => HTTP::Date::time2str( $lastmod ) } );
return $output;
}
}
my @dirs = sort File::Find::Rule->directory->mindepth( 1 )->maxdepth( 1 )
->in( $directory );
my @galleries;
for my $dir ( $directory, @dirs ) {
my @files = map { s/^$photo_dir//; { filename => $_ }; }
$self->get_photos( $dir );
# only limit the number of photos on sub-galleries
if ( $dir ne $directory ) {
@files = @files[ 0 .. $limit - 1 ] if @files > $limit;
}
( my $location = $dir ) =~ s/^$photo_dir//;
push @galleries,
{
dir => $location,
title => basename( $dir ),
photos => \@files
};
}
my $current = shift( @galleries );
my $html = $self->load_tmpl(
$self->param( 'index_template' )
|| $self->_dist_file( 'photos_index.tmpl' ),
associate => $self,
global_vars => 1,
loop_context_vars => 1,
die_on_bad_params => 0
);
$html->param(
photos => $current->{ photos },
gallery_name =>
( $user_dir ? $current->{ title } : $self->param( 'title' ) ),
galleries => \@galleries,
parent => $parent,
);
$self->header_add(
{ -last_modified => HTTP::Date::time2str( $lastmod ) } );
$output = $html->output;
$cache->set( $key => $output );
$cache->set( $cstamp => $lastmod );
return $output;
}
=head2 thumbnail( )
Generates a thumbnail for the requested image using the selected graphics
library.
=cut
sub thumbnail {
my $self = shift;
my $query = $self->query;
my $dir = $self->param( 'photos_dir' );
my $photo = $query->param( 'photo' );
my $size = $self->param( 'thumb_size' );
die 'ERROR: Missing photo query argument.' unless $photo;
my $path = "$dir$photo";
my $cache = $self->cache;
my $key = "$path$size";
my $lastmod = ( stat( $path ) )[ 9 ];
my $data;
if ( $data = $cache->get( $key ) ) {
my $reqmod;
if ( my $header = $query->http( 'If-Modified-Since' ) ) {
$reqmod
= HTTP::Date::str2time( ( split( /;/, $header, 2 ) )[ 0 ] );
}
if ( $reqmod && $reqmod == $lastmod ) {
$self->header_props( { -status => '304 Not Modified' } );
return;
}
else {
$data = undef;
}
}
unless ( $data ) {
my $gfx = $self->gfx_lib;
$data = $gfx->resize( $path, $size );
$cache->set( $key => $data );
}
$self->header_props(
{ -type => $self->mime_types->mimeTypeOf( $path ),
-last_modified => HTTP::Date::time2str( $lastmod )
}
);
binmode STDOUT;
return $data;
}
=head2 show_image( )
Sends the contents of the image to the browser.
=cut
sub show_image {
my $self = shift;
my $query = $self->query;
my $dir = $self->param( 'photos_dir' );
my $photo = $query->param( 'photo' );
my $path = "$dir$photo";
die 'ERROR: Missing $photo query argument.' unless $photo;
my $lastmod = ( stat( $path ) )[ 9 ];
my $reqmod;
if ( my $header = $query->http( 'If-Modified-Since' ) ) {
$reqmod = HTTP::Date::str2time( ( split( /;/, $header, 2 ) )[ 0 ] );
}
if ( $reqmod && $reqmod == $lastmod ) {
$self->header_props( { -status => '304 Not Modified' } );
return;
}
open( PHOTO, $path ) or die "ERROR: Cannot open $path: $!";
binmode PHOTO;
my $data = do { local $/; <PHOTO> };
close( PHOTO );
$self->header_props(
{ -type => $self->mime_types->mimeTypeOf( $path ),
-last_modified => HTTP::Date::time2str( $lastmod )
}
);
return $data;
}
=head2 single_index( )
Fills and sends the template for viewing an individual image.
=cut
sub single_index {
my $self = shift;
my $query = $self->query();
my $dir = $self->param( 'photos_dir' );
my $photo = $query->param( 'photo' );
my $path = "$dir$photo";
die 'ERROR: Missing photo query argument.' unless $photo;
die 'ERROR: File not found' unless -e $path;
my $caption_path = "$dir/captions.txt";
my $output;
my $cache = $self->cache;
my $key = "$path.#frame";
my $lastmod = ( stat( $path ) )[ 9 ];
# Directory change means links may have changed
# Caption file change is a content change
my $lastdir = ( stat( $dir ) )[ 9 ];
$lastmod = $lastdir if ( $lastdir > $lastmod );
my $lastcap = 0;
$lastcap = ( stat( $caption_path ) )[ 9 ] if ( -r $caption_path );
$lastmod = $lastcap if ( $lastcap > $lastmod );
my $cstamp = "$key#cachetime";
if ( $output = $cache->get( $key ) ) {
my $cachetime = $cache->get( $cstamp );
if ( $cachetime && $cachetime == $lastmod ) {
my $reqmod;
if ( my $header = $query->http( 'If-Modified-Since' ) ) {
$reqmod = HTTP::Date::str2time(
( split( /;/, $header, 2 ) )[ 0 ] );
if ( $reqmod && $reqmod == $lastmod ) {
$self->header_props( { -status => '304 Not Modified' } );
return;
}
}
$self->header_add(
{ -last_modified => HTTP::Date::time2str( $lastmod ) } );
return $output;
}
}
my $gfx = $self->gfx_lib;
my ( $width, $height ) = eval { $gfx->size( $path ); };
die "Unable to determine size of $path; file may be corrupt.\nError string: $@" if $@;
# get data for prev/next/parent links
my ( undef, $search_dir ) = fileparse( $path );
my ( undef, $parent ) = fileparse( $photo );
my @files = $self->get_photos( $search_dir );
my ( $prev, $next );
while ( my $f = shift @files ) {
$f =~ s{^$dir}{};
if ( $f ne $photo ) {
$prev = $f;
next;
}
else {
$next = shift @files;
$next =~ s{^$dir}{} if $next;
last;
}
}
my $html = $self->load_tmpl(
$self->param( 'single_template' )
|| $self->_dist_file( 'photos_single.tmpl' ),
associate => $self,
global_vars => 1,
die_on_bad_params => 0
);
if ( defined( my $max_width = $self->param( 'max_width' ) ) ) {
if ( $width > $max_width ) {
my $scale = $max_width / $width;
$width = int( $width * $scale );
( run in 1.489 second using v1.01-cache-2.11-cpan-d8267643d1d )