Attachment 'Gallery-087.py'

Download

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

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.