in reply to Photo Database Application

I've written something that's about 60% of what you're looking for, and it's in Perl, and while I hadn't considered licensing issues, I think I'd tend towards the PAL. I've kind of stalled with moving forward with refactoring and other changes, because it pretty much does everything I want (so far!).

Here's why it could likely be adapted to what you're looking for:

I could easily see modifying my database schema to allow for 'tags', and the indexing CGI to query the database for a given set of tags.

Here's a link to it in action, and the code for the indexing CGI is included here in a <readmore> should you be interested. (There's also some of the modules used listed here which I won't place here for (some!) brevity)

#!/usr/bin/perl use strict; use warnings; use CGI qw/:standard/; use CGI::Cookie; use CGI::Carp qw(fatalsToBrowser); use File::Basename; use Skin; use FileReader; my $cellheight = 76; my $cellwidth = 95; my $cellspc = 4; my $cellpad = 4; my $pwidth = 700; my $pheight = 540; my $year = (localtime)[5] + 1900; my $author = "kent cowgill"; my $title = "$author :: photo archive"; my $next = "previous &gt;"; my $prev = "&lt; next"; my $first = "|&lt; last"; my $last = "first &gt;|"; my $basedir = "/www/kentcowgill"; my $archdir = "/photos/arch"; my $thmbdir = "/photos/thumbs"; my $maindir = $basedir . $archdir; my( $cols, $rows, $header, $footer, $stylesheet, $javascript, $picdir, $start, $skin, $scheme, $end, $picture, $picbase, $datelabel, $hour, $minute, $table ); my @datearray; my @files; my $abscounter = 0; my $colcounter = 0; my $counter = 0; my $rowcounter = 0; my $meridian = "am"; my $filepath = $0; my $basename = basename( $filepath ); $start = ( param( 'start' ) and param( 'start' ) =~ /^\d{1,4}$/ ) ? param( 'start' ) : 0; my %cookies = fetch CGI::Cookie; $scheme = $cookies{'skin'} ? ( ( param( 'scheme' ) and param( 'scheme' ) =~ /^\w{1,6}$/ ) ? param( 'scheme' ) : $cookies{'skin'}->value ) : ( ( param( 'scheme' ) and param( 'scheme' ) =~ /^\w{1,6}$/ ) ? param( 'scheme' ) : 'default' ); $rows = $cookies{'rows'} ? ( ( param( 'rows' ) and param( 'rows' ) =~ /^\d{1,2}$/ ) ? param( 'rows' ) : $cookies{'rows'}->value ) : ( ( param( 'rows' ) and param( 'rows' ) =~ /^\d{1,2}$/ ) ? param( 'rows' ) : 5 ); $cols = $cookies{'cols'} ? ( ( param( 'cols' ) and param( 'cols' ) =~ /^\d{1,2}$/ ) ? param( 'cols' ) : $cookies{'cols'}->value ) : ( ( param( 'cols' ) and param( 'cols' ) =~ /^\d{1,2}$/ ) ? param( 'cols' ) : 5 ); my $skincookie = new CGI::Cookie( -name => 'skin', -value => $scheme, -expires => '+3M', ); my $rowscookie = new CGI::Cookie( -name => 'rows', -value => $rows, -expires => '+3M', ); my $colscookie = new CGI::Cookie( -name => 'cols', -value => $cols, -expires => '+3M', ); my $TWIDTH = $cellwidth * $cols + $cellspc * $cols - 1 + $cellpad * $cols * 2; my $THEIGHT = $cellheight * $rows + $cellspc * $cols - 1 + $cellspc * $cols * 2; $skin = Skin->new( SKIN => "$scheme", ); my $bodybg = $skin->body_background(); my $mainbg = $skin->main_background(); my $tblebg = $skin->table_background(); my $cellfont = $skin->cell_font(); my $filereader = FileReader->new( PICDIR => $picdir, MAINDIR => $maindir, FILEARRAY => @files, ); my $files = $filereader->get_files(); foreach $picture( @$files ){ if( $start > $colcounter ){ $abscounter++; $colcounter++; next; } if( $rowcounter >= $rows ){ $abscounter++; next; } $picture =~ m/(.*)\.jpg/; $picbase = $1; SWITCH: for( $counter ){ $_ == 0 && do { $table .= "<tr><td align=\"center\" " . "valign=\"bottom\">\n"; last SWITCH; }; $_ == ($cols) && do { $table .= "</tr>\n"; last SWITCH; }; $table .= " <td align=\"center\" valign=\"bottom\">\n"; } @datearray = split( /\s+/, scalar localtime( $picbase ) ), $datelabel = join ' ', splice( @datearray, 1, 5 ); ( $hour, $minute ) = ( split /:/, ( split /\s+/, $datelabel )[2] )[0 +,1]; if( $hour > 12 ){ $hour -= 12; $meridian = "pm" } else { $meridian = "am" } $datelabel =~ s/(\w+) (\d+) .* (\d+)/$1-$2-$3 $hour:$minute$meridian +/; $table .= " <a href=\"javascript:OpenWindow('$archdir/$picture' +," . "'$pwidth','$pheight','$datelabel')\">"; $table .= "<img src=\"$thmbdir/${picbase}_thumb.jpg\" border=\"0\" " . "alt=\"$datelabel\"></a><br>" . "$datelabel\n </td>\n"; $counter++; $abscounter++; if( $counter == $cols ){ $counter = 0; $rowcounter++; } } $table .="<tr><td align=\"left\" valign=\"bottom\">"; if( $start > 0 ){ $table .= build_link( $basename, 0, $scheme, $rows, $cols, $first ) . " " . build_link( $basename, ($start-$rows*$cols), $scheme +, $rows, $cols, $prev ); } else { $table .= "&nbsp;"; } $table .= "</td><td colspan=\"".($cols-2)."\">&nbsp;</td><td " . "align=\"right\" valign=\"bottom\">"; if( $rowcounter > ($rows-1) && $abscounter > $colcounter && ( $start + $rows * $cols ) < $abscounter ){ $table .= build_link( $basename, ($start+$rows*$cols), $scheme, $rows, $cols, $next ) . " " . build_link( $basename, ($abscounter-$rows*$cols), $s +cheme, $rows, $cols, $last ); } else { $table .= "&nbsp;"; } $table .= "</td></tr></table>\n</center>\n"; $end = ( $start + ($rows*$cols) > $abscounter ) ? $abscounter : ( $start + ($rows*$cols) ); $table .= "<div id=\"counttext\">pictures ".($colcounter+1) . " through $end of $abscounter</div>\n"; $table .= "<p></p>\n<div id=\"scheme\">view in |\n"; for( 'default', 'yellow', 'green', 'blue', 'teal', 'purple', 'pink', 'red', 'orange' ){ $table .= scheme_link( $_ ) . " |\n"; } $table .= "</div>\n"; $table .= "<p></p><div id=\"scheme2\">view in |\n"; for( [3,3], [4,4], [5,5], [6,6], [7,7], [8,8] ){ $table .= size_link( $_->[0], $_->[1] ) . " |\n"; } $table .= "</div>\n"; set_content(); print header(-cookie=>[$skincookie,$rowscookie,$colscookie]); print $header, $table, $footer; sub scheme_link { my $color = shift; return build_link( $basename, $start, $color, $rows, $cols, $color ) +; } sub size_link { my ( $r, $c ) = @_; return build_link( $basename, $start, $scheme, $r, $c, "${r}x$c" ); } sub build_link { my( $bas, $sta, $sch, $row, $col, $nam ) = @_; return "<a href=\"$bas?start=$sta&amp;scheme=$sch&amp;rows=$row" ."&amp;cols=$col" ."\">$nam</a>"; } sub set_content { ($stylesheet=<<" EOSTYLESHEET") =~ s/ //m; td { font-family: $cellfont; font-size: 7pt; width: ${cellwidth}px; height: ${cellheight}px; } td.validateright{ font-family: $cellfont; font-size: 7pt; width: ${cellwidth}px; height: ${cellheight}px; text-align: right; } td.validateleft{ font-family: $cellfont; font-size: 7pt; width: ${cellwidth}px; height: ${cellheight}px; text-align: left; } #counttext { font-family: $cellfont; font-size: 8pt; font-style: + italic; text-align: center; } #copyright { font-family: $cellfont; font-size: 6pt; color: #292 +929; } #copyright2{ font-family: $cellfont; font-size: 6pt; color: #292 +929; } #scheme { font-family: $cellfont; font-size: 6pt; color: #292 +929; text-align: center; } #scheme2 { font-family: $cellfont; font-size: 6pt; color: #292 +929; text-align: center; } body { background: $bodybg; } #main { border: solid 1px #000000; padding: 25px; background: $mainbg } table { border:0 ; height: ${THEIGHT}px; width: ${TWIDTH}px; background-color: $tblebg; } table.validator { border:0 ; height: 12px; width: ${TWIDTH}px; background-color: $bodybg; } EOSTYLESHEET ($javascript=<<" EOJAVASCRIPT") =~ s/ //m; function OpenWindow(url,wid,hig,txt){ var myhight=Number(hig)+60 newWindow=window.open('viewer.cgi?scheme=$scheme&' +'url='+url,'Photo','width='+wid+',' +'height='+myhight+',scrollbars=no,resizable=no,toolbar=no,loc +ation' +'=no,menubar=no,status=no') newWindow.focus() } EOJAVASCRIPT ($header=<<" EOHEADER") =~ s/ //gm; <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head><title>$title</title> <style type="text/css"> <!-- $stylesheet --> </style> <script language="javascript" type="text/javascript"> //<!-- $javascript //--> </script> </head> <body bgcolor="#ffffff"> <div id="main"> <center> <table border="0" cellpadding="$cellpad" cellspacing="$cellspc" bgcolor="$tblebg"> EOHEADER ($footer=<<" EOFOOTER") =~ s/ //gm; </div> <p></p> <div id="copyright" align="center"> all photographs, images, javascript, css and html copyright &copy; $year $author </div> <p></p> <div id="copyright2" align="center"> <a href="colophon.cgi">colophon</a> </div> <p></p> <center> <table class="validator"> <tr><td class="validateleft"> <a href="http://validator.w3.org/check?uri=referer"><img src="http://www.w3.org/Icons/valid-html401" alt="Valid HTML 4.01 Transitional" height="31" width="88" border="0"></a> </td><td class="validateright"> <a href="http://jigsaw.w3.org/css-validator/"><img style="border:0;width:88px;height:31px" src="http://jigsaw.w3.org/css-validator/images/vcss" alt="Valid CSS!"></a> </td></tr> </table> </center> </body> </html> EOFOOTER }

I could actually be persuaded to add this for myself, as I find that I have 'sets' of pictures of my own that it might be nice to 'sort' by - house pictures, construction pictures, party pictures, dog pictures, etc. :)



--chargrill
$,=42;for(34,0,-3,9,-11,11,-17,7,-5){$*.=pack'c'=>$,+=$_}for(reverse s +plit//=>$* ){$%++?$ %%2?push@C,$_,$":push@c,$_,$":(push@C,$_,$")&&push@c,$"}$C[$# +C]=$/;($#C >$#c)?($ c=\@C)&&($ C=\@c):($ c=\@c)&&($C=\@C);$%=$|;for(@$c){print$_^ +$$C[$%++]}