Attachment 'PDF-1.5.py'

Download

   1 # -*- coding: iso-8859-1 -*-
   2 """
   3     MoinMoin - PageAsPDF action Version 1.5, 04.07.2014 
   4 
   5     @copyright: 2013, 2014 Ian Riley <ian@riley.asia>
   6     
   7     History:
   8     
   9         Version 1.5 = 17.10.2014: enable SlideSet pdf, minor fixes. 
  10     
  11         Version 1.4 - 04.07.2014: pdf in page cache, app in cfg.
  12     
  13         Version 1.3 - 19.02.2013: enabled call from guestpass action.
  14         
  15         Version 1.2 - 12.02.2013: enabled pragma settings for options.
  16 
  17         Version 1.1 - 12.02.2013: added option to show the subrocess call.
  18          
  19         Version 1.0 - 06.02.2013: initial version.
  20    
  21     incorporting code from:
  22     
  23     @copyright: 2001 by Ken Sugino (sugino@mediaone.net),
  24                 2001-2004 by Juergen Hermann <jh@web.de>,
  25                 2005 MoinMoin:AlexanderSchremmer,
  26                 2005 DiegoOngaro at ETSZONE (diego@etszone.com),
  27                 2005-2007 MoinMoin:ReimarBauer,
  28                 2007-2008 MoinMoin:ThomasWaldmann
  29 
  30     @license: GNU GPL, see COPYING for details.
  31 """
  32 import os, time, codecs, datetime
  33 from subprocess import call
  34 from werkzeug import http_date
  35 from MoinMoin import caching, config, wikiutil
  36 from MoinMoin.Page import Page
  37 
  38 action_name = __name__.split('.')[-1]
  39 actname = {'actname': action_name}
  40 
  41 class Options():
  42     def __init__(self):
  43         #               Option              Defaults: wiki and wkhtmltopdf Order              
  44         self.values = {'margin-top':       ['20mm',           '10mm',   1],
  45                        'margin-bottom':    ['20mm',           '10mm',   2],
  46                        'margin-left':      ['11mm',           '10mm',   3],
  47                        'margin-right':     ['10mm',           '10mm',   4],
  48                        'orientation':      ['Portrait',   'Portrait',   5],
  49                        'page-height':      ['297mm',         '297mm',   6],
  50                        'page-size':        ['A4',               'A4',   7],
  51                        'page-width':       ['210mm',         '210mm',   8],
  52                        'title':            ['[pagename]',         '',   9],
  53                        'header-font-name': ['Arial',         'Arial',  10],
  54                        'header-font-size': ['9',                '12',  11],
  55                        'header-spacing':   ['10',                '0',  12],
  56                        'header-line':      [False,             False,  13],
  57                        'header-left':      ['[pagename]',         '',  14],
  58                        'header-center':    ['',                   '',  15],
  59                        'header-right':     ['[date] [time]',      '',  16],
  60                        'footer-font-name': ['Arial',         'Arial',  17],
  61                        'footer-font-size': ['9',                '12',  18],
  62                        'footer-spacing':   ['10',                 '',  19],
  63                        'footer-line':      [False,             False,  20],
  64                        'footer-left':      ['[url]',              '',  21],
  65                        'footer-center':    ['',                   '',  22],
  66                        'footer-right':     ['[page] of [topage]', '',  23],
  67                        }
  68     def __repr__(self):
  69         return str(self.formatted())
  70         
  71     def update(self, new_opts = {}):
  72         for k, v in new_opts.iteritems():
  73             if k in self.values.keys():
  74                 if type(v) <> type(self.values[k][1]):
  75                     v = False
  76                 self.values[k][0] = v
  77         return self.values
  78     
  79     def replace(self, code, value):
  80         code = '[%s]' % code
  81         for k, v in self.values.iteritems():
  82             if code in str(v[0]):
  83                 self.values[k][0] = v[0].replace(code, str(value))    
  84     
  85     def formatted(self):
  86         values = sorted(self.values.iteritems(), key=lambda i: i[1][2])
  87         couplets = [('--%s%s' % (k, [' %s' % v[0],''][type(v[0])==bool])).
  88                      split(' ', 1)
  89                      for k, v in values
  90                      if v[0] and (v[0] <> v[1])] #only if not app default 
  91         return [item for sublist in couplets for item in sublist] #flatten
  92         
  93 def cookies(request):
  94     triplets = [['--cookie', k, str(v)] for k, v in request.cookies.iteritems()]
  95     return [item for sublist in triplets for item in sublist] #flatten
  96            
  97 def page_options(request):
  98     args = [str(args[4:]).split(' ',1)
  99             for verb, args in request.page.meta
 100             if verb == 'pragma' and args.startswith('pdf ')]
 101     options = {}
 102     for i, option in enumerate(args):
 103         option[0] = option[0].replace('centre', 'center')
 104         if len(option) == 1:
 105             args[i].append(True)
 106         options[option[0]] = option[1]
 107     return options
 108     
 109 def error(request):
 110     msg = '%(actname)s failed.' % actname
 111     request.theme.add_msg(msg, 'error')
 112     return
 113     
 114 def make_pdf(pagename, request, show='text', guestpass=False):
 115 
 116     filename = '%s.pdf' % pagename.replace('/','-')
 117     pdf_datetime = request.user.getFormattedDateTime(time.time())
 118     pdf_date = request.user.getFormattedDate(time.time())
 119     pdf_time = pdf_datetime.replace(pdf_date, '').strip()
 120     
 121     options = Options()
 122     options.update(page_options(request))
 123     
 124     #prepare for subprocess call  
 125     try:
 126         app = request.cfg.pdf_app
 127     except AttributeError:
 128         error(request)
 129         return
 130     if not guestpass:
 131         querystr={'action': 'print'}
 132         if show == 'slides': 
 133             querystr={'action': 'SlideSet'}
 134             options.update({'orientation': 'Landscape'})
 135     else:
 136         querystr={'action': 'GuestPass', 'pass': guestpass, 'print': '1'}
 137     url = (request.url_root + request.page.url(request, 
 138            querystr=querystr, relative=True))
 139     clean_url = (request.url_root + 
 140                  request.page.url(request, relative=True)).replace('%20',' ')
 141     #Store the pdf in the page cache
 142     #Modify refresh action to allow for its removal when needed
 143     #Use caching to decide if update needed
 144     cache = '%s_pdf.pdf' % show
 145     arena = Page(request, pagename)
 146     apath = arena.getPagePath()
 147     cached = caching.CacheEntry(request, arena, cache, scope='item')
 148     update = cached.needsUpdate(apath)
 149     fpath = '%s/cache/%s' % (apath, cache)
 150     if update == 1: #does not exist yet
 151         update = True
 152 
 153     if update or show == 'call':
 154         options.replace('pagename', pagename)
 155         options.replace('url', clean_url)
 156         #overide to give user not server date/time
 157         options.replace('date', pdf_date)
 158         options.replace('time', pdf_time)
 159 
 160         params = ([app, '-q'] + 
 161               options.formatted() +
 162               [url] + 
 163               cookies(request) + 
 164               [fpath])
 165   
 166     #make and respond to subprocess call
 167     if show == 'call' and request.user.may.admin(pagename):
 168         for i, p in enumerate(params):
 169             if ' ' in p:
 170                 params[i] = '"%s"' % p
 171         request.theme.add_msg(' '.join(params), 'info')
 172     elif show in ['text', 'slides']:
 173         call_error = False
 174         if update:
 175             try:
 176                 call_error = call(params)
 177             except:
 178                 error(request)
 179                 return
 180         if call_error and not guestpass: #TODO remove this test once resolved
 181             error(request)
 182         else:
 183             filename_enc = filename.encode(config.charset)
 184             timestamp = datetime.datetime.fromtimestamp(os.path.getmtime(fpath))
 185             mt = wikiutil.MimeType(filename=filename)
 186             content_type = mt.content_type()
 187             mime_type = mt.mime_type()
 188             now = time.time()
 189             request.headers['Date'] = http_date(now)
 190             request.headers['Content-Type'] = content_type
 191             request.headers['Last-Modified'] = http_date(timestamp)
 192             request.headers['Expires'] = http_date(now - 365 * 24 * 3600)
 193             request.headers['Content-Length'] = os.path.getsize(fpath)
 194             content_dispo_string = '%s; filename="%s"' % ('inline', filename_enc)
 195             request.headers['Content-Disposition'] = content_dispo_string
 196             # send data
 197             request.send_file(open(fpath, 'rb'))
 198     else:
 199         msg = '%(actname)s show request invalid.' % actname
 200         request.theme.add_msg(msg, 'error')
 201     return
 202                                  
 203 def execute(pagename, request):
 204 
 205     if request.user.may.read(pagename):
 206         show = request.values.get('show', 'text')
 207         make_pdf(pagename, request, show)
 208 
 209     Page(request, pagename).send_page()

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