File Coverage

blib/lib/HTML/PhotoAlbum.pm
Criterion Covered Total %
statement 21 220 9.5
branch 0 106 0.0
condition 0 59 0.0
subroutine 7 16 43.7
pod 3 5 60.0
total 31 406 7.6


widths \n); \n); " if $i % $opt{eachrow} == 0;
line stmt bran cond sub pod time code
1              
2             # Copyright (c) 2001 Nathan Wiger
3             # Use "perldoc PhotoAlbum.pm" for documentation
4              
5             package HTML::PhotoAlbum;
6              
7             =head1 NAME
8              
9             HTML::PhotoAlbum - Create web photo albums and slideshows
10              
11             =head1 SYNOPSIS
12              
13             use HTML::PhotoAlbum;
14              
15             # Create a new album object, specifying the albums we have
16              
17             my $album = HTML::PhotoAlbum->new(
18             albums => {
19             sf_trip => 'San Francisco Trip',
20             sjc_vac => 'San Jose Vacation',
21             puppy_1 => 'Puppy - First Week',
22             puppy_2 => 'Puppy - Second Week'
23             }
24             );
25              
26             # By using the "selected" method, we can change what each one
27             # looks like. However, note these if statements are optional!
28              
29             if ($album->selected eq 'sf_trip') {
30             print $album->render(
31             header => 1,
32             eachrow => 3,
33             eachpage => 12
34             );
35             } elsif ($album->selected eq 'sjc_vac') {
36             print $album->render(
37             header => 1,
38             eachrow => 5,
39             eachpage => 20,
40             font_face => 'times'
41             body_bgcolor => 'silver',
42             );
43             } else {
44             # Standard album just uses the defaults
45             # You can leave out the if's above and just use this
46             print $album->render(header => 1);
47             }
48              
49             =head1 REQUIREMENTS
50              
51             This module requires B or later.
52              
53             =head1 DESCRIPTION
54              
55             Admittedly a somewhat special-purpose module, this is designed to
56             dynamically create and display a photo album. Actually, it manages
57             multiple photo albums, each of which can be independently formatted
58             and navigated.
59              
60             Basic usage of this module amounts to the examples shown above. This
61             module supports table-based thumbnail pages, auto-pagination, and slideshows.
62             The HTML produced is fully-customizable. It should be all you need for
63             creating online photo albums (besides the pictures, of course).
64              
65             The directory structure of a basic album looks like this:
66              
67             albums/
68             index.cgi (your script)
69             hawaii_trip/
70             captions.txt (optional)
71             intro.html (optional)
72             image001.jpg
73             image001.sm.jpg
74             image002.gif
75             image002-mini.jpg
76             pict0003.jpeg
77             pict0003.sm.png
78             dsc00004.png
79             dsc00004.thumb.gif
80             xmas_2001/
81             captions.txt
82             pic0001.jpg
83             pic0001.sm.jpg
84             pic0002.jpg
85             pic0002.sm.jpg
86             pic0004.png
87             pic0004.mini.png
88              
89             You'll probably end up choosing just one naming scheme for your images,
90             but the point is that C is flexible enough to handle
91             all of them or any combination thereof. What happens is that the
92             module looks in the dir that you specify and does an ASCII sort
93             on the files. Anything that looks like a valid web image (ends in
94             C<.jpe?g>, C<.gif>, or C<.png>) will be indexed and displayed.
95             Then, it does basenames on the images and looks for their
96             thumbnails, if present. If there are no thumbnails you get a generic
97             link that says "Image 4" or whatever.
98              
99             An optional C file can be included in the directory as
100             well. If this file is present, you can specify captions that will be
101             placed beneath each of the images. For example:
102              
103             # Sample captions.txt file
104             image001 Us atop Haleakala
105             image002 Sunset from Maui
106             pict0003 Hiking on Kauai
107             dsc00004 Snorkeling on Hawaii
108              
109             Also, if the optional C file is present in the directory,
110             then that will be shown as the first page, with a link at the bottom
111             that says "See the Pictures". This allows you to put introductory HTML
112             to tell about your photos. You can put any HTML you want into this file.
113              
114             This module attempts to give you a lot of fine-grained control over
115             image placement and layout while still keeping it simple. You should
116             be able to place images and cells in tables fairly precisely.
117              
118             =cut
119              
120 1     1   21483 use 5.004;
  1         4  
  1         54  
121 1     1   7 use Carp;
  1         2  
  1         82  
122 1     1   5 use strict;
  1         11  
  1         38  
123 1     1   6 use vars qw($VERSION);
  1         2  
  1         120  
124             $VERSION = do { my @r=(q$Revision: 1.20 $=~/\d+/g); sprintf "%d."."%02d"x$#r,@r };
125              
126             # Must twiddle CGI a lot so must include this
127 1     1   5608 use CGI;
  1         24165  
  1         7  
128 1     1   1137 use CGI::FormBuilder;
  1         34221  
  1         1019  
129              
130             # The global %CONFIG hash contains pairs of key/value thingies
131             # that serve as defaults if stuff is not specified.
132              
133             my %CONFIG = (
134             dir => '.',
135             header => 0,
136             eachrow => 4,
137             eachpage => 16,
138             navbar => 1,
139             navwrap => 0,
140             navfull => 1,
141             prevtext => 'Prev',
142             nexttext => 'Next',
143             linktext => 'Image',
144              
145             # Preset HTML options
146             body_bgcolor => 'white',
147             font_face => 'arial,helvetica',
148             div_align => 'center',
149             td_align => 'center',
150             td_valign => 'top',
151              
152             # These are technically options but completely unsupported
153             thumbs => [qw( .thumb .mini .sm
154             -thumb -mini -sm
155             _thumb _mini _sm )],
156             images => [qw( .jpg .jpeg .gif .png
157             .mpg .mpeg .avi .mpa )],
158             intro => 'intro.html',
159             captions => 'captions.txt',
160              
161             );
162              
163             # Internal tag routines stolen from CGI::FormBuilder, which
164             # in turn stole them from CGI.pm
165              
166             sub _escapeurl ($) {
167             # minimalist, not 100% correct, URL escaping
168 0   0 0     my $toencode = shift || return undef;
169 0           $toencode =~ s!([^a-zA-Z0-9_,.-/])!sprintf("%%%02x",ord($1))!eg;
  0            
170 0           return $toencode;
171             }
172              
173             sub _escapehtml ($) {
174 0 0   0     defined(my $toencode = shift) or return;
175             # must do these in order or the browser won't decode right
176 0           $toencode =~ s!&!&!g;
177 0           $toencode =~ s!
178 0           $toencode =~ s!>!>!g;
179 0           $toencode =~ s!"!"!g;
180 0           return $toencode;
181             }
182              
183             sub _tag ($;@) {
184             # called as _tag('tagname', %attr)
185             # creates an HTML tag on the fly, quick and dirty
186 0   0 0     my $name = shift || return;
187 0           my @tag = ();
188 0 0         my @args = ref $_[0] eq 'HASH' ? %{$_[0]} : @_;
  0            
189 0           while (@args) {
190             # this cleans out all the internal junk kept in each data
191             # element, returning everything else (for an html tag)
192 0           my $key = shift @args;
193 0           my $val = _escapehtml shift @args; # minimalist HTML escaping
194 0 0 0       next unless $key && $val;
195 0           push @tag, qq($key="$val");
196             }
197 0           return '<' . join(' ', $name, sort @tag) . '>';
198             }
199              
200             sub _round (@) {
201 0     0     my($int,$dec) = split '\.', shift;
202 0 0         $int++ if $dec >= 5;
203 0           return $int;
204             }
205              
206             sub error_404 {
207 0     0 0   my $self = shift;
208 0   0       my $mesg = shift || "The requested album or image was not found.";
209 0           my $real = shift;
210 0 0         my $mail = $ENV{SERVER_ADMIN} =~ /\@/
211             ? qq($ENV{SERVER_ADMIN})
212             : "the webmaster";
213 0           print <
214             Status: 404 Not Found
215             Content-type: text/html
216              
217             404 Not Found
218            
219            

404 Not Found

220             $mesg
221            

222             Click here to start over, or hit "Back" on your browser.
223            

224             Please contact $mail for more details.
225            
226             EOH
227 0 0         carp "[HTML::PhotoAlbum] $real" if $real; # optional message
228 0           exit 0;
229             }
230              
231             sub file2hash ($) {
232 0     0 0   my $self = shift;
233 0           my $file = shift;
234 0           my %data = ();
235 0 0         open FILE, "<$file"
236             or $self->error_404("Sorry, cannot access photo albums.", "Can't read $file: $!");
237 0           while () {
238 0           warn " $file= $_";
239 0 0 0       next if /^\s*#/ || /^\s*$/;
240 0           chomp;
241 0           my($k,$v) = split /\s+/, $_, 2;
242             #$c =~ s!$image_pat!!; # lose any file suffix - slow
243             # fix encoding of path
244            
245 0 0         carp "[HTML::PhotoAlbum] Warning: duplicate value for '$k' found in $file" if $data{$k};
246 0           warn "\$data{$k} = $v;";
247 0           $data{$k} = $v;
248             }
249 0           close FILE;
250 0 0         return wantarray ? %data : \%data;
251             }
252              
253             =head1 FUNCTIONS
254              
255             =head2 new(opt => val, opt => val)
256              
257             Create a new C object. Typically, the only option
258             you need to specify is the C option, which tells this module
259             which albums you're going to allow indexing:
260              
261             my $album = HTML::PhotoAlbum->new(
262             albums => {
263             dir1 => "My First Album",
264             dir2 => "My Second Album"
265             }
266             );
267              
268             The C method accepts the following options:
269              
270             =over
271              
272             =item albums => { dir => 'Title', dir => 'Title' }
273              
274             This accepts a hashref holding subdir and title pairs. Each of
275             the subdirs must live beneath C<"."> (or whatever you set C
276             to below). The title is what will be displayed as the album
277             title both in the thumbnails page as well as the navigation bar.
278              
279             You can also specify a filename, in which case it will be read
280             for the names of the albums. The format is the same as the
281             C file:
282              
283             # Sample albums.txt file
284             sf_trip San Francisco Trip
285             sjc_vac San Jose Vacation
286              
287             You would then use this like so:
288              
289             my $album = HTML::PhotoAlbum->new(albums => 'albums.txt');
290              
291             If you have a lot of albums, this will allow less code maintenance
292             in the long run.
293              
294             =item dir => $path
295              
296             The directory holding the images. This defaults to C<".">, meaning
297             it assumes your CGI script lives at the top level of your albums
298             directory (as shown above). If you mess with this, you must
299             understand that this directory must be visible from the web as a
300             URL. It is recommended that you don't mess with this.
301              
302             =back
303              
304             =cut
305              
306             sub new {
307 0     0 1   my $class = shift;
308 0   0       my $self = bless {}, ref $class || $class;
309 0           $self->{opt} = { %CONFIG, @_ }; # remainder of args are key/val
310 0           $self->{data} = []; # holds all the images/etc
311 0           push @{$self->{data}}, []; # blank first element so data @ 1
  0            
312              
313 0           $self->{cgi} = new CGI;
314 0           $self->{script} = $self->{cgi}->script_name;
315              
316             # Check for whether our 'albums' option is a hashref or not; if not,
317             # assume it's a filename and read it in verbatim
318 0 0         unless (ref $self->{opt}{albums} eq 'HASH') {
319 0           $self->{opt}{albums} = $self->file2hash($self->{opt}{albums});
320             }
321              
322             # Populate our data if we have an album
323 0 0         if (my $album = $self->{cgi}->param('album')) {
324              
325 1     1   1203 use Data::Dumper;
  1         9860  
  1         3300  
326 0           warn Dumper($self->{opt}{albums}{$album});
327              
328             # If not allowed, show forbidden
329 0 0         $self->error_404("Sorry, that is not a valid photo album.",
330             "Album $album not specified in albums option to new()")
331             unless $self->{opt}{albums}{$album};
332              
333             # Always need the album dir
334 0           my $albumdir = $self->{cgi}->unescape("$self->{opt}{dir}/$album");
335              
336             # Now, try to get to directory and populate all our data
337             # We must populate data before our navbar or else we won't
338             # be able to know what we should be generating...
339 0 0         opendir ALBUM, $albumdir or $self->error_404("Sorry, that is not a valid photo album.",
340             "Cannot read directory $albumdir: $!");
341              
342             # We want to just get our images out
343 0           my $image_pat = join '|', @{ $self->{opt}{images} };
  0            
344 0           my $thumb_pat = join '|', @{ $self->{opt}{thumbs} };
  0            
345              
346             # Real quick - any captions.txt file?
347 0           my %captions = ();
348 0 0         if (-s "$albumdir/$self->{opt}{captions}") {
349 0           %captions = $self->file2hash("$albumdir/$self->{opt}{captions}");
350             }
351              
352 0           for my $image (sort grep /(?:$image_pat)$/, readdir ALBUM) {
353              
354             # skip thumbs (get below)
355 0 0         next if $image =~ /(?:$thumb_pat)(?:$image_pat)$/;
356              
357             # chop apart the image name into a basename and suffix
358 0           my($basename, $suffix) = $image =~ /(.*?)($image_pat)$/;
359              
360             # Look for a thumbnail
361 0           my $image = "$basename$suffix";
362 0           my $thumb = '';
363 0           for my $thsuf ( @{$self->{opt}{thumbs}} ) {
  0            
364 0 0         if ( -s "$albumdir/$basename$thsuf$suffix" ) {
365 0           $thumb = "$albumdir/$basename$thsuf$suffix";
366             }
367             }
368              
369             # check to see if we have a caption
370 0   0       my $caption = $captions{$basename} || $self->{opt}{nocaption};
371              
372             # put all our thumbs onto an ordered array
373             # each element of the array is an array ref which points
374             # to the thumbnail name, the image name, and the caption
375 0           push @{$self->{data}}, [ $thumb, $image, $caption ];
  0            
376             }
377 0           closedir ALBUM;
378             }
379              
380 0           return $self;
381             }
382              
383             =head2 render(opt => val, opt => val)
384              
385             The C method is responsible for formatting the HTML
386             for the actual pages. It returns a string, which can then be
387             printed out like so:
388              
389             print $album->render(header => 1);
390              
391             This method takes a number of options which allow you to tweak
392             the formatting of the HTML produced:
393              
394             =over
395              
396             =item eachrow => $num
397              
398             The number of images to put in each row of the thumbnail page.
399             Defaults to 4.
400              
401             =item eachpage => $num
402              
403             The number of images to display on each thumbnail page.
404             Defaults to 16. This should be a multiple of C, but
405             doesn't have to be.
406              
407             =item header => 1 | 0
408              
409             If set to 1, a "Content-type" header and HTML title will be
410             printed out, meaning you don't have to do this yourself.
411             Defaults to 0.
412              
413             =item navwrap => 1 | 0
414              
415             If set to 1, the navigation bar will wrap from last page to
416             the first for both thumbnails and full-size images. Defaults
417             to 0.
418              
419             =item navfull => 1 | 0
420              
421             If set to 0, then a navigation page will I be created
422             for the full-size images. Instead, the thumbnail pages will
423             link to the full-size images directly.
424              
425             =item linktext => $string
426              
427             Printed out followed by a number if no thumbnail is found.
428             Defaults to "Image".
429              
430             =item nexttext => $string
431              
432             The text for the "next page" link. Defaults to "Next". Note
433             you can do snazzy navigation by doing something tricky like
434             this:
435              
436             nexttext => ""
437              
438             But don't tell anyone I said that.
439              
440             =item prevtext => $string
441              
442             The text for the "previous page" link. Defaults to "Prev".
443              
444             =back
445              
446             In addition, you can specify tags for any HTML element in one
447             of two ways. This is stolen directly from L.
448             First, you can specify them as "tag_attr", for example:
449              
450             body_alink => 'silver' #
451              
452             td_bgcolor => 'white' #
453              
454             font_face => 'arial', #
455             font_size => '3'
456              
457             Or, you can point the tag name to an attr hashref. These would
458             have the same effect as the above:
459              
460             body => { alink => 'silver' }
461              
462             td => { bgcolor => 'white' }
463              
464             font => { face => 'arial', size => 3 }
465              
466             These tags will then be changed appropriately in the HTML, allowing
467             you to completely manipulate what the HTML that is printed out looks
468             like. Several of these options are set by default to make the standard
469             HTML look as nice as possible.
470              
471             =cut
472              
473             sub render {
474 0     0 1   my $self = shift;
475 0 0         carp "Odd number of arguments passed into \$album->render" unless @_ % 2 == 0;
476 0           my %opt = ( %{$self->{opt}}, @_ ); # rest are option => 'value' pairs
  0            
477              
478             # lose fucking uninitialized warnings
479 0           local $^W = 0;
480              
481             # We print out a navigational form up top of each page
482 0           my $navform = CGI::FormBuilder->new(fields => [qw/album/], params => $self->{cgi});
483              
484             # What will be printed out
485 0           my @print = ();
486              
487             # Re-parse our %opt to look for things that resemble HTML tags,
488             # since all our options are single words. Note that "htmltag => { hashref }"
489             # is already implicitly handled by the simple %opt = assign way at the top.
490             # All the "||=" parts are needed so that our defaults don't kill customs
491              
492 0           while (my($key, $value) = each %opt) {
493 0 0         if ($key =~ /^([a-zA-Z]+)_(\w+)/) {
    0          
    0          
    0          
    0          
    0          
494             # split up based on _
495 0   0       $opt{$1}{$2} ||= $value;
496             } elsif ($key eq 'font') {
497 0   0       $opt{font}{face} ||= $value;
498             } elsif ($key eq 'bgcolor') {
499 0   0       $opt{body}{bgcolor} ||= $value;
500             } elsif ($key eq 'width') {
501 0   0       $opt{table}{width} ||= $value;
502             } elsif ($key eq 'align') {
503 0   0       $opt{div}{align} ||= $value;
504             } elsif ($key eq 'center') {
505             # super-special, undocumented for a reason
506 0 0 0       $opt{div}{align} ||= $value ? 'center' : 'left';
507             }
508             }
509              
510             # Get any album if present via CGI
511 0   0       my $album = $navform->field('album') || '';
512              
513             # See if we have a name text
514 0 0 0       my $name = $album ? $self->{opt}{albums}{$album} || ucfirst $album
515             : 'Select a Photo Album';
516              
517             # Extra meta gunk if slideshow
518 0           my $head = '';
519             # Print a header if requested
520 0 0         if ($opt{header}) {
521 0           push @print, <
522             Content-type: text/html
523              
524            
525             $head$name
526             EOF
527 0           push @print, _tag('body', $opt{body}),
528             _tag('div', $opt{div}),
529             _tag('font', $opt{font});
530             }
531              
532             # Closing copyright message
533 0           my $close = _tag('div', $opt{div}) . <
534            

Generated by

535             HTML::PhotoAlbum
536             by Nateware
537            
538             EOF
539              
540             # Add album select form
541 0           $navform->field(name => 'album', options => $self->{opt}{albums}, type => 'select');
542 0           push @print, $navform->render(reset => 0, submit => 'View');
543              
544             # Do we have an album? If so, keep going, otherwise print generic text
545 0 0         if (! $album) {
546 0           push @print, qq(Please select a photo album from the list above and click "View".\n);
547             } else {
548              
549             # Always need the album dir
550 0           my $albumdir = "$self->{opt}{dir}/$album";
551              
552 0 0 0       if ($self->{cgi}->param('image') || $self->{cgi}->param('slideshow')) {
553 0   0       my $img = $self->{cgi}->param('image') || ($opt{eachpage} * ($self->{cgi}->param('page') - 1) + 1);
554              
555             # Print a single image out
556 0           my $data = $self->{data}[$img];
557              
558             # If the image doesn't exist, show 404
559 0 0         $self->error_404("Sorry, image $img was not found in the $name photo album.")
560             unless ref $data;
561              
562             # Boundary checks for min/max image
563 0           my $nextimg = $img + 1;
564 0           my $previmg = $img - 1;
565 0           my $numimgs = @{$self->{data}} - 1; # length
  0            
566              
567             # Setup links just like for pages
568 0           my($prevlink, $nextlink);
569 0 0         if ($nextimg > $numimgs) {
570 0 0         if ($opt{navwrap}) {
571 0           $nextimg = 1;
572 0           $nextlink = qq($opt{nexttext});
573             } else {
574 0           $nextimg = undef;
575 0           $prevlink = qq($opt{nexttext});
576             }
577             } else {
578 0           $nextlink = qq($opt{nexttext});
579             }
580              
581             # Setup links just like for pages
582 0 0         if ($previmg < 1){
583 0 0         if ($opt{navwrap}) {
584 0           $previmg = $numimgs;
585 0           $prevlink = qq($opt{prevtext});
586             } else {
587 0           $previmg = undef;
588 0           $prevlink = qq($opt{prevtext});
589             }
590             } else {
591 0           $prevlink = qq($opt{prevtext});
592             }
593              
594             # Print out slideshow stuff
595 0 0 0       if ($self->{cgi}->param('slideshow') && $nextimg && $self->{cgi}->param('submit') ne 'Stop') {
      0        
596 0           my $sec = $self->{cgi}->param('slideshow');
597 0           push @print, qq( 598             . qq(url=$self->{script}?album=$album&image=$nextimg&slideshow=$sec">);
599             }
600              
601             # Figure out what page we'd be one
602 0           my $page = int(($img - 1) / $opt{eachpage}) + 1;
603              
604             # Now print out HTML, nice and simple
605 0 0         my $caption = $data->[2] ? "

$data->[2]" : '';

606 0           push @print, <
607            

$name - Image $img of $numimgs

608             $prevlink | Back to Page $page | $nextlink

609             $caption
610             EOF
611              
612             } else {
613              
614             # Print the whole album w/ thumbs out
615 0           my $numpages = _round @{$self->{data}} / $opt{eachpage};
  0            
616              
617             # Setup a couple vars and a title
618 0           my $page = 0;
619 0 0         unless ($page = $self->{cgi}->param('page')) {
620 0 0         if (-f "$albumdir/$opt{intro}") {
621 0 0         if (open INTRO, "<$albumdir/$opt{intro}") {
622 0           push @print, '', ;
623 0           push @print, _tag('div', $opt{div}),
624             qq(

See the Pictures\n);

625 0           push @print, $close;
626 0           close INTRO;
627 0 0         return wantarray ? @print : join '', @print;
628             } else {
629 0           carp "[HTML::PhotoAlbum] Warning: $albumdir/$opt{intro} present but unreadable: $!";
630             }
631             }
632 0           $page = 1;
633             }
634              
635 0 0 0       $self->error_404("Sorry, we could not find page $page of the $name photo album.")
636             unless $page >= 0 && $page <= $numpages;
637 0           push @print, "\n

$name - Page $page of $numpages

\n";
638              
639             # Print a navbar?
640 0 0         if ($opt{navbar}) {
641              
642             # We setup our pages, tweak our page CGI param, then generate query_string
643 0           my $nextpage = $page + 1;
644 0           my $prevpage = $page - 1;
645             #push @print, "\n";
646              
647             # Sanity check: See if the previous page is less than 1,
648 0           my($prevlink, $nextlink);
649 0 0         if ($page - 1 > 0) {
    0          
650 0           $prevlink = qq($opt{prevtext});
651             } elsif ($opt{navwrap}) {
652 0           $prevlink = qq($opt{prevtext});
653             } else {
654 0           $prevlink = qq($opt{prevtext});
655             }
656              
657             # And if the next page is bigger than how many we have
658 0 0         if ($page == $numpages) {
659 0 0         if ($opt{navwrap}) {
660 0           $nextlink = qq($opt{nexttext});
661             } else {
662 0           $nextlink = qq($opt{nexttext});
663             }
664             } else {
665 0           $nextlink = qq($opt{nexttext});
666             }
667              
668             # Finally, push together a list of page numbers
669 0           my $pagelinks;
670 0           for (my $i=1; $i <= $numpages; $i++) {
671             #push @print, "\n";
672 0 0         if ($i == $page) {
673 0           $pagelinks .= qq( | $i);
674             } else {
675 0           $pagelinks .= qq( | $i);
676             }
677             }
678              
679 0           push @print, qq($prevlink $pagelinks | $nextlink

\n);

680             }
681              
682             # Browsers should always render tables correctly based
683             # on the individual and
684 0           push @print, _tag('table', $opt{table});
685              
686             # Here we take a slice of the data based on our
687             # page and eachpage definitions
688 0           my $first_img = $opt{eachpage} * ($page - 1) + 1;
689 0           my $last_img = $opt{eachpage} + $first_img - 1;
690             #push @print, "\n";
691              
692 0           my $i = 0;
693 0           for my $data ( @{$self->{data}}[$first_img .. $last_img] ) {
  0            
694 0 0         push @print, _tag('tr', $opt{tr}), "\n" if $i % $opt{eachrow} == 0;
695              
696             # The for loop w/ slice will autoviv array elements if needed, so
697             # we must explicitly check to see if there's really any data first
698 0 0         if (ref $data) {
699 0           my $n = $first_img + $i;
700 0   0       my $thlink = $data->[2] || "$opt{linktext} $n";
701 0           my $caption = '';
702 0 0         if ($data->[0]) {
703 0           $opt{img}{src} = $data->[0];
704 0           $thlink = _tag('img', $opt{img});
705 0           $caption = qq(
$data->[2]);
706             }
707             # This is the td for each image w/ a link to display
708 0           push @print, _tag('td', $opt{td}), _tag('font', $opt{font});
709              
710             # We change from an HTML nav page to a direct img link based on navfull
711 0 0         my $imglink = $opt{navfull}
712             ? qq()
713             : qq();
714              
715             # Create the link
716 0           push @print, qq($imglink$thlink$caption
717             } else {
718 0           push @print, qq(
719             }
720 0           $i++;
721 0 0         push @print, "
722             }
723              
724             # Close image table
725 0           push @print, "
\n"; 726               727             } # end if for $image param 728               729             # Add on things at the end for slideshow 730 0           my $sliform = CGI::FormBuilder->new(fields => { slideshow => 3 }, keepextras => 1); 731 0           $sliform->field(name => 'slideshow', comment => 'seconds', size => 2); 732 0           push @print, $sliform->render(reset => 0, submit => [qw/Start Stop/]); 733               734             } # end huge if for $album 735               736             # Close the document w/ a source note 737 0           push @print, "\n", $close; 738               739 0 0         return wantarray ? @print : join '', @print; 740             } 741               742             =head2 selected 743               744             This returns the name of the selected album, allowing you to 745             conditionally change its layout: 746               747             if ($album->selected eq 'sf_trip') { ... } 748               749             If no album is selected, this will return undef. 750               751             =cut 752               753             sub selected { 754 0     0 1   my $self = shift; 755 0           return $self->{cgi}->param('album'); 756             } 757               758             =head1 EXAMPLE 759               760             Here's a simple photo album script that I use to manage my albums. 761             Note that it dynamically builds a list of the albums from a file in 762             the top-level albums directory, since I have a lot of albums. 763               764             #!/usr/bin/perl -w 765               766             use HTML::PhotoAlbum; 767               768             my $album = HTML::PhotoAlbum->new( 769             albums => 'albums.txt', 770             nexttext => '>>', # >> 771             prevtext => '<<', # << 772             font_color => 'white', 773             body => { 774             bgcolor => 'black', 775             link => 'orange', 776             alink => 'silver', 777             vlink => 'gray', 778             }, 779             table_width => '95%' 780             ); 781               782               783               784             if ($album->selected eq 'sf_trip') { 785             # Larger images in this album 786             print $album->render(header => 1, table_width => '100%', 787             eachrow => 3, eachpage => 9); 788             } else { 789             # All other albums standard 790             print $album->render(header => 1, table_width => '100%'); 791             } 792               793             If you put this script in C<~/public_html/albums>, then people would 794             access your photo albums via C. 795             Easy enough. 796               797             =head1 NOTES 798               799             On an error condition, a 404 Not Found page will be printed in the browser. 800             If the error is suspected to be the programmer's fault, a message will be 801             printed to the error_log. Some errors are not logged because they can be 802             triggered by users trying to screw around (specifying a large page number 803             or image number, for example). 804               805             There are a number of other photo albums on CPAN that are worth looking 806             at, and the PHP "Gallery" alternative is nice too (albeit SLOW). 807               808             =head1 VERSION 809               810             $Id: PhotoAlbum.pm,v 1.20 2005/07/13 20:48:42 nwiger Exp $ 811               812             =head1 AUTHOR 813               814             Copyright (c) 2000-2005, Nathan Wiger, . All Rights Reserved. 815               816             This module is free software; you may copy this under the terms of the 817             GNU General Public License, or the Artistic License, copies of which 818             should have accompanied your Perl kit. 819               820             =cut 821