Attachment 'PDF-1.6.py'

Download

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

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