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.