Attachment 'Gallery-082.py'

Download

   1 # -*- coding: iso-8859-1 -*-
   2 """
   3     Gallery.py  Version 0.82
   4                                                                                                            
   5     This macro creates dynamic tabulated displays based on attachment contents
   6                                                                                                            
   7     @copyright: 2004 by Simon Ryan <simon<at>smartblackbox.com>  http://smartblackbox.com/simon
   8     @license: GPL
   9 
  10     Special thanks go to: 
  11 	My beautiful wife Jenny: For keeping the kids at bay long enough for me to code it :-)
  12         Adam Shand: For his GallerySoftware feature wish list, support, ideas and suggestions.
  13 
  14     Usage: [[Gallery(key1=value1,key2=value2....)]]
  15 
  16       where the following keys are valid:
  17 	  thumbnailwidth     = no of pixels wide to make the thumbnails
  18 	  webnailwidth       = width in pixels of the web sized images
  19 	  numberofcolumns    = no of columns used in the thumbnail table
  20 
  21     Bugs:
  22 
  23       All attachments are expected to be images
  24       Continued rotation will degrade the tmp images (but they can be forced to regen)
  25 
  26     Features:
  27 
  28       Simple usage, just put [[Gallery]] on any page and upload some pictures as attachments
  29       Rotate buttons
  30       Annotation
  31 
  32     Not yet implemented, but in the works:
  33       Comment on this Pic button (to create a moinmoin subpage)
  34       Handling of video formats 
  35       Support for Python Imaging Library
  36 
  37     Speed up:
  38 	# When you get really sick of how slow the moinmoin image system is, 
  39 	# you can set the following variables in your moin_config.py
  40 	gallerytempdir (the path to a writable directory)
  41 	gallerytempurl (the path in your webservers url space where this directory can be read from)
  42         eg:
  43 	    gallerytempdir='/var/www/html/nails'
  44 	    gallerytempurl='/nails'
  45 	  or maybe:
  46 	    gallerytempurl=url_prefix+'/nails'
  47 	# There are other ways of getting speedups for attachment, but this method is the safest (IMHO)
  48 
  49 """
  50 
  51 __author__ = "Simon D. Ryan"
  52 __version__ = "0.82"
  53 
  54 from MoinMoin import config, wikiutil
  55 import string, cStringIO, os
  56 import commands, shutil
  57 import moin_config
  58 
  59 class Globs:
  60     # A quick place to plonk those shared variables
  61     thumbnailwidth='200'
  62     webnailwidth='600'
  63     numberofcolumns=4
  64     adminmsg=''
  65     debuglevel=0
  66     originals={}
  67     convertbin=''
  68     annotated={}
  69     attachmentdir=''
  70     gallerytempdirroot=''
  71     gallerytempdir=''
  72     gallerytempurl=''
  73     pagename=''
  74     admin=''
  75     bcomp=''
  76     baseurl=''
  77     timeout=40
  78 
  79 def message(astring,level=1):
  80     if level<=Globs.debuglevel:
  81         Globs.adminmsg=Globs.adminmsg+'<font color="#FF0000"><strong>Gallery</strong></font>:&nbsp;&nbsp;'+astring+'<br>\n'
  82 
  83 def version():
  84         return(' version <b>'+Globs.version+'</b> by Simon D. Ryan.'+\
  85 	'<br>Copyright 2004 Simon D. Ryan<br>Gallery is a MoinMoin macro and is released under the '+\
  86 	'<a href="http://www.gnu.org/licenses/gpl.txt">GPL</a>\n'+\
  87 	'<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.')
  88 
  89 # Thanks to denny<at>ece.arizona.edu
  90 # This can be replaced with a static translation table to speed things up (later)
  91 def mktrans():
  92         # Allow only letters and digits and a few other valid file characters
  93 	alphanumeric=string.letters+string.digits+'.,-_\'!"'
  94 	source_string=""
  95 	destination_string=""
  96 	for i in range(256):
  97 		source_string=source_string+chr(i)
  98 		if chr(i) in alphanumeric:
  99 			destination_string=destination_string+chr(i)
 100 		else:
 101 			destination_string=destination_string+' '
 102 	return string.maketrans(source_string,destination_string)
 103 
 104 def qlink(pagename, querystring, query, description=''):
 105     # Returns a hyperlink constructed as a form query on pagename
 106     if not description:
 107         description=query
 108     return '<a href="'+Globs.baseurl+pagename+'?'+querystring+'='+query+Globs.bcomp+'">'+description+'</a>'
 109 
 110 def navibar(target,querystring):
 111     # Returns a navigational bar with PREV,THUMBS,NEXT
 112     positions=Globs.originals.keys()
 113     positions.sort()
 114     thumbs='<a href="'+Globs.pagename+'">THUMBS</a>'
 115     index=positions.index(target)
 116     back,forward='',''
 117     if not index==0:
 118         # We are not the first so we can provide a back link
 119 	back=qlink(Globs.pagename, querystring, positions[index-1], 'PREV')
 120     if not index==len(positions)-1:
 121         # We are not the last so we can provide a forward link
 122 	forward=qlink(Globs.pagename, querystring, positions[index+1], 'NEXT')
 123     return '<table><tr><td>'+back+'</td><td>'+thumbs+'</td><td>'+forward+'</td></tr></table>'
 124 
 125 def toolbar(target,naillevel):
 126     if Globs.admin:
 127 	rotateleft='<input type="submit" name="rotate" value="rotate left">'
 128 	rotateright='<input type="submit" name="rotate" value="rotate right">'
 129 	htarget='<input type=hidden value="'+target+'" name="'+naillevel+'">'
 130 	compat='<input type=hidden value="show" name="action">'
 131 	return '<form METHOD=POST><table><tr><td>'+rotateleft+'</td><td>'+rotateright+'</td></tr></table>\n'+htarget+compat+'</form>'
 132     else:
 133         return ''
 134 
 135 def buildnails(items):
 136     # For now we use commands.getoutput to do our dirty work
 137     # Later we can build a batch job and fork it off.
 138 
 139     # Make sure our temp directory is writable and generate a message if it isn't
 140     try:
 141 	if not os.path.isfile(Globs.gallerytempdir+'/tmp.writetest'):
 142 	    # There is probably a less ugly was to do this using stat (later)
 143 	    open(Globs.gallerytempdir+'/tmp.writetest','w').close()
 144     except IOError:
 145         message('I had some trouble writing to the temp directory. Is it owned by me and writable?',0)
 146 
 147     # Don't go further if there is a lock in place
 148     if os.path.isfile(Globs.attachmentdir+'/tmp.lock'):
 149         message("I'm currently busy generating thumbnails and webnails, please try again later.",0)
 150 	return ''
 151 
 152     # Find the convert binary in standard locations
 153     if not os.path.isfile('/usr/bin/convert'):
 154         if not os.path.isfile('/usr/X11R6/bin/convert'):
 155 	    message('<b>Please install ImageMagick so I can build thumbnails and webnails</b><p>',0)
 156 	    return
 157 	else:
 158 	    Globs.convertbin='/usr/X11R6/bin/convert'
 159     else:
 160 	Globs.convertbin='/usr/bin/convert'
 161 
 162     # Create a lock file in the attachments dir so we can always remotely remove it if there is a problem
 163     open(Globs.attachmentdir+'/tmp.lock','w').close()
 164     
 165     import time
 166     tstart=time.time()
 167     pid,pid2='',''
 168 
 169     # For each original file, check for the existance of a nail
 170     for item in items:
 171         basename,prefix,width=item
 172 
 173 	# Check to see if we tarry too long on the road
 174 	if tstart and (time.time()-tstart) > Globs.timeout:
 175 	    # This is taking waaay too long let us fork and detach else the browser will time out or worse, the webserver may kill us
 176 	    pid = os.fork()
 177 	    if pid != 0:
 178 	        # We are in the parent so we break out
 179 		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)
 180 		break
 181 	    else:
 182 		# Once we are forked we want to ignore the time
 183 		tstart=''
 184 	        # Break away from the controlling terminal, so that the web server cannot kill us by killing our parent
 185 	        os.setsid()
 186 		# Fork again so we can get away without a controlling terminal
 187 		pid2 = os.fork()
 188 		if (pid2 != 0):
 189 		    os._exit(0)
 190 		else:
 191 		    # Close all open file descriptors
 192 		    try:
 193 		        max_fd = os.sysconf("SC_OPEN_MAX")
 194 		    except (AttributeError, ValueError):
 195 		        max_fd = 256
 196 		    for fd in range(0, max_fd):
 197                         try:
 198 			    os.close(fd)
 199 			except OSError:
 200 			    pass
 201 		    # Redirect the standard file descriptors to /dev/null
 202 		    os.open("/dev/null", os.O_RDONLY)    # stdin
 203 		    os.open("/dev/null", os.O_RDWR)      # stdout
 204 		    os.open("/dev/null", os.O_RDWR)      # stderr
 205 
 206 		    # Now we are finally free to continue the conversions as a daemon
 207 		    # If you would like to know more about the above, see:
 208 		    #   Advanced Programming in the Unix Environment: W. Richard Stevens
 209 		    # It is also explained in:
 210 		    #   Unix Network Programming (Volume 1): W. Richard Stevens
 211 
 212 	pathtooriginal='"'+Globs.attachmentdir+'/'+Globs.originals[basename]+'"'
 213 	# Warning:
 214 	# Take care if modifying the following line, 
 215 	# you may inadvertantly overwrite your original images!
 216 	convout=commands.getoutput('%s -geometry %s %s "%s/%s.%s.jpg"' % (Globs.convertbin,width+'x'+width,pathtooriginal,Globs.gallerytempdir,prefix,basename))
 217 	convout=string.strip(convout)
 218 	if convout:
 219 	    message(convout)
 220 
 221     if (not pid) and (not pid2):
 222 	# Release the lock file when finished
 223 	os.unlink(Globs.attachmentdir+'/tmp.lock')
 224 
 225     # We have built thumbnails so we can deposit an indicator file to prevent rebuilding next time
 226     if not os.path.isfile(Globs.attachmentdir+'/delete.me.to.regenerate.thumbnails.and.webnails'):
 227         open(Globs.attachmentdir+'/delete.me.to.regenerate.thumbnails.and.webnails','w').close()
 228 
 229 
 230 def rotate(target,direction):
 231     # Rotate the images
 232     # Don't go further if there is a lock in place
 233     if os.path.isfile(Globs.attachmentdir+'/tmp.lock'):
 234         message("I'm currently busy generating thumbnails and webnails. Please try your rotate request again later.",0)
 235 	return ''
 236 
 237     # Find the correct binary
 238     if not os.path.isfile('/usr/bin/mogrify'):
 239         if not os.path.isfile('/usr/X11R6/bin/mogrify'):
 240 	    message('<b>Please install ImageMagick so I can build thumbnails and webnails</b><p>',0)
 241 	    return
 242 	else:
 243 	    Globs.convertbin='/usr/X11R6/bin/mogrify'
 244     else:
 245 	Globs.convertbin='/usr/bin/mogrify'
 246 
 247     # Do the actual rotations
 248     if direction=='rotate right':
 249 	degs='90'
 250     else:
 251 	degs='270'
 252     convout=commands.getoutput(Globs.convertbin+' -rotate '+degs+' '+Globs.gallerytempdir+'/tmp.webnail.'+target+'.jpg')
 253     convout=commands.getoutput(Globs.convertbin+' -rotate '+degs+' '+Globs.gallerytempdir+'/tmp.thumbnail.'+target+'.jpg')
 254     if not os.path.isfile(Globs.gallerytempdir+'/tmp.rotated.'+target+'.jpg'):
 255         # Generate from original
 256 	pathtooriginal=Globs.attachmentdir+'/'+Globs.originals[target]
 257 	shutil.copy(pathtooriginal,Globs.gallerytempdir+'/tmp.rotated.'+target+'.jpg')
 258     convout=commands.getoutput(Globs.convertbin+' -rotate '+degs+' '+Globs.gallerytempdir+'/tmp.rotated.'+target+'.jpg')
 259 
 260 def getannotation(target):
 261     # Annotations are stored as a file for now (later to be stored in images)
 262     atext=''
 263     if Globs.annotated.has_key(target):
 264 	atext=open(Globs.attachmentdir+'/tmp.annotation.'+target+'.txt').readline()
 265 	message('was annotated')
 266     else:
 267 	message('was not annotated')
 268     # replace double quotes with the html escape so quoted annotations appear
 269     return string.replace(atext,'"','&quot;')
 270 
 271 def execute(macro, args):
 272 
 273     Globs.version=__version__
 274 
 275     # Containers
 276     formvals={}
 277     thumbnails={}
 278     webnails={}
 279     rotated={}
 280 
 281     # Class variables need to be specifically set 
 282     # (except for the case where a value is to be shared with another Gallery macro on the same wiki page)
 283     Globs.originals={}
 284     Globs.annotated={}
 285     Globs.attachmentdir=''
 286     Globs.admin=''
 287     Globs.adminmsg=''
 288     Globs.pagename=''
 289 
 290     # process arguments
 291     if args:
 292 	# Arguments are comma delimited key=value pairs
 293 	sargs=string.split(args,',')
 294 	for item in sargs:
 295 	    sitem=string.split(item,'=')
 296 	    if len(sitem)==2:
 297 		key,value=sitem[0],sitem[1]
 298 		if key=='thumbnailwidth':
 299 		    Globs.thumbnailwidth=value
 300 		elif key=='webnailwidth':
 301 		    Globs.webnailwidth=value
 302 		elif key=='numberofcolumns':
 303 		    try:
 304 			Globs.numberofcolumns=string.atoi(value)
 305 		    except TypeError:
 306 		        pass
 307 		# Experimental, uncomment at own risk
 308 		#elif key=='pagename':
 309 		#    Globs.pagename=value
 310 
 311     transtable=mktrans()
 312 
 313     # Useful variables
 314     dontregen=''
 315     annotationmessage=''
 316     textdir=config.text_dir
 317     Globs.baseurl=macro.request.getBaseURL()+'/'
 318     if not Globs.pagename:
 319 	Globs.pagename = string.replace(macro.formatter.page.page_name,'/','_2f')
 320     # Hmmm. A bug in moinmoin? underscores are getting escaped. These doubly escaped pagenames are even appearing in data/pages
 321     pagepath = string.replace(wikiutil.getPagePath(Globs.pagename),'_5f','_')
 322     Globs.attachmentdir = pagepath+'/attachments'
 323     if hasattr(moin_config,'gallerytempdir') and hasattr(moin_config,'gallerytempurl'):
 324         message('gallerytempdir and gallerytempurl found')
 325 	Globs.gallerytempdirroot=moin_config.gallerytempdir
 326 	Globs.gallerytempdir=moin_config.gallerytempdir+'/'+Globs.pagename+'/'
 327 	Globs.gallerytempurl=moin_config.gallerytempurl+'/'+Globs.pagename+'/'
 328     elif hasattr(moin_config,'attachments'):
 329 	Globs.gallerytempdirroot=moin_config.attachments['dir']
 330 	Globs.gallerytempdir=moin_config.attachments['dir']+'/'+Globs.pagename+'/attachments/'
 331 	Globs.gallerytempurl=moin_config.attachments['url']+'/'+Globs.pagename+'/attachments/'
 332 	Globs.attachmentdir = Globs.gallerytempdir
 333     else:
 334 	Globs.gallerytempdir=Globs.attachmentdir
 335 	Globs.gallerytempurl=Globs.pagename+'?action=AttachFile&amp;do=get&amp;target='
 336     if args:
 337         args=macro.request.getText(args)
 338 
 339 
 340     # HTML Constants
 341     tleft='<table><tr><td><center>'
 342     tmidd='</center></td><td><center>'
 343     trigh='</center></td></tr></table>\n'
 344     # Add this to the end of each URL to keep some versions of moinmoin happy 
 345     Globs.bcomp='&action=show'
 346 
 347     # Process any form items into a dictionary (values become unique)
 348     for item in macro.form.items():
 349         if not formvals.has_key(item[0]):
 350             # Here is where we clean the untrusted web input
 351 	    # (sometimes we get foreign keys from moinmoin when the page is edited)
 352 	    try:
 353 		formvals[item[0]]=string.translate(item[1][0],transtable)
 354 	    except AttributeError:
 355 	        pass
 356 
 357     # Figure out if we have delete privs
 358     try:
 359         # If a user can delete the page containing the Gallery, then they are considered a Gallery administrator
 360 	# This probably should be configurable via a moin_config variable eg: galleryadminreq = <admin|delete|any>
 361         if macro.request.user.may.delete(macro.formatter.page.page_name):
 362             Globs.admin='true'
 363     except AttributeError:
 364         pass
 365     
 366     out=cStringIO.StringIO()
 367 
 368     # Grab a list of the files in the attachment directory
 369     if os.path.isdir(Globs.attachmentdir):
 370         if Globs.gallerytempdir==Globs.attachmentdir:
 371 	    afiles=os.listdir(Globs.attachmentdir)
 372 	else:
 373 	    if not os.path.isdir(Globs.gallerytempdir):
 374 	        # Try to create it if it is absent
 375 		spagename=string.split(Globs.pagename,'/')
 376 		compbit=''
 377 		for component in spagename:
 378 		    compbit=compbit+'/'+component
 379 		    os.mkdir(Globs.gallerytempdirroot+compbit)
 380 		#os.mkdir(Globs.gallerytempdir)
 381 	    if os.path.isdir(Globs.gallerytempdir):
 382 		afiles=os.listdir(Globs.attachmentdir)+os.listdir(Globs.gallerytempdir)
 383 	    else:
 384 	        message('You need to create the temp dir first:'+Globs.gallerytempdir,0)
 385 		return macro.formatter.rawHTML(
 386 		    Globs.adminmsg+'<p>')
 387 
 388 	# Split out the thumbnails and webnails
 389 	for item in afiles:
 390 	    if item.startswith('tmp.thumbnail.'):
 391 	        origname=item[14:-4]
 392 		thumbnails[origname]=''
 393 	    elif item.startswith('tmp.webnail.'):
 394 	        origname=item[12:-4]
 395 		webnails[origname]=''
 396 	    elif item.startswith('tmp.rotated.'):
 397 	        origname=item[12:-4]
 398 		rotated[origname]=''
 399 	    elif item.startswith('tmp.annotation.'):
 400 	        origname=item[15:-4]
 401 		Globs.annotated[origname]=''
 402 	    elif item == 'delete.me.to.regenerate.thumbnails.and.webnails':
 403 	        dontregen='true'
 404 	    elif item == 'tmp.writetest' or item == 'tmp.lock':
 405 	        pass
 406 	    else:
 407 	        # This must be one of the original images
 408 		lastdot=string.rfind(item,'.')
 409 		origname=item[:lastdot]
 410 		Globs.originals[origname]=item
 411     else:
 412         message(version(),0)
 413 	return macro.formatter.rawHTML( Globs.adminmsg )
 414 
 415     if not Globs.gallerytempdir==Globs.attachmentdir and os.path.isfile(Globs.attachmentdir+'/tmp.writetest'):
 416 	    # If we are using the new gallerytempdir and we were using the old system then make sure there are no 
 417 	    # remnant files from the old system in the attachment dir to confuse us
 418 	    message('You have changed to using a gallerytempdir so I am cleaning old tmp files from your attachment dir.',0)
 419 	    for item in webnails.keys():
 420 	        try:
 421 		    os.unlink(Globs.attachmentdir+'/tmp.webnail.'+item+'.jpg')
 422 		except:
 423 		    pass
 424 	    # Try deleting any old thumbnails which may be in the attachment directory
 425             for item in thumbnails.keys():	
 426 	        try:
 427 		    os.unlink(Globs.attachmentdir+'/tmp.thumbnail.'+item+'.jpg')
 428 		except:
 429 		    pass
 430 	    # Try deleting any old rotated originals which may be in the attachment directory
 431             for item in rotated.keys():	
 432 	        try:
 433 		    os.unlink(Globs.attachmentdir+'/tmp.rotated.'+item+'.jpg')
 434 		except:
 435 		    pass
 436 	    os.unlink(Globs.attachmentdir+'/tmp.writetest')
 437 
 438     newnails=[]
 439     # Any thumbnails need to be built?
 440     for key in Globs.originals.keys():
 441         if (not thumbnails.has_key(key)) or (not dontregen):
 442 	    # Create a thumbnail for this original
 443 	    newnails.append((key,'tmp.thumbnail',Globs.thumbnailwidth))
 444     # Any webnails need to be built?
 445     for key in Globs.originals.keys():
 446         if (not webnails.has_key(key)) or (not dontregen):
 447 	    # Create a webnail for this original
 448 	    newnails.append((key,'tmp.webnail',Globs.webnailwidth))
 449     # Ok, lets build them all at once
 450     if not len(newnails)==0:
 451 	buildnails(newnails)
 452 
 453     # If a regen of thumbnails and webnails has occurred, then we should also delete any tmp.rotated files.
 454     if not dontregen:
 455         for key in rotated.keys():
 456 	    # Wrapped in a try except since child processes may try to unlink a second time
 457 	    try:
 458 		os.unlink(Globs.gallerytempdir+'/tmp.rotated.'+key+'.jpg')
 459 	    except:
 460 		pass
 461 
 462     if formvals.has_key('annotate'):
 463         if Globs.admin and formvals.has_key('target'):
 464 	    target=formvals['target']
 465 	    # Write an annotation file
 466 	    atext=string.replace(formvals['annotate'],'"','&quot;')
 467 	    target=formvals['target']
 468 	    ouf=open(Globs.attachmentdir+'/tmp.annotation.'+target+'.txt','w')
 469 	    ouf.write(atext)
 470 	    ouf.close()
 471 	    message('Annotation updated to <i>'+atext+'</i>',0)
 472 	    # Now update the annotated dictionary
 473             if not Globs.annotated.has_key(target):
 474 	        Globs.annotated[target]=''
 475 
 476     if formvals.has_key('webnail'):
 477 	# Does the webnail exist?
 478         message('webnail requested')
 479 	target=formvals['webnail']
 480 	if Globs.originals.has_key(target):
 481 	    out.write(navibar(target,'webnail'))
 482 	    if formvals.has_key('rotate'):
 483 		direction=formvals['rotate']
 484 		message(direction)
 485 		rotate(target,direction)
 486 	    # Put things in a table
 487 	    out.write(tleft)
 488 	    # Lets build up an image tag
 489 	    out.write('<a href="'+Globs.baseurl+Globs.pagename+'?original='+target+'&action=content"><img src="'+Globs.gallerytempurl+'tmp.webnail.'+target+'.jpg"></a>\n')
 490 	    out.write(trigh)
 491 	    out.write(tleft)
 492 
 493 	    atext=getannotation(target)
 494 
 495 	    # Are we an administrator?
 496 	    if Globs.admin:
 497 	        # We always provide an annotation text field
 498 		out.write('<form action='+Globs.pagename+' name=annotate METHOD=POST>')
 499 		out.write('<input maxLength=256 size=55 name=annotate value="'+atext+'">')
 500 		out.write('<input type=hidden value="'+target+'" name="target">')
 501 		out.write('<input type=hidden value="show" name="action">')
 502 		out.write('<input type=hidden value="'+target+'" name="webnail">')
 503 		out.write('</form>')
 504 	    else:
 505 		out.write(atext)
 506 	    out.write(trigh)
 507 	    out.write(toolbar(target,'webnail'))
 508 
 509 	else:
 510 	    message('I do not have file: '+target,0)
 511     elif formvals.has_key('original'):
 512 	# Now we just construct a single item rather than a table
 513 	# Does the webnail exist?
 514         message('original requested')
 515 	target=formvals['original']
 516 	if not Globs.originals.has_key(target):
 517 	    message('I do not have file: '+target,0)
 518 	else:
 519 	    if formvals.has_key('rotate'):
 520 		direction=formvals['rotate']
 521 		message(direction)
 522 		rotate(target,direction)
 523 	    # Lets build up an image tag
 524 	    out.write(navibar(target,'original'))
 525 	    out.write(tleft)
 526 	    originalfilename=Globs.originals[target]
 527 	    # If there is a rotated version, show that instead
 528 	    if rotated.has_key(target):
 529 		out.write('<a href="'+Globs.baseurl+Globs.pagename+'?webnail='+target+Globs.bcomp+'"><img src="'+Globs.gallerytempurl+'tmp.rotated.'+target+'.jpg"></a>\n')
 530 	    else:
 531 		out.write('<a href="'+Globs.baseurl+Globs.pagename+'?webnail='+target+Globs.bcomp+'"><img src="'+Globs.pagename+'?action=AttachFile&amp;do=get&amp;target='+originalfilename+'"></a>\n')
 532 	    out.write(trigh)
 533 	    out.write(tleft)
 534 
 535 	    atext=getannotation(target)
 536 
 537 	    # Are we an administrator?
 538 	    if Globs.admin:
 539 	        # We always provide an annotation text field
 540 		out.write('<form action='+Globs.pagename+' name=annotate METHOD=POST>')
 541 		out.write('<input maxLength=256 size=55 name=annotate value="'+atext+'">')
 542 		out.write('<input type=hidden value="'+target+'" name="target">')
 543 		out.write('<input type=hidden value="show" name="action">')
 544 		out.write('<input type=hidden value="'+target+'" name="original">')
 545 		out.write('</form>')
 546 	    else:
 547 		out.write(atext)
 548 	    out.write(trigh)
 549 	    out.write(toolbar(target,'original'))
 550 
 551     elif formvals.has_key('rotate'):
 552         # We rotate all sizes of this image to the left or right
 553 	message('rotate requested')
 554 	target=formvals['target']
 555 	direction=formvals['rotate']
 556 	if not Globs.originals.has_key(target):
 557 	    message('I do not have file: '+target,0)
 558 	else:
 559 	    # Do the rotation
 560 	    rotate(target,direction)
 561             # Display the new image in webnail mode 
 562 	    # We may need a way of forcing the browser to reload the newly rotated image here (later)
 563 	    out.write(tleft)
 564 	    out.write('<a href="'+Globs.baseurl+Globs.pagename+'?webnail='+target+Globs.bcomp+'"><img src="'+Globs.gallerytempurl+'tmp.webnail.'+target+'.jpg"></a>\n')
 565 	    out.write(trigh)
 566 
 567     else:
 568 	# Finally lets build a table of thumbnails
 569 	thumbs=Globs.originals.keys()
 570 	thumbs.sort()
 571 	thumbs.reverse()
 572 	# If version number is requested (append a ?version=tellme&action=show to the page request)
 573 	# or if there are no original images, just give help message and return
 574 	if formvals.has_key('version') or len(thumbs)==0:
 575 	    message(version(),0)
 576 	    return macro.formatter.rawHTML( Globs.adminmsg )
 577 	out.write('\n<table>')
 578 	cease=''
 579 	rollover=''
 580 	while 1:
 581 	    out.write('<tr>')
 582 	    for i in range(Globs.numberofcolumns):
 583 		try:
 584 		    item=thumbs.pop()
 585 		except IndexError:
 586 		    cease='true'
 587 		    break
 588 
 589 		# Alt text
 590 		atext=getannotation(item)
 591 		rollover='alt="'+atext+'" title="'+atext+'"'
 592 
 593 		# Table entry for thumbnail image
 594 		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>')
 595 	    out.write('</tr>\n')
 596 	    if cease:
 597 		out.write('</table>')
 598 		break
 599 	
 600     out.seek(0)
 601     # Finally output any administrative messages at the top followed by any generated content
 602     return macro.formatter.rawHTML(
 603         Globs.adminmsg+'<p>'
 604         +out.read()	
 605     )

Attached Files

To refer to attachments on a page, use attachment:filename, as shown below in the list of files. Do NOT use the URL of the [get] link, since this is subject to change and can break easily.
  • [get | view] (2004-07-12 23:54:14, 22.8 KB) [[attachment:Gallery-082.py]]
  • [get | view] (2005-08-19 07:17:36, 26.0 KB) [[attachment:Gallery-086.py]]
  • [get | view] (2006-07-21 22:15:07, 28.0 KB) [[attachment:Gallery-087.py]]
  • [get | view] (2008-04-07 11:37:15, 9.0 KB) [[attachment:UmlautError_Gallery.txt]]
 All files | Selected Files: delete move to page copy to page

You are not allowed to attach a file to this page.