Category: CGI Applications
Author/Contact Info /msg LTjake
Description: This is a pretty simple Photo Gallery using CGI::Application and HTML::Template. Files are simply stored in directories (and don't have to be stored off the document root) and a file with the same name and a .txt entension supplies a caption for said photo. Templates can be modified to suit your purpose. Requires the following modules: CGI::Application, GD (and libs for each format you want supported), and File::Basename
This is "for fun" code. Don't run a nuclear powerplant with it or anything =). Any fixes/modifications are welcome and encouraged! (Note: This script has only been tested on a Linux box...)

Special thanks to jeffa, vladb, petruchio and jcwren for their help.

As far as I can remember, all of the HTML produced is valid XHTML 1.0 Transitional code.

Photo directories are the category titles. Ex: /photos/my_baby_pictures results in "my_baby_pictures" being the category title.

Instance Script (index.cgi): Put this somewhere off of your document root. example: {Document Root}/photos/index.cgi
---
#!/usr/bin/perl -w

# Location of the ImageGallery.pm module
# (if not in the current path)
use lib '/path/to/modules';
use ImageGallery;

my $webapp = ImageGallery->new
(
    # Location of templates.
    TMPL_PATH => '/path/to/templates/',
    PARAMS =>
    {
        # Full path to images
        photos_dir => '/path/to/photos',

        # Thumbnail size (s, m or l)
        thumb_size => 'm',

        # Number of thumbnails per row
        thumbs_per_row => 4,

        script_name => $0
    }
);
$webapp->run();


Main gallery template (photos_index.tmpl): Put this in your templates directory (specified in the instance script)
---
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http:/
+/www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
    <title><TMPL_VAR NAME="title"></title>
    <meta http-equiv="content-type" content="text/html; charset=iso-88
+59-1" />
</head>
<body>
    <center>
    <table>
    <TMPL_LOOP NAME="dir_row">
        <tr>
            <th colspan="5" bgcolor="#E0E0E0"><font size="+3"><TMPL_VA
+R NAME="dir"></font></th>
        </tr>
        <TMPL_LOOP NAME="file_row">
        <tr>
            <TMPL_LOOP NAME="images">
            <TMPL_IF NAME="filename">
            <td bgcolor="#F5F5F5"><a href="<TMPL_VAR NAME="script_name
+">?r=1&amp;i=<TMPL_VAR NAME="filename">"><img border="0" src="<TMPL_V
+AR NAME="script_name">?r=1&amp;i=<TMPL_VAR NAME="filename">&amp;<TMPL
+_VAR NAME="thumb_size">" alt="<TMPL_VAR NAME="alt">" /></a></td>
            <TMPL_ELSE>
            <td bgcolor="#F5F5F5">&nbsp;</td>
            </TMPL_IF>
            </TMPL_LOOP>
        </tr>
        </TMPL_LOOP>
        <TMPL_UNLESS NAME="__LAST__">
        <tr>
            <td><br /></td>
        </tr>
        </TMPL_UNLESS>
    </TMPL_LOOP>
    </table>
    </center>
</body>
</html>


Single photo template (photos_single.tmpl): Put this in your templates directory as well.
---
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http:/
+/www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
    <title><TMPL_VAR NAME="title"></title>
    <meta http-equiv="content-type" content="text/html; charset=iso-88
+59-1" />
</head>
<body>
<table width="100%" height="100%">
<tr><td align="center" valign="middle">
    <table>
        <tr>
            <th><img border="0" src="<TMPL_VAR NAME="script_name">?r=1
+&amp;i=<TMPL_VAR NAME="filename">&amp;f" alt="<TMPL_VAR NAME="alt">" 
+width="<TMPL_VAR NAME="width">" height="<TMPL_VAR NAME="height">" /><
+/th>
        </tr>
        <TMPL_IF NAME="caption">
        <tr>
            <td bgcolor="#F5F5F5" align="center"><TMPL_VAR NAME="capti
+on"></td>
        </tr>
        </TMPL_IF>
    </table>
</td></tr>
</table>
</body>
</html>


Main image gallery script (ImageGallery.pm): Put this in your modules directory (specified in the instance script)
---
package ImageGallery;

use base 'CGI::Application';
use File::Basename;
use GD;
use strict;

sub setup
{
    my $self = shift;
    $self->run_modes
    (
        '0' => 'show_gallery',
        '1' => 'show_image',
        'AUTOLOAD' => 'show_gallery'
    );
    $self->start_mode('0');
    $self->mode_param('r');
}

sub show_gallery
{
    my $self = shift;

    my $row_limit = $self->param('thumbs_per_row');
    my %ok_ext = map { $_ => 1 } qw(jpg gif png);
    my $image_dir = $self->param('photos_dir');

    my @dir_row;
    while(my $dir = <$image_dir/*>)
    {
        next unless -d $dir;
        my $dir_row = {dir => substr($dir, length($image_dir) + 1)};

        my ($i, $j) = (0, 0);
        while (my $full = <$dir/*>)
        {
            my ($file, $ext) = (fileparse($full,keys %ok_ext))[0,2];
            next unless $ok_ext{$ext};

            push @{$dir_row->{file_row}->[$j]->{images}}, { filename =
+> substr($full, length($self->param('photos_dir'))), alt => $file };
            $j++ unless ++$i % $row_limit;
        }
        for ($i = $i; $i % $row_limit; $i++)
        {
            push @{$dir_row->{file_row}->[$j]->{images}}, { filename =
+> '', alt => '' };
        }

        push @dir_row, $dir_row;
    }
    my $html = $self->load_tmpl('photos_index.tmpl', global_vars => 1)
+;
    $html->param(script_name => $self->param('script_name'));
    $html->param(thumb_size => $self->param('thumb_size'));
    $html->param(title => 'My Photo Gallery');
    $html->param(dir_row => \@dir_row);
    return $html->output;
}

sub show_image
{
    my $self = shift;
    my $q = $self->query();
    my $image = GD::Image->new($self->param('photos_dir') . $q->param(
+'i'));

    my $newimage;
    if (defined($q->param('l')))
    {
        $newimage = resize_image(150, $image);
    }
    elsif (defined($q->param('m')))
    {
        $newimage = resize_image(100, $image);
    }
    elsif (defined($q->param('s')))
    {
        $newimage = resize_image(50, $image);
    }
    elsif (defined($q->param('f')))
    {
        my $type = substr($q->param('i'), length($q->param('i')) - 3);
        if ($type eq 'jpg')
        {
            $self->header_props({-type=>'image/jpeg'});
            return $image->jpeg;
        }
        elsif ($type eq 'png' || $type eq 'gif')
        {
            $self->header_props({-type=>'image/png'});
            return $image->png;
        }
    }
    else 
    {
        my ($width, $height) = $image->getBounds();
        my $html = $self->load_tmpl('photos_single.tmpl', global_vars 
+=> 1);
        $html->param(script_name => $self->param('script_name'));
        $html->param(title => 'My Photo Gallery');
        $html->param(filename => $q->param('i'));
        $html->param(alt => $q->param('i'));
        $html->param(width => $width);
        $html->param(height => $height);
        my $file = $self->param('photos_dir') . substr($q->param('i'),
+ 0, length($q->param('i')) - 4);
        open (FH, "<$file.txt");
        local $/ = undef;
        $html->param(caption => <FH>);
        close FH;
        return $html->output;
    }
    $self->header_props({-type=>'image/png'});
    return $newimage->png;
}

sub resize_image
{
    my $newsize = shift;
    my $image = shift;
    my ($width, $height) = $image->getBounds();

    my $image2 = new GD::Image($newsize, $newsize);
    $image2->transparent($image2->colorAllocate(0,0,0));

    if ($width > $height)
    {
        $image2->copyResized($image, 0, int((($newsize - int(($height 
+* $newsize / $width) + 0.5)) / 2) + 0.5), 0, 0, $newsize, int(($heigh
+t * $newsize / $width) + 0.5), $width, $height);
    }
    elsif ($width < $height)
    {
        $image2->copyResized($image, int((($newsize - int(($width * $n
+ewsize / $height) + 0.5)) / 2) + 0.5), 0, 0, 0, int(($width * $newsiz
+e / $height) + 0.5), $newsize, $width, $height);
    }
    else
    {
    $image2->copyResized($image, 0, 0, 0, 0, $newsize, $newsize, $widt
+h, $height);
    }

    return $image2;
}

1;


Replies are listed 'Best First'.
Re: Photo Gallery using CGI::Application and HTML::Template
by samtregar (Abbot) on Jul 06, 2002 at 21:25 UTC
    Looks good! Maybe you would consider fleshing it out with some POD and putting it on CPAN as CGI::Application::PhotoGallery? If you're curious how you can get the templates to install and work as defaults, check out CGI::Application::MailPage.

    -sam

      Thank you Sam. That sounds like a grand idea. I'll get right on it. -Brian