746 lines
28 KiB
Python
746 lines
28 KiB
Python
# -*- coding: iso-8859-1 -*-
|
|
"""
|
|
Gallery.py Version 0.87
|
|
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.87"
|
|
|
|
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>: '+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()
|
|
# Append the action to the end of the URLS. This allows us to keep modes such as action=print
|
|
thumbs='<a href="'+Globs.subname+'?'+Globs.bcomp+'">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">'
|
|
deleteitem='<input type="submit" name="delete" value="delete">'
|
|
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><td>'+deleteitem+'</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 deletefromdisk(target):
|
|
# 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 delete request again later.",0)
|
|
return ''
|
|
# Ok do the actual delete
|
|
if 1:
|
|
# Delete first the temp dir webnail and thumbnail
|
|
try:
|
|
os.unlink(Globs.gallerytempdir+'/tmp.webnail.'+target+'.jpg')
|
|
except:
|
|
pass
|
|
try:
|
|
os.unlink(Globs.gallerytempdir+'/tmp.thumbnail.'+target+'.jpg')
|
|
except:
|
|
pass
|
|
try:
|
|
os.unlink(Globs.gallerytempdir+'/tmp.rotated.'+target+'.jpg')
|
|
except:
|
|
pass
|
|
|
|
# Now delete the original (except we actually just move it to /tmp)
|
|
# TODO: insert a random number in the destination filename to cope with deleting files with same name
|
|
origfn=Globs.originals[target]
|
|
try:
|
|
#shutil.copy(Globs.attachmentdir+'/'+origfn,'/tmp/deleted.'+origfn)
|
|
shutil.copy(Globs.attachmentdir+'/'+origfn,'/tmp/'+origfn)
|
|
os.unlink(Globs.attachmentdir+'/'+origfn)
|
|
except:
|
|
pass
|
|
try:
|
|
#shutil.copy(Globs.attachmentdir+'/tmp.annotation.'+target+'.txt','/tmp/deleted.tmp.annotation.'+target+'.txt')
|
|
shutil.copy(Globs.attachmentdir+'/tmp.annotation.'+target+'.txt','/tmp/tmp.annotation.'+target+'.txt')
|
|
os.unlink(Globs.attachmentdir+'/tmp.annotation.'+target+'.txt')
|
|
except:
|
|
pass
|
|
|
|
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"')
|
|
# Don't bother rotating the original. Since we don't want to reduce its quality incase it is used for printing
|
|
#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):
|
|
try:
|
|
atext=open(Globs.attachmentdir+'/tmp.annotation.'+target+'.txt').readline()
|
|
except:
|
|
atext=''
|
|
message('was annotated')
|
|
else:
|
|
message('was not annotated')
|
|
# replace double quotes with the html escape so quoted annotations appear
|
|
return string.replace(atext,'"','"')
|
|
|
|
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&do=get&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'
|
|
|
|
# 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
|
|
|
|
# Add this to the end of each URL to keep some versions of moinmoin happy
|
|
if formvals.has_key('action'):
|
|
if formvals['action']=='content':
|
|
# translate content action to print action for now
|
|
Globs.bcomp='&action=print'
|
|
else:
|
|
Globs.bcomp='&action='+formvals['action']
|
|
else:
|
|
Globs.bcomp='&action=show'
|
|
|
|
# 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:
|
|
# The following line stops an exception being raised when trying
|
|
# to create a directory that already exists (thanks Magnus Wahrenberg)
|
|
if not os.access(Globs.gallerytempdirroot+compbit, os.F_OK):
|
|
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'],'"','"')
|
|
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'))
|
|
out.write(toolbar(target,'webnail'))
|
|
if formvals.has_key('rotate'):
|
|
direction=formvals['rotate']
|
|
message(direction)
|
|
rotate(target,direction)
|
|
rotend=Globs.galleryrotchar+'rot='+repr(randint(1,10000))
|
|
if formvals.has_key('delete'):
|
|
message('Deleted <i>'+target+'</i>',0)
|
|
deletefromdisk(target)
|
|
# 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&do=get&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()
|
|
)
|
|
|