##################################################################### # EDIT THE EXPORT LINE BELOW TO FIT YOUR NEEDS, BUT DO NOT UNCOMMENT! # Export = macro gallery ##################################################################### # Plugin Name = 8t88's Photo Gallery v1.1 # Author = Marko Kleen # Copyright = GPL -> see http://www.8t88.de/ # Purpose = This plugin adds photo gallery functionality ##################################################################### # Plugin types are specified in the EXPORT line above. # global: Your plugin is applied on all output # articles: Your plugin is applied on all articles' output # responses: Your plugin is applied on guestbook entries and comments # override: Your plugin overrides one of 8t88's existing subroutines # macro: Your plugin adds a new macro with new functionality ##################################################################### use Image::ExifTool qw(:Public); # Create "galleries" table if non-existent yet ###################### if (join(' ', @tables) !~ /\bgalleries\b/i) { db_query('CREATE TABLE galleries (name, user, pass, sid)'); } ##################################################################### # Begin configurable variables ###################################### my $basedir = '../gallery/'; # My galleries' location my $expires = 60; # A session expires after ?? minutes? # End of configurable variables ##################################### ##################################################################### my $sid = int(param('sid')); my $scale = param('scale') || 1; my $action = param('action') || undef; my $gallery = param('gallery') || undef; my $image = param('image') || undef; my ($addon, $button, $imgx, $imgy, $pass, $res, $tmp, @galleries, @images, @tmp) = undef; # Clean up parameters ############################################### if ($gallery) { $gallery =~ s/.*\\|.*\/|.*\|//g; } if ($image) { $image =~ s/.*\\|.*\/|.*\|//g; } # Build gallery index (subdirectories in $basedir) ################## if ((-d $basedir) && opendir(DIR, $basedir)) { @galleries = sort(readdir(DIR)); closedir(DIR); } for ($tmp = (scalar(@galleries) - 1); $tmp >= 0; $tmp--) { if (($galleries[$tmp] =~ /^$|^\.{1,2}/) || !(-d $basedir.$galleries[$tmp])) { splice(@galleries, $tmp, 1); }} # Check $gallery parameter ########################################## if ($gallery && (join(' ', @galleries) !~ /\b$gallery\b/i)) { undef($gallery); } ##################################################################### # User authentication ############################################### if (!$user && $gallery && $sid) { # Invalid SID? ############## $tmp = (db_query('SELECT sid FROM galleries WHERE name=?', $gallery))[0]; if ($tmp !~ /$sid/) { ($gallery, $sid) = undef; } else { # Session expired? ########## if (($sid + ($expires * 60)) <= time()) { ($gallery, $sid) = undef; } } } elsif (!$user && param('name') && param('pass')) { # User login? @tmp = db_query('SELECT * FROM galleries WHERE name=?', $gallery); if (param('name') && param('pass')) { if ((param('name') eq $tmp[1]) && (crypt(param('pass'), $tmp[2]) eq $tmp[2])) { # Successful login -> update SIDs in DB as well! ############## @tmp = split(',', $tmp[3]); $sid = time(); push(@tmp, $sid); # Store new SID for ($tmp = (scalar(@tmp) - 1); $tmp >= 0; $tmp--) { if ((int($tmp[$tmp]) + ($expires * 60)) <= time()) { splice(@tmp, $tmp, 1); }} $tmp = join(',', @tmp); # Update SIDs in our DB ##### # Create new DB entry ######################################### @tmp = ($gallery, param('name'), crypt(param('pass'), make_salt()), $tmp); db_query('DELETE FROM galleries WHERE name=?', $gallery); db_query('INSERT INTO galleries VALUES (?, ?, ?, ?)', @tmp); }} if (!$sid) { $res = h3('Anmeldung fehlgeschlagen!').input({-type => 'button', -value => 'zurück', -onclick => 'history.back();'}); } } ##################################################################### # Administrative tasks ############################################## if ($user && $action) { if ($action eq 'set') { # Set a gallery image as default #### if (-f $basedir.$gallery.'/'.$image) { @tmp = ($gallery, undef, $image, undef); db_query('DELETE FROM galleries WHERE name=?', $gallery); db_query('INSERT INTO galleries VALUES (?, ?, ?, ?)', @tmp); ($gallery, $image) = undef; } } elsif ($action eq 'protect') { # Set password protection ######## if (param('name') && param('pass') && (param('pass') eq param('pass2'))) { $pass = crypt(param('pass'), make_salt()); @tmp = ($gallery, param('name'), $pass, undef); db_query('DELETE FROM galleries WHERE name=?', $gallery); db_query('INSERT INTO galleries VALUES (?, ?, ?, ?)', @tmp); ($gallery, $image) = undef; } else { # Show username and password dialogue if (param('pass2')) { $tmp = h3('Fehlerhafte Eingabe, bitte versuchen Sie es erneut!'); } else { $tmp = h3('Bitte vergeben Sie Benutzername und Passwort für "'.$gallery.'"'); } for (qw(action cat gallery)) { $res .= input({-type => 'hidden', -name => $_, -value => param($_)}); } $res = $tmp.$res.input({-name => 'name', -value => 'Benutzername', -onfocus => 'this.value="";', -onblur => 'if (!this.value) { this.value="Benutzername"; }'}).br.input({-name => 'pass', -type => 'password', -value => 'pass1', -onfocus => 'this.value="";', -onblur => 'if (!this.value) { this.value="pass1"; }'}).br.input({-name => 'pass2', -type => 'password', -value => 'pass2', -onfocus => 'this.value="";', -onblur => 'if (!this.value) { this.value="pass2"; }'}).br.br.input({-type => 'submit', -value => 'absenden'}).'  '.input({-type => 'button', -value => 'zurück', -onclick => 'self.location.href="?cat='.$cat.'";'}); return (table({-align => 'center'}, TR(th($res)))); } } elsif ($action eq 'unprotect') { # Remove password protection ... if (-d $basedir.$gallery) { db_query('DELETE FROM galleries WHERE name=?', $gallery); } ($gallery, $image) = undef; } elsif ($action eq 'update') { # Update image comments (if any) for (param()) { $tmp = ($basedir.$gallery.'/'.$_); if (-f $tmp) { $image = new Image::ExifTool; if ($image->ImageInfo($tmp, 'Comment') || param($_)) { $image->SetNewValue('Comment', (param($_) || undef)); $image->WriteInfo($tmp); }} ($image, $tmp) = undef; } } elsif ($action eq 'rotate') { # Rotate image (90° CW via Exif) $tmp = ($basedir.$gallery.'/'.$image); if (-f $tmp) { $image = new Image::ExifTool; $resx = $image->ImageInfo($tmp); $resx = $$resx{'Orientation'}; if ($resx =~ /normal/i) { $resx = 'Rotate 90 CW'; } elsif ($resx =~ /rotate.90/i) { $resx = 'Rotate 180'; } elsif ($resx =~ /rotate.180/i) { $resx = 'Rotate 270 CW'; } else { $resx = 'Horizontal (normal)'; } $image->SetNewValue('Orientation', $resx); $image->WriteInfo($tmp); ($image, $resx, $tmp) = undef; }}} ##################################################################### # Here are the photo gallery bits ... ############################### if (!$gallery) { # Generate Gallery Index View ####### if (!@galleries) { $res = div({-align => 'center'}, h3('Keine Galerien gefunden!'), input({-type => 'button', -value => 'zurück', -onclick => 'history.back();'})); } else { for (@galleries) { undef($image); ($tmp, $pass) = db_query('SELECT user,pass FROM galleries WHERE name=?', $_); ############################################################### if ($tmp && $pass) { # Restricted access ################# $image = a({-href => '?cat='.$cat.'&gallery='.$_, -style => 'display: block; padding: 56px 0 50px 0;', -title => 'Galerie "'.$_.'"'}, 'Private Galerie'); ############################################################### } elsif ($pass) { # We have a gallery index photo ##### if (-e $basedir.$_.'/'.$pass) { $image = ImageInfo($basedir.$_.'/'.$pass); $imgy = $$image{'ImageHeight'}; $imgx = $$image{'ImageWidth'}; if ($imgx > $imgy) { $addon = img({-src => '?cat='.$cat.'&gallery='.$_.'&preview=1&image='.$$image{'FileName'}, -border => 0, -alt => 'Galerie "'.$_.'"', -width => (160 * $scale), -height => int(($imgy / $imgx) * (160 * $scale))}); } else { $addon = img({-src => '?cat='.$cat.'&gallery='.$_.'&preview=1&image='.$$image{'FileName'}, -border => 0, -alt => 'Galerie "'.$_.'"', -height => (120 * $scale), -width => int(($imgx / $imgy) * (120 * $scale))}); } $image = a({-href => '?cat='.$cat.'&gallery='.$_, -title => 'Galerie "'.$_.'"'}, $addon); }} ############################################################### if (!$image) { # No index photo, public access ##### $image = a({-href => '?cat='.$cat.'&gallery='.$_, -style => 'display: block; padding: 56px 0 50px 0;', -title => 'Galerie "'.$_.'"'}, 'Öffentliche Galerie'); } # Add administrative bits ... ################################# if ($user) { if ($tmp && $pass) { $addon = a({-href => '?cat='.$cat.'&gallery='.$_.'&action=unprotect'}, 'Zugriff freigeben'); } else { $addon = a({-href => '?cat='.$cat.'&gallery='.$_.'&action=protect'}, 'Zugriff beschränken'); } $addon = div({-class => 'edit'}, $addon); } else { undef($addon); } ############################################################### $res .= div({-class => 'story', -style => 'float: left; margin: 10px;'}, $addon.div({-style => 'height: '.(120 * $scale).'px; width: '.(160 * $scale).'px; text-align: center; vertical-align: center;'}, $image), div({-class => 'story_info'}, $_)); }} ##################################################################### } else { # Fetch specified gallery ################### ($tmp, $pass) = db_query('SELECT user,pass FROM galleries WHERE name=?', $gallery); ################################################################### if (!$user && !$sid && $tmp && $pass) { # Unauthorized user tries to access a protected ressource ####### $res = div({-align => 'center'}, h3('Geschützer Bereich!'), input({-name => 'name', -value => 'Name', -onfocus => 'this.value="";', -onblur => 'if (!this.value) { this.value="Name"; }'}), br, input({-name => 'pass', -type => 'password', -value => '******', -onfocus => 'this.value="";', -onblur => 'if (!this.value) { this.value="******"; }'}), br, br, input({-type => 'submit', -value => 'anmelden', -style => 'margin-right: 25px;'}), input({-type => 'button', -value => 'Zurück', -onclick => 'self.location.href="?cat='.$cat.'";'}), input({-type => 'hidden', -name => 'gallery', -value => $gallery}), input({-type => 'hidden', -name => 'cat', -value => $cat})); } elsif ($image) { # Fetch an image! ############################################### if (-e $basedir.$gallery.'/'.$image) { $image = ImageInfo($basedir.$gallery.'/'.$image); undef($tmp); print header(-type => $$image{'MIMEType'}); # We want to save bandwidth in preview mode! Send a thumbnail! if (param('preview') && $$image{'ThumbnailImage'}) { print ${$$image{'ThumbnailImage'}}; } # No thumbnail found, or single picture mode ... ############## else { if (open(FILE, $basedir.$gallery.'/'.$$image{'FileName'})) { binmode(FILE); binmode($tmp); while (read FILE, $tmp, 10240) { print $tmp; } close(FILE); }} exit; } } else { # Build image index ############################################# if (opendir(DIR, $basedir.$gallery)) { @images = sort(readdir(DIR)); closedir(DIR); } for ($tmp = (scalar(@images) - 1); $tmp >= 0; $tmp--) { if (($images[$tmp] =~ /^$|^\.{1,2}/) || (-d $basedir.$gallery.'/'.$images[$tmp])) { splice(@images, $tmp, 1); }} if (!@images) { $res = h3('Die Galerie enthält keine Bilder!').input({-type => 'button', -value => 'zurück', -onclick => 'history.back();'}); } else { # Prepare SID for use in image index ... ###################### if ($sid) { $sid = '&sid='.$sid; } else { undef($sid); } # Check image scaling, create a button ######################## $tmp = $ENV{'QUERY_STRING'}; $tmp =~ s/\&sid=.*|\&scale=.*//i; if ($scale & 1) { $button = input({-type => 'button', -value => 'Große Bilder', -onclick => 'self.location.href="?'.$tmp.$sid.'&scale=2";', -style => 'margin-right: 10px;'}); } else { $button = input({-type => 'button', -value => 'Kleine Bilder', -onclick => 'self.location.href="?'.$tmp.$sid.'&scale=1";', -style => 'margin-right: 10px;'}); } # Display headline and picture count ########################## $res = div({-class => 'story', -style => 'margin: 5px 10px 0 10px; text-align: right;'}, h3({-style => 'display: inline; float: left; margin: 0;'}, 'Galerie "'.$gallery.'" ('.scalar(@images).' Bilder)').$button.input({-type => 'button', -value => 'Zur Übersicht', -onclick => 'self.location.href="?cat='.$cat.'";'})); if (scalar(@images) == 1) { $res =~ s/(bild)er(\))/$1$2/i; } # Start building image index ################################## for (@images) { $image = ImageInfo($basedir.$gallery.'/'.$_); $imgy = $$image{'ImageHeight'}; $imgx = $$image{'ImageWidth'}; if ($imgx > $imgy) { $tmp = ($imgx / $imgy); $imgx = (160 * $scale); $imgy = int($imgx / $tmp); } else { $tmp = ($imgx / $imgy); $imgy = (120 * $scale); $imgx = int($imgy * $tmp); } # Add administrative bits ... ################################# ($addon, $tmp) = undef; if ($user) { $addon = div({-class => 'edit'}, a({-href => '?cat='.$cat.'&gallery='.$gallery.'&image='.$_.'&action=rotate', -title => 'Das Bild wird um 90 Grad im Uhrzeigersinn gedreht'}, 'Bild drehen'), '::', a({-href => '?cat='.$cat.'&gallery='.$gallery.'&image='.$_.'&action=set'}, 'Galeriebild')); $tmp = input({-style => 'margin: 0; padding: 0; width: '.((160 * $scale) - 10).'px;', -value => ($$image{'Comment'} || undef), -name => $$image{'FileName'}}); } ############################################################### $res .= div({-class => 'story', -style => 'float: left; margin: 10px;'}, $addon.div({-style => 'height: '.(120 * $scale).'px; width: '.(160 * $scale).'px; text-align: center; vertical-align: center;'}, a({-href => '?cat='.$cat.'&gallery='.$gallery.$sid.'&image='.$$image{'FileName'}}, img({-alt => ($$image{'Comment'} || undef), -title => 'Aufnahmedatum: '.($$image{'DateTimeOriginal'} || 'nicht gespeichert'), -border => 0, -height => $imgy, -width => $imgx, -src => '?cat='.$cat.'&gallery='.$gallery.$sid.'&preview=1&image='.$$image{'FileName'}}))), div({-class => 'story_info', -style => 'text-align: center; overflow: hidden; width: '.((160 * $scale) - 10).'px;'}, ($tmp || $$image{'Comment'} || $$image{'FileName'}))); } # Administrative bits, part 2 ################################### if ($user) { $res = input({-type => 'hidden', -name => 'cat', -value => $cat}).input({-type => 'hidden', -name => 'action', -value => 'update'}).input({-type => 'hidden', -name => 'gallery', -value => $gallery}).$res.div({-class => 'story', -style => 'clear: left; margin: 10px; text-align: right;'}, input({-type => 'submit', -value => 'Änderungen übernehmen', -style => 'float: left;'}), $button, input({-type => 'button', -value => 'Zur Übersicht', -onclick => 'self.location.href="?cat='.$cat.'";'})); } else { $res .= div({-class => 'story', -style => 'clear: left; margin: 0 10px 5px 10px; text-align: right;'}, $button.input({-type => 'button', -value => 'Zur Übersicht', -onclick => 'self.location.href="?cat='.$cat.'";'})); }}}} ##################################################################### if ($res) { $res = start_form().$res.div({-style => 'clear: left;'}, '').end_form(); } return ($res); ##################################################################### # USAGE: # # To use this plugin simply drop it into your "8t88's CMS v2" plugins # directory. Edit the $basedir and $expires variables to fit your # needs. Inside "8t88's CMS v2" simply use the macro # to use this plugin. # # Enjoy! ##################################################################### # EOF