From 1e7ce4861e0c02c49e780d0f00896bb78dda476f Mon Sep 17 00:00:00 2001 From: salles Date: Sun, 16 Apr 2006 11:27:13 +0200 Subject: [PATCH] Imports initiaux darcs-hash:20060416092713-72cb0-421d3cbb49699fce4971355f5dbbaa19091945e8.gz --- wiki/macro/Gallery.py | 688 +++++++++++++++++++++++++++++++ wiki/macro/ImageLink.py | 173 ++++++++ wiki/macro/ProgressBar.py | 220 ++++++++++ wiki/macro/RandomIncludeQuote.py | 244 +++++++++++ wiki/macro/RandomPageInclude.py | 219 ++++++++++ wiki/macro/RandomQuoteNum.py | 57 +++ wiki/macro/latex.py | 44 ++ 7 files changed, 1645 insertions(+) create mode 100644 wiki/macro/Gallery.py create mode 100644 wiki/macro/ImageLink.py create mode 100644 wiki/macro/ProgressBar.py create mode 100644 wiki/macro/RandomIncludeQuote.py create mode 100644 wiki/macro/RandomPageInclude.py create mode 100644 wiki/macro/RandomQuoteNum.py create mode 100644 wiki/macro/latex.py diff --git a/wiki/macro/Gallery.py b/wiki/macro/Gallery.py new file mode 100644 index 00000000..8a1e1613 --- /dev/null +++ b/wiki/macro/Gallery.py @@ -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 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+'Gallery:  '+astring+'
\n' + +def version(): + return(' version '+Globs.version+' by Simon D. Ryan.'+\ + '
Copyright 2004,2005 Simon D. Ryan
Gallery is a MoinMoin macro and is released under the '+\ + 'GPL\n'+\ + '

Upload some images as attachments to '+Globs.pagename+' and I will generate a gallery for you.') + +# Thanks to dennyece.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 ''+description+'' + +def navibar(target,querystring): + # Returns a navigational bar with PREV,THUMBS,NEXT + positions=Globs.originals.keys() + positions.sort() + thumbs='THUMBS' + 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 '
'+back+''+thumbs+''+forward+'
' + +def toolbar(target,naillevel): + if Globs.admin: + rotateleft='' + rotateright='' + htarget='' + compat='' + return '

'+rotateleft+''+rotateright+'
\n'+htarget+compat+'
' + 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('Please install ImageMagick or PIL so I can build thumbnails and webnails

',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('Please install ImageMagick so I can build thumbnails and webnails

',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='
' + tmidd='
' + trigh='
\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 = + 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+'

') + 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+'

') + + 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+'

') + + # 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 '+atext+'',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('\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('

') + out.write('') + out.write('') + out.write('') + out.write('') + out.write('
') + 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('\n') + else: + out.write('\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('
') + out.write('') + out.write('') + out.write('') + out.write('') + out.write('
') + 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('\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') + cease='' + rollover='' + while 1: + out.write('') + 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('') + out.write('\n') + if cease: + out.write('
') + break + + out.seek(0) + # Finally output any administrative messages at the top followed by any generated content + return macro.formatter.rawHTML( + Globs.adminmsg+'

' + +out.read() + ) + diff --git a/wiki/macro/ImageLink.py b/wiki/macro/ImageLink.py new file mode 100644 index 00000000..e280adfd --- /dev/null +++ b/wiki/macro/ImageLink.py @@ -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 and/or + 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 element. To add class to 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) + diff --git a/wiki/macro/ProgressBar.py b/wiki/macro/ProgressBar.py new file mode 100644 index 00000000..b4b7592b --- /dev/null +++ b/wiki/macro/ProgressBar.py @@ -0,0 +1,220 @@ +""" +MoinMoin - ProgressBar Macro +Generates a progress bar (in the form of a table) + +@copyright: Pascal Bauermeister +@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: + # black on white bar + # same, 90% table + + A third format may be given for STARTDATE,ENDDATE usage + + By default: <><#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 """ +

+ Error: macro ProgressBar: %s

+ """ % 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 """ +

+

%s

+ """ % 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 diff --git a/wiki/macro/RandomIncludeQuote.py b/wiki/macro/RandomIncludeQuote.py new file mode 100644 index 00000000..78efccb2 --- /dev/null +++ b/wiki/macro/RandomIncludeQuote.py @@ -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 + @copyright: 2000-2001 by Richard Jones + @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 = '

%s

' + +## keep in sync with TableOfContents macro! +_arg_heading = r'(?P,)\s*(|(?P[\'"])(?P.+?)(?P=hquote))' +_arg_level = r',\s*(?P\d*)' +_arg_from = r'(,\s*from=(?P[\'"])(?P.+?)(?P=fquote))?' +_arg_to = r'(,\s*to=(?P[\'"])(?P.+?)(?P=tquote))?' +_arg_sort = r'(,\s*sort=(?P(ascending|descending)))?' +_arg_items = r'(,\s*items=(?P\d+))?' +_arg_skipitems = r'(,\s*skipitems=(?P\d+))?' +_arg_titlesonly = r'(,\s*(?Ptitlesonly))?' +_arg_editlink = r'(,\s*(?Peditlink))?' +_args_re_pattern = r'^(?P[^,]+)(%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\s*(?P=+)\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'

Recursive include of "%s" forbidden

' % (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(' 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([ + '', + ]) + # 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 diff --git a/wiki/macro/RandomPageInclude.py b/wiki/macro/RandomPageInclude.py new file mode 100644 index 00000000..dc4844fb --- /dev/null +++ b/wiki/macro/RandomPageInclude.py @@ -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 = '

%s

' + +## keep in sync with TableOfContents macro! +_arg_heading = r'(?P,)\s*(|(?P[\'"])(?P.+?)(?P=hquote))' +_arg_level = r',\s*(?P\d*)' +_arg_from = r'(,\s*from=(?P[\'"])(?P.+?)(?P=fquote))?' +_arg_to = r'(,\s*to=(?P[\'"])(?P.+?)(?P=tquote))?' +_arg_sort = r'(,\s*sort=(?P(ascending|descending)))?' +_arg_items = r'(,\s*items=(?P\d+))?' +_arg_skipitems = r'(,\s*skipitems=(?P\d+))?' +_arg_titlesonly = r'(,\s*(?Ptitlesonly))?' +_arg_editlink = r'(,\s*(?Peditlink))?' +_args_re_pattern = r'^(?P[^,]+)(%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\s*(?P=+)\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'

Recursive include of "%s" forbidden

' % (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(' 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([ + '', + ]) + # 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 diff --git a/wiki/macro/RandomQuoteNum.py b/wiki/macro/RandomQuoteNum.py new file mode 100644 index 00000000..5b237729 --- /dev/null +++ b/wiki/macro/RandomQuoteNum.py @@ -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 + @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 + diff --git a/wiki/macro/latex.py b/wiki/macro/latex.py new file mode 100644 index 00000000..16b2baa9 --- /dev/null +++ b/wiki/macro/latex.py @@ -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("<>") + 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()