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.