scripts/wiki/macro/Gallery.py
salles 1e7ce4861e Imports initiaux
darcs-hash:20060416092713-72cb0-421d3cbb49699fce4971355f5dbbaa19091945e8.gz
2006-04-16 11:27:13 +02:00

688 lines
26 KiB
Python

# -*- coding: iso-8859-1 -*-
"""
Gallery.py Version 0.86
This macro creates dynamic tabulated displays based on attachment contents
@copyright: 2004,2005 by Simon Ryan <simon<at>smartblackbox.com> http://smartblackbox.com/simon
@license: GPL
Special thanks go to:
My beautiful wife Jenny: For keeping the kids at bay long enough for me to code it :-)
Adam Shand: For his GallerySoftware feature wish list, support, ideas and suggestions.
Usage: [[Gallery(key1=value1,key2=value2....)]]
where the following keys are valid:
thumbnailwidth = no of pixels wide to make the thumbnails
webnailwidth = width in pixels of the web sized images
numberofcolumns = no of columns used in the thumbnail table
Bugs:
Continued rotation will degrade the tmp images (but they can be forced to regen)
Features:
Simple usage, just put [[Gallery]] on any page and upload some pictures as attachments
Rotate buttons
Annotation
Should work with MoinMoin versions 1.2.x and 1.3.x
Support for Python Imaging Library
Works with FastCGI mode
Not yet implemented:
Handling of video formats
Speed up:
# When you get really sick of how slow the moinmoin image system is,
# you can either reconfigure your setup for FastCGI (highly recommended)
# or: set the following variables in your wikiconfig.py
gallerytempdir (the path to a writable directory)
gallerytempurl (the path in your webservers url space where this directory can be read from)
eg:
gallerytempdir='/var/www/html/nails'
gallerytempurl='/nails'
or maybe:
gallerytempurl=url_prefix+'/nails'
# There are other ways of getting speedups for attachments, but these methods are the safest (IMHO)
"""
__author__ = "Simon D. Ryan"
__version__ = "0.86"
from MoinMoin import config, wikiutil
import string, cStringIO, os
import commands, shutil
from random import randint
try:
import Image
except:
pass
class Globs:
# A quick place to plonk those shared variables
thumbnailwidth='200'
webnailwidth='600'
numberofcolumns=4
adminmsg=''
debuglevel=0
originals={}
convertbin=''
annotated={}
attachmentdir=''
gallerytempdirroot=''
gallerytempdir=''
gallerytempurl=''
galleryrotchar=''
pagename=''
admin=''
bcomp=''
baseurl=''
timeout=40
allowedextensions=['jpg','jpeg','png','bmp','tiff','gif']
def message(astring,level=1):
if level<=Globs.debuglevel:
Globs.adminmsg=Globs.adminmsg+'<font color="#FF0000"><strong>Gallery</strong></font>:&nbsp;&nbsp;'+astring+'<br>\n'
def version():
return(' version <b>'+Globs.version+'</b> by Simon D. Ryan.'+\
'<br>Copyright 2004,2005 Simon D. Ryan<br>Gallery is a MoinMoin macro and is released under the '+\
'<a href="http://www.gnu.org/licenses/gpl.txt">GPL</a>\n'+\
'<p>Upload some images as attachments to <a href="'+Globs.baseurl+Globs.pagename+'?action=AttachFile"><b>'+Globs.pagename+'</b></a> and I will generate a gallery for you.')
# Thanks to denny<at>ece.arizona.edu
# This can be replaced with a static translation table to speed things up (later)
def mktrans():
# Allow only letters and digits and a few other valid file characters
alphanumeric=string.letters+string.digits+'.,-_\'!"'
source_string=""
destination_string=""
for i in range(256):
source_string=source_string+chr(i)
if chr(i) in alphanumeric:
destination_string=destination_string+chr(i)
else:
destination_string=destination_string+' '
return string.maketrans(source_string,destination_string)
def qlink(pagename, querystring, query, description=''):
# Returns a hyperlink constructed as a form query on pagename
if not description:
description=query
return '<a href="'+Globs.baseurl+pagename+'?'+querystring+'='+query+Globs.bcomp+'">'+description+'</a>'
def navibar(target,querystring):
# Returns a navigational bar with PREV,THUMBS,NEXT
positions=Globs.originals.keys()
positions.sort()
thumbs='<a href="'+Globs.subname+'">THUMBS</a>'
index=positions.index(target)
back,forward='',''
if not index==0:
# We are not the first so we can provide a back link
back=qlink(Globs.pagename, querystring, positions[index-1], 'PREV')
if not index==len(positions)-1:
# We are not the last so we can provide a forward link
forward=qlink(Globs.pagename, querystring, positions[index+1], 'NEXT')
return '<table><tr><td>'+back+'</td><td>'+thumbs+'</td><td>'+forward+'</td></tr></table>'
def toolbar(target,naillevel):
if Globs.admin:
rotateleft='<input type="submit" name="rotate" value="rotate left">'
rotateright='<input type="submit" name="rotate" value="rotate right">'
htarget='<input type=hidden value="'+target+'" name="'+naillevel+'">'
compat='<input type=hidden value="show" name="action">'
return '<form METHOD=POST><table><tr><td>'+rotateleft+'</td><td>'+rotateright+'</td></tr></table>\n'+htarget+compat+'</form>'
else:
return ''
def buildnails(items):
# For now we use commands.getoutput to do our dirty work
# Later we can build a batch job and fork it off.
# Make sure our temp directory is writable and generate a message if it isn't
try:
if not os.path.isfile(Globs.gallerytempdir+'/tmp.writetest'):
# There is probably a less ugly was to do this using stat (later)
open(Globs.gallerytempdir+'/tmp.writetest','w').close()
except IOError:
message('I had some trouble writing to the temp directory. Is it owned by me and writable?',0)
# Don't go further if there is a lock in place
if os.path.isfile(Globs.attachmentdir+'/tmp.lock'):
message("I'm currently busy generating thumbnails and webnails, please try again later.",0)
return ''
# Find the convert binary in standard locations
if not globals().has_key('Image'):
if not os.path.isfile('/usr/bin/convert'):
if not os.path.isfile('/usr/X11R6/bin/convert'):
message('<b>Please install ImageMagick or PIL so I can build thumbnails and webnails</b><p>',0)
return
else:
Globs.convertbin='/usr/X11R6/bin/convert'
else:
Globs.convertbin='/usr/bin/convert'
else:
# Use Python Imaging Library
Globs.convertbin='Image'
# Create a lock file in the attachments dir so we can always remotely remove it if there is a problem
open(Globs.attachmentdir+'/tmp.lock','w').close()
import time
tstart=time.time()
pid,pid2='',''
# For each original file, check for the existance of a nail
for item in items:
basename,prefix,width=item
# Check to see if we tarry too long on the road
if tstart and (time.time()-tstart) > Globs.timeout:
# This is taking waaay too long let us fork and detach else the browser will time out or worse, the webserver may kill us
pid = os.fork()
if pid != 0:
# We are in the parent so we break out
message('The thumbnail generation process was taking too long so it has been backgrounded. Please try again later to see the full set of thumbnails',0)
break
else:
# Once we are forked we want to ignore the time
tstart=''
# Break away from the controlling terminal, so that the web server cannot kill us by killing our parent
os.setsid()
# Fork again so we can get away without a controlling terminal
pid2 = os.fork()
if (pid2 != 0):
os._exit(0)
else:
# Close all open file descriptors
try:
max_fd = os.sysconf("SC_OPEN_MAX")
except (AttributeError, ValueError):
max_fd = 256
for fd in range(0, max_fd):
try:
os.close(fd)
except OSError:
pass
# Redirect the standard file descriptors to /dev/null
os.open("/dev/null", os.O_RDONLY) # stdin
os.open("/dev/null", os.O_RDWR) # stdout
os.open("/dev/null", os.O_RDWR) # stderr
# Now we are finally free to continue the conversions as a daemon
# If you would like to know more about the above, see:
# Advanced Programming in the Unix Environment: W. Richard Stevens
# It is also explained in:
# Unix Network Programming (Volume 1): W. Richard Stevens
#pathtooriginal='"'+Globs.attachmentdir+'/'+Globs.originals[basename]+'"'
pathtooriginal='"'+os.path.join(Globs.attachmentdir,Globs.originals[basename])+'"'
# Warning:
# Take care if modifying the following line,
# you may inadvertantly overwrite your original images!
if not Globs.convertbin == 'Image':
#print 'building nail for '+pathtooriginal
#convout=commands.getoutput('%s -geometry %s \"%s\" "\"%s/%s.%s.jpg\""' % (Globs.convertbin,width+'x'+width,pathtooriginal,Globs.gallerytempdir,prefix,basename))
convout=commands.getoutput('%s -geometry %s %s "%s/%s.%s.jpg"' % (Globs.convertbin,width+'x'+width,pathtooriginal,Globs.gallerytempdir,prefix,basename))
convout=''
convout=string.strip(convout)
if convout:
message(convout)
else:
# Use PIL (strip off the "")
im = Image.open(pathtooriginal[1:-1])
# Use the integer version for PIL
width=string.atoi(width)
im.thumbnail((width,width), Image.ANTIALIAS)
im.save(os.path.join(Globs.gallerytempdir,prefix)+'.'+basename+'.jpg','JPEG')
if (not pid) and (not pid2):
# Release the lock file when finished
os.unlink(Globs.attachmentdir+'/tmp.lock')
# We have built thumbnails so we can deposit an indicator file to prevent rebuilding next time
if not os.path.isfile(Globs.attachmentdir+'/delete.me.to.regenerate.thumbnails.and.webnails'):
open(Globs.attachmentdir+'/delete.me.to.regenerate.thumbnails.and.webnails','w').close()
def rotate(target,direction):
# Rotate the images
# Don't go further if there is a lock in place
if os.path.isfile(Globs.attachmentdir+'/tmp.lock'):
message("I'm currently busy generating thumbnails and webnails. Please try your rotate request again later.",0)
return ''
# Find the correct binary
if not globals().has_key('Image'):
if not os.path.isfile('/usr/bin/mogrify'):
if not os.path.isfile('/usr/X11R6/bin/mogrify'):
message('<b>Please install ImageMagick so I can build thumbnails and webnails</b><p>',0)
return
else:
Globs.convertbin='/usr/X11R6/bin/mogrify'
else:
Globs.convertbin='/usr/bin/mogrify'
else:
Globs.convertbin = 'Image'
# Do the actual rotations
if direction=='rotate right':
degs='90'
else:
degs='270'
if not Globs.convertbin == 'Image':
convout=commands.getoutput(Globs.convertbin+' -rotate '+degs+' "'+Globs.gallerytempdir+'/tmp.webnail.'+target+'.jpg"')
convout=commands.getoutput(Globs.convertbin+' -rotate '+degs+' "'+Globs.gallerytempdir+'/tmp.thumbnail.'+target+'.jpg"')
if not os.path.isfile(Globs.gallerytempdir+'/tmp.rotated.'+target+'.jpg'):
# Generate from original
pathtooriginal=Globs.attachmentdir+'/'+Globs.originals[target]
shutil.copy(pathtooriginal,Globs.gallerytempdir+'/tmp.rotated.'+target+'.jpg')
convout=commands.getoutput(Globs.convertbin+' -rotate '+degs+' "'+Globs.gallerytempdir+'/tmp.rotated.'+target+'.jpg"')
else:
# Use PIL (strip off the "")
if direction=='rotate right':
degs=270.0
else:
degs=90.0
im = os.path.join(Globs.gallerytempdir,'tmp.webnail.')+target+'.jpg'
imw = Image.open(im)
imw.rotate(degs).save(im,'JPEG')
im = os.path.join(Globs.gallerytempdir,'tmp.thumbnail.')+target+'.jpg'
imw = Image.open(im)
imw.rotate(degs).save(im,'JPEG')
imo = os.path.join(Globs.gallerytempdir,'tmp.rotated.')+target+'.jpg'
if not os.path.isfile(Globs.gallerytempdir+'/tmp.rotated.'+target+'.jpg'):
# Generate from original
im=Globs.attachmentdir+'/'+Globs.originals[target]
else:
im = imo
imw = Image.open(im)
imw.rotate(degs).save(imo,'JPEG')
def getannotation(target):
# Annotations are stored as a file for now (later to be stored in images)
atext=''
if Globs.annotated.has_key(target):
atext=open(Globs.attachmentdir+'/tmp.annotation.'+target+'.txt').readline()
message('was annotated')
else:
message('was not annotated')
# replace double quotes with the html escape so quoted annotations appear
return string.replace(atext,'"','&quot;')
def execute(macro, args):
Globs.version=__version__
# Containers
formvals={}
thumbnails={}
webnails={}
rotated={}
try:
import wikiconfig
except:
wikiconfig=''
# Class variables need to be specifically set
# (except for the case where a value is to be shared with another Gallery macro on the same wiki page)
Globs.originals={}
Globs.annotated={}
Globs.attachmentdir=''
Globs.admin=''
Globs.adminmsg=''
Globs.pagename=''
# process arguments
if args:
# Arguments are comma delimited key=value pairs
sargs=string.split(args,',')
for item in sargs:
sitem=string.split(item,'=')
if len(sitem)==2:
key,value=sitem[0],sitem[1]
if key=='thumbnailwidth':
Globs.thumbnailwidth=value
elif key=='webnailwidth':
Globs.webnailwidth=value
elif key=='numberofcolumns':
try:
Globs.numberofcolumns=string.atoi(value)
except TypeError:
pass
# Experimental, uncomment at own risk
#elif key=='pagename':
# Globs.pagename=value
transtable=mktrans()
# Useful variables
dontregen=''
annotationmessage=''
Globs.baseurl=macro.request.getBaseURL()+'/'
if not Globs.pagename:
#Globs.pagename = string.replace(macro.formatter.page.page_name,'/','_2f')
Globs.pagename = macro.formatter.page.page_name
# This fixes the subpages bug. subname is now used instead of pagename when creating certain urls
Globs.subname = string.split(Globs.pagename,'/')[-1]
# Hmmm. A bug in moinmoin? underscores are getting escaped. These doubly escaped pagenames are even appearing in data/pages
try:
# Try the old MoinMoin-1.2.x way first
textdir=config.text_dir
pagepath = string.replace(wikiutil.getPagePath(Globs.pagename),'_5f','_')
except:
pagepath = macro.formatter.page.getPagePath()
Globs.attachmentdir = pagepath+'/attachments'
Globs.galleryrotchar='?'
if hasattr(macro,'cfg') and hasattr(macro.cfg,'gallerytempdir') and hasattr(macro.cfg,'gallerytempurl'):
Globs.gallerytempdirroot=macro.cfg.gallerytempdir
Globs.gallerytempdir=macro.cfg.gallerytempdir+'/'+Globs.pagename+'/'
Globs.gallerytempurl=macro.cfg.gallerytempurl+'/'+Globs.pagename+'/'
elif hasattr(wikiconfig,'gallerytempdir') and hasattr(wikiconfig,'gallerytempurl'):
message('gallerytempdir and gallerytempurl found')
Globs.gallerytempdirroot=wikiconfig.gallerytempdir
Globs.gallerytempdir=wikiconfig.gallerytempdir+'/'+Globs.pagename+'/'
Globs.gallerytempurl=wikiconfig.gallerytempurl+'/'+Globs.pagename+'/'
elif hasattr(wikiconfig,'attachments'):
Globs.gallerytempdirroot=wikiconfig.attachments['dir']
Globs.gallerytempdir=wikiconfig.attachments['dir']+'/'+Globs.pagename+'/attachments/'
Globs.gallerytempurl=wikiconfig.attachments['url']+'/'+Globs.pagename+'/attachments/'
Globs.attachmentdir = Globs.gallerytempdir
else:
Globs.gallerytempdir=Globs.attachmentdir
Globs.gallerytempurl=Globs.subname+'?action=AttachFile&amp;do=get&amp;target='
# MoinMoin no longer allows us to use a ? to trigger a refetch, so we pass it a &
Globs.galleryrotchar='&'
if args:
args=macro.request.getText(args)
# HTML Constants
tleft='<table><tr><td><center>'
tmidd='</center></td><td><center>'
trigh='</center></td></tr></table>\n'
# Add this to the end of each URL to keep some versions of moinmoin happy
Globs.bcomp='&action=show'
# Process any form items into a dictionary (values become unique)
for item in macro.form.items():
if not formvals.has_key(item[0]):
# Here is where we clean the untrusted web input
# (sometimes we get foreign keys from moinmoin when the page is edited)
try:
formvals[item[0]]=string.translate(item[1][0],transtable)
except AttributeError:
pass
# Figure out if we have delete privs
try:
# If a user can delete the page containing the Gallery, then they are considered a Gallery administrator
# This probably should be configurable via a wikiconfig variable eg: galleryadminreq = <admin|delete|any>
if macro.request.user.may.delete(macro.formatter.page.page_name):
Globs.admin='true'
except AttributeError:
pass
out=cStringIO.StringIO()
# Grab a list of the files in the attachment directory
if os.path.isdir(Globs.attachmentdir):
if Globs.gallerytempdir==Globs.attachmentdir:
afiles=os.listdir(Globs.attachmentdir)
else:
if not os.path.isdir(Globs.gallerytempdirroot):
message('You need to create the temp dir first:'+Globs.gallerytempdirroot,0)
return macro.formatter.rawHTML(
Globs.adminmsg+'<p>')
if not os.path.isdir(Globs.gallerytempdir):
# Try to create it if it is absent
spagename=string.split(Globs.pagename,'/')
compbit=''
for component in spagename:
compbit=compbit+'/'+component
try:
os.mkdir(Globs.gallerytempdirroot+compbit)
except:
message('Please check permissions on temp dir:'+Globs.gallerytempdirroot,0)
return macro.formatter.rawHTML(
Globs.adminmsg+'<p>')
if os.path.isdir(Globs.gallerytempdir):
afiles=os.listdir(Globs.attachmentdir)+os.listdir(Globs.gallerytempdir)
else:
message('You need to create the temp dir first:'+Globs.gallerytempdir,0)
return macro.formatter.rawHTML(
Globs.adminmsg+'<p>')
# Split out the thumbnails and webnails
for item in afiles:
if item.startswith('tmp.thumbnail.'):
origname=item[14:-4]
thumbnails[origname]=''
elif item.startswith('tmp.webnail.'):
origname=item[12:-4]
webnails[origname]=''
elif item.startswith('tmp.rotated.'):
origname=item[12:-4]
rotated[origname]=''
elif item.startswith('tmp.annotation.'):
origname=item[15:-4]
Globs.annotated[origname]=''
elif item == 'delete.me.to.regenerate.thumbnails.and.webnails':
dontregen='true'
elif item == 'tmp.writetest' or item == 'tmp.lock':
pass
else:
# This must be one of the original images
lastdot=string.rfind(item,'.')
origname=item[:lastdot]
ext = item[lastdot+1:]
if string.lower(ext) not in Globs.allowedextensions:
continue
Globs.originals[origname]=item
else:
message(version(),0)
return macro.formatter.rawHTML( Globs.adminmsg )
if not Globs.gallerytempdir==Globs.attachmentdir and os.path.isfile(Globs.attachmentdir+'/tmp.writetest'):
# If we are using the new gallerytempdir and we were using the old system then make sure there are no
# remnant files from the old system in the attachment dir to confuse us
message('You have changed to using a gallerytempdir so I am cleaning old tmp files from your attachment dir.',0)
for item in webnails.keys():
try:
os.unlink(Globs.attachmentdir+'/tmp.webnail.'+item+'.jpg')
except:
pass
# Try deleting any old thumbnails which may be in the attachment directory
for item in thumbnails.keys():
try:
os.unlink(Globs.attachmentdir+'/tmp.thumbnail.'+item+'.jpg')
except:
pass
# Try deleting any old rotated originals which may be in the attachment directory
for item in rotated.keys():
try:
os.unlink(Globs.attachmentdir+'/tmp.rotated.'+item+'.jpg')
except:
pass
os.unlink(Globs.attachmentdir+'/tmp.writetest')
newnails=[]
# Any thumbnails need to be built?
for key in Globs.originals.keys():
if (not thumbnails.has_key(key)) or (not dontregen):
# Create a thumbnail for this original
newnails.append((key,'tmp.thumbnail',Globs.thumbnailwidth))
# Any webnails need to be built?
for key in Globs.originals.keys():
if (not webnails.has_key(key)) or (not dontregen):
# Create a webnail for this original
newnails.append((key,'tmp.webnail',Globs.webnailwidth))
# Ok, lets build them all at once
if not len(newnails)==0:
buildnails(newnails)
# If a regen of thumbnails and webnails has occurred, then we should also delete any tmp.rotated files.
if not dontregen:
for key in rotated.keys():
# Wrapped in a try except since child processes may try to unlink a second time
try:
os.unlink(Globs.gallerytempdir+'/tmp.rotated.'+key+'.jpg')
except:
pass
if formvals.has_key('annotate'):
if Globs.admin and formvals.has_key('target'):
target=formvals['target']
# Write an annotation file
atext=string.replace(formvals['annotate'],'"','&quot;')
target=formvals['target']
ouf=open(Globs.attachmentdir+'/tmp.annotation.'+target+'.txt','w')
ouf.write(atext)
ouf.close()
message('Annotation updated to <i>'+atext+'</i>',0)
# Now update the annotated dictionary
if not Globs.annotated.has_key(target):
Globs.annotated[target]=''
if formvals.has_key('webnail'):
# Does the webnail exist?
message('webnail requested')
target=formvals['webnail']
rotend=''
if Globs.originals.has_key(target):
out.write(navibar(target,'webnail'))
if formvals.has_key('rotate'):
direction=formvals['rotate']
message(direction)
rotate(target,direction)
rotend=Globs.galleryrotchar+'rot='+repr(randint(1,10000))
# Put things in a table
out.write(tleft)
# Lets build up an image tag
out.write('<a href="'+Globs.baseurl+Globs.pagename+'?original='+target+'&action=content"><img src="'+Globs.gallerytempurl+'tmp.webnail.'+target+'.jpg'+rotend+'"></a>\n')
out.write(trigh)
out.write(tleft)
atext=getannotation(target)
# Are we an administrator?
if Globs.admin:
# We always provide an annotation text field
out.write('<form action='+Globs.subname+' name=annotate METHOD=POST>')
out.write('<input maxLength=256 size=55 name=annotate value="'+atext+'">')
out.write('<input type=hidden value="'+target+'" name="target">')
out.write('<input type=hidden value="show" name="action">')
out.write('<input type=hidden value="'+target+'" name="webnail">')
out.write('</form>')
else:
out.write(atext)
out.write(trigh)
out.write(toolbar(target,'webnail'))
else:
message('I do not have file: '+target,0)
elif formvals.has_key('original'):
# Now we just construct a single item rather than a table
# Does the webnail exist?
message('original requested')
target=formvals['original']
rotend=''
if not Globs.originals.has_key(target):
message('I do not have file: '+target,0)
else:
if formvals.has_key('rotate'):
direction=formvals['rotate']
message(direction)
rotate(target,direction)
rotend=Globs.galleryrotchar+'rot='+repr(randint(1,10000))
rotated[target]=''
# Lets build up an image tag
out.write(navibar(target,'original'))
out.write(tleft)
originalfilename=Globs.originals[target]
# If there is a rotated version, show that instead
if rotated.has_key(target):
out.write('<a href="'+Globs.baseurl+Globs.pagename+'?webnail='+target+Globs.bcomp+'"><img src="'+Globs.gallerytempurl+'tmp.rotated.'+target+'.jpg'+rotend+'"></a>\n')
else:
out.write('<a href="'+Globs.baseurl+Globs.pagename+'?webnail='+target+Globs.bcomp+'"><img src="'+Globs.subname+'?action=AttachFile&amp;do=get&amp;target='+originalfilename+'"></a>\n')
out.write(trigh)
out.write(tleft)
atext=getannotation(target)
# Are we an administrator?
if Globs.admin:
# We always provide an annotation text field
out.write('<form action='+Globs.subname+' name=annotate METHOD=POST>')
out.write('<input maxLength=256 size=55 name=annotate value="'+atext+'">')
out.write('<input type=hidden value="'+target+'" name="target">')
out.write('<input type=hidden value="show" name="action">')
out.write('<input type=hidden value="'+target+'" name="original">')
out.write('</form>')
else:
out.write(atext)
out.write(trigh)
out.write(toolbar(target,'original'))
elif formvals.has_key('rotate'):
# We rotate all sizes of this image to the left or right
message('rotate requested')
target=formvals['target']
direction=formvals['rotate']
if not Globs.originals.has_key(target):
message('I do not have file: '+target,0)
else:
# Do the rotation
rotate(target,direction)
# Display the new image in webnail mode
# We may need a way of forcing the browser to reload the newly rotated image here (later)
out.write(tleft)
out.write('<a href="'+Globs.baseurl+Globs.pagename+'?webnail='+target+Globs.bcomp+'"><img src="'+Globs.gallerytempurl+'tmp.webnail.'+target+'.jpg"></a>\n')
out.write(trigh)
else:
# Finally lets build a table of thumbnails
thumbs=Globs.originals.keys()
thumbs.sort()
thumbs.reverse()
# If version number is requested (append a ?version=tellme&action=show to the page request)
# or if there are no original images, just give help message and return
if formvals.has_key('version') or len(thumbs)==0:
message(version(),0)
return macro.formatter.rawHTML( Globs.adminmsg )
out.write('\n<table>')
cease=''
rollover=''
while 1:
out.write('<tr>')
for i in range(Globs.numberofcolumns):
try:
item=thumbs.pop()
except IndexError:
cease='true'
break
# Alt text
atext=getannotation(item)
rollover='alt="'+atext+'" title="'+atext+'"'
# Table entry for thumbnail image
out.write('<td><a href="'+Globs.baseurl+Globs.pagename+'?webnail='+item+Globs.bcomp+'"><center><img src="'+Globs.gallerytempurl+'tmp.thumbnail.'+item+'.jpg" '+rollover+'></a></center></td>')
out.write('</tr>\n')
if cease:
out.write('</table>')
break
out.seek(0)
# Finally output any administrative messages at the top followed by any generated content
return macro.formatter.rawHTML(
Globs.adminmsg+'<p>'
+out.read()
)