Imports initiaux
darcs-hash:20060416092713-72cb0-421d3cbb49699fce4971355f5dbbaa19091945e8.gz
This commit is contained in:
parent
0192d9e074
commit
1e7ce4861e
7 changed files with 1645 additions and 0 deletions
688
wiki/macro/Gallery.py
Normal file
688
wiki/macro/Gallery.py
Normal file
|
@ -0,0 +1,688 @@
|
||||||
|
# -*- 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>: '+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,'"','"')
|
||||||
|
|
||||||
|
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'
|
||||||
|
# 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'],'"','"')
|
||||||
|
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&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()
|
||||||
|
)
|
||||||
|
|
173
wiki/macro/ImageLink.py
Normal file
173
wiki/macro/ImageLink.py
Normal file
|
@ -0,0 +1,173 @@
|
||||||
|
# -*- coding: iso-8859-1 -*-
|
||||||
|
"""
|
||||||
|
MoinMoin - ImageLink Macro
|
||||||
|
|
||||||
|
PURPOSE:
|
||||||
|
This macro is used to set a link as WikiName for an attached image. Optional the size of the
|
||||||
|
image could be adjusted. If no WikiName is given the attachment is linked to the image itselfs.
|
||||||
|
|
||||||
|
CALLING SEQUENCE:
|
||||||
|
[[ImageLink(attachment|URL,[WikiName|URL],[width=width,[height=heigt]])]]
|
||||||
|
|
||||||
|
INPUTS:
|
||||||
|
attachment:image name of attachment or the URL to an image
|
||||||
|
|
||||||
|
OPTIONAL INPUTS:
|
||||||
|
WikiName: the page to set the link to or the URL to link to
|
||||||
|
|
||||||
|
|
||||||
|
KEYWORD PARAMETERS:
|
||||||
|
width: width of the embedded image
|
||||||
|
height: height of the embedded image
|
||||||
|
|
||||||
|
EXAMPLE:
|
||||||
|
[[ImageLink(plot.png,FrontPage,width=20,height=20)]]
|
||||||
|
[[ImageLink(plot.png,FrontPage,height=20)]]
|
||||||
|
[[ImageLink(plot.png,FrontPage)]]
|
||||||
|
[[ImageLink(plot.png,width=20,height=20)]]
|
||||||
|
[[ImageLink(plot.png,width=20)]]
|
||||||
|
[[ImageLink(plot.png)]]
|
||||||
|
[[ImageLink(http://localhost/wiki/modern/img/to_slide.png,http://localhost/StartSeite,width=50)]]
|
||||||
|
[[ImageLink(http://localhost/wiki/modern/img/to_slide.png,FrontPage,width=50)]]
|
||||||
|
[[ImageLink(plot.png,http://localhost/StartSeite,width=50)]]
|
||||||
|
[[ImageLink(http://localhost/wiki/modern/img/to_slide.png)]]
|
||||||
|
[[ImageLink(http://localhost/wiki/modern/img/to_slide.png,alt=whateveryouwant)]]
|
||||||
|
|
||||||
|
PROCEDURE:
|
||||||
|
This routine requires attachment enabled. If the attachment isn't downloaded at all
|
||||||
|
the attachment line is shown.
|
||||||
|
If you give only one image size argument e.g. width only the other one is calculated
|
||||||
|
|
||||||
|
It must be in "MoinMoin/macro"
|
||||||
|
|
||||||
|
Please remove the version number from the file name!
|
||||||
|
|
||||||
|
From JeffKunce ImageLink.py I have copied _is_URL to this routine. I have no better idea too.
|
||||||
|
|
||||||
|
HISTORY:
|
||||||
|
The first published version on MoinMoin I know of ImageLink was written by JeffKunce in 2001.
|
||||||
|
|
||||||
|
MODIFICATION HISTORY:
|
||||||
|
@copyright: 2004 by Reimar Bauer (R.Bauer@fz-juelich.de)
|
||||||
|
@license: GNU GPL, see COPYING for details.
|
||||||
|
|
||||||
|
Marcin Zalewski:
|
||||||
|
Some things that were causing problems on my wiki are changed
|
||||||
|
(wikiutil.link_tag and macro.formatter.pagelink implemented)
|
||||||
|
|
||||||
|
Marcin Zalewski:
|
||||||
|
Added title attribute to the created link. One could generalize that to
|
||||||
|
add arbitrary attributes.
|
||||||
|
|
||||||
|
One could also add class attributes to <a> and/or
|
||||||
|
<img> elements. I do not see the need for such modifications. If however this is
|
||||||
|
to be done in the future one would need to add 'html_class' key to the kw dictionary
|
||||||
|
with a corresponding value to add class to <img> element. To add class to <a> element
|
||||||
|
one needs to add 'css_class' key with a corresponding value to the dictionary passed to
|
||||||
|
pagelink call.
|
||||||
|
Reimar Bauer:
|
||||||
|
2004-12-23 Adopted to MoinMoin Version 1.3.1-1
|
||||||
|
2004-12-23 SYNTAX CHANGE Version 1.3.1-2
|
||||||
|
width and height and probably other keywords must be called as keywords (e.g. height=20)
|
||||||
|
2004-12-31 Version 1.3.1-3 code clean up
|
||||||
|
2005-01-16 Bug fixed in the errorhandler found and patch code from Malte Helmert
|
||||||
|
2005-03-05 Version 1.3.3-5 Bug fixed found by cypress
|
||||||
|
''If I put [[ImageLink(moinmoin.png)]] it bombs''
|
||||||
|
2005-03-28 Version 1.3.3-6 feature request added by CDPark:
|
||||||
|
''Can we use an external image? And an external target? ''
|
||||||
|
2005-04-16 Version 1.3.3-7 no default alt tag definition requested by CDPark and AlexanderSchremmer
|
||||||
|
Chong-Dae Park:
|
||||||
|
2005-04-17 Version 1.3.3-8 code refactored
|
||||||
|
IMG with no alt tag is not recommended in the HTML standard.
|
||||||
|
It keeps user specified alt tag. If it is not, it tries to use WikiName or image name instead.
|
||||||
|
Reimar Bauer:
|
||||||
|
2005-04-21 Version 1.3.3-9 bug fixed
|
||||||
|
When the image does not exist yet, it gives you a "Upload Image" link, this link does not
|
||||||
|
work. I suspect that this only is a problem on sub-pages, caused by incorrect escaping of
|
||||||
|
"/". -- CraigJohnson
|
||||||
|
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
from MoinMoin.action import AttachFile
|
||||||
|
from MoinMoin import wikiutil, config
|
||||||
|
import os,string
|
||||||
|
|
||||||
|
def _is_URL(aString):
|
||||||
|
'''answer true if aString is a URL.
|
||||||
|
The method used here is pretty dumb. Improvements are welcome.
|
||||||
|
jjk 03/28/01'''
|
||||||
|
return string.find(aString, '://')>0
|
||||||
|
|
||||||
|
|
||||||
|
def execute(macro, text):
|
||||||
|
|
||||||
|
kw ={} # create a dictionary for the formatter.image call
|
||||||
|
|
||||||
|
if text:
|
||||||
|
args=text.split(',')
|
||||||
|
else:
|
||||||
|
args=[]
|
||||||
|
|
||||||
|
number_args=len(args)
|
||||||
|
count=0
|
||||||
|
for a in args :
|
||||||
|
if (a.find('=') > -1):
|
||||||
|
count=count+1
|
||||||
|
key=a.split('=')
|
||||||
|
kw[str(key[0])]=wikiutil.escape(string.join(key[1],''), quote=1)
|
||||||
|
|
||||||
|
number_args=number_args-count
|
||||||
|
|
||||||
|
if (number_args < 1):
|
||||||
|
msg='Not enough arguments to ImageLink macro! Try [[ImageLink(attachment,[WikiName],[width=width,[height=heigt]])]]'
|
||||||
|
return macro.formatter.sysmsg(1)+macro.formatter.text(msg)+ macro.formatter.sysmsg(0)
|
||||||
|
|
||||||
|
|
||||||
|
attname=wikiutil.taintfilename(macro.formatter.text(args[0]))
|
||||||
|
if (number_args >= 2) :
|
||||||
|
wikiname=args[1]
|
||||||
|
current_pagename=macro.formatter.page.page_name
|
||||||
|
|
||||||
|
|
||||||
|
if _is_URL(args[0]):
|
||||||
|
kw['src']=args[0]
|
||||||
|
else:
|
||||||
|
kw['src']=AttachFile.getAttachUrl(current_pagename,attname,macro.request)
|
||||||
|
attachment_path = os.path.join(AttachFile.getAttachDir(macro.request,current_pagename), attname)
|
||||||
|
|
||||||
|
if not os.path.exists(attachment_path):
|
||||||
|
|
||||||
|
import urllib
|
||||||
|
linktext = macro.request.getText('Upload new attachment "%(filename)s"'%{
|
||||||
|
"filename":attname})
|
||||||
|
|
||||||
|
return wikiutil.link_tag(macro.request,
|
||||||
|
"%(pagename)s?action=AttachFile&rename=%(newname)s" % {
|
||||||
|
"pagename":current_pagename,
|
||||||
|
"newname": attname},linktext)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if not kw.has_key('alt'):
|
||||||
|
if (number_args == 1) or _is_URL(args[1]):
|
||||||
|
if _is_URL(args[0]):
|
||||||
|
# Get image name http://here.com/dir/image.gif -> image.gif
|
||||||
|
kw['alt'] = wikiutil.taintfilename(macro.formatter.text(args[0].split('/')[-1]))
|
||||||
|
kw['alt'] = args[0].split('/')[-1]
|
||||||
|
else:
|
||||||
|
kw['alt'] = attname
|
||||||
|
else:
|
||||||
|
kw['alt'] = wikiname
|
||||||
|
|
||||||
|
if (number_args == 1):
|
||||||
|
image_link=macro.formatter.image(**kw)
|
||||||
|
return macro.formatter.url(1,kw['src'] )+image_link +macro.formatter.url(0)
|
||||||
|
|
||||||
|
if (number_args == 2 ):
|
||||||
|
image_link=macro.formatter.image(**kw)
|
||||||
|
if _is_URL(args[1]):
|
||||||
|
return macro.formatter.url(1,args[1] )+image_link+macro.formatter.url(0)
|
||||||
|
else:
|
||||||
|
return macro.formatter.pagelink(1,wikiname)+image_link+macro.formatter.url(0)
|
||||||
|
|
220
wiki/macro/ProgressBar.py
Normal file
220
wiki/macro/ProgressBar.py
Normal file
|
@ -0,0 +1,220 @@
|
||||||
|
"""
|
||||||
|
MoinMoin - ProgressBar Macro
|
||||||
|
Generates a progress bar (in the form of a table)
|
||||||
|
|
||||||
|
@copyright: Pascal Bauermeister <pascal DOT bauermeister AT gmail DOT cm>
|
||||||
|
@license: GPL
|
||||||
|
|
||||||
|
Updates:
|
||||||
|
|
||||||
|
* [v0.1.1] Sun Dec 18 21:31:17 CET 2005
|
||||||
|
Changed table cell percentage markup.
|
||||||
|
|
||||||
|
* [v0.1.0] Fri Dec 16 22:30:10 CET 2005
|
||||||
|
Original version
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
The ProgressBar macro generates a table showing a progress indicator.
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
[[ ProgressBar ]]
|
||||||
|
[[ ProgressBar (TABLEWIDTH TABLEFORMAT PROGRESS%) ]]
|
||||||
|
[[ ProgressBar (TABLEWIDTH TABLEFORMAT CURRENT/STEPS) ]]
|
||||||
|
[[ ProgressBar (TABLEWIDTH TABLEFORMAT STARTDATE,ENDDATE) ]]
|
||||||
|
|
||||||
|
If no arguments are given, the usage is inserted in the HTML result.
|
||||||
|
|
||||||
|
Options:
|
||||||
|
|
||||||
|
TABLEWIDTH (optional prefix)
|
||||||
|
A wiki tablewidth attribute value between []'s
|
||||||
|
Examples:
|
||||||
|
[100%]
|
||||||
|
[80px]
|
||||||
|
|
||||||
|
TABLEFORMAT (optional prefix)
|
||||||
|
A pair of wiki table attribute, to format the inactive and active cells.
|
||||||
|
Examples:
|
||||||
|
<bgcolor="black"><bgcolor="white"> # black on white bar
|
||||||
|
<tablewidth="90%" bgcolor="black"><bgcolor="white"> # same, 90% table
|
||||||
|
|
||||||
|
A third format may be given for STARTDATE,ENDDATE usage
|
||||||
|
|
||||||
|
By default: <tablewidth="100px"#808080><><#8080ff>
|
||||||
|
|
||||||
|
PROGRESS
|
||||||
|
Will display a table with two cells:
|
||||||
|
- left: completion, taking PROGRESS % of the table width
|
||||||
|
- right: remaining
|
||||||
|
|
||||||
|
CURRENT/STEPS
|
||||||
|
Will display a table with STEPS cells, CURRENT of which are active.
|
||||||
|
|
||||||
|
STARTDATE,ENDDATE
|
||||||
|
Will display a table with the number of days, with the cell
|
||||||
|
representing today in active format and background in inactive format.
|
||||||
|
|
||||||
|
If today is before STARTDATE, the left-most cell will be in the
|
||||||
|
3rd format. If today is after ENDDATE the rightmost cell will be
|
||||||
|
in the 3rd format.
|
||||||
|
|
||||||
|
Dates are in this format: YYYY-MM-DD
|
||||||
|
|
||||||
|
Debugging
|
||||||
|
Please prepend a '?' to the arguments.
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
[[ProgressBar(60%)]]
|
||||||
|
[[ProgressBar(6/10)]]
|
||||||
|
[[ProgressBar(2005-11-01,2006-01-06)]]
|
||||||
|
|
||||||
|
[[ProgressBar([50%] 60%)]]
|
||||||
|
[[ProgressBar([50px] 60%)]]
|
||||||
|
[[ProgressBar([90%]<#8080ff><#808080> 6/10)]]
|
||||||
|
----
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
# Imports
|
||||||
|
import time, re, StringIO
|
||||||
|
from MoinMoin import version
|
||||||
|
from MoinMoin.parser import wiki
|
||||||
|
|
||||||
|
Dependencies = ["time"] # macro cannot be cached
|
||||||
|
|
||||||
|
|
||||||
|
class _Error (Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def escape (str):
|
||||||
|
return str.replace ('&','&').replace ('<', '<').replace ('>', '>')
|
||||||
|
|
||||||
|
def usage (full = False):
|
||||||
|
|
||||||
|
"""Returns the interesting part of the module's doc"""
|
||||||
|
|
||||||
|
if full:
|
||||||
|
return __doc__
|
||||||
|
else:
|
||||||
|
rx = re.compile ("--$(.*)^--", re.DOTALL + re.MULTILINE)
|
||||||
|
return rx.findall (__doc__) [0].strip ()
|
||||||
|
|
||||||
|
|
||||||
|
def s2t (s):
|
||||||
|
return time.mktime (time.strptime (s, "%Y-%m-%d"))
|
||||||
|
|
||||||
|
|
||||||
|
def execute (macro, text, args_re=None):
|
||||||
|
|
||||||
|
try: res = _execute (macro, text)
|
||||||
|
except Exception, msg:
|
||||||
|
return """
|
||||||
|
<p><strong class="error">
|
||||||
|
Error: macro ProgressBar: %s</strong> </p>
|
||||||
|
""" % escape ("%s" % msg)
|
||||||
|
return res
|
||||||
|
|
||||||
|
|
||||||
|
def _execute (macro, text):
|
||||||
|
|
||||||
|
fmt = ['#808080','','#8080ff']
|
||||||
|
width ="100px"
|
||||||
|
res = ""
|
||||||
|
text = text.strip ()
|
||||||
|
|
||||||
|
# help if empty text
|
||||||
|
help = len (text) == 0
|
||||||
|
|
||||||
|
# debug if starts with '?'
|
||||||
|
if text.startswith ('?'):
|
||||||
|
debug = True
|
||||||
|
text = text [1:]
|
||||||
|
else:
|
||||||
|
debug = False
|
||||||
|
orig_text = text
|
||||||
|
|
||||||
|
# Formats
|
||||||
|
try:
|
||||||
|
# Table width
|
||||||
|
if text.startswith ('['):
|
||||||
|
pos = text.rfind (']')
|
||||||
|
width = text [1:pos]
|
||||||
|
text = text [pos+1:].strip ()
|
||||||
|
|
||||||
|
# Cells format
|
||||||
|
if text.startswith ('<'):
|
||||||
|
pos = text.rfind ('>')
|
||||||
|
f = text [1:pos].split ('><')
|
||||||
|
text = text [pos+1:].strip ()
|
||||||
|
fmt [:len (f)] = f
|
||||||
|
except:
|
||||||
|
help = True
|
||||||
|
|
||||||
|
# Show help
|
||||||
|
if help:
|
||||||
|
return """
|
||||||
|
<p>
|
||||||
|
<pre>%s</pre></p>
|
||||||
|
""" % escape (usage (0))
|
||||||
|
|
||||||
|
# Cell formatting utility
|
||||||
|
def cell (txt, fmt):
|
||||||
|
if len (txt) == 0:
|
||||||
|
fmt = 'tablewidth="%s" ' % width + fmt
|
||||||
|
txt = "||"
|
||||||
|
if len (fmt): t = "<%s> ||" % fmt
|
||||||
|
else: t = " ||"
|
||||||
|
return txt + t
|
||||||
|
|
||||||
|
# Progress
|
||||||
|
if text.endswith ('%'):
|
||||||
|
for f in fmt [0] + ' %s' % text, fmt [1] :
|
||||||
|
res = cell (res, f)
|
||||||
|
|
||||||
|
# Current/Steps
|
||||||
|
elif text.find ('/') > 0:
|
||||||
|
cur, steps = map (int, text.split ('/'))
|
||||||
|
for i in range (steps):
|
||||||
|
res = cell (res, fmt [i>=cur])
|
||||||
|
|
||||||
|
# Start/end date
|
||||||
|
else:
|
||||||
|
starts, ends = map (lambda s:s.strip (), text.split (","))
|
||||||
|
start, end = s2t (starts), s2t (ends)
|
||||||
|
now = time.mktime (time.localtime ())
|
||||||
|
|
||||||
|
duration = int ( (end-start) / 86400)
|
||||||
|
progress = int ( (now-start) / 86400) -1
|
||||||
|
pcent = int (90 / duration)
|
||||||
|
|
||||||
|
for i in range (duration):
|
||||||
|
if i == 0 and progress < 0:
|
||||||
|
f = fmt [2]
|
||||||
|
elif i == progress:
|
||||||
|
f = fmt [0]
|
||||||
|
else:
|
||||||
|
f = fmt [1]
|
||||||
|
res = cell (res, f)
|
||||||
|
|
||||||
|
if progress >= duration:
|
||||||
|
res = cell (res, fmt [2])
|
||||||
|
else:
|
||||||
|
res = cell (res, fmt [1])
|
||||||
|
|
||||||
|
# Output
|
||||||
|
if debug:
|
||||||
|
res = "{{{[[ProgressBar(%s)]]\n%s}}}\n%s" % (orig_text, res, res)
|
||||||
|
return _format (res, macro.request, macro.formatter)
|
||||||
|
|
||||||
|
|
||||||
|
def _format (src_text, request, formatter):
|
||||||
|
# parse the text (in wiki source format) and make HTML,
|
||||||
|
# after diverting sys.stdout to a string
|
||||||
|
str_out = StringIO.StringIO () # create str to collect output
|
||||||
|
request.redirect (str_out) # divert output to that string
|
||||||
|
# parse this line
|
||||||
|
wiki.Parser (src_text, request).format (formatter)
|
||||||
|
request.redirect () # restore output
|
||||||
|
return str_out.getvalue () # return what was generated
|
244
wiki/macro/RandomIncludeQuote.py
Normal file
244
wiki/macro/RandomIncludeQuote.py
Normal file
|
@ -0,0 +1,244 @@
|
||||||
|
# -*- coding: iso-8859-1 -*-
|
||||||
|
"""
|
||||||
|
MoinMoin - RandomIncludeQuote macro
|
||||||
|
|
||||||
|
This macro includes the formatted content of the given page(s). See
|
||||||
|
|
||||||
|
http://purl.net/wiki/moinmaster/HelpOnMacros/Include
|
||||||
|
|
||||||
|
for detailed docs.
|
||||||
|
|
||||||
|
@copyright: 2000-2004 by Jürgen Hermann <jh@web.de>
|
||||||
|
@copyright: 2000-2001 by Richard Jones <richard@bizarsoftware.com.au>
|
||||||
|
@license: GNU GPL, see COPYING for details.
|
||||||
|
"""
|
||||||
|
|
||||||
|
#Dependencies = ["pages"] # included page
|
||||||
|
Dependencies = ["time"] # works around MoinMoinBugs/TableOfContentsLacksLinks
|
||||||
|
|
||||||
|
import re, StringIO
|
||||||
|
from MoinMoin import wikiutil
|
||||||
|
from MoinMoin.Page import Page
|
||||||
|
from MoinMoin.util import web
|
||||||
|
|
||||||
|
_sysmsg = '<p><strong class="%s">%s</strong></p>'
|
||||||
|
|
||||||
|
## keep in sync with TableOfContents macro!
|
||||||
|
_arg_heading = r'(?P<heading>,)\s*(|(?P<hquote>[\'"])(?P<htext>.+?)(?P=hquote))'
|
||||||
|
_arg_level = r',\s*(?P<level>\d*)'
|
||||||
|
_arg_from = r'(,\s*from=(?P<fquote>[\'"])(?P<from>.+?)(?P=fquote))?'
|
||||||
|
_arg_to = r'(,\s*to=(?P<tquote>[\'"])(?P<to>.+?)(?P=tquote))?'
|
||||||
|
_arg_sort = r'(,\s*sort=(?P<sort>(ascending|descending)))?'
|
||||||
|
_arg_items = r'(,\s*items=(?P<items>\d+))?'
|
||||||
|
_arg_skipitems = r'(,\s*skipitems=(?P<skipitems>\d+))?'
|
||||||
|
_arg_titlesonly = r'(,\s*(?P<titlesonly>titlesonly))?'
|
||||||
|
_arg_editlink = r'(,\s*(?P<editlink>editlink))?'
|
||||||
|
_args_re_pattern = r'^(?P<name>[^,]+)(%s(%s)?%s%s%s%s%s%s%s)?$' % (
|
||||||
|
_arg_heading, _arg_level, _arg_from, _arg_to, _arg_sort, _arg_items,
|
||||||
|
_arg_skipitems, _arg_titlesonly, _arg_editlink)
|
||||||
|
|
||||||
|
_title_re = r"^(?P<heading>\s*(?P<hmarker>=+)\s.*\s(?P=hmarker))$"
|
||||||
|
|
||||||
|
def extract_titles(body, title_re):
|
||||||
|
titles = []
|
||||||
|
for title, _ in title_re.findall(body):
|
||||||
|
h = title.strip()
|
||||||
|
level = 1
|
||||||
|
while h[level:level+1] == '=': level = level+1
|
||||||
|
depth = min(5,level)
|
||||||
|
title_text = h[level:-level].strip()
|
||||||
|
titles.append((title_text, level))
|
||||||
|
return titles
|
||||||
|
|
||||||
|
def execute(macro, text, args_re=re.compile(_args_re_pattern), title_re=re.compile(_title_re, re.M), called_by_toc=0):
|
||||||
|
request = macro.request
|
||||||
|
_ = request.getText
|
||||||
|
|
||||||
|
# return immediately if getting links for the current page
|
||||||
|
if request.mode_getpagelinks:
|
||||||
|
return ''
|
||||||
|
|
||||||
|
# parse and check arguments
|
||||||
|
args = args_re.match(text)
|
||||||
|
if not args:
|
||||||
|
return (_sysmsg % ('error', _('Invalid include arguments "%s"!')) % (text,))
|
||||||
|
|
||||||
|
# prepare including page
|
||||||
|
result = []
|
||||||
|
print_mode = macro.form.has_key('action') and macro.form['action'][0] == "print"
|
||||||
|
this_page = macro.formatter.page
|
||||||
|
if not hasattr(this_page, '_macroInclude_pagelist'):
|
||||||
|
this_page._macroInclude_pagelist = {}
|
||||||
|
|
||||||
|
# get list of pages to include
|
||||||
|
inc_name = wikiutil.AbsPageName(request, this_page.page_name, args.group('name'))
|
||||||
|
pagelist = [inc_name]
|
||||||
|
if inc_name.startswith("^"):
|
||||||
|
try:
|
||||||
|
inc_match = re.compile(inc_name)
|
||||||
|
except re.error:
|
||||||
|
pass # treat as plain page name
|
||||||
|
else:
|
||||||
|
# Get user filtered readable page list
|
||||||
|
pagelist = request.rootpage.getPageList(filter=inc_match.match)
|
||||||
|
|
||||||
|
# sort and limit page list
|
||||||
|
pagelist.sort()
|
||||||
|
sort_dir = args.group('sort')
|
||||||
|
if sort_dir == 'descending':
|
||||||
|
pagelist.reverse()
|
||||||
|
max_items = args.group('items')
|
||||||
|
if max_items:
|
||||||
|
pagelist = pagelist[:int(max_items)]
|
||||||
|
|
||||||
|
skipitems = 0
|
||||||
|
if args.group("skipitems"):
|
||||||
|
skipitems = int(args.group("skipitems"))
|
||||||
|
titlesonly = args.group('titlesonly')
|
||||||
|
editlink = args.group('editlink')
|
||||||
|
|
||||||
|
# iterate over pages
|
||||||
|
for inc_name in pagelist:
|
||||||
|
if not request.user.may.read(inc_name):
|
||||||
|
continue
|
||||||
|
if this_page._macroInclude_pagelist.has_key(inc_name):
|
||||||
|
result.append(u'<p><strong class="error">Recursive include of "%s" forbidden</strong></p>' % (inc_name,))
|
||||||
|
continue
|
||||||
|
if skipitems:
|
||||||
|
skipitems -= 1
|
||||||
|
continue
|
||||||
|
fmt = macro.formatter.__class__(request, is_included=True)
|
||||||
|
fmt._base_depth = macro.formatter._base_depth
|
||||||
|
inc_page = Page(request, inc_name, formatter=fmt)
|
||||||
|
inc_page._macroInclude_pagelist = this_page._macroInclude_pagelist
|
||||||
|
|
||||||
|
# check for "from" and "to" arguments (allowing partial includes)
|
||||||
|
body = inc_page.get_raw_body() + '\n'
|
||||||
|
from_pos = 0
|
||||||
|
to_pos = -1
|
||||||
|
from_re = args.group('from')
|
||||||
|
if from_re:
|
||||||
|
try:
|
||||||
|
from_match = re.compile(from_re, re.M).search(body)
|
||||||
|
except re.error, e:
|
||||||
|
##result.append("*** fe=%s ***" % e)
|
||||||
|
from_match = re.compile(re.escape(from_re), re.M).search(body)
|
||||||
|
if from_match:
|
||||||
|
from_pos = from_match.end()
|
||||||
|
else:
|
||||||
|
result.append(_sysmsg % ('warning', 'Include: ' + _('Nothing found for "%s"!')) % from_re)
|
||||||
|
to_re = args.group('to')
|
||||||
|
if to_re:
|
||||||
|
try:
|
||||||
|
to_match = re.compile(to_re, re.M).search(body, from_pos)
|
||||||
|
except re.error:
|
||||||
|
to_match = re.compile(re.escape(to_re), re.M).search(body, from_pos)
|
||||||
|
if to_match:
|
||||||
|
to_pos = to_match.start()
|
||||||
|
else:
|
||||||
|
result.append(_sysmsg % ('warning', 'Include: ' + _('Nothing found for "%s"!')) % to_re)
|
||||||
|
|
||||||
|
if titlesonly:
|
||||||
|
newbody = []
|
||||||
|
levelstack = []
|
||||||
|
for title, level in extract_titles(body[from_pos:to_pos], title_re):
|
||||||
|
if levelstack:
|
||||||
|
if level > levelstack[-1]:
|
||||||
|
result.append(macro.formatter.bullet_list(1))
|
||||||
|
levelstack.append(level)
|
||||||
|
else:
|
||||||
|
while levelstack and level < levelstack[-1]:
|
||||||
|
result.append(macro.formatter.bullet_list(0))
|
||||||
|
levelstack.pop()
|
||||||
|
if not levelstack or level != levelstack[-1]:
|
||||||
|
result.append(macro.formatter.bullet_list(1))
|
||||||
|
levelstack.append(level)
|
||||||
|
else:
|
||||||
|
result.append(macro.formatter.bullet_list(1))
|
||||||
|
levelstack.append(level)
|
||||||
|
result.append(macro.formatter.listitem(1))
|
||||||
|
result.append(inc_page.link_to(request, title))
|
||||||
|
result.append(macro.formatter.listitem(0))
|
||||||
|
while levelstack:
|
||||||
|
result.append(macro.formatter.bullet_list(0))
|
||||||
|
levelstack.pop()
|
||||||
|
continue
|
||||||
|
|
||||||
|
if from_pos or to_pos != -1:
|
||||||
|
inc_page.set_raw_body(body[from_pos:to_pos], modified=True)
|
||||||
|
##result.append("*** f=%s t=%s ***" % (from_re, to_re))
|
||||||
|
##result.append("*** f=%d t=%d ***" % (from_pos, to_pos))
|
||||||
|
|
||||||
|
if called_by_toc:
|
||||||
|
result.append(inc_page.get_raw_body())
|
||||||
|
continue
|
||||||
|
|
||||||
|
if not hasattr(request, "_Include_backto"):
|
||||||
|
request._Include_backto = this_page.page_name
|
||||||
|
|
||||||
|
# do headings
|
||||||
|
level = None
|
||||||
|
if args.group('heading') and args.group('hquote'):
|
||||||
|
heading = args.group('htext') or inc_page.split_title(request)
|
||||||
|
level = 1
|
||||||
|
if args.group('level'):
|
||||||
|
level = int(args.group('level'))
|
||||||
|
if print_mode:
|
||||||
|
result.append(macro.formatter.heading(1, level) +
|
||||||
|
macro.formatter.text(heading) +
|
||||||
|
macro.formatter.heading(0, level))
|
||||||
|
else:
|
||||||
|
import sha
|
||||||
|
from MoinMoin import config
|
||||||
|
# this heading id might produce duplicate ids,
|
||||||
|
# if the same page is included multiple times
|
||||||
|
# Encode stuf we feed into sha module.
|
||||||
|
pntt = (inc_name + heading).encode(config.charset)
|
||||||
|
hid = "head-" + sha.new(pntt).hexdigest()
|
||||||
|
request._page_headings.setdefault(pntt, 0)
|
||||||
|
request._page_headings[pntt] += 1
|
||||||
|
if request._page_headings[pntt] > 1:
|
||||||
|
hid += '-%d'%(request._page_headings[pntt],)
|
||||||
|
result.append(
|
||||||
|
#macro.formatter.heading(1, level, hid,
|
||||||
|
# icons=edit_icon.replace('<img ', '<img align="right" ')) +
|
||||||
|
macro.formatter.heading(1, level, hid) +
|
||||||
|
inc_page.link_to(request, heading, css_class="include-heading-link") +
|
||||||
|
macro.formatter.heading(0, level)
|
||||||
|
)
|
||||||
|
|
||||||
|
# set or increment include marker
|
||||||
|
this_page._macroInclude_pagelist[inc_name] = \
|
||||||
|
this_page._macroInclude_pagelist.get(inc_name, 0) + 1
|
||||||
|
|
||||||
|
# output the included page
|
||||||
|
strfile = StringIO.StringIO()
|
||||||
|
request.redirect(strfile)
|
||||||
|
try:
|
||||||
|
cid = request.makeUniqueID("Include_%s" % wikiutil.quoteWikinameFS(inc_page.page_name))
|
||||||
|
inc_page.send_page(request, content_only=1, content_id=cid)
|
||||||
|
result.append(strfile.getvalue())
|
||||||
|
finally:
|
||||||
|
request.redirect()
|
||||||
|
|
||||||
|
# decrement or remove include marker
|
||||||
|
if this_page._macroInclude_pagelist[inc_name] > 1:
|
||||||
|
this_page._macroInclude_pagelist[inc_name] = \
|
||||||
|
this_page._macroInclude_pagelist[inc_name] - 1
|
||||||
|
else:
|
||||||
|
del this_page._macroInclude_pagelist[inc_name]
|
||||||
|
|
||||||
|
# if no heading and not in print mode, then output a helper link
|
||||||
|
if editlink and not (level or print_mode):
|
||||||
|
result.extend([
|
||||||
|
'<div class="include-link">',
|
||||||
|
inc_page.link_to(request, '[%s]' % (inc_name,), css_class="include-page-link"),
|
||||||
|
inc_page.link_to(request, '[%s]' % (_('edit'),), css_class="include-edit-link", querystr={'action': 'edit', 'backto': request._Include_backto}),
|
||||||
|
'</div>',
|
||||||
|
])
|
||||||
|
# XXX page.link_to is wrong now, it escapes the edit_icon html as it escapes normal text
|
||||||
|
|
||||||
|
# return include text
|
||||||
|
return ''.join(result)
|
||||||
|
|
||||||
|
# vim:ts=4:sw=4:et
|
219
wiki/macro/RandomPageInclude.py
Normal file
219
wiki/macro/RandomPageInclude.py
Normal file
|
@ -0,0 +1,219 @@
|
||||||
|
# -*- coding: iso-8859-1 -*-
|
||||||
|
#Dependencies = ["pages"] # included page
|
||||||
|
Dependencies = ["time"] # works around MoinMoinBugs/TableOfContentsLacksLinks
|
||||||
|
|
||||||
|
import re, StringIO
|
||||||
|
from MoinMoin import wikiutil, search
|
||||||
|
from MoinMoin.Page import Page
|
||||||
|
from MoinMoin.util import web
|
||||||
|
from random import choice
|
||||||
|
|
||||||
|
_sysmsg = '<p><strong class="%s">%s</strong></p>'
|
||||||
|
|
||||||
|
## keep in sync with TableOfContents macro!
|
||||||
|
_arg_heading = r'(?P<heading>,)\s*(|(?P<hquote>[\'"])(?P<htext>.+?)(?P=hquote))'
|
||||||
|
_arg_level = r',\s*(?P<level>\d*)'
|
||||||
|
_arg_from = r'(,\s*from=(?P<fquote>[\'"])(?P<from>.+?)(?P=fquote))?'
|
||||||
|
_arg_to = r'(,\s*to=(?P<tquote>[\'"])(?P<to>.+?)(?P=tquote))?'
|
||||||
|
_arg_sort = r'(,\s*sort=(?P<sort>(ascending|descending)))?'
|
||||||
|
_arg_items = r'(,\s*items=(?P<items>\d+))?'
|
||||||
|
_arg_skipitems = r'(,\s*skipitems=(?P<skipitems>\d+))?'
|
||||||
|
_arg_titlesonly = r'(,\s*(?P<titlesonly>titlesonly))?'
|
||||||
|
_arg_editlink = r'(,\s*(?P<editlink>editlink))?'
|
||||||
|
_args_re_pattern = r'^(?P<name>[^,]+)(%s(%s)?%s%s%s%s%s%s%s)?$' % (
|
||||||
|
_arg_heading, _arg_level, _arg_from, _arg_to, _arg_sort, _arg_items,
|
||||||
|
_arg_skipitems, _arg_titlesonly, _arg_editlink)
|
||||||
|
|
||||||
|
_title_re = r"^(?P<heading>\s*(?P<hmarker>=+)\s.*\s(?P=hmarker))$"
|
||||||
|
|
||||||
|
def extract_titles(body, title_re):
|
||||||
|
titles = []
|
||||||
|
for title, _ in title_re.findall(body):
|
||||||
|
h = title.strip()
|
||||||
|
level = 1
|
||||||
|
while h[level:level+1] == '=': level = level+1
|
||||||
|
depth = min(5,level)
|
||||||
|
title_text = h[level:-level].strip()
|
||||||
|
titles.append((title_text, level))
|
||||||
|
return titles
|
||||||
|
|
||||||
|
def execute(macro, text, args_re=re.compile(_args_re_pattern), title_re=re.compile(_title_re, re.M), called_by_toc=0):
|
||||||
|
request = macro.request
|
||||||
|
_ = request.getText
|
||||||
|
|
||||||
|
qnumber = choice(range(1, 10, 1))
|
||||||
|
text = u'QuestionnaireCrans/Question\d+[a-z]?$,, from="^= Question =$", to="^----$"'
|
||||||
|
|
||||||
|
# return immediately if getting links for the current page
|
||||||
|
if request.mode_getpagelinks:
|
||||||
|
return ''
|
||||||
|
|
||||||
|
# parse and check arguments
|
||||||
|
args = args_re.match(text)
|
||||||
|
if not args:
|
||||||
|
return (_sysmsg % ('error', _('Invalid include arguments "%s"!')) % (text,))
|
||||||
|
|
||||||
|
# Search the pages and return the results
|
||||||
|
query = search.QueryParser(regex=1).parse_query(args.group('name'))
|
||||||
|
results = search.searchPages(request, query)
|
||||||
|
results.sortByPagename()
|
||||||
|
pagelist = [results.hits[qnumber].page_name]
|
||||||
|
|
||||||
|
# prepare including page
|
||||||
|
result = []
|
||||||
|
print_mode = macro.form.has_key('action') and macro.form['action'][0] == "print"
|
||||||
|
this_page = macro.formatter.page
|
||||||
|
if not hasattr(this_page, '_macroInclude_pagelist'):
|
||||||
|
this_page._macroInclude_pagelist = {}
|
||||||
|
|
||||||
|
skipitems = 0
|
||||||
|
if args.group("skipitems"):
|
||||||
|
skipitems = int(args.group("skipitems"))
|
||||||
|
titlesonly = args.group('titlesonly')
|
||||||
|
editlink = args.group('editlink')
|
||||||
|
|
||||||
|
# iterate over pages
|
||||||
|
for inc_name in pagelist:
|
||||||
|
if not request.user.may.read(inc_name):
|
||||||
|
continue
|
||||||
|
if this_page._macroInclude_pagelist.has_key(inc_name):
|
||||||
|
result.append(u'<p><strong class="error">Recursive include of "%s" forbidden</strong></p>' % (inc_name,))
|
||||||
|
continue
|
||||||
|
if skipitems:
|
||||||
|
skipitems -= 1
|
||||||
|
continue
|
||||||
|
fmt = macro.formatter.__class__(request, is_included=True)
|
||||||
|
fmt._base_depth = macro.formatter._base_depth
|
||||||
|
inc_page = Page(request, inc_name, formatter=fmt)
|
||||||
|
inc_page._macroInclude_pagelist = this_page._macroInclude_pagelist
|
||||||
|
|
||||||
|
# check for "from" and "to" arguments (allowing partial includes)
|
||||||
|
body = inc_page.get_raw_body() + '\n'
|
||||||
|
from_pos = 0
|
||||||
|
to_pos = -1
|
||||||
|
from_re = args.group('from')
|
||||||
|
if from_re:
|
||||||
|
try:
|
||||||
|
from_match = re.compile(from_re, re.M).search(body)
|
||||||
|
except re.error, e:
|
||||||
|
##result.append("*** fe=%s ***" % e)
|
||||||
|
from_match = re.compile(re.escape(from_re), re.M).search(body)
|
||||||
|
if from_match:
|
||||||
|
from_pos = from_match.end()
|
||||||
|
else:
|
||||||
|
result.append(_sysmsg % ('warning', 'Include: ' + _('Nothing found for "%s"!')) % from_re)
|
||||||
|
to_re = args.group('to')
|
||||||
|
if to_re:
|
||||||
|
try:
|
||||||
|
to_match = re.compile(to_re, re.M).search(body, from_pos)
|
||||||
|
except re.error:
|
||||||
|
to_match = re.compile(re.escape(to_re), re.M).search(body, from_pos)
|
||||||
|
if to_match:
|
||||||
|
to_pos = to_match.start()
|
||||||
|
else:
|
||||||
|
result.append(_sysmsg % ('warning', 'Include: ' + _('Nothing found for "%s"!')) % to_re)
|
||||||
|
|
||||||
|
if titlesonly:
|
||||||
|
newbody = []
|
||||||
|
levelstack = []
|
||||||
|
for title, level in extract_titles(body[from_pos:to_pos], title_re):
|
||||||
|
if levelstack:
|
||||||
|
if level > levelstack[-1]:
|
||||||
|
result.append(macro.formatter.bullet_list(1))
|
||||||
|
levelstack.append(level)
|
||||||
|
else:
|
||||||
|
while levelstack and level < levelstack[-1]:
|
||||||
|
result.append(macro.formatter.bullet_list(0))
|
||||||
|
levelstack.pop()
|
||||||
|
if not levelstack or level != levelstack[-1]:
|
||||||
|
result.append(macro.formatter.bullet_list(1))
|
||||||
|
levelstack.append(level)
|
||||||
|
else:
|
||||||
|
result.append(macro.formatter.bullet_list(1))
|
||||||
|
levelstack.append(level)
|
||||||
|
result.append(macro.formatter.listitem(1))
|
||||||
|
result.append(inc_page.link_to(request, title))
|
||||||
|
result.append(macro.formatter.listitem(0))
|
||||||
|
while levelstack:
|
||||||
|
result.append(macro.formatter.bullet_list(0))
|
||||||
|
levelstack.pop()
|
||||||
|
continue
|
||||||
|
|
||||||
|
if from_pos or to_pos != -1:
|
||||||
|
inc_page.set_raw_body(body[from_pos:to_pos], modified=True)
|
||||||
|
##result.append("*** f=%s t=%s ***" % (from_re, to_re))
|
||||||
|
##result.append("*** f=%d t=%d ***" % (from_pos, to_pos))
|
||||||
|
|
||||||
|
if called_by_toc:
|
||||||
|
result.append(inc_page.get_raw_body())
|
||||||
|
continue
|
||||||
|
|
||||||
|
if not hasattr(request, "_Include_backto"):
|
||||||
|
request._Include_backto = this_page.page_name
|
||||||
|
|
||||||
|
# do headings
|
||||||
|
level = None
|
||||||
|
if args.group('heading') and args.group('hquote'):
|
||||||
|
heading = args.group('htext') or inc_page.split_title(request)
|
||||||
|
level = 1
|
||||||
|
if args.group('level'):
|
||||||
|
level = int(args.group('level'))
|
||||||
|
if print_mode:
|
||||||
|
result.append(macro.formatter.heading(1, level) +
|
||||||
|
macro.formatter.text(heading) +
|
||||||
|
macro.formatter.heading(0, level))
|
||||||
|
else:
|
||||||
|
import sha
|
||||||
|
from MoinMoin import config
|
||||||
|
# this heading id might produce duplicate ids,
|
||||||
|
# if the same page is included multiple times
|
||||||
|
# Encode stuf we feed into sha module.
|
||||||
|
pntt = (inc_name + heading).encode(config.charset)
|
||||||
|
hid = "head-" + sha.new(pntt).hexdigest()
|
||||||
|
request._page_headings.setdefault(pntt, 0)
|
||||||
|
request._page_headings[pntt] += 1
|
||||||
|
if request._page_headings[pntt] > 1:
|
||||||
|
hid += '-%d'%(request._page_headings[pntt],)
|
||||||
|
result.append(
|
||||||
|
#macro.formatter.heading(1, level, hid,
|
||||||
|
# icons=edit_icon.replace('<img ', '<img align="right" ')) +
|
||||||
|
macro.formatter.heading(1, level, hid) +
|
||||||
|
inc_page.link_to(request, heading, css_class="include-heading-link") +
|
||||||
|
macro.formatter.heading(0, level)
|
||||||
|
)
|
||||||
|
|
||||||
|
# set or increment include marker
|
||||||
|
this_page._macroInclude_pagelist[inc_name] = \
|
||||||
|
this_page._macroInclude_pagelist.get(inc_name, 0) + 1
|
||||||
|
|
||||||
|
# output the included page
|
||||||
|
strfile = StringIO.StringIO()
|
||||||
|
request.redirect(strfile)
|
||||||
|
try:
|
||||||
|
cid = request.makeUniqueID("Include_%s" % wikiutil.quoteWikinameFS(inc_page.page_name))
|
||||||
|
inc_page.send_page(request, content_only=1, content_id=cid)
|
||||||
|
result.append(strfile.getvalue())
|
||||||
|
finally:
|
||||||
|
request.redirect()
|
||||||
|
|
||||||
|
# decrement or remove include marker
|
||||||
|
if this_page._macroInclude_pagelist[inc_name] > 1:
|
||||||
|
this_page._macroInclude_pagelist[inc_name] = \
|
||||||
|
this_page._macroInclude_pagelist[inc_name] - 1
|
||||||
|
else:
|
||||||
|
del this_page._macroInclude_pagelist[inc_name]
|
||||||
|
|
||||||
|
# if no heading and not in print mode, then output a helper link
|
||||||
|
if editlink and not (level or print_mode):
|
||||||
|
result.extend([
|
||||||
|
'<div class="include-link">',
|
||||||
|
inc_page.link_to(request, '[%s]' % (inc_name,), css_class="include-page-link"),
|
||||||
|
inc_page.link_to(request, '[%s]' % (_('edit'),), css_class="include-edit-link", querystr={'action': 'edit', 'backto': request._Include_backto}),
|
||||||
|
'</div>',
|
||||||
|
])
|
||||||
|
# XXX page.link_to is wrong now, it escapes the edit_icon html as it escapes normal text
|
||||||
|
|
||||||
|
# return include text
|
||||||
|
return ''.join(result)
|
||||||
|
|
||||||
|
# vim:ts=4:sw=4:et
|
57
wiki/macro/RandomQuoteNum.py
Normal file
57
wiki/macro/RandomQuoteNum.py
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
# -*- coding: iso-8859-1 -*-
|
||||||
|
"""
|
||||||
|
MoinMoin - RandomQuote Macro
|
||||||
|
|
||||||
|
Selects a random quote from FortuneCookies or a given page.
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
[[RandomQuote()]]
|
||||||
|
[[RandomQuote(WikiTips)]]
|
||||||
|
|
||||||
|
Comments:
|
||||||
|
It will look for list delimiters on the page in question.
|
||||||
|
It will ignore anything that is not in an "*" list.
|
||||||
|
|
||||||
|
@copyright: 2002-2004 by Jürgen Hermann <jh@web.de>
|
||||||
|
@license: GNU GPL, see COPYING for details.
|
||||||
|
|
||||||
|
Originally written by Thomas Waldmann.
|
||||||
|
Gustavo Niemeyer added wiki markup parsing of the quotes.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import random, StringIO
|
||||||
|
from MoinMoin.Page import Page, wikiutil
|
||||||
|
|
||||||
|
Dependencies = ["time"]
|
||||||
|
|
||||||
|
def execute(macro, args):
|
||||||
|
_ = macro.request.getText
|
||||||
|
|
||||||
|
pagename = args or 'FortuneCookies'
|
||||||
|
if macro.request.user.may.read(pagename):
|
||||||
|
page = Page(macro.request, pagename)
|
||||||
|
raw = page.get_raw_body()
|
||||||
|
else:
|
||||||
|
raw = ""
|
||||||
|
|
||||||
|
# this selects lines looking like a list item
|
||||||
|
# !!! TODO: make multi-line quotes possible (optionally split by "----" or something)
|
||||||
|
quotes = raw.splitlines()
|
||||||
|
quotes = [quote.strip() for quote in quotes]
|
||||||
|
quotes = [quote[2:] for quote in quotes if quote.startswith('1. ')]
|
||||||
|
|
||||||
|
if not quotes:
|
||||||
|
return (macro.formatter.highlight(1) +
|
||||||
|
_('No quotes on %(pagename)s.') % {'pagename': pagename} +
|
||||||
|
macro.formatter.highlight(0))
|
||||||
|
|
||||||
|
quote = random.choice(quotes)
|
||||||
|
page.set_raw_body(quote, 1)
|
||||||
|
out = StringIO.StringIO()
|
||||||
|
macro.request.redirect(out)
|
||||||
|
page.send_page(macro.request, content_only=1, content_id="RandomQuote_%s" % wikiutil.quoteWikinameFS(page.page_name) )
|
||||||
|
quote = out.getvalue()
|
||||||
|
macro.request.redirect()
|
||||||
|
|
||||||
|
return quote
|
||||||
|
|
44
wiki/macro/latex.py
Normal file
44
wiki/macro/latex.py
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
#FORMAT python
|
||||||
|
"""
|
||||||
|
See the latex parser, this is just a thin wrapper around it.
|
||||||
|
"""
|
||||||
|
# Imports
|
||||||
|
from MoinMoin import wikiutil
|
||||||
|
import re
|
||||||
|
|
||||||
|
Dependencies = []
|
||||||
|
|
||||||
|
splitre = re.compile(r'([^\\])%')
|
||||||
|
|
||||||
|
class latex:
|
||||||
|
def __init__(self, macro, args):
|
||||||
|
self.macro = macro
|
||||||
|
self.formatter = macro.formatter
|
||||||
|
self.text = args
|
||||||
|
|
||||||
|
def renderInPage(self):
|
||||||
|
# return immediately if getting links for the current page
|
||||||
|
if self.macro.request.mode_getpagelinks:
|
||||||
|
return ''
|
||||||
|
|
||||||
|
if self.text is None: # macro call without parameters
|
||||||
|
return ''
|
||||||
|
|
||||||
|
# get an exception? for moin before 1.3.2 use the following line instead:
|
||||||
|
# L = wikiutil.importPlugin('parser', 'latex', 'Parser', self.macro.cfg.data_dir)
|
||||||
|
L = wikiutil.importPlugin(self.macro.cfg, 'parser', 'latex', 'Parser')
|
||||||
|
if L is None:
|
||||||
|
return self.formatter.text("<<please install the latex parser>>")
|
||||||
|
l = L('', self.macro.request)
|
||||||
|
tmp = splitre.split(self.text, 1)
|
||||||
|
if len(tmp) == 3:
|
||||||
|
prologue,p2,tex=tmp
|
||||||
|
prologue += p2
|
||||||
|
else:
|
||||||
|
prologue = ''
|
||||||
|
tex = tmp[0]
|
||||||
|
return l.get(self.formatter, tex, prologue)
|
||||||
|
|
||||||
|
|
||||||
|
def execute(macro, args):
|
||||||
|
return latex(macro, args).renderInPage()
|
Loading…
Add table
Add a link
Reference in a new issue