Author: MichaelDaum Date: 2005-09-14 10:46:28 -0700 (Wed, 14 Sep 2005) New Revision: 6359 Added: twiki/branches/DEVELOP/twikiplugins/ImageGalleryPlugin/lib/TWiki/Plugins/ImageGalleryPlugin/ twiki/branches/DEVELOP/twikiplugins/ImageGalleryPlugin/lib/TWiki/Plugins/ImageGalleryPlugin/Core.pm twiki/branches/DEVELOP/twikiplugins/ImageGalleryPlugin/lib/TWiki/Plugins/ImageGalleryPlugin/MANIFEST twiki/branches/DEVELOP/twikiplugins/ImageGalleryPlugin/pub/TWiki/ImageGalleryPlugin/style.css Removed: twiki/branches/DEVELOP/twikiplugins/ImageGalleryPlugin/pub/TWiki/ImageGalleryPlugin/twikiwide.gif Modified: twiki/branches/DEVELOP/twikiplugins/ImageGalleryPlugin/data/TWiki/ImageGalleryPlugin.txt twiki/branches/DEVELOP/twikiplugins/ImageGalleryPlugin/lib/TWiki/Plugins/ImageGalleryPlugin.pm Log: Item0: synced twiki.org with svn version of the ImageGalleryPlugin Modified: twiki/branches/DEVELOP/twikiplugins/ImageGalleryPlugin/data/TWiki/ImageGalleryPlugin.txt =================================================================== --- twiki/branches/DEVELOP/twikiplugins/ImageGalleryPlugin/data/TWiki/ImageGalleryPlugin.txt 2005-09-14 17:01:37 UTC (rev 6358) +++ twiki/branches/DEVELOP/twikiplugins/ImageGalleryPlugin/data/TWiki/ImageGalleryPlugin.txt 2005-09-14 17:46:28 UTC (rev 6359) @@ -1,131 +1,194 @@ -%META:TOPICINFO{author="RobertSuna" date="1075560360" format="1.0" version="1.3"}% -%META:TOPICPARENT{name="TWikiPreferences"}% ----++ TWiki Image Gallery Plugin - -Pages containing ==%<nop>IMAGEGALLERY%== variables are expanded at view time, providing a thumbnailed image gallery for those images attached to a wiki page. Thumbnails are created automatically and subsequently cached. Display is simple ( trivial--probably not really good enough, although each picture is surrounded by a =span= with a class of =imgGallery= ) - - * size => =small=, *<tt>medium</tt>*, =large=, =xlarge=, =huge= - * web => _web_; default is *%<nop>WEB%* - * topic => _topic_; default is *%<nop>TOPIC%* - * format => how to format each image (see [[#VariAbles][Variables]]), default format is: -<verbatim> - <span class="imgGallery"><a href="$imageurl"><img src="$thumburl" title="$sizeK: $comment"/></a></span>$n -</verbatim> - * options => additionaly oprins for the CONVERT program, default is "" (empty) - * columns => number of columns default is "0" which means infinity%BR% - if set to some number, the following sequence is generated:%BR% - __$rowstart$format$rowinside$format ... $format$rowend__ - * rowstart => formatted text at start of a row if columns > 0, default is "" (empty) - * rowinside => formatted text inside of a row if columns > 0, default is "" (empty) - * rowend => formatted text at end of a row if columns > 0, default is "" (empty) - * rowinsideempty => formatted text inside of a row if columns > 0 and no more images, default is "" (empty) - * rowendempty => formatted text at end of a row if columns > 0 and no more images, default is "" (empty) - -#VariAbles ----+++ Variables -Variables are recognized and replaced in the format strings: _format_, _rowstart_, _rowinside_, _rowend_, _rowinsideempty_, _rowendempty_ - * ==$web== the *%<nop>WEB%* the image is from - * ==$topic== the *%<nop>TOPIC%* the image is from - * ==$width== width in pixels of the original image - * ==$height== height in pixels of the original image - * ==$version== version number of the ioriginal mage - * ==$name== complete name of the original image (no path) - * ==$sizeK== size of the original image in kilobytes (example: 8k) - * ==$size== size of the original image in bytes (example: 8234) - * ==$comment== the comment of the image attachment - * ==$wikiusername== the WikiName of the user checked in the image - * ==$username== the user name of the user checked in the image - * ==$thumburl== the complete URL of the thumbnail image - * ==$imageurl== the complete URL of the original image - * ==$date{...}== the check in date of the original image, can be formatted like *%<nop>GMTIME%* (see %TWIKIWEB%.TWikiVariables). The default format is like (if no {} spezified) "31 Dec 2002 - 19:30" - * ==$n== insert a line break - * ==$imgnr== number of the image (starts at 1) - ----+++ Examples - -=%<nop>IMAGEGALLERY%<br/>= -%IMAGEGALLERY% - -=%<nop>IMAGEGALLERY{ web="%<nop>WEB%" topic="%<nop>TOPIC%" size="small" }%<br/>= -%IMAGEGALLERY{ web="%WEB%" size="small" }% - -=%<nop>IMAGEGALLERY{ web="Saneasylum" topic="ArtworkGallery" size="x50" }%<br/>= -%IMAGEGALLERY{ web="Saneasylum" topic="ArtworkGallery" size="x50" }% - -Generates a table with 4 rows, images are centerd and with a tooltip. Below each image is a simple description line with a image number. -<verbatim> -%IMAGEGALLERY{columns="4" rowstart="| " rowinside=" | " rowend=" |$n" rowinsideempty=" |" rowendempty=" |$n" - format="<span class='imgGallery'><a href='$imageurl'><img src='$thumburl' title='$tooltip'/></a></span><br/>$imgnr: $comment"}% -</verbatim> -%BR% -%IMAGEGALLERY{columns="4" rowstart="| " rowinside=" | " rowend=" |$n" rowinsideempty=" |" rowendempty=" |$n" format="<span class='imgGallery'><a href='$imageurl'><img src='$thumburl' title='$tooltip'/></a></span><br/>$imgnr: $comment"}% - -Format the Images as a list, images have tool tips. -<verbatim> -%IMAGEGALLERY{format=" * <span class='imgGallery'><a href='$imageurl'><img src='$thumburl' title='$tooltip'/></a></span> $wikiusername $date$n"}% -</verbatim> -%IMAGEGALLERY{format=" * <span class='imgGallery'><a href='$imageurl'><img src='$thumburl' title='$tooltip'/></a></span> $wikiusername $date$n"}% - -Format the Images as a list, images have tool tips and customized date. -<verbatim> -%IMAGEGALLERY{format=" * <span class='imgGallery'><a href='$imageurl'><img src='$thumburl' title='$tooltip'/></a></span> $wikiusername $date{$year$mo$day-$hour:$min:$sec}$n"}% -</verbatim> -%IMAGEGALLERY{format=" * <span class='imgGallery'><a href='$imageurl'><img src='$thumburl' title='$tooltip'/></a></span> $wikiusername $date{$year$mo$day-$hour:$min:$sec}$n"}% - - ----++ <nop>%TOPIC% Settings - -Plugin settings are stored as preferences variables. To reference -a plugin setting write ==%<nop><plugin>_<setting>%==, i.e. ==%<nop>IMAGEGALLERYPLUGIN_SHORTDESCRIPTION%== - - * Names and sizes for thumbnails (feel free to add additional entries or to modify these defaults) - * Set TINY = 25x25 - * Set SMALL = 50x50 - * Set MEDIUM = 95x95 - * Set LARGE = 150x150 - * Set XLARGE = 250x250 - - * Name and location of program used to generate thumbnails - * Set IMAGE_MAGICK = /usr - * Set CONVERT = /usr/bin/convert - * Set IDENTIFY = /usr/bin/identify - - * Additionaly program options for the CONVERT program - * Set CONVERT_OPTIONS = "" - - * One line description, is shown in the %TWIKIWEB%.TextFormattingRules topic: - * Set SHORTDESCRIPTION = Displays image gallery with auto-generated thumbnails for attachments. - - * Debug plugin: (See output in =data/debug.txt=) - * Set DEBUG = 0 - - ----++ Plugin Installation Instructions - - * Download the ZIP file from the <nop>%TOPIC% home - * Unzip == ImageGalleryPlugin.zip== in your twiki installation directory. Content: - | *File:* | *Description:* | - | ==data/TWiki/%TOPIC%.txt== | Plugin topic | - | ==data/TWiki/%TOPIC%.txt,v== | Plugin topic repository | - | ==lib/TWiki/Plugins/%TOPIC%.pm== | Plugin Perl module | - | ==pub/TWiki/%TOPIC%/twikiwide.gif== | test image for this page | - ----++ Plugin Info - -| Plugin Author: | TWiki:Main/WillNorris | -| Plugin Version: | 31 Jan 2004 (V1.1) | -| Change History: | <!-- versions below in reverse order --> | -| 31 Jan 2004 | Add format fields and variable substitution (autor TWiki:Main.RobertSuna) | -| 1 Aug 2003 | Updates from feedback from a Windows installation | -| 25 Jul 2003 | Initial version posted to TWiki.org | -| 15 Mar 2002 | Initial (internal) version | -| CPAN Dependencies: | none | -| Other Dependencies: | [[http://www.imagemagick.org/][ImageMagick]]'s =convert= and =identify= | -| Perl Version: | 5 something ? (not sure--i'm using 5.8.0) | -| Plugin Home: | http://TWiki.org/cgi-bin/view/Plugins/%TOPIC% | -| Feedback: | http://TWiki.org/cgi-bin/view/Plugins/%TOPIC%Dev | - -__Related Topics:__ %TWIKIWEB%.TWikiPreferences, %TWIKIWEB%.TWikiPlugins - --- Main.WillNorris - 1 Aug 2003 <br> -%META:FILEATTACHMENT{name="twikiwide.gif" attr="" comment="" date="1059773705" path="twikiwide.gif" size="5850" user="WillNorris" version="1.1"}% +%META:TOPICINFO{author="MichaelDaum" date="1126719428" format="1.0" version="1.13"}% +%META:TOPICPARENT{name="WebHome"}% +---+!! <nop>ImageGalleryPlugin +%TOC% +---++ Description +This plugin helps you in viewing pictures that are attached to a topic in a nice thumbnail +gallery. Clicking on a thumbnail zooms the image. You might navigate within your images using +a navigation toolbar to visit the next, previous, first or last image in your gallery. +Images and thumbnails are resized according to the given settings and are cached thereafter. +The order of images can be customized by putting a sequence number into the attachment comment. +An image attachment can be managed by clicking on the red dot in its title. +The plugin will generate the document relations "parent", "first", "last", "next", "previous" +for each image to improve navigation (only a few browsers support this, e.g. mozilla). +The image gallery is highly customizable using cascading stylesheets and html formats (see +below). + +---++ Example +%IMAGEGALLERY{"TWiki.TWikiLogos"}% +There are %NRIMAGES{"TWiki.TWikiLogos"}% images at TWiki.TWikiLogos. +<style type="text/css"> +.igp { border:1px dashed gray; margin:0 auto;} +</style> + +---++ Syntax +---+++ Arguments +This plugin introduces two tags: + * %<nop>IMAGEGALLERY{...}%: render an image gallery + * %<nop>NRIMAGES%: returns the number of images attachted to a (list of) topics +that both take the following arguments: + +| *Argument* | *Description* | +| topic | comma separated list of topics whose attachments should be displayed %BR% \ + (default: the current topic) | +| size | the thumbnail size, the actual geometries of a size can be configured below; \ + possible values: tiny, small, medium, large, huge \ + (default: medium) | +| columns | number of thumbnais to be displayed in one row of the gallery %BR% \ + (default: 4) | +| docrels | flag to disable/enable document relations between images; \ + possible values are on, off, 0, 1 (default: on) | +| maxwidth | maximal display width of an image; images are scaled not to exceed this limit \ + while preserving aspect ratio %BR% \ + (default: 640) | +| maxheight | maximal display height of an image %BR% (default: 480) | +| minwidth | minimal display width of an image; images are scaled not to fall below this \ + limit while preserving aspect ratio %BR% (default: 0) | +| minheight | minimal display height of an image %BR% (default: 0) | +| format | html format of an image %BR% \ + (default: <a href="$origurl"><img src="$imageurl" title="$comment" width="$width" height="$height"/></a>) | +| titles | toggles image and thumnail titles on and off %BR% \ + (default: on) | +| title | html format of an image title; "off" will hide the image's title %BR% \ + (default: $comment ($imgnr/$nrimgs) $reddot) | +| thumbtitle | html format of a thumbnail title; "off" will hide the thumbnail's title %BR% \ + (default: $comment $reddot) | + +Note, that only =topic= is meaningful to %<nop>NRIMAGES%. + +---+++ Variables +The parameters "format", "title" and "thumbtitle" can use the following variables to +refer to image properties: + +| *Variable* | *Description* | +| $web | the web the image is located | +| $topic | the topic the image is located | +| $nrimages | the total number of images in the gallery | +| $n | a linefeed | +| $reddot | render a red-dot anchor to access the attachment | +| $width | the display width of the image | +| $height | the display height of the image | +| $thumbwidth | the thumbnail width of the image | +| $thumbheight | the thumbnail height of the image | +| $origwidth | the original width of the image | +| $origheight | the original height of the image | +| $size | the size of the image | +| $sizeK | the size of the image in kilobytes | +| $comment | the comment of the image (with its sequence number stripped off) | +| $imgnr | the sequence number of the image | +| $date | the date when the image has been uploaded | +| $version | the version of the image attachment | +| $name | the filename of the image | +| $wikiusername | the wikiusername who uploaded the image | +| $username | the username who uploaded the image | +| $imageurl | the url path of the display image | +| $thumburl | the url path of the thumbnail image | +| $origurl | the url path pointing to the original image attachment | + +---+++ Query Parameters +The IGP will check for certain query parameters in the url. These are: + * id=<n;>: distinguish multiple IMAGEGALLERYs in the same topic + * filename=<name>: display the given file of the image set + * refresh=on: recompute all images and thumbnails in the IGP cache +Each IMAGEGALLERY has an anchor, namely a <a name="igp<n>"/>, that +is used to scroll the display appropriately. + +---++ Customization +First of all this plugin should come with reasonable default values, so that +you will get a nice image gallery (with regards to my taste) right on. +There are several things that are customizable: + * customize a gallery using a compination of the arguments described above + * add custom cascading styles in a topic to format the gallery + * alter the default cascading styles in the [[%ATTACHURL%/styles.css][styles.css]] + file attached to this topic + * customize the plugin settings below + +---+++ Cascading Stylesheets +The plugin emits the following css classes (see the comments in the [[%ATTACHURL%/styles.css][styles.css]] file): + +<noautolink> +| *Class* | *Description* | +| igp | division for the complete output of the image gallery | +| igpThumbNails | division of all thumbnails | +| igpThumbNailsTable | table of thumbnail images | +| igpThumbNail | table cell of one thumbnail image | +| igpThumbNailTitle | table cell of the thumbnail title | +| igpPictureTable | table of the display image and the navigation bar | +| igpPicture | table cell of the display image | +| igpPictureTitle | division of the title of the display image | +| igpNavigation | table cell of the navigation bar | +| igpRedDot | span surrounding the red dot | +| igpAlert | span surrounding error messages | +</noautolink> + +---+++ Adjusting the image sequence +By default the image gallery will display images in the order that they where uploaded. +This determines their _natural_ order. When you want to change this order you can +prefix the comment of the attachment with a sequence number. For example: given a +picture whose comment is "My old bike" that should be put at position 12 +in your image gallery then change the comment to "12 - My old bike". This +will put the image at the desired position. Sequence numbers don't necessarily need +to be unique, that is if there's another picture with a comment like +"12 - My new bike" then the natural order of the both is relevant. In general, +a picture will be put at position =<nr>= if its attachment comment has the format +<verbatim> + <nr><space>-<space><comment> +</verbatim> +Note, that the $comment variable will only display =<comment>= , that is with +=<nr><space>-<space>= stripped off. + +---++ Plugin Settings + + * Names and sizes for thumbnails (feel free to add additional entries or to modify these defaults) + * Set TINY = 25x25 + * Set SMALL = 50x50 + * Set MEDIUM = 95x95 + * Set LARGE = 150x150 + * Set HUGE = 250x250 + + * cascading style sheets + * Set STYLE = %PUBURLPATH%/%TWIKIWEB%/ImageGalleryPlugin/style.css + + * One line description: + * Set SHORTDESCRIPTION = Displays image gallery with auto-generated thumbnails from attachments. + +---++ Plugin Installation Instructions + + * Download the ZIP file from the <nop>%TOPIC% home + * Unzip == ImageGalleryPlugin.zip== in your twiki installation directory. Content: + | *File:* | *Description:* | + | ==data/TWiki/%TOPIC%.txt== | plugin topic | + | ==pub/TWiki/%TOPIC%/style.css== | cascading style sheets | + | ==lib/TWiki/Plugins/%TOPIC%.pm== | plugin interface | + | ==lib/TWiki/Plugins/%TOPIC%/Core.pm== | core implementation | + +---++ Plugin Info + +| Plugin Author: | TWiki:Main/MichaelDaum | +| Plugin Version: | v3.1 (14 Sep 2005) | +| Change History: | <!-- versions below in reverse order --> | +| 14 Sep 2005 | don't strictly depend on normalizeFilename() | +| 15 June 2005 | new release | +| 10 May 2005 | fixed igp id clash error found by TWiki:Main/MartinCleaver; \ + capture <nop>ImageMagick error messages; \ + don't trust attachment info - check for attachment existence aswell ;\ + added refresh query parameter to recompute images | +| 09 May 2005 | fixed errors reported by TWiki:Main/MarcelTrap; \ + added support for multiple topics (proposed by TWiki:Main/MartinCleaver; \ + added %<nop>NRIMAGES% tag | +| | fix error when called from within rename view | +| 03 May 2005 | fixed thumbnail resizing; reintroduced =titles= parameter; \ + support for multiple galleries per topic | +| 27 Apr 2005 | complete rewrite | +| 1 Aug 2003 | Updates from feedback from a Windows installation | +| 25 Jul 2003 | Initial version posted to TWiki.org | +| 15 Mar 2002 | Initial (internal) version | +| CPAN Dependencies: | Image::Magick | +| Other Dependencies: | | +| Perl Version: | 5 something ? (not sure--i'm using 5.8.0) | +| Plugin Home: | TWiki:Plugins/%TOPIC% | +| Feedback: | TWiki:Plugins/%TOPIC%Dev | + +-- TWiki:Main.MichaelDaum - 14 Sep 2005 +%META:FILEATTACHMENT{name="ImageGalleryPlugin.zip" attr="" comment="v3.0 (15 June 2005)" date="1118829177" path="ImageGalleryPlugin.zip" size="12781" user="MichaelDaum" version="1.7"}% +%META:FILEATTACHMENT{name="style.css" attr="" comment="" date="1114698585" path="style.css" size="1039" user="MichaelDaum" version="1.1"}% Property changes on: twiki/branches/DEVELOP/twikiplugins/ImageGalleryPlugin/data/TWiki/ImageGalleryPlugin.txt ___________________________________________________________________ Name: svn:eol-style - native Added: twiki/branches/DEVELOP/twikiplugins/ImageGalleryPlugin/lib/TWiki/Plugins/ImageGalleryPlugin/Core.pm =================================================================== --- twiki/branches/DEVELOP/twikiplugins/ImageGalleryPlugin/lib/TWiki/Plugins/ImageGalleryPlugin/Core.pm 2005-09-14 17:01:37 UTC (rev 6358) +++ twiki/branches/DEVELOP/twikiplugins/ImageGalleryPlugin/lib/TWiki/Plugins/ImageGalleryPlugin/Core.pm 2005-09-14 17:46:28 UTC (rev 6359) @@ -0,0 +1,902 @@ +# +# TWiki WikiClone ($wikiversion has version info) +# +# Copyright (C) 2002-2003 Will Norris. All Rights Reserved. (wb...@sa...) +# Copyright (C) 2005 Michael Daum <mi...@na...> +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details, published at +# http://www.gnu.org/copyleft/gpl.html +# +# ========================= +package TWiki::Plugins::ImageGalleryPlugin::Core; + +use strict; + +# ========================= +# constructor +sub new { + my ($class, $id, $topic, $web) = @_; + my $this = bless({}, $class); + + eval 'use Image::Magick;'; + + # init + $this->{id} = $id; + $this->{query} = &TWiki::Func::getCgiQuery(); + $this->{debug} = 0; + $this->{mage} = new Image::Magick; + $this->{isDakar} = defined $TWiki::cfg{MimeTypesFileName}; + $this->{topic} = $topic; + $this->{web} = $web; + $this->{doRefresh} = 0; + $this->{errorMsg} = ''; # from image mage + + # get style url + my $hostUrl = ($this->{isDakar})? $TWiki::cfg{DefaultUrlHost}:$TWiki::defaultUrlHost; + + $this->{styleUrl} = TWiki::Func::getPreferencesValue("IMAGEGALLERYPLUGIN_STYLE") || + $hostUrl . &TWiki::Func::getPubUrlPath() . "/" . &TWiki::Func::getTwikiWebname() . + "/ImageGalleryPlugin/style.css"; + + # get image mimes + my $mimeTypesFilename = ($this->{isDakar})? + $TWiki::cfg{MimeTypesFileName}:$TWiki::mimeTypesFilename; + my $fileContent = &TWiki::Func::readFile($mimeTypesFilename); + $this->{isImageSuffix} = (); + foreach my $line (split(/\r?\n/, $fileContent)) { + next if $line =~ /^#/; + next if $line =~ /^$/; + next unless $line =~ /^image/;# only image types + next unless $line =~ /^\s*[^\s]+\s+(.*)\s*$/; + foreach my $suffix (split(/ /, $1)) { + $this->{isImageSuffix}{$suffix} = 1; + } + } + + my $topicPubDir = $this->normalizeFileName(&TWiki::Func::getPubDir() . "/$web/$topic"); + mkdir $topicPubDir unless -d $topicPubDir; + + if ($this->{id}) { + $this->{igpDir} = $this->normalizeFileName("$topicPubDir/igp$this->{id}"); + mkdir $this->{igpDir} unless -d $this->{igpDir}; + + $this->{igpPubUrl} = &TWiki::Func::getPubUrlPath() . + "/$this->{web}/$this->{topic}/igp$this->{id}"; + $this->{infoFile} = $this->normalizeFileName("$this->{igpDir}/info.txt"); + } + + return $this; +} + +# ========================= +sub debug { + my $this = shift; + + $this->{debug} = shift if @_; + return $this->{debug}; +} + +# ========================= +# debug logger +sub writeDebug { + my $this = shift; + return unless $this->{debug}; + &TWiki::Func::writeDebug("ImageGallery - $_[0]"); +} + +# ========================= +# test if a file is an image +sub isImage { + my ($this, $attachment) = @_; + + my $suffix = ''; + if ($attachment->{name} =~ /\.(.+)$/) { + $suffix = lc($1); + } + + return defined $this->{isImageSuffix}{$suffix}; +} + +# ========================= +sub init { + my ($this, $args) = @_; + + $args = '' unless $args; + $this->writeDebug("init($args) called"); + + # read attributes + $this->{size} = &TWiki::Func::extractNameValuePair($args, "size") || 'medium'; + my $thumbsize = TWiki::Func::getPreferencesValue(uc "IMAGEGALLERYPLUGIN_$this->{size}") + || $this->{size}; + my $thumbwidth = 95; + my $thumbheight = 95; + if ($thumbsize =~ /^(.*)x(.*)$/) { + $thumbwidth = $1; + $thumbheight = $2; + } elsif ($thumbsize =~ /^x(.*)$/) { + $thumbwidth = $1; + $thumbheight = $1; + } elsif ($thumbsize =~ /^(.*)x$/) { + $thumbwidth = $1; + $thumbheight = $1; + } + $this->{thumbwidth} = $thumbwidth; + $this->{thumbheight} = $thumbheight; + + $this->writeDebug("size=$this->{size} thumbsize=$thumbsize thumbwidth=$thumbwidth thumbheight=$thumbheight"); + + my $topics = + &TWiki::Func::extractNameValuePair($args) + || &TWiki::Func::extractNameValuePair($args, "topic") + || &TWiki::Func::extractNameValuePair($args, "topics") + || "$this->{web}.$this->{topic}"; + + $this->{topics} = undef; + + # normalize topic names + my $topicRegex = &TWiki::Func::getRegularExpression('mixedAlphaNumRegex'); + my $webRegex = &TWiki::Func::getRegularExpression('webNameRegex'); + foreach my $theTopic (split(/,\s*/, $topics)) { + my $theWeb; + if ($theTopic =~ /^($webRegex)\.($topicRegex)$/) { + $theWeb = $1; + $theTopic = $2; + } elsif ($theTopic =~ /^($topicRegex)$/) { + $theWeb = $this->{web}; + } else { + $this->writeDebug("oops, skipping $theTopic"); + next; + } + push @{$this->{topics}}, "$theWeb.$theTopic"; + } + + if (!$this->{topics}) { + $this->writeDebug("oops, no topics found"); + return 0; + } + $this->writeDebug("topics=" . join(", ", @{$this->{topics}})); + + + $this->{columns} = &TWiki::Func::extractNameValuePair($args, "columns") || 4; + + $this->{doDocRels} = &TWiki::Func::extractNameValuePair($args, "docrels") || 1; + $this->{doDocRels} = ($this->{doDocRels} eq "off")?0:1; + + $this->{maxheight} = &TWiki::Func::extractNameValuePair($args, "maxheight") || 480; + $this->{maxwidth} = &TWiki::Func::extractNameValuePair($args, "maxwidth") || 640; + + $this->{minheight} = &TWiki::Func::extractNameValuePair($args, "minheight") || 0; + $this->{minheight} = $this->{maxheight} if $this->{minheight} > $this->{maxheight}; + + $this->{minwidth} = &TWiki::Func::extractNameValuePair($args, "minwidth") || 0; + $this->{minwidth} = $this->{maxwidth} if $this->{minwidth} > $this->{maxwidth}; + + $this->{format} = &TWiki::Func::extractNameValuePair($args, "format") || + '<a href="$origurl"><img src="$imageurl" title="$comment" width="$width" height="$height"/></a>'; + + $this->{title} = &TWiki::Func::extractNameValuePair($args, "title") || '$comment ($imgnr/$nrimgs) $reddot'; + $this->{doTitles} = ($this->{title} eq 'off')?0:1; + + $this->{thumbtitle} = &TWiki::Func::extractNameValuePair($args, "thumbtitle") || '$comment $reddot'; + $this->{doThumbTitles} = ($this->{thumbtitle} eq 'off')?0:1; + + $this->{titles} = &TWiki::Func::extractNameValuePair($args, "titles"); + if ($this->{titles}) { + $this->{doTitles} = ($this->{titles} eq 'off')?0:1; + $this->{doThumbTitles} = $this->{doTitles}; + } + + my $refresh = $this->{query}->param("refresh") || ''; + $this->{doRefresh} = ($refresh eq 'on')?1:0; + + return 1; +} + +# ========================= +# main +sub render { + my ($this, $args) = @_; + + if (!$this->init($args)) { + return &renderError("can't initialize from '$args'"); + } + + $this->getImages(); + $this->readInfo(); + + # delete lost images + foreach my $entry (values %{$this->{info}}) { + next if $entry->{type} eq 'global'; + my $found = 0; + foreach my $image (@{$this->{images}}) { + if ($image->{name} eq $entry->{name}) { + $found = 1; + last; + } + } + next if $found; + my $img = "$this->{igpDir}/$entry->{name}"; + my $thumb = "$this->{igpDir}/thumb_$entry->{name}"; + + $img = $this->normalizeFileName($img); + $thumb = $this->normalizeFileName($thumb); + + unlink $img; + unlink $thumb; + + delete $this->{info}{$entry->{name}}; + } + + + # check for changes + $this->{infoChanged} = 1 + if !$this->{info}{thumbwidth} || + !$this->{info}{thumbheight} || + !$this->{info}{topics} || + $this->{info}{thumbwidth}{value} ne $this->{thumbwidth} || + $this->{info}{thumbheight}{value} ne $this->{thumbheight} || + join(', ', $this->{info}{topics}{value}) ne join(', ', @{$this->{topics}}); + + my $result = "<div class=\"igp\"><a name=\"igp$this->{id}\"/>"; + + # get filename query string + my $filename = $this->{query}->param("filename"); + my $id = $this->{query}->param("id") || ''; + + if ($id eq $this->{id} && $filename) { + # picture mode + $result .= $this->renderImage($filename); + } else { + # thumbnails mode + $result .= $this->renderThumbnails(); + } + + # add style + $result .= "</div><style type=\"text/css\">\@import url(\"$this->{styleUrl}\");</style>\n"; + + $this->writeInfo(); + return $result; +} + +# ========================= +# display one image +sub renderImage { + my ($this, $filename) = @_; + + $this->writeDebug("renderImage($filename)"); + + my $result = ''; + + my $firstFile; + my $lastFile; + my $nextFile; + my $thisImage; + my $prevFile; + + my $state = 0; + + # find the first, prev, this, next and last image in the list + # relative to the current filename + foreach my $image (@{$this->{images}}) { + $state = 3 if $state == 2; + $state = 2 if $state == 1; + $state = 1 if $image->{name} eq $filename; + + $firstFile = $image->{name} if ! $firstFile; + $prevFile = $image->{name} if $state == 0; + $thisImage = $image if $state == 1; + $nextFile = $image->{name} if $state == 2; + $lastFile = $image->{name}; + } + return &renderError("unknown file $filename") if !$thisImage; + + my $viewUrl = &TWiki::Func::getViewUrl($this->{web}, $this->{topic}); + + # document relations + if ($this->{doDocRels}) { + $result .= + "<link rel=\"parent\" href=\"$viewUrl\" title=\"Thumbnails\" />\n"; + if ($firstFile && $firstFile ne $filename) { + $result .= + "<link rel=\"first\" href=\"$viewUrl?id=$this->{id}&filename=$firstFile#igp$this->{id}\" title=\"$firstFile\" />\n"; + } + if ($lastFile && $lastFile ne $filename) { + $result .= + "<link rel=\"last\" href=\"$viewUrl?id=$this->{id}&filename=$lastFile#igp$this->{id}\" title=\"$lastFile\" />\n"; + } + if ($nextFile && $lastFile ne $filename) { + $result .= + "<link rel=\"next\" href=\"$viewUrl?id=$this->{id}&filename=$nextFile#igp$this->{id}\" title=\"$nextFile\" />\n"; + } + if ($prevFile && $firstFile ne $filename) { + $result .= + "<link rel=\"previous\" href=\"$viewUrl?id=$this->{id}&filename=$prevFile#igp$this->{id}\" title=\"$prevFile\" />\n"; + } + } + + # collect image information + $this->computeImageSize($thisImage); + if (!$this->createImg($thisImage)) { + return &renderError($this->{errorMsg}); + } + + # title + if ($this->{doTitles}) { + $result .= "<div class=\"igpPictureTitle\"><h2>" + . $this->replaceVars($this->{title}, $thisImage) + . "</h2></div>\n"; + } + + # layout img table + $result .= "<table class=\"igpPictureTable\">\n"; + + # navi + $result .= "<tr><td class=\"igpNavigation\">"; + if ($firstFile && $firstFile ne $filename) { + $result .= "<a href=\"$viewUrl?id=$this->{id}&filename=$firstFile#igp$this->{id}\">first</a>"; + } else { + $result .= "first"; + } + $result .= ' | '; + if ($prevFile) { + $result .= "<a href=\"$viewUrl?id=$this->{id}&filename=$prevFile#igp$this->{id}\">prev</a>"; + } else { + $result .= "prev"; + } + $result .= " | [[$this->{web}.$this->{topic}][up]] | "; + if ($nextFile) { + $result .= "<a href=\"$viewUrl?id=$this->{id}&filename=$nextFile#igp$this->{id}\">next</a>"; + } else { + $result .= "next"; + } + $result .= ' | '; + if ($lastFile && $lastFile ne $filename) { + $result .= "<a href=\"$viewUrl?id=$this->{id}&filename=$lastFile#igp$this->{id}\">last</a>"; + } else { + $result .= "last"; + } + $result .= "</td></tr>\n"; + + # img + $result .= "<tr><td class=\"igpPicture\">" + . $this->replaceVars($this->{format}, $thisImage) + . "</td></tr></table>\n"; + + return $result; +} + +# ========================= +sub renderThumbnails { + + my $this = shift; + + $this->writeDebug("renderThumbnails()"); + + if (!@{$this->{images}}) { + my $msg = "no images found"; + $this->writeDebug($msg); + return &renderError($msg); + } + + my $maxCols = $this->{columns}; + my $result = "<div class=\"igpThumbNails\"><table class=\"igpThumbNailsTable\"><tr>\n"; + my $imageNr = 0; + my @rowOfImages = (); + foreach my $image (@{$this->{images}}) { + $this->computeImageSize($image); + + if ($this->{doThumbTitles}) { + push @rowOfImages, $image; + } + + $result .= "<td width=\"" . (100 / $maxCols) . "%\" class=\"igpThumbNail\"><a href=\"" + . &TWiki::Func::getScriptUrl($this->{web}, $this->{topic}, "view") + . "?id=$this->{id}&filename=$image->{name}#igp$this->{id}\">" + . "<img src=\"$this->{igpPubUrl}/thumb_$image->{name}\" " + . "title=\"$image->{IGP_comment}\" alt=\"$image->{name}\"/></a></td>\n"; + + if (!$this->createImg($image, 1)) { + return &renderError($this->{errorMsg}); + } + + $imageNr++; + if ($imageNr % $maxCols == 0) { + $result .= "</tr>\n"; + if ($this->{doThumbTitles}) { + $result .= $this->renderTitleRow(\@rowOfImages); + @rowOfImages = (); + } + } + } + while ($imageNr % $maxCols != 0) { + $result .= "<td> </td>\n"; + $imageNr++; + } + $result .= "</tr>\n"; + if ($this->{doThumbTitles}) { + $result .= $this->renderTitleRow(\@rowOfImages); + } + $result .= "</table></div>\n"; + + return $result; +} + +# ========================= +sub renderTitleRow { + + my ($this, $images) = @_; + + my $result = '<tr>'; + + my $imageNr = 0; + foreach my $image (@$images) { + $result .= + "<td class=\"igpThumbNailTitle\">" . + $this->replaceVars($this->{thumbtitle}, $image) . + "</td>\n"; + $imageNr++; + } + my $maxCols = $this->{columns}; + while ($imageNr % $maxCols != 0) { + $result .= "<td> </td>"; + $imageNr++; + } + $result .= "</tr>\n"; + + return $result; +} + +# ========================= +sub renderRedDot { + my ($this, $image) = @_; + + return + "<span class=\"igpRedDot\"><a href=\"" + . &TWiki::Func::getScriptUrl($image->{IGP_web}, $image->{IGP_topic}, "attach") + . "?filename=$image->{name}\">.</a></span>"; +} + +# ========================= +sub getImages { + my $this = shift; + + $this->writeDebug("getImages(" . join(', ', @{$this->{topics}}) . ") called"); + + # collect images from all topics + my $wikiUserName = &TWiki::Func::getWikiUserName(); + my $pubDir = &TWiki::Func::getPubDir(); + my $pubUrl = &TWiki::Func::getPubUrlPath(); + my $topicRegex = &TWiki::Func::getRegularExpression('mixedAlphaNumRegex'); + my $webRegex = &TWiki::Func::getRegularExpression('webNameRegex'); + my @images; + my $imgnr = 1; + foreach (@{$this->{topics}}) { + my $webtopic= $_; + my $theWeb; + my $theTopic; + if ($webtopic =~ /^($webRegex)\.($topicRegex)$/) { + $theWeb = $1; + $theTopic = $2; + } else { + $this->writeDebug("oops, skipping $webtopic"); + next; + } + $this->writeDebug("reading from $theWeb.$theTopic}"); + + my $viewAccessOK = &TWiki::Func::checkAccessPermission("view", $wikiUserName, '', + $theTopic, $theWeb); + + if (!$viewAccessOK) { + $this->writeDebug("no view access to ... skipping"); + next; + } + + my ($meta, undef) = &TWiki::Func::readTopic($theWeb, $theTopic); + + foreach my $attachment ($meta->find('FILEATTACHMENT')) { + next unless $this->isImage($attachment); + my $image = $attachment; + + $image->{IGP_comment} = &getImageTitle($image); + $image->{IGP_sizeK} = sprintf("%dk", $image->{size} / 1024); + $image->{IGP_natnr} = $imgnr; + $image->{IGP_topic} = $theTopic; + $image->{IGP_web} = $theWeb; + $image->{IGP_filename} = $this->normalizeFileName( + $pubDir . "/$image->{IGP_web}/$image->{IGP_topic}/$image->{name}"); + $image->{IGP_url} = + $pubUrl . "/$image->{IGP_web}/$image->{IGP_topic}/$image->{name}"; + $imgnr++; + if ($image->{IGP_comment} =~ /^([0-9]+)\s*-\s*(.*)$/) { + $image->{IGP_imgnr} = $1; + $image->{IGP_comment} = $2; + } + + # check for file existence + if (! -e $image->{IGP_filename}) { + &TWiki::Func::writeWarning("attachment error in " . + "$image->{IGP_web}.$image->{IGP_topic}: " . + "no such file '$image->{IGP_filename}'"); + next; + } + + push @images, $image; + } + } + + # order images + my @sortedImages; + + # obey explicite image positioning + foreach my $image (@images) { + next unless $image->{IGP_imgnr}; + my $imgnr = $image->{IGP_imgnr}; + while ($sortedImages[$imgnr]) { # first come first serve + $imgnr++; + } + $sortedImages[$imgnr] = $image; + } + + # merge rest according to natural position + foreach my $image (@images) { + next if $image->{IGP_imgnr}; + my $imgnr = $image->{IGP_natnr}; + while ($sortedImages[$imgnr]) { # first come first serve + $imgnr++; + } + $sortedImages[$imgnr] = $image; + } + + # reconstruct images list and normalize their number + $imgnr = 1; + @images = (); + foreach my $image (@sortedImages) { + next unless $image; + push @images, $image; + $image->{IGP_imgnr} = $imgnr++; + } + + $this->{images} = \@images; + return \@images; +} + +# ========================= +# use the mage to get the image size +# enrich the givem image with the following information +# - IGP_origwidth: the original width of the image +# - IGP_origheight: the original height of the image +# - IGP_width: the max width to be used +# - IGP_height: the max height to be used +# - IGP_thumbwidth: the max thumbnail width to be used for +# - IGP_thumbheight: the max thumbnail height to be used +sub computeImageSize { + my ($this, $image) = @_; + + $this->writeDebug("computeImageSize($image->{name})"); + + my $entry = $this->{info}{$image->{name}}; + if (!$this->{doRefresh} && $entry) { + + # look up igp info + $this->writeDebug("found cached info"); + $image->{IGP_origwidth} = $entry->{origwidth}; + $image->{IGP_origheight} = $entry->{origheight}; + + } else { + + # compute + $this->writeDebug("consulting image mage on $image->{IGP_filename}"); + ($image->{IGP_origwidth}, $image->{IGP_origheight}, undef, undef) = + $this->{mage}->Ping($image->{IGP_filename}); + + # forget + my $mage = $this->{mage}; + @$mage = (); + } + + # compute max image width and height + my $width = $image->{IGP_origwidth}; + my $height = $image->{IGP_origheight}; + my $aspect = $height / $width; + + if ($width < $this->{minwidth}) { + $width = $this->{minwidth}; + $height = $width * $aspect; + } + if ($height < $this->{minheight}) { + $height = $this->{minheight}; + $width = $height / $aspect; + } + if ($this->{maxwidth} && $width > $this->{maxwidth}) { + $width = $this->{maxwidth}; + $height = $width * $aspect; + } + if ($this->{maxheight} && $height > $this->{maxheight}) { + $height = $this->{maxheight}; + $width = $height / $aspect; + } + $image->{IGP_width} = int($width+0.5); + $image->{IGP_height} = int($height+0.5); + + #$this->writeDebug("minwidth=$this->{minwidth}, minheight=$this->{minheight}, width=$width, height=$height"); + + # compute max thumnail width and height + $width = $image->{IGP_origwidth}; + $height = $image->{IGP_origheight}; + $aspect = $height / $width; + + if ($width > $this->{thumbwidth}) { + $width = $this->{thumbwidth}; + $height = $width * $aspect; + } + if ($height > $this->{thumbheight}) { + $height = $this->{thumbheight}; + $width = $height / $aspect; + } + $image->{IGP_thumbwidth} = int($width+0.5); + $image->{IGP_thumbheight} = int($height+0.5); + + # update image info + my $imgChanged = 0; + if (!$entry || + $entry->{width} ne $image->{IGP_width} || + $entry->{height} ne $image->{IGP_height} || + $entry->{origwidth} ne $image->{IGP_origwidth} || + $entry->{origheight} ne $image->{IGP_origheight} || + $entry->{thumbwidth} ne $image->{IGP_thumbwidth} || + $entry->{thumbheight} ne $image->{IGP_thumbheight}) { + $this->{infoChanged} = 1; + $imgChanged = 1; + } + + $entry->{name} = $image->{name}; + $entry->{type} = 'image'; + $entry->{width} = $image->{IGP_width}; + $entry->{height} = $image->{IGP_height}; + $entry->{origwidth} = $image->{IGP_origwidth}; + $entry->{origheight} = $image->{IGP_origheight}; + $entry->{thumbwidth} = $image->{IGP_thumbwidth}; + $entry->{thumbheight} = $image->{IGP_thumbheight}; + $entry->{imgChanged} = $imgChanged; + $this->{info}{$entry->{name}} = $entry; +} + +# ========================= +sub replaceVars { + my ($this, $format, $image) = @_; + + if ($image) { + + $format =~ s/\$reddot/$this->renderRedDot($image)/goes; + $format =~ s/\$width/$image->{IGP_width}/gos; + $format =~ s/\$height/$image->{IGP_height}/gos; + $format =~ s/\$thumbwidth/$image->{IGP_thumbwidth}/gos; + $format =~ s/\$thumbheight/$image->{IGP_thumbheight}/gos; + $format =~ s/\$origwidth/$image->{IGP_origwidth}/gos; + $format =~ s/\$origheight/$image->{IGP_origheight}/gos; + $format =~ s/\$sizeK/$image->{IGP_sizeK}/gos; + $format =~ s/\$comment/$image->{IGP_comment}/geos; + $format =~ s/\$imgnr/$image->{IGP_imgnr}/gos; + + $format =~ s/\$date(\{([^\}]*)\})?/&formatTime($image->{date}, $2)/goes; + $format =~ s/\$version/$image->{version}/gos; + $format =~ s/\$name/$image->{name}/gos; + $format =~ s/\$size/$image->{size}/gos; + $format =~ s/\$wikiusername/$image->{user}/gos; + $format =~ s/\$username/TWiki::Func::wikiToUserName($image->{user})/geos; + $format =~ s,\$thumburl,$this->{igpPubUrl}/thumb_$image->{name},gos; + $format =~ s,\$imageurl,$this->{igpPubUrl}/$image->{name},gos; + $format =~ s,\$origurl,$image->{IGP_url},gos; + $format =~ s/\$web/$image->{IGP_web}/gos; + $format =~ s/\$topic/$image->{IGP_topic}/gos; + } + + $format =~ s/\$nrimgs/scalar @{$this->{images}}/geos; + $format =~ s/\$n((\([^\)]*\))|(\{[^\}]*\}))?/\n/gos; # $n or $n(....) or $n{...} + + return $format; +} + +# ========================= +# only update the image if +# (1) it doesn't exist or +# (2) the thumbnail is older than the source image +# (3) it should be resized +# returns 1 on success and 0 on an error (see errorMsg) +sub createImg { + my ($this, $image, $thumbMode) = @_; + + #$this->writeDebug("createImg($image->{name}) called"); + + my $prefix = ($thumbMode)?'thumb_':''; + + my $target = "$this->{igpDir}/$prefix$image->{name}"; + $target = $this->normalizeFileName($target); + + my $entry = $this->{info}{$image->{name}}; + return 1 if !$this->{doRefresh} && -e $target && !$entry->{imgChanged}; + + $this->{errorMsg} = ''; + + # read + my $error = $this->{mage}->Read($image->{IGP_filename}); + if ($error =~ /(\d+)/) { + $this->writeDebug("Read(): error=$error"); + $this->{errorMsg} = " $error"; + return 0 if $1 >= 400; + } + + # compute + if ($thumbMode) { + $error = + $this->{mage}->Scale(geometry=>"$image->{IGP_thumbwidth}x$image->{IGP_thumbheight}"); + } else { + $error = + $this->{mage}->Scale(geometry=>"$image->{IGP_width}x$image->{IGP_height}"); + } + if ($error =~ /(\d+)/) { + $this->writeDebug("Scale(): error=$error"); + $this->{errorMsg} .= " $error"; + return 0 if $1 >= 400; + } + + # write + $error = $this->{mage}->Write($target); + if ($error =~ /(\d+)/) { + $this->writeDebug("Write(): error=$error"); + $this->{errorMsg} .= " $error"; + return 0 if $1 >= 400; + } + + $this->writeDebug("writing target '$target'"); + + # forget + my $mage = $this->{mage}; + @$mage = (); + return 1; +} + +# ========================= +# stolen form TWiki::handleTime() +sub formatTime { + my ($time, $format) = @_; + $format ||= '$day $mon $year - $hour:$min'; + my $value = ""; + + my ($sec, $min, $hour, $day, $mon, $year) = localtime($time); + $year = sprintf("%.4u", $year + 1900); + use constant ISOMONTH => qw(Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec); + my $tmon = (ISOMONTH)[$mon]; + + $value = $format; + $value =~ s/\$sec[o]?[n]?[d]?[s]?/sprintf("%.2u",$sec)/geoi; + $value =~ s/\$min[u]?[t]?[e]?[s]?/sprintf("%.2u",$min)/geoi; + $value =~ s/\$hou[r]?[s]?/sprintf("%.2u",$hour)/geoi; + $value =~ s/\$day/sprintf("%.2u",$day)/geoi; + $value =~ s/\$mon[t]?[h]?/$tmon/goi; + $value =~ s/\$mo/sprintf("%.2u",$mon+1)/geoi; + $value =~ s/\$yea[r]?/$year/goi; + $value =~ s/\$ye/sprintf("%.2u",$year%100)/geoi; + + return $value; +} + +# ========================= +# wrapper +sub normalizeFileName { + my ($this, $fileName) = @_; + + #$this->writeDebug("normalizeFileName($fileName)"); + + if (defined &TWiki::Sandbox::normalizeFileName) { + return &TWiki::Sandbox::normalizeFileName($fileName); + } + + if (defined &TWiki::normalizeFileName) { + return &TWiki::normalizeFileName($fileName); + } + + return $fileName; +} + +# ========================= +sub renderError { + my $msg = shift; + return "<span class=\"igpAlert\">Error: $msg</span>" ; +} + +# ========================= +sub getImageTitle { + my $image = shift; + + my $title; + if ($image->{comment}) { + $title = $image->{comment}; + } else { + $title = $image->{name}; + $title =~ s/^(.*)\.[a-zA-Z]*$/$1/; + } + $title =~ s/^\s+//; + $title =~ s/\s+$//; + + return $title; +} + +# ========================= +sub readInfo { + my $this = shift; + + $this->writeDebug("readInfo() called"); + + $this->{infoChanged} = 1; + return unless -e $this->{infoFile}; + + my $text = &TWiki::Func::readFile($this->{infoFile}); + foreach my $line (split(/\n/, $text)) { + my $entry; + if ($line =~ /^name=(.*), origwidth=(.*), origheight=(.*), width=(.*), height=(.*), thumbwidth=(.*), thumbheight=(.*)$/) { + $entry = { + name=>$1, + type=>'image', + origwidth=>$2, + origheight=>$3, + width=>$4, + height=>$5, + thumbwidth=>$6, + thumbheight=>$7, + }; + } elsif ($line =~ /^(thumbwidth|thumbheight|topics|web)=(.*)$/) { + $entry = { + name=>$1, + type=>'global', + value=>$2, + }; + } else { + next; + } + $this->{info}{$entry->{name}} = $entry; + } + + $this->{infoChanged} = 0; +} + +# ========================= +sub writeInfo { + my $this = shift; + + $this->writeDebug("writeInfo() called"); + + return unless $this->{infoChanged}; + + my $text = "# ImageGalleryPlugin info file: DON'T EDIT BY HAND\n"; + $text .= "thumbwidth=$this->{thumbwidth}\n"; + $text .= "thumbheight=$this->{thumbheight}\n"; + $text .= "topics=" . join (', ', @{$this->{topics}}) . "\n"; + foreach my $entry (values %{$this->{info}}) { + next if $entry->{type} eq 'global'; + $text .= + "name=$entry->{name}, " . + "origwidth=$entry->{origwidth}, " . + "origheight=$entry->{origheight}, " . + "width=$entry->{width}, " . + "height=$entry->{height}, " . + "thumbwidth=$entry->{thumbwidth}, " . + "thumbheight=$entry->{thumbheight}" . + "\n"; + } + + $this->writeDebug("writing infoFile=$this->{infoFile}"); + + &TWiki::Func::saveFile($this->{infoFile}, $text); +} + + +1; Added: twiki/branches/DEVELOP/twikiplugins/ImageGalleryPlugin/lib/TWiki/Plugins/ImageGalleryPlugin/MANIFEST =================================================================== --- twiki/branches/DEVELOP/twikiplugins/ImageGalleryPlugin/lib/TWiki/Plugins/ImageGalleryPlugin/MANIFEST 2005-09-14 17:01:37 UTC (rev 6358) +++ twiki/branches/DEVELOP/twikiplugins/ImageGalleryPlugin/lib/TWiki/Plugins/ImageGalleryPlugin/MANIFEST 2005-09-14 17:46:28 UTC (rev 6359) @@ -0,0 +1,4 @@ +./data/TWiki/ImageGalleryPlugin.txt +./lib/TWiki/Plugins/ImageGalleryPlugin.pm +./lib/TWiki/Plugins/ImageGalleryPlugin/Core.pm +./pub/TWiki/ImageGalleryPlugin/style.css Modified: twiki/branches/DEVELOP/twikiplugins/ImageGalleryPlugin/lib/TWiki/Plugins/ImageGalleryPlugin.pm =================================================================== --- twiki/branches/DEVELOP/twikiplugins/ImageGalleryPlugin/lib/TWiki/Plugins/ImageGalleryPlugin.pm 2005-09-14 17:01:37 UTC (rev 6358) +++ twiki/branches/DEVELOP/twikiplugins/ImageGalleryPlugin/lib/TWiki/Plugins/ImageGalleryPlugin.pm 2005-09-14 17:46:28 UTC (rev 6359) @@ -1,9 +1,8 @@ -#! perl -w -use strict; # # TWiki WikiClone ($wikiversion has version info) # -# Copyright (C) 2002-2005 Will Norris. All Rights Reserved. (wb...@sa...) +# Copyright (C) 2002-2003 Will Norris. All Rights Reserved. (wb...@sa...) +# Copyright (C) 2005 Michael Daum <mi...@na...> # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -13,262 +12,95 @@ # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details, published at +# GNU General Public License for more details, published at # http://www.gnu.org/copyleft/gpl.html # # ========================= - - -# ========================= package TWiki::Plugins::ImageGalleryPlugin; -use Data::Dumper qw( Dumper ); - # ========================= use vars qw( - $web $topic $user $installWeb $VERSION $debug - $IMAGE_MAGICK $CONVERT $IDENTIFY $CONVERT_OPTIONS + $web $topic $user $installWeb $VERSION $isInitialized $igpId ); -$VERSION = '1.11'; +$VERSION = '3.1'; # ========================= -sub initPlugin -{ - ( $topic, $web, $user, $installWeb ) = @_; +sub initPlugin { + ($topic, $web, $user, $installWeb) = @_; - # check for Plugins.pm versions - if( $TWiki::Plugins::VERSION < 1 ) { - &TWiki::Func::writeWarning( "Version mismatch between ImageGalleryPlugin and Plugins.pm" ); - return 0; - } + if ($TWiki::Plugins::VERSION < 1) { + &TWiki::Func::writeWarning("Version mismatch between ImageGalleryPlugin and Plugins.pm"); + return 0; + } + $isInitialized = 0; + $igpId = 1; - # Get plugin preferences, the variable defined by: * Set CONVERT = ... - $IMAGE_MAGICK = TWiki::Func::getPreferencesValue( "IMAGEGALLERYPLUGIN_IMAGE_MAGICK" ) || "/usr"; - $CONVERT = TWiki::Func::getPreferencesValue( "IMAGEGALLERYPLUGIN_CONVERT" ) || "$IMAGE_MAGICK/bin/convert"; - $IDENTIFY = TWiki::Func::getPreferencesValue( "IMAGEGALLERYPLUGIN_IDENTIFY" ) || "$IMAGE_MAGICK/bin/identify"; - $CONVERT_OPTIONS = TWiki::Func::getPreferencesValue('IMAGEGALLERYPLUGIN_CONVERT_OPTIONS'); - - # Get plugin debug flag - $debug = &TWiki::Func::getPreferencesFlag( "IMAGEGALLERYPLUGIN_DEBUG" ) || 0; - - if ( $debug ) { - &TWiki::Func::writeDebug( "image_magick=[$IMAGE_MAGICK]" ); - &TWiki::Func::writeDebug( "convert=[$CONVERT]: " . ((-x $CONVERT) ? 'found executable' : 'missing') ); - &TWiki::Func::writeDebug( "identify=[$IDENTIFY]: " . ((-x $IDENTIFY) ? 'found executable' : 'missing')); - # Plugin correctly initialized - &TWiki::Func::writeDebug( "- TWiki::Plugins::ImageGalleryPlugin::initPlugin( $web.$topic ) is OK" ); - } - return 1; + return 1; } - - # ========================= -# When %IMAGEGALLERY is seen, it: -# 1 finds the meta data for the current topic -# 2 initialises a buffer $t for the output to be inserted -# 3 for each attachment _$attachment_ (does it check to ensure Image type?), it: -# a finds the dimensions of the image (using IMAGEGALLERYPLUGIN_IDENTIFY) -# (it aborts this attachment in the results if it cannot find the dimension) -# b adds to $t the image <IMG SRC> pointer; this is subject to formatting into columns and rows -# 4 returns the buffer -sub handleImageGallery -{ - my( $attributes ) = @_; -# return '<b>IMAGE(' . $settings->{topic} . ")</b>\n"; +sub commonTagsHandler { - my $settings = { - size => scalar &TWiki::Func::extractNameValuePair( $attributes, "size" ) || 'medium', - topic => scalar TWiki::Func::extractNameValuePair( $attributes ) || scalar TWiki::Func::extractNameValuePair( $attributes, "topic" ) || $topic, - web => scalar &TWiki::Func::extractNameValuePair( $attributes, "web" ) || $web, - columns => scalar &TWiki::Func::extractNameValuePair( $attributes, "columns" ) || '0', - rowstart => scalar &TWiki::Func::extractNameValuePair( $attributes, "rowstart" ) || '', - rowinside => scalar &TWiki::Func::extractNameValuePair( $attributes, "rowinside" ) || '', - rowend => scalar &TWiki::Func::extractNameValuePair( $attributes, "rowend" ) || '', - rowinsideempty => scalar &TWiki::Func::extractNameValuePair( $attributes, "rowinsideempty" ) || '', - rowendempty => scalar &TWiki::Func::extractNameValuePair( $attributes, "rowendempty" ) || '', - options => scalar &TWiki::Func::extractNameValuePair( $attributes, "options" ) || $CONVERT_OPTIONS, - format => scalar &TWiki::Func::extractNameValuePair( $attributes, "format") - || q(<span class="imgGallery"><a href="$imageurl"><img src="$thumburl" title="$sizeK: $comment"/></a></span>$n), - max => scalar &TWiki::Func::extractNameValuePair( $attributes, "max") || 0, - }; - $settings->{resize} = TWiki::Func::getPreferencesValue( uc "IMAGEGALLERYPLUGIN_$settings->{size}" ) || $settings->{size}; - - my @topics = $settings->{topic} eq 'all' && TWiki::Func::getTopicList() || ( $settings->{topic} ); - my $output = ''; - foreach my $wantedTopic (@topics) { - $settings->{topic} = $wantedTopic; - my ( $meta, undef ) = &TWiki::Func::readTopic( $settings->{web}, $wantedTopic ); - $output .= _formatTMLforTopic( $settings->{web}, $wantedTopic, $meta, $settings ); - } - return $output; + $_[0] =~ s/%IMAGEGALLERY%/&renderImageGallery($igpId++)/geo; + $_[0] =~ s/%IMAGEGALLERY{(.*?)}%/&renderImageGallery($igpId++, $1)/geo; + $_[0] =~ s/%NRIMAGES{(.*?)}%/&renderNrImages($1)/geo; } -# Potentially we could provide a topic name/other-attribute(e.g. form) filter... -sub _getTopics { -} +# ========================= +sub doInit { -# DESIGN: I chose to reset the image number with each new topic, but arguably -# you'd want to be able to compile pictures from different topics into the -# same rows. -# -# DESIGN: I chose to select all images attached to a page but -# you might choose to select only the first image from each topic -sub _formatTMLforTopic { - my ($web, $topic, $meta, $settings) = @_; - - my @attachments = $meta->find( 'FILEATTACHMENT' ); - &TWiki::Func::writeDebug( "- web=[$web] topic=[$topic] size=[$settings->{size}] resize=[$settings->{resize}]" ) if $debug; -#TODO: check what's resize vs size - - my $t = ""; - my $imageNumber = 0; - my $max = $settings->{max}; + return if $isInitialized; - foreach my $attachment (@attachments) { - - last if ($max > 0 and $imageNumber >= $max); + if ($TWiki::Plugins::VERSION < 1.020) { + eval 'use TWiki::Contrib::CairoContrib;'; + return "Error:\%BR\%\n<pre style=\"font-size:9pt\">\n$@</pre>\n" + if $@; + } - $attachment->{humanReadableSize} = sprintf( "%dk", $attachment->{size}/1024 ); + eval 'use TWiki::Plugins::ImageGalleryPlugin::Core();'; + return "Error:\%BR\%\n<pre style=\"font-size:9pt\">\n$@</pre>\n" + if $@; - my $filename = &TWiki::Func::getPubDir() . "/$web/$topic/$attachment->{name}"; - &TWiki::Func::writeDebug( Dumper( $attachment ) ) if $debug; + $isInitialized = 1; - next unless (my $dimensions = `"$IDENTIFY" "$filename"`) =~ - m/(\d+)x(\d+)/; # fix, else don't work for jpg, png ...! - #m/(\d+)x(\d+)\+(\d+)\+(\d+)/; - my ( $width, $height ) = ( $1, $2 ); - - $imageNumber++; - $t .= _formatImageSRC($imageNumber, $web, $topic, $attachment, $width, $height, $settings ); - _updateThumb( $filename, my $thumb = &TWiki::Func::getPubDir() . "/$web/$topic/thumbs/$settings->{resize}/$attachment->{name}", $settings ); # SMELL - might want to do this periodically? - } - - if ($settings->{columns} && ($imageNumber>0)) { - $t .= _completeAnyUnfinishedTables($imageNumber, $web, $topic, $settings->{width}, $settings->{height}, $settings); - } - - return $t; + return undef; } -sub _updateThumb { - my ($fn, $thumb, $settings) = @_; - my $resize = $settings->{resize}; - my $options = $settings->{options}; - - TWiki::Func::writeDebug( "thumb=[$thumb] fn=[$fn]" ) if $debug; - unless ( ( -M $thumb ) && ( -M $fn > -M $thumb ) ) - { # only update the thumbnail if (1) it doesn't exist or (2) the thumbnail is older than the source image - my $thumbDir = &TWiki::Func::getPubDir() . "/$settings->{web}/$settings->{topic}/thumbs"; - mkdir $thumbDir unless -d $thumbDir; - $thumbDir .= "/$resize"; - mkdir $thumbDir unless -d $thumbDir; +# ========================= +sub renderImageGallery { + my ($id, $args) = @_; - my $cmdThumbnail = qq{$CONVERT -sample $resize $options "$fn" "$thumb"}; - TWiki::Func::writeDebug( "settings: " . Dumper( $settings ) ) if $debug; + my $error = &doInit(); - &TWiki::Func::writeDebug( "- running CONVERT [$CONVERT] [$cmdThumbnail] - options=[$options]" ) if $debug; - system( $cmdThumbnail ); - } -} + if ($error) { + &TWiki::Func::writeWarning("ImageGalleryPlugin::doInit() - $error"); + return $error; + } - -sub _formatImageSRC { - my ($inr, $web, $topic, $i, $width, $height, $settings) = @_; - my $ans; - if($settings->{columns} && (($inr-1)%$settings->{columns}==0 || $inr==1)){ # row start - $ans = &_replaceVars($settings->{rowstart}, $i, $web, $topic, $settings->{resize}, $width, $height, $inr); - }else{ # inside row - $ans = &_replaceVars($settings->{rowinside}, $i, $web, $topic, $settings->{resize}, $width, $height, $inr); - } - $ans .= &_replaceVars($settings->{format}, $i, $web, $topic, $settings->{resize}, $width, $height, $inr); - # row end - if ($settings->{columns} && $inr%$settings->{columns}==0) { - $ans .= &_replaceVars($settings->{rowend}, $i, $web, $topic, $settings->{resize}, $width, $height, $inr) - } - return $ans; + my $igp = TWiki::Plugins::ImageGalleryPlugin::Core->new($id, $topic, $web); + $igp->debug(0); + return $igp->render($args); } - -=pod - complete incomplete last row, needed for tables!! -=cut -sub _completeAnyUnfinishedTables { - my ($inr, $web, $topic, $width, $height, $settings) = @_; - # finish columns - my $t = ''; - while($inr%$settings->{columns}!=0) { - $t .= &_replaceVars($settings->{rowinsideempty}, undef, $web, $topic, $settings->{resize}, '', '', $inr); - $inr++; - } - - # empty end row - $t .= &_replaceVars($settings->{rowendempty}, undef, $web, $topic, $settings->{resize}, '', '', $inr); -} - # ========================= -sub _replaceVars -{ - my( $format, $img, $web, $topic, $resize, $width, $height, $imgnr) = @_; +sub renderNrImages { + my ($args) = @_; - $format =~ s/\$web/$web/gos; - $format =~ s/\$topic/$topic/gos; - if($img){ # make sure no access for ***empty formats - $format =~ s/\$width/$width/gos; - $format =~ s/\$height/$height/gos; - $format =~ s/\$date(\{([^\}]*)\})?/&_formatTime($img->{date}, $2)/goes; - $format =~ s/\$version/$img->{version}/gos; - $format =~ s/\$name/$img->{name}/gos; - $format =~ s/\$sizeK/$img->{humanReadableSize}/gos; - $format =~ s/\$size/$img->{size}/gos; - $format =~ s/\$comment/$img->{comment}/geos; - $format =~ s/\$wikiusername/$img->{user}/gos; - $format =~ s/\$username/TWiki::Func::wikiToUserName($img->{user})/geos; - $format =~ s,\$thumburl,%PUBURL%/$web/$topic/thumbs/$resize/$img->{name},gos; - $format =~ s,\$imageurl,%PUBURL%/$web/$topic/$img->{name},gos; - } - $format =~ s/\$imgnr/$imgnr/gos; - $format =~ s/\$n((\([^\)]*\))|(\{[^\}]*\}))?/\n/gos; # $n or $n(....) or $n{...} + my $error = &doInit(); - return $format; -} + if ($error) { + &TWiki::Func::writeWarning("ImageGalleryPlugin::doInit() - $error"); + return $error; + } -# ========================= -# stolen form TWiki::handleTime() which implements formatting of %GMTIME{...}% -sub _formatTime -{ - my ( $time, $format ) = @_; - $format ||= '$day $mon $year - $hour:$min'; # Default format, e.g. "31 Dec 2002 - 19:30" - my $value = ""; - - my( $sec, $min, $hour, $day, $mon, $year ) = localtime( $time ); - $year = sprintf( "%.4u", $year + 1900 ); - use constant ISOMONTH => qw( Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec ); - my $tmon = (ISOMONTH)[$mon]; - - $value = $format; - $value =~ s/\$sec[o]?[n]?[d]?[s]?/sprintf("%.2u",$sec)/geoi; - $value =~ s/\$min[u]?[t]?[e]?[s]?/sprintf("%.2u",$min)/geoi; - $value =~ s/\$hou[r]?[s]?/sprintf("%.2u",$hour)/geoi; - $value =~ s/\$day/sprintf("%.2u",$day)/geoi; - $value =~ s/\$mon[t]?[h]?/$tmon/goi; - $value =~ s/\$mo/sprintf("%.2u",$mon+1)/geoi; - $value =~ s/\$yea[r]?/$year/goi; - $value =~ s/\$ye/sprintf("%.2u",$year%100)/geoi; - - return $value; + my $igp = TWiki::Plugins::ImageGalleryPlugin::Core->new(undef, $topic, $web); + $igp->debug(0); + if ($igp->init($args)) { + return scalar @{$igp->getImages()}; + } else { + return TWiki::Plugins::ImageGalleryPlugin::Core::renderError("can't initialize using '$args'"); + } } -# ========================= -sub commonTagsHandler -{ -### my ( $text, $topic, $web ) = @_; # do not uncomment, use $_[0], $_[1]... instead - - $_[0] =~ s/%IMAGEGALLERY%/&handleImageGallery()/geo; - $_[0] =~ s/%IMAGEGALLERY{(.*?)}%/&handleImageGallery($1)/geo; -} - -# ================... [truncated message content] |