Attachment 'CreatePdfDocument2_1_0.py'
Download 1 # -*- coding: iso-8859-1 -*-
2 __doc__ = """
3 MoinMoin - Generate PDF document using HTMLDOC
4
5 This action script generate PDF documents from a Wiki site using
6 the HTMLDOC (http://www.htmldoc.org) software packages which has
7 to be preinstalled first.
8
9 To use this feature install this script in your's MoinMoin action
10 script plugin directory.
11
12 Thanks goes to Pascal Bauermeister who initiated the implementaion.
13 Lot of things changes since then but the idear using HTMLDOC is the
14 main concept of this implementation.
15
16 @copyright: (C) 2006 Pascal Bauermeister
17 @copyright: (C) 2006-2007 Raphael Bossek <raphael.bossek@solutions4linux.de>
18 @license: GNU GPL, see COPYING for details
19 """
20
21 __version__ = u'2.1.0'
22
23 release_information = """
24 2007-08-20 RaphaelBossek
25 * Release v2.1.0
26 * Configuration is seperated by tabbs.
27 * Added support for font style and colors.
28 * Fixed save/load of configuration variables within page (may be empty).
29
30 2007-08-17 RaphaelBossek
31 * Release v2.0.11
32 * Added support for alternative title page and title page logo.
33 * Added support for additional fields for the title page author, docnumber
34 and copyright.
35
36 2006-12-18 RaphaelBossek
37 * Release v2.0.10
38 * Improved support for webpage/book styles if the HTML documents does not.
39
40 2006-12-17 RaphaelBossek
41 * Release v2.0.9
42 * Added AUTH_TYPE for RedirectOutputRequest()
43
44 2006-11-16 RaphaelBossek
45 * Release v2.0.8
46 * Fixed another missing configuration for RedirectOutputRequest()
47
48 2006-11-15 RaphaelBossek
49 * Release v2.0.7
50 * Fixed support for MoinMoin 1.5.3
51 * Fixed SSL support.
52
53 2006-11-14 RaphaelBossek
54 * Release v2.0.6
55 * MoinMoin 1.6 support added.
56 * Fixed Windows(TM) platform support.
57 * Added support for alternative title text.
58
59 2006-09-13 RaphaelBossek
60 * Release v2.0.5
61 * Fixed RedirectOutputRequest class where definition of script_name
62 was missing.
63
64 2006-09-12 RaphaelBossek
65 * Release v2.0.4
66 * Fixed RedirectOutputRequest class where function was redifined
67 to boolean value.
68
69 2006-09-06 RaphaelBossek
70 * Release v2.0.3
71 * Fixed FastCGI support by removing stdout redirect output code. The
72 same functionality will be done by the RedirectOutputRequest class.
73 * Renamed to CreatePdfDocument (for better translation possibilities).
74 * Fixed encoding of title page.
75 * Added charset set option.
76 * Fixed waiting for HTMLDOC.
77
78 2006-09-02 RaphaelBossek
79 * Release v2.0.2
80 * Added createpdfdocument_validoptions and createpdfdocument_defaultvalues
81 configuration parameter support. You are not able to preset available
82 options and which defaults are used in your configuration file.
83
84 2006-08-30 RaphaelBossek
85 * Release v2.0.1
86 * Fixed issue with page revision number forwarding (traceback).
87
88 2006-08-30 RaphaelBossek
89 * Release v2.0.0
90 * Feature enchanced and bug fixed version.
91
92 2006-05-26 PascalBauermeister
93 * Release v1.0.2
94 * Relative image URLs turned absolute was bogus. It is less bogus now.
95
96 2006-05-26 PascalBauermeister
97 * Release v1.0.1
98 * Set env var HTMLDOC_NOCGI to solve CGI issue
99
100 2006-05-24 PascalBauermeister
101 * Initial release v1.0.0
102 """
103
104 import os
105 import sys
106 import stat
107 import re
108 import copy
109 import shutil
110 import StringIO
111 from MoinMoin import config
112 from MoinMoin import util
113 from MoinMoin import wikiutil
114 from MoinMoin import packages
115 from MoinMoin import error
116 from MoinMoin.Page import Page
117 from MoinMoin.request import RequestBase
118 from MoinMoin.widget.dialog import Dialog
119 from MoinMoin.version import release as moinmoin_release
120
121 # http://www.barelyfitz.com/projects/tabber/
122 tabber_minimized = '''
123 // Version 1.9 stripped by Creativyst SS & JavaScript Compressor v2.2c (http://www.creativyst.com/Prod/3/)
124 function tabberObj(argsObj)
125 { var arg; this.div = null; this.classMain = "tabber"; this.classMainLive = "tabberlive"; this.classTab = "tabbertab"; this.classTabDefault = "tabbertabdefault"; this.classNav = "tabbernav"; this.classTabHide = "tabbertabhide"; this.classNavActive = "tabberactive"; this.titleElements = ['h2','h3','h4','h5','h6']; this.titleElementsStripHTML = true; this.removeTitle = true; this.addLinkId = false; this.linkIdFormat = '<tabberid>nav<tabnumberone>'; for (arg in argsObj) { this[arg] = argsObj[arg];}
126 this.REclassMain = new RegExp('\\\\b' + this.classMain + '\\\\b', 'gi'); this.REclassMainLive = new RegExp('\\\\b' + this.classMainLive + '\\\\b', 'gi'); this.REclassTab = new RegExp('\\\\b' + this.classTab + '\\\\b', 'gi'); this.REclassTabDefault = new RegExp('\\\\b' + this.classTabDefault + '\\\\b', 'gi'); this.REclassTabHide = new RegExp('\\\\b' + this.classTabHide + '\\\\b', 'gi'); this.tabs = new Array(); if (this.div) { this.init(this.div); this.div = null;}
127 }
128 tabberObj.prototype.init = function(e)
129 { var
130 childNodes, i, i2, t, defaultTab=0, DOM_ul, DOM_li, DOM_a, aId, headingElement; if (!document.getElementsByTagName) { return false;}
131 if (e.id) { this.id = e.id;}
132 this.tabs.length = 0; childNodes = e.childNodes; for(i=0; i < childNodes.length; i++) { if(childNodes[i].className &&
133 childNodes[i].className.match(this.REclassTab)) { t = new Object(); t.div = childNodes[i]; this.tabs[this.tabs.length] = t; if (childNodes[i].className.match(this.REclassTabDefault)) { defaultTab = this.tabs.length-1;}
134 }
135 }
136 DOM_ul = document.createElement("ul"); DOM_ul.className = this.classNav; for (i=0; i < this.tabs.length; i++) { t = this.tabs[i]; t.headingText = t.div.title; if (this.removeTitle) { t.div.title = '';}
137 if (!t.headingText) { for (i2=0; i2<this.titleElements.length; i2++) { headingElement = t.div.getElementsByTagName(this.titleElements[i2])[0]; if (headingElement) { t.headingText = headingElement.innerHTML; if (this.titleElementsStripHTML) { t.headingText.replace(/<br>/gi," "); t.headingText = t.headingText.replace(/<[^>]+>/g,"");}
138 break;}
139 }
140 }
141 if (!t.headingText) { t.headingText = i + 1;}
142 DOM_li = document.createElement("li"); t.li = DOM_li; DOM_a = document.createElement("a"); DOM_a.appendChild(document.createTextNode(t.headingText)); DOM_a.href = "javascript:void(null);"; DOM_a.title = t.headingText; DOM_a.onclick = this.navClick; DOM_a.tabber = this; DOM_a.tabberIndex = i; if (this.addLinkId && this.linkIdFormat) { aId = this.linkIdFormat; aId = aId.replace(/<tabberid>/gi, this.id); aId = aId.replace(/<tabnumberzero>/gi, i); aId = aId.replace(/<tabnumberone>/gi, i+1); aId = aId.replace(/<tabtitle>/gi, t.headingText.replace(/[^a-zA-Z0-9\-]/gi, '')); DOM_a.id = aId;}
143 DOM_li.appendChild(DOM_a); DOM_ul.appendChild(DOM_li);}
144 e.insertBefore(DOM_ul, e.firstChild); e.className = e.className.replace(this.REclassMain, this.classMainLive); this.tabShow(defaultTab); if (typeof this.onLoad == 'function') { this.onLoad({tabber:this});}
145 return this;}; tabberObj.prototype.navClick = function(event)
146 { var
147 rVal, a, self, tabberIndex, onClickArgs; a = this; if (!a.tabber) { return false;}
148 self = a.tabber; tabberIndex = a.tabberIndex; a.blur(); if (typeof self.onClick == 'function') { onClickArgs = {'tabber':self, 'index':tabberIndex, 'event':event}; if (!event) { onClickArgs.event = window.event;}
149 rVal = self.onClick(onClickArgs); if (rVal === false) { return false;}
150 }
151 self.tabShow(tabberIndex); return false;}; tabberObj.prototype.tabHideAll = function()
152 { var i; for (i = 0; i < this.tabs.length; i++) { this.tabHide(i);}
153 }; tabberObj.prototype.tabHide = function(tabberIndex)
154 { var div; if (!this.tabs[tabberIndex]) { return false;}
155 div = this.tabs[tabberIndex].div; if (!div.className.match(this.REclassTabHide)) { div.className += ' ' + this.classTabHide;}
156 this.navClearActive(tabberIndex); return this;}; tabberObj.prototype.tabShow = function(tabberIndex)
157 { var div; if (!this.tabs[tabberIndex]) { return false;}
158 this.tabHideAll(); div = this.tabs[tabberIndex].div; div.className = div.className.replace(this.REclassTabHide, ''); this.navSetActive(tabberIndex); if (typeof this.onTabDisplay == 'function') { this.onTabDisplay({'tabber':this, 'index':tabberIndex});}
159 return this;}; tabberObj.prototype.navSetActive = function(tabberIndex)
160 { this.tabs[tabberIndex].li.className = this.classNavActive; return this;}; tabberObj.prototype.navClearActive = function(tabberIndex)
161 { this.tabs[tabberIndex].li.className = ''; return this;}; function tabberAutomatic(tabberArgs)
162 { var
163 tempObj, divs, i; if (!tabberArgs) { tabberArgs = {};}
164 tempObj = new tabberObj(tabberArgs); divs = document.getElementsByTagName("div"); for (i=0; i < divs.length; i++) { if (divs[i].className &&
165 divs[i].className.match(tempObj.REclassMain)) { tabberArgs.div = divs[i]; divs[i].tabber = new tabberObj(tabberArgs);}
166 }
167 return this;}
168 function tabberAutomaticOnLoad(tabberArgs)
169 { var oldOnLoad; if (!tabberArgs) { tabberArgs = {};}
170 oldOnLoad = window.onload; if (typeof window.onload != 'function') { window.onload = function() { tabberAutomatic(tabberArgs);};} else { window.onload = function() { oldOnLoad(); tabberAutomatic(tabberArgs);};}
171 }
172 if (typeof tabberOptions == 'undefined') { tabberAutomaticOnLoad();} else { if (!tabberOptions['manualStartup']) { tabberAutomaticOnLoad(tabberOptions);}
173 }
174 '''
175
176 tabber_minimized_css = '''
177 <style type="text/css">
178 /*--------------------------------------------------
179 REQUIRED to hide the non-active tab content.
180 But do not hide them in the print stylesheet!
181 --------------------------------------------------*/
182 .tabberlive .tabbertabhide {
183 display:none;
184 }
185
186 /*--------------------------------------------------
187 .tabber = before the tabber interface is set up
188 .tabberlive = after the tabber interface is set up
189 --------------------------------------------------*/
190 .tabber {
191 }
192 .tabberlive {
193 margin-top:1em;
194 }
195
196 /*--------------------------------------------------
197 ul.tabbernav = the tab navigation list
198 li.tabberactive = the active tab
199 --------------------------------------------------*/
200 ul.tabbernav
201 {
202 margin:0;
203 padding: 3px 0;
204 border-bottom: 1px solid #778;
205 font: bold 12px Verdana, sans-serif;
206 }
207
208 ul.tabbernav li
209 {
210 list-style: none;
211 margin: 0;
212 display: inline;
213 }
214
215 ul.tabbernav li a
216 {
217 padding: 3px 0.5em;
218 margin-left: 3px;
219 border: 1px solid #778;
220 border-bottom: none;
221 background: #DDE;
222 text-decoration: none;
223 }
224
225 ul.tabbernav li a:link { color: #448; }
226 ul.tabbernav li a:visited { color: #667; }
227
228 ul.tabbernav li a:hover
229 {
230 color: #000;
231 background: #AAE;
232 border-color: #227;
233 }
234
235 ul.tabbernav li.tabberactive a
236 {
237 background-color: #fff;
238 border-bottom: 1px solid #fff;
239 }
240
241 ul.tabbernav li.tabberactive a:hover
242 {
243 color: #000;
244 background: white;
245 border-bottom: 1px solid white;
246 }
247
248 /*--------------------------------------------------
249 .tabbertab = the tab content
250 Add style only after the tabber interface is set up (.tabberlive)
251 --------------------------------------------------*/
252 .tabberlive .tabbertab {
253 padding:5px;
254 border:1px solid #aaa;
255 border-top:0;
256
257 /* If you don't want the tab size changing whenever a tab is changed
258 you can set a fixed height */
259
260 /* height:200px; */
261
262 /* If you set a fix height set overflow to auto and you will get a
263 scrollbar when necessary */
264
265 /* overflow:auto; */
266 }
267
268 /* If desired, hide the heading since a heading is provided by the tab */
269 .tabberlive .tabbertab h2 {
270 display:none;
271 }
272 .tabberlive .tabbertab h3 {
273 display:none;
274 }
275
276 /* Example of using an ID to set different styles for the tabs on the page */
277 .tabberlive#tab1 {
278 }
279 .tabberlive#tab2 {
280 }
281 .tabberlive#tab2 .tabbertab {
282 height:200px;
283 overflow:auto;
284 }
285 </style>
286 '''
287
288 class RedirectOutputRequest(RequestBase):
289 """Redirect output to string without HTTP headers."""
290 def __init__ (self, req, action=u'print'):
291 """Prepare a compatible request."""
292 # 1.5,1.6:req.path_info = u'/RaphaelBossek/\xd6nologe' | u'/FrontPage'
293 # after enconde() self.path_info = '/RaphaelBossek/\xd6nologe'
294 self.path_info = req.path_info.encode(config.charset)
295 # 1.5,1.6:query_string = req.query_string = u'action=CreatePdfDocument'
296 self.query_string = u'action=' + action
297 # 1.5,1.6:request_uri = req.request_uri = u'/FrontPage?action=CreatePdfDocument'
298 self.request_uri = req.path_info.replace(req.query_string, self.query_string)
299 # 1.5,1.6:url = req.url = u'localhost:8080/FrontPage?action=CreatePdfDocument'
300 self.url = req.url.replace(req.query_string, self.query_string)
301 # 1.5,1.6: Not all kinds of request support SSL.
302 if 'is_ssl' in req.__dict__:
303 self.is_ssl = req.is_ssl
304 else:
305 self.is_ssl = 0
306 # 1.5,1.6: Not all kinds of request set env.
307 self.env = {}
308 if 'env' in req.__dict__:
309 # 1.5,1.6: Do not copy env otherwise UNICODE encoding does not work right.
310 #self._setup_vars_from_std_env(req.env)
311 self.env['AUTH_TYPE'] = req.env.get('AUTH_TYPE', '')
312 self.http_host = req.http_host
313 self.http_user_agent = req.http_user_agent
314 self.request_method = None
315 self.saved_cookie = req.saved_cookie
316 self.remote_addr = req.remote_addr
317 self.script_name = getattr (req, u'script_name', u'')
318
319 self.req = req
320 RequestBase.__init__(self)
321
322 def run(self, rev = None):
323 """Start processing the document."""
324 # 1.6:MoinMoin/request/__init__.py: Initialise action from environment variables not from form.
325 self.action = u'print'
326 if rev:
327 self.form[u'rev'] = [rev]
328 self.output_string = u''
329 self.error_string = u''
330 self.sent_headers = False
331 self.failed = 0
332 RequestBase.run(self)
333 return (self.output_string, self.error_string)
334
335 def http_headers (self, *args, **kw):
336 """Used by MoinMoin 1.5.x instead of emit_http_headers()."""
337 self.sent_headers = True
338
339 def emit_http_headers(self, *args, **kw):
340 """Used by MoinMoin 1.6.x instaed of http_headers()."""
341 self.sent_headers = 1
342
343 def fail (self, err):
344 """Catch if a error occurs and save the message in our string."""
345 RequestBase.fail (self, err)
346 if not self.error_string:
347 self.error_string = str(err)
348
349 def write (self, *data):
350 """Catch the document in our output_string."""
351 if self.sent_headers:
352 if self.failed:
353 self.error_string += data[0]
354 else:
355 self.output_string += data[0]
356
357 def flush(self):
358 pass
359
360
361 def attachment_fsname(attachment, page, request):
362 """Return location of attament on the file system. current_page is the relative location
363 where attachment is used.
364 """
365 from MoinMoin.action import AttachFile
366 pagename, filename = AttachFile.absoluteName(attachment, page.page_name)
367 #self.request.log("attachment_link: url %s pagename %s filename %s" % (url, pagename, filename))
368 fname = wikiutil.taintfilename(filename)
369 return AttachFile.getFilename(request, pagename, fname)
370
371
372 def shell_quote(parameter):
373 o = parameter
374 for c in [u' ', u'(', u')']:
375 o = o.replace(c, u'\\' + c)
376 return o
377
378
379 def getEditorName(request):
380 """Return name of the last editor."""
381 editorname = u''
382 log = request.page._last_edited(request)
383 if log:
384 if request.cfg.show_hosts:
385 title = " @ %s[%s]" % (log.hostname, log.addr)
386 else:
387 title = ""
388 kind, info = log.getInterwikiEditorData(request)
389 if kind in ['interwiki', 'email']:
390 if log._usercache[log.userid].__dict__.get('aliasname', u''):
391 editorname = log._usercache[log.userid].aliasname
392 else:
393 editorname = log._usercache[log.userid].name
394 elif kind == 'ip':
395 try:
396 idx = info.index('.')
397 except ValueError:
398 idx = len(info)
399 editorname = wikiutil.escape(info[:idx])
400 return editorname
401
402
403 def pipeCommand(cmdstr, input=None):
404 inp, out, err = os.popen3 (cmdstr, u'b')
405 try:
406 if input:
407 inp.write(input)
408 inp.close()
409 except:
410 pass
411
412 output = out.read()
413 out.close()
414
415 erroutput = err.read()
416 err.close()
417
418 if os.name in ['posix', 'mac']:
419 try:
420 # REMARK: Otherwise we get <defunct> processes.
421 os.wait()
422 except OSError, e:
423 # 10: No child processes.
424 if e.errno != 10:
425 raise
426 return (output, erroutput)
427
428
429 class CreatePdfDocument:
430 """Implementation of the PDF document generator."""
431
432 def __init__(self):
433 self.action_name = self.__class__.__name__
434 self.pagename = None
435 self.request = None
436 self._ = None
437 self.debug = False
438 self.msg = None
439 self.errormsgsent = False
440 self.default_values = {
441 'style': u'webpage',
442 'format': u'pdf13',
443 'titlefileimage': u'',
444 'linkstyle': u'underline',
445 'headerleft': u't',
446 'headermiddle': u'.',
447 'headerright': u'D',
448 'footerleft': u'.',
449 'footermiddle': u'/',
450 'footerright': u'.',
451 'tocheaderleft': u'.',
452 'tocheadermiddle': u't',
453 'tocheaderright': u'.',
454 'tocfooterleft': u'.',
455 'tocfootermiddle': u'.',
456 'tocfooterright': u'i',
457 'bodycolor': u'FFFFFF',
458 'bodyimage': u'',
459 'textcolor': u'000000',
460 'linkcolor': u'0000E0',
461 'size': u'legal',
462 'user-password': u'',
463 'owner-password': u'',
464 'toclevels': u'3',
465 'grayscale': u'unchecked',
466 'title': u'checked',
467 'duplex': u'unchecked',
468 'landscape': u'unchecked',
469 'usersize': u'',
470 'margintop': u'0.50in',
471 'marginbottom': u'0.50in',
472 'marginleft': u'1.00in',
473 'marginright': u'0.50in',
474 'no-toc': u'checked',
475 'no-links': u'checked',
476 'firstpage': u'p1',
477 'jpeg': u'0',
478 'compression': u'0',
479 'pagemode': u'outline',
480 'pagelayout': u'single',
481 'firstpage': u'c1',
482 'numbered': u'checked',
483 'encryption': u'unchecked',
484 'permissioncopy': u'checked',
485 'permissionprint': u'checked',
486 'permissionannotate': u'checked',
487 'permissionmodify': u'checked',
488 'charset': u'iso-8859-1',
489 'debug': u'',
490 'rev': 0,
491 'extra-titledocnumber': u'',
492 'extra-titleauthor': u'',
493 'extra-titlecopyright': u'',
494 'pageinfo': u'unchecked',
495 'bodyfont': u'times',
496 'headingfont': u'helvetica',
497 'headfootfont': u'helvetica',
498 'fontsize': u'11.0',
499 'headfootsize': u'11.0',
500 'fontspacing': u'1.2',
501 'embedfonts': u'checked',
502 }
503 # We have to know which values are checkboxes within the form. If a key does
504 # not exists wihtin the form the corresponding checkbox is not checked.
505 self.form_checkbox = []
506 for key, value in self.default_values.items():
507 if value in [u'checked', u'unchecked']:
508 self.form_checkbox += [key]
509 self.contenttype = u'application/pdf'
510
511 def error_msg (self, msg):
512 """Display error message."""
513 if not self.errormsgsent:
514 # Backward compatiblity with MoinMoin 1.5.
515 if Page.send_page.func_code.co_varnames[1] == "request":
516 Page(self.request, self.pagename).send_page(self.request, msg=msg)
517
518 else:
519 Page(self.request, self.pagename).send_page(msg=msg)
520 self.errormsgsent = True
521
522 def fixhtmlstr (self, str):
523 """Convert utf-8 encoded multi-byte sequences into &#XXXX; format."""
524 htmlstr = u''
525 for c in str:
526 if ord(c) >= 128:
527 htmlstr = htmlstr + '&#%d;' % ord(c)
528 else:
529 htmlstr = htmlstr + c
530 return htmlstr
531
532 def set_page_values(self):
533 """Scan raw page for additional information relating PDF generation."""
534 #pdflines = False
535 for line in self.request.page.get_raw_body().split(u'\n'):
536 if line[:6] == u'##pdf ' and len(line[6:]):
537 line = line[6:]
538 key = line.split()[0]
539 value = line[len(key) + 1:]
540 # Only accept known values/settings.
541 if key in self.default_values:
542 # Check if there are any restrictions for key.
543 if key in self.valid_options:
544 # Set only the value if the restrictions are confirmed.
545 valid_values = self.valid_options[key].keys()
546 if value in valid_values:
547 self.values[key] = value
548 else:
549 # There are no restrictions for value.
550 self.values[key] = value
551 elif not line:
552 break
553
554 def set_page_default_values(self):
555 # We are not able to recognise if this string is part of a verbatim area.
556 matchtoclvl = re.compile(r'^\[\[TableOfContents\(\s*(\d+)\s*\)\]\]')
557 matchtoc = re.compile(r'^\[\[TableOfContents\(*\)*\]\]')
558 toc = False
559 for line in self.request.page.get_raw_body().split(u'\n'):
560 if line[:10] == u'#language ' and not u'language' in self.values:
561 lang = self.make_isolang(line[10:])
562 if lang:
563 self.default_values[u'language'] = lang
564 elif not u'toclevels' in self.values and not toc:
565 result = matchtoclvl.match(line)
566 if result:
567 toclevels = int(result.group(1).strip())
568 if toclevels > 4:
569 toclevels = 4
570 self.default_values[u'toclevels'] = str(toclevels)
571 toc = True
572 elif matchtoc.match(line):
573 toc = True
574 # We assume if table-of-contents is used we intent to generate a book.
575 if toc:
576 # Do not change style if set manually or by configuration.
577 self.default_values[u'style'] = self.default_values.get(u'style', u'book')
578 # Do not generate a table of contents page.
579 if self.default_values[u'style'] != u'book':
580 # Do not change style if set manually or by configuration.
581 self.default_values[u'no-toc'] = self.default_values.get(u'no-toc', u'unchecked')
582
583 def _select(self, name, description=None):
584 """Helper function to create a selection control."""
585 str = u'<select name="%s" size="1">' % (name,)
586 if not description:
587 description = self.valid_options[name]
588 keys = description.keys()
589 keys.sort()
590 for value in keys:
591 if value == self.values[name]:
592 selected = u'selected'
593 else:
594 selected = u''
595 str += u'<option value="%s" %s>%s</option>' % (value, selected, description[value],)
596 str += u'</select>'
597 return str
598
599 def _chooseformat(self, name):
600 """Helper function to create left/middle/right selection controls."""
601 str = u""" <tr>
602 <td class="label"><label>%s :</label></td>
603 <td><table>
604 <tr>
605 <td>%s :</td>
606 <td>%s</td>
607 </tr>
608 <tr>
609 <td>%s :</td>
610 <td>%s</td>
611 </tr>
612 <tr>
613 <td>%s :</td>
614 <td>%s</td>
615 </tr>
616 </table>
617 </td>
618 <td>%s</td>
619 </tr>""" % (self.fields[u'label_' + name],
620 self.fields[u'label_left'], self._select(name + u'left', self.valid_options[u'tocformats']),
621 self.fields[u'label_middle'], self._select(name + u'middle', self.valid_options[u'tocformats']),
622 self.fields[u'label_right'], self._select(name + u'right', self.valid_options[u'tocformats']),
623 self.fields[u'help_' + name],)
624 return str
625
626 def makeform(self, errormsg=u''):
627 self.fields = {
628 'error': errormsg,
629 'pagename': wikiutil.escape(self.pagename),
630 'action': self.action_name,
631 'version': __version__,
632 'moinmoin_release': moinmoin_release,
633
634 'label_input': self._(u'Input'),
635 'label_output': self._(u'Output'),
636 'label_page': self._(u'Page'),
637 'label_tableofcontents': self._(u'Contents'),
638 'label_pdf': self._(u'PDF'),
639 'label_security': self._(u'Security'),
640
641 'label_choose_style': self._(u'Choose style'),
642 'help_choose_style': self._(u'book: Create a structured PDF document with headings, chapters, etc.') + u'<br/>' + \
643 self._(u'webpage: Specifies that the HTML sources are unstructured (plain web pages.) A page break is inserted between each file or URL in the output.') + u'<br/>' + \
644 self._(u'continuous: Specifies that the HTML sources are unstructured (plain web pages.) No page breaks are inserted between each file or URL in the output.'),
645
646 'help_titletext': self._(u'Title of the document for the front page.'),
647
648 'label_titlefileimage': self._(u'Title file/image'),
649 'help_titlefileimage': self._(u'The title image or HTML page. These file has to be an attachments!'),
650
651 'label_extra-titledocnumber': self._(u'Version'),
652 'help_extra-titledocnumber': self._(u'Specify document version to be displayed on the title page.'),
653
654 'label_extra-titleauthor': self._(u'Author'),
655 'help_extra-titleauthor': self._(u'Intellectual property owner of this document.'),
656
657 'label_extra-titlecopyright': self._(u'Copyright'),
658 'help_extra-titlecopyright': self._(u'Copyright notice for this document.'),
659
660 'label_pageinfo': self._(u'Apply page information'),
661 'help_pageinfo': self._(u'Information about who and when modified the document are applied at the end.'),
662
663 'label_format': self._(u'Output format'),
664 'help_format': self._(u'Specifies the output format.'),
665
666 'label_outputoptions': self._(u'Output options'),
667 'label_grayscale': self._(u'Grayscale document'),
668 'label_titlepage': self._(u'Title page'),
669 'label_titletext': self._(u'Title'),
670 'label_jpeg': self._(u'JPEG big images'),
671 'label_compression': self._(u'Compression'),
672
673 'label_no-toc': self._(u'Generate a table of contents'),
674 'help_no-toc': self._(u''),
675
676 'label_toclevels': self._(u'Limit the number of levels in the table-of-contents'),
677 'help_toclevels': self._(u'Sets the number of levels in the table-of-contents.') + u' ' + self._(u'Empty for unlimited levels.'),
678
679 'label_numbered': self._(u'Numbered headings'),
680 'help_numbered': self._(u'Check to number all of the headings in the document.'),
681
682 'label_toctitle': self._(u'Table-of-contents title'),
683 'help_toctitle': self._(u'Sets the title for the table-of-contents.') + u' ' + self._(u'Empty for default title.'),
684
685 'label_left': self._(u'Left'),
686 'label_middle': self._(u'Middle'),
687 'label_right': self._(u'Right'),
688
689 'label_tocheader': self._(u'Header of table-of-contantes page'),
690 'help_tocheader': self._(u'Sets the page header to use on table-of-contents pages.'),
691
692 'label_tocfooter': self._(u'Footer of table-of-contantes page'),
693 'help_tocfooter': self._(u'Sets the page footer to use on table-of-contents pages.'),
694
695 'label_header': self._(u'Page header'),
696 'help_header': self._(u'Sets the page header to use on body pages.'),
697
698 'label_footer': self._(u'Page footer'),
699 'help_footer': self._(u'Sets the page footer to use on body pages.'),
700
701 'label_colors': self._(u'Colors'),
702 'label_no-links': self._(u'Create HTTP links'),
703 'help_no-links': self._(u'Enables generation of links in PDF files.'),
704
705 'label_linkstyle': self._(u'Style of HTTP links'),
706 'help_linkstyle': self._(u''),
707
708 'label_linkcolor': self._(u'HTTP links color'),
709 'help_linkcolor': self._(u'Sets the color of links.'),
710
711 'label_bodycolor': self._(u'Body color'),
712 'help_bodycolor': self._(u'Enter the HTML color for the body (background).'),
713
714 'label_bodyimage': self._(u'Body image'),
715 'help_bodyimage': self._(u'Enter the image file for the body (background). These file has to be an attachments!'),
716
717 'label_textcolor': self._(u'Text color'),
718 'help_textcolor': self._(u'Enter the HTML color for the text.'),
719
720 'label_duplex': self._(u'2-Sided'),
721 'help_duplex': self._(u'Specifies that the output should be formatted for double-sided printing.'),
722
723 'label_landscape': self._(u'Landscape'),
724
725 'label_choose_size': self._(u'Choose page size'),
726 'help_choose_size': self._(u'Choose one of the predefined standard sizes or select user defined.'),
727
728 'label_usersize': self._(u'User defined page size'),
729 'help_usersize': self._(u'Specifies the page size using a standard name or in points (no suffix or ##x##pt), inches (##x##in), centimeters (##x##cm), or millimeters (##x##mm).'),
730
731 'label_margin': self._(u'User defined margin'),
732 'label_margintop': self._(u'Top'),
733 'label_marginbottom': self._(u'Bottom'),
734 'label_marginleft': self._(u'Left'),
735 'label_marginright': self._(u'Right'),
736 'help_margin': self._(u'Specifies the margin size using points (no suffix or ##x##pt), inches (##x##in), centimeters (##x##cm), or millimeters (##x##mm).') + u' ' + self._(u'Keep empty for default value.'),
737
738 'label_pagemode': self._(u'Page mode'),
739 'help_pagemode': self._(u'Controls the initial viewing mode for the document.') + u'<br/>' + self._(u'Document: Displays only the docuemnt pages.') + u'<br/>' + self._(u'Outline: Display the table-of-contents outline as well as the document pages.') + u'<br/>' + self._(u'Full-screen: Displays pages on the whole screen; this mode is used primarily for presentations.'),
740
741 'label_pagelayout': self._(u'Page layout'),
742 'help_pagelayout': self._(u'Controls the initial layout of document pages on the screen.') + u'<br/>' + self._(u'Single: Displays a single page at a time.') + u'<br/>' + self._(u'One column: Displays a single column of pages at a time.') + u'<br/>' + self._(u'Two column left/right: Display two columns of pages at a time; the first page is displayed in the left or right column as selected.'),
743
744 'label_firstpage': self._(u'First page'),
745 'help_firstpage': self._(u'Choose the initial page that will be shown.'),
746
747 'label_encryption': self._(u'Encryption'),
748 'help_encryptin': self._(u'Enables encryption and security features for PDF output.'),
749 'label_permissions': self._(u'Permissions'),
750 'help_permissions': self._(u'Specifies the document permissions.'),
751
752 'label_permissionannotate': self._(u'Annotate'),
753 'label_permissionprint': self._(u'Print'),
754 'label_permissionmodify': self._(u'Modify'),
755 'label_permissioncopy': self._(u'Copy'),
756
757 'label_owner-password': self._(u'Owner password'),
758 'help_owner-password': self._(u'Specifies the owner password to control who can change document permissions etc.') + u' ' + self._(u'If this field is left blank, a random 32-character password is generated so that no one can change the document.'),
759
760 'label_user-password': self._(u'User password'),
761 'help_user-password': self._(u'Specifies the user password to restrict viewing permissions on this PDF document.') + u' ' + self._(u'Empty for no encryption.'),
762
763 'label_expert': self._(u'Expert'),
764 'label_language': self._(u'Language translation'),
765 'help_language': self._(u'Specify language to use for date and time format.'),
766
767 'label_fonts': self._(u'Fonts'),
768 'label_fontsize': self._(u'Base font size'),
769 'help_fontsize': self._(u'Set the default size of text.'),
770 'label_fontspacing': self._(u'Line spacing'),
771 'help_fontspacing': self._(u'Set the spacing between lines of text.'),
772 'label_bodyfont': self._(u'Body typeface'),
773 'help_bodyfont': self._(u'Choose the default typeface (font) of text.'),
774 'label_headingfont': self._(u'Heading typeface'),
775 'help_headingfont': self._(u'Choose the default typeface (font) of headings.'),
776 'label_headfootsize': self._(u'Header/Footer size'),
777 'help_headfootsize': self._(u'Set the size of header and footer text.'),
778 'label_headfootfont': self._(u'Header/Footer font'),
779 'help_headfootfont': self._(u'Choose the font for header and footer text.'),
780 'label_charset': self._(u'Charset set'),
781 'help_charset': self._(u'Change the encoding of the text in document.'),
782 'label_embedfonts': self._(u'Embed fonts'),
783 'help_embedfonts': self._(u'Check to embed font in the output file.'),
784
785 'label_about': self._(u'About'),
786 'copyright': u'',
787 'version': self._(u'Version') + u' ' + __version__,
788
789 'button_generate': self._(u'Generate PDF'),
790 'button_remember': self._(u'Remember form'),
791 'button_cancel': self._(u'Cancel'),
792 'button_reset': self._(u'Reset'),
793 }
794
795 self.fields['copyright'] = u"<br/>\n".join(wikiutil.escape(__doc__).split(u"\n"))
796 self.fields.update(self.values)
797
798 # Status of debug.
799 if self.debug:
800 self.fields[u'debug'] = u'1'
801 else:
802 self.fields[u'debug'] = u'0'
803
804 # Go through all format strings.
805 for name in [u'tocheader', u'tocfooter', u'header', u'footer']:
806 self.fields[u'choose_' + name] = self._chooseformat(name)
807
808 self.fields[u'select_style'] = self._select(u'style')
809 self.fields[u'select_format'] = self._select(u'format')
810 self.fields[u'select_linkstyle'] = self._select(u'linkstyle')
811 self.fields[u'select_size'] = self._select(u'size')
812 self.fields[u'select_jpeg'] = self._select(u'jpeg')
813 self.fields[u'select_compression'] = self._select(u'compression')
814 self.fields[u'select_toclevels'] = self._select(u'toclevels')
815 self.fields[u'select_pagemode'] = self._select(u'pagemode')
816 self.fields[u'select_pagelayout'] = self._select(u'pagelayout')
817 self.fields[u'select_firstpage'] = self._select(u'firstpage')
818 self.fields[u'select_charset'] = self._select(u'charset')
819 self.fields[u'select_fontsize'] = self._select(u'fontsize')
820 self.fields[u'select_bodyfont'] = self._select(u'bodyfont')
821 self.fields[u'select_headingfont'] = self._select(u'headingfont')
822 self.fields[u'select_headfootsize'] = self._select(u'headfootsize')
823 self.fields[u'select_headfootfont'] = self._select(u'headfootfont')
824 self.fields[u'select_fontspacing'] = self._select(u'fontspacing')
825
826 # Add tabber implementation.
827 self.request.cfg.html_head += """
828 <script type="text/javascript">
829 <!-- //
830 %s
831 //-->
832 </script>
833
834 %s
835 """ % (tabber_minimized, tabber_minimized_css,)
836
837 form = u''
838 if self.debug:
839 form += u'<p class="warning">Debug mode activated.</p>'
840 if not moinmoin_release[:3] in [u'1.6', u'1.5']:
841 form += u'<p class="warning">This plugin is not designed for MoinMoin %(moinmoin_release)s.</p>'
842 form += """
843 <p class="error">%(error)s</p>
844 <form method="post" action="">
845 <input type="hidden" name="action" value="%(action)s"/>
846 <div class="tabber">
847 <div class="tabbertab">
848 <h3>%(label_input)s</h3>
849 <table width="100%%">
850 <tr>
851 <td class="label"><label>%(label_choose_style)s :</label></td>
852 <td class="content">%(select_style)s</td>
853 <td>%(help_choose_style)s</td>
854 </tr>
855 <tr>
856 <td class="label"><label>%(label_titletext)s :</label></td>
857 <td class="content"><input type="text" size="30" name="titletext" value="%(titletext)s" /></td>
858 <td>%(help_titletext)s</td>
859 </tr>
860 <tr>
861 <td class="label"><label>%(label_titlefileimage)s :</label></td>
862 <td class="content"><input type="text" size="30" name="titlefileimage" value="%(titlefileimage)s" /></td>
863 <td>%(help_titlefileimage)s</td>
864 </tr>
865 <tr>
866 <td class="label"><label>%(label_extra-titledocnumber)s :</label></td>
867 <td class="content"><input type="text" size="30" name="extra-titledocnumber" value="%(extra-titledocnumber)s" /></td>
868 <td>%(help_extra-titledocnumber)s</td>
869 </tr>
870 <tr>
871 <td class="label"><label>%(label_extra-titleauthor)s :</label></td>
872 <td class="content"><input type="text" size="30" name="extra-titleauthor" value="%(extra-titleauthor)s" /></td>
873 <td>%(help_extra-titleauthor)s</td>
874 </tr>
875 <tr>
876 <td class="label"><label>%(label_extra-titlecopyright)s :</label></td>
877 <td class="content"><input type="text" size="30" name="extra-titlecopyright" value="%(extra-titlecopyright)s" /></td>
878 <td>%(help_extra-titlecopyright)s</td>
879 </tr>
880 <tr>
881 <td class="label"><label>%(label_pageinfo)s :</label></td>
882 <td class="checkbox"><input type="checkbox" name="pageinfo" value="checked" %(pageinfo)s /></td>
883 <td>%(help_pageinfo)s</td>
884 </tr>
885 </table>
886 </div>
887 <div class="tabbertab">
888 <h3>%(label_output)s</h3>
889 <table width="100%%">
890 <tr>
891 <td class="label"><label>%(label_format)s :</label></td>
892 <td class="content">%(select_format)s</td>
893 <td>%(help_format)s</td>
894 </tr>
895 <tr>
896 <td class="label"><label>%(label_outputoptions)s :</label></td>
897 <td colspan="2"><input type="checkbox" name="grayscale" value="checked" %(grayscale)s />%(label_grayscale)s
898 <input type="checkbox" name="title" value="checked" %(title)s />%(label_titlepage)s<br />
899 %(label_compression)s : %(select_compression)s
900 %(label_jpeg)s : %(select_jpeg)s</td>
901 </tr>
902 </table>
903 </div>
904 <div class="tabbertab">
905 <h3>%(label_page)s</h3>
906 <table width="100%%">
907 <tr>
908 <td class="label"><label>%(label_choose_size)s :</label></td>
909 <td>%(select_size)s <br /><nobr>%(label_usersize)s : <input type="text" size="15" name="usersize" value="%(usersize)s" /></nobr></td>
910 <td>%(help_choose_size)s<br />%(help_usersize)s</td>
911 </tr>
912 <tr>
913 <td> </td>
914 <td colspan="2"><input type="checkbox" name="duplex" value="checked" %(duplex)s /> %(label_duplex)s
915 <input type="checkbox" name="landscape" value="checked" %(landscape)s /> %(label_landscape)s</td>
916 </tr>
917 <tr>
918 <td class="label"><label>%(label_margin)s :</label></td>
919 <td><table><tr><td> </td><td><nobr><label>%(label_margintop)s :</label> <input type="text" name="margintop" value="%(margintop)s" size="7" /></nobr></td><td> </td></tr>
920 <tr><td><nobr><label>%(label_marginleft)s :</label> <input type="text" name="marginleft" value="%(marginleft)s" size="7" /></nobr></td><td> </td><td><nobr><label>%(label_marginright)s :</label> <input type="text" name="marginright" value="%(marginright)s" size="7" /></nobr></td></tr>
921 <tr><td> </td><td><nobr><label>%(label_marginbottom)s :</label> <input type="text" name="marginbottom" value="%(marginbottom)s" size="7" /></nobr></td><td> </td></tr></table>
922 <td>%(help_margin)s</td>
923 </tr>
924 %(choose_header)s
925 %(choose_footer)s
926 </table>
927 </div>
928 <div class="tabbertab">
929 <h3>%(label_tableofcontents)s</h3>
930 <table width="100%%">
931 <tr>
932 <td class="label"><label>%(label_no-toc)s :</label></td>
933 <td class="checkbox"><input type="checkbox" name="no-toc" value="checked" %(no-toc)s /></td>
934 <td>%(help_no-toc)s</td>
935 </tr>
936 <tr>
937 <td class="label"><label>%(label_toclevels)s :</label></td>
938 <td class="content">%(select_toclevels)s</td>
939 <td>%(help_toclevels)s</td>
940 </tr>
941 <tr>
942 <td> </td>
943 <td><input type="checkbox" name="numbered" value="checked" %(numbered)s /> %(label_numbered)s</td>
944 <td>%(help_numbered)s</td>
945 </tr>
946 <tr>
947 <td class="label"><label>%(label_toctitle)s :</label></td>
948 <td class="content"><input type="text" size="30" name="toctitle" value="%(toctitle)s" /></td>
949 <td>%(help_toctitle)s</td>
950 </tr>
951 %(choose_tocheader)s
952 %(choose_tocfooter)s
953 </table>
954 </div>
955 <div class="tabbertab">
956 <h3>%(label_colors)s</h3>
957 <table width="100%%">
958 <tr>
959 <td class="label"><label>%(label_bodycolor)s :</label></td>
960 <td class="content"><input type="text" size="6" name="bodycolor" value="%(bodycolor)s" /></td>
961 <td>%(help_bodycolor)s</td>
962 </tr>
963 <tr>
964 <td class="label"><label>%(label_bodyimage)s :</label></td>
965 <td class="content"><input type="text" size="30" name="bodyimage" value="%(bodyimage)s" /></td>
966 <td>%(help_bodyimage)s</td>
967 </tr>
968 <tr>
969 <td class="label"><label>%(label_textcolor)s :</label></td>
970 <td class="content"><input type="text" size="6" name="textcolor" value="%(textcolor)s" /></td>
971 <td>%(help_textcolor)s</td>
972 </tr>
973 <tr>
974 <td class="label"><label>%(label_linkcolor)s :</label></td>
975 <td class="content"><input type="text" size="6" name="linkcolor" value="%(linkcolor)s" /></td>
976 <td>%(help_linkcolor)s</td>
977 </tr>
978 <tr>
979 <td class="label"><label>%(label_linkstyle)s :</label></td>
980 <td class="content">%(select_linkstyle)s</td>
981 <td>%(help_linkstyle)s</td>
982 </tr>
983 <tr>
984 <td class="label"><label>%(label_no-links)s :</label></td>
985 <td><input type="checkbox" name="no-links" value="checked" %(no-links)s /></td>
986 <td>%(help_no-links)s</td>
987 </tr>
988 </table>
989 </div>
990 <div class="tabbertab">
991 <h3>%(label_fonts)s</h3>
992 <table widht="100%%">
993 <tr>
994 <td class="label"><label>%(label_fontsize)s</label></td>
995 <td class="content">%(select_fontsize)s</td>
996 <td>%(help_fontsize)s</td>
997 </tr>
998 <tr>
999 <td class="label"><label>%(label_fontspacing)s</label></td>
1000 <td class="content">%(select_fontspacing)s</td>
1001 <td>%(help_fontspacing)s</td>
1002 </tr>
1003 <tr>
1004 <td class="label"><label>%(label_bodyfont)s</label></td>
1005 <td class="content">%(select_bodyfont)s</td>
1006 <td>%(help_bodyfont)s</td>
1007 </tr>
1008 <tr>
1009 <td class="label"><label>%(label_headingfont)s</label></td>
1010 <td class="content">%(select_headingfont)s</td>
1011 <td>%(help_headingfont)s</td>
1012 </tr>
1013 <tr>
1014 <td class="label"><label>%(label_headfootsize)s</label></td>
1015 <td class="content">%(select_headfootsize)s</td>
1016 <td>%(help_headfootsize)s</td>
1017 </tr>
1018 <tr>
1019 <td class="label"><label>%(label_headfootfont)s</label></td>
1020 <td class="content">%(select_headfootfont)s</td>
1021 <td>%(help_headfootfont)s</td>
1022 </tr>
1023 <tr>
1024 <td class="label"><label>%(label_charset)s :</label></td>
1025 <td class="content">%(select_charset)s</td>
1026 <td>%(help_charset)s</td>
1027 </tr>
1028 <tr>
1029 <td class="label"><label>%(label_embedfonts)s :</label></td>
1030 <td class="checkbox"><input type="checkbox" name="embedfonts" value="checked" %(embedfonts)s /></td>
1031 <td>%(help_embedfonts)s</td>
1032 </tr>
1033 </table>
1034 </div>
1035 <div class="tabbertab">
1036 <h3>%(label_pdf)s</h3>
1037 <table width="100%%">
1038 <tr>
1039 <td class="label"><label>%(label_pagemode)s :</label></td>
1040 <td class="content">%(select_pagemode)s</td>
1041 <td>%(help_pagemode)s</td>
1042 </tr>
1043 <tr>
1044 <td class="label"><label>%(label_pagelayout)s :</label></td>
1045 <td class="content">%(select_pagelayout)s</td>
1046 <td>%(help_pagelayout)s</td>
1047 </tr>
1048 <tr>
1049 <td class="label"><label>%(label_firstpage)s :</label></td>
1050 <td class="content">%(select_firstpage)s</td>
1051 <td>%(help_firstpage)s</td>
1052 </tr>
1053 </table>
1054 </div>
1055 <div class="tabbertab">
1056 <h3>%(label_security)s</h3>
1057 <table width="100%%">
1058 <tr>
1059 <td class="label"><label>%(label_encryption)s :</label></td>
1060 <td><input type="checkbox" name="encryption" value="checked" %(encryption)s /></td>
1061 <td>%(help_numbered)s</td>
1062 </tr>
1063 <tr>
1064 <td class="label"><label>%(label_permissions)s :</label></td>
1065 <td><nobr><input type="checkbox" name="permissionprint" value="checked" %(permissionprint)s /> %(label_permissionprint)s</nobr>
1066 <nobr><input type="checkbox" name="permissionmodify" value="checked" %(permissionmodify)s /> %(label_permissionmodify)s</nobr><br />
1067 <nobr><input type="checkbox" name="permissioncopy" value="checked" %(permissioncopy)s /> %(label_permissioncopy)s</nobr>
1068 <nobr><input type="checkbox" name="permissionannotate" value="checked" %(permissionannotate)s /> %(label_permissionannotate)s</nobr></td>
1069 <td>%(help_permissions)s</td>
1070 </tr>
1071 <tr>
1072 <td class="label"><label>%(label_user-password)s :</label></td>
1073 <td class="content"><input type="password" size="30" name="user-password" value="%(user-password)s" /></td>
1074 <td>%(help_user-password)s</td>
1075 </tr>
1076 <tr>
1077 <td class="label"><label>%(label_owner-password)s :</label></td>
1078 <td class="content"><input type="password" size="30" name="owner-password" value="%(owner-password)s" /></td>
1079 <td>%(help_owner-password)s</td>
1080 </tr>
1081 </table>
1082 </div>
1083 <div class="tabbertab">
1084 <h3>%(label_expert)s</h3>
1085 <table width="100%%">
1086 <tr>
1087 <td class="label"><label>%(label_language)s :</label></td>
1088 <td class="content"><input type="text" size="6" name="language" value="%(language)s" /></td>
1089 <td>%(help_language)s</td>
1090 </tr>
1091 </table>
1092 </div>
1093 <div class="tabbertab">
1094 <h3>%(label_about)s</h3>
1095 <p>%(version)s (MoinMoin %(moinmoin_release)s)</p>
1096 <p>%(copyright)s</p>
1097 </div>
1098 </div>
1099 <p>
1100 <input type="hidden" name="debug" value="%(debug)s" /><input type="hidden" name="rev" value="%(rev)s" />
1101 <input type="submit" name="generate_from_form" value="%(button_generate)s" />
1102 <input type="submit" name="remember" value="%(button_remember)s" />
1103 <input type="submit" name="cancel" value="%(button_cancel)s" />
1104 </p>
1105 </form>
1106 """ % self.fields
1107 return Dialog(self.request, content=form)
1108
1109 def run(self, pagename, request):
1110 """ Main dispatcher for the action."""
1111 self.pagename = pagename
1112 self.request = request
1113 self._ = self.request.getText
1114 self.form = self.request.form
1115 self.cfg = self.request.cfg
1116
1117 # Canceled by user.
1118 if self.form.has_key(u'cancel'):
1119 # Backward compatiblity with MoinMoin 1.5.
1120 if Page.send_page.func_code.co_varnames[1] == "request":
1121 return self.request.page.send_page(self.request)
1122 else:
1123 return self.request.page.send_page()
1124
1125 # Determine calling parameters.
1126 if self.form.get(u'debug', [u'0']) == [u'1']:
1127 self.debug = True
1128
1129 # This dict contains all possible values.
1130 self.valid_options = {}
1131
1132 self.valid_options[u'tocformats'] = {
1133 u'/': self._(u'1/N,2/N Arabic page numbers'),
1134 u':': self._(u'1/C,2/C Arabic chapter page numbers'),
1135 u'1': self._(u'1,2,3,...'),
1136 u'a': self._(u'a,b,c,...'),
1137 u'A': self._(u'A,B,C,...'),
1138 u'c': self._(u'Chapter title'),
1139 u'C': self._(u'Chapter page number'),
1140 u'd': self._(u'Date'),
1141 u'D': self._(u'Date + Time'),
1142 u'h': self._(u'Heading'),
1143 u'i': self._(u'i,ii,iii,iv,...'),
1144 u'I': self._(u'I,II,III,IV,...'),
1145 u't': self._(u'Title'),
1146 u'T': self._(u'Time'),
1147 u'.': self._(u'Blank'),
1148 # TODO: Not supported yet; u'l': self._(u'Logo image'),
1149 }
1150 self.valid_options[u'style'] = {
1151 u'webpage': self._(u'webpage'),
1152 u'book': self._(u'book'),
1153 u'continuous': self._(u'continuous'),
1154 }
1155 self.valid_options[u'size'] = {
1156 u'legal': self._(u'Legal (8.5x14in)'),
1157 u'a4': self._(u'A4 (210x297mm)'),
1158 u'letter': self._(u'Letter (8.5x11in)'),
1159 u'universal': self._(u'Universal (8.27x11in)'),
1160 u'': self._(u'User defined'),
1161 }
1162 self.valid_options[u'format'] = {
1163 u'pdf11': self._(u'PDF 1.1 (Acrobat 2.0)'),
1164 u'pdf12': self._(u'PDF 1.2 (Acrobat 3.0)'),
1165 u'pdf13': self._(u'PDF 1.3 (Acrobat 4.0)'),
1166 u'pdf14': self._(u'PDF 1.4 (Acrobat 5.0)'),
1167 # TODO: Not supported yet:
1168 #u'ps1': self._(u'PostScript Level 1'),
1169 #u'ps2': self._(u'PostScript Level 2'),
1170 #u'ps3': self._(u'PostScript Level 3'),
1171 }
1172 self.valid_options[u'linkstyle'] = {
1173 u'underline': self._(u'Underline'),
1174 u'plain': self._(u'Plain'),
1175 }
1176 self.valid_options[u'firstpage'] = {
1177 u'c1': self._(u'1st chapter'),
1178 u'p1': self._(u'1st page'),
1179 u'toc': self._(u'Contents'),
1180 }
1181 self.valid_options[u'jpeg'] = {
1182 u'0': self._(u'None'),
1183 u'50': self._(u' 50% (Good)'),
1184 u'55': u'55%', u' 60': u' 60%', u' 65': ' 65%', u' 70': ' 70%', u' 75': ' 75%',
1185 u'80': ' 80%', u' 85': ' 85%', u' 90': ' 90%', u' 95': ' 95%',
1186 u'100': self._(u'100% (Best)'),
1187 }
1188 self.valid_options[u'compression'] = {
1189 u'0': self._(u'None'),
1190 u'1': self._(u'1 (Fast)'),
1191 u'2': u'2', u'3': u'3', u'4': u'4', u'5': u'5', u'6': u'6', u'7': u'7', u'8': u'8',
1192 u'9': self._(u'9 (Best)'),
1193 }
1194 self.valid_options[u'toclevels'] = {
1195 u'0': self._(u'None'),
1196 u'1': u'1', u'2': '2', u'3': '3', u'4': '4'
1197 }
1198 self.valid_options[u'pagemode'] = {
1199 u'outline': self._(u'Outline'),
1200 u'document': self._(u'Document'),
1201 u'fullscreen': self._(u'Full-screen'),
1202 }
1203 self.valid_options[u'pagelayout'] = {
1204 u'single': self._(u'Single'),
1205 u'one': self._(u'One column'),
1206 u'twoleft': self._(u'Two column left'),
1207 u'tworight': self._(u'Two column right'),
1208 }
1209 self.valid_options[u'charset'] = {
1210 u'iso-8859-1': self._(u'ISO 8859-1'),
1211 u'iso-8859-2': self._(u'ISO 8859-2'),
1212 u'iso-8859-3': self._(u'ISO 8859-3'),
1213 u'iso-8859-4': self._(u'ISO 8859-4'),
1214 u'iso-8859-5': self._(u'ISO 8859-5'),
1215 u'iso-8859-6': self._(u'ISO 8859-6'),
1216 u'iso-8859-7': self._(u'ISO 8859-7'),
1217 u'iso-8859-8': self._(u'ISO 8859-8'),
1218 u'iso-8859-9': self._(u'ISO 8859-9'),
1219 u'iso-8859-14': self._(u'ISO 8859-14'),
1220 u'iso-8859-15': self._(u'ISO 8859-15'),
1221 u'cp-874': self._(u'cp-847'),
1222 u'cp-1250': self._(u'cp-1250'),
1223 u'cp-1251': self._(u'cp-1251'),
1224 u'cp-1252': self._(u'cp-1252'),
1225 u'cp-1253': self._(u'cp-1253'),
1226 u'cp-1254': self._(u'cp-1254'),
1227 u'cp-1255': self._(u'cp-1255'),
1228 u'cp-1256': self._(u'cp-1256'),
1229 u'cp-1257': self._(u'cp-1257'),
1230 u'cp-1258': self._(u'cp-1258'),
1231 u'koi-8r': self._(u'koi-8r'),
1232 }
1233 self.valid_options[u'bodyfont'] = {
1234 u'courier': self._(u'Courier'),
1235 u'helvetica': self._(u'Helvetica'),
1236 u'monospace': self._(u'Monospace'),
1237 u'sans': self._(u'Sans'),
1238 u'serif': self._(u'Serif'),
1239 u'times': self._(u'Times'),
1240 }
1241 self.valid_options[u'headingfont'] = self.valid_options[u'bodyfont']
1242 self.valid_options[u'headfootfont'] = self.valid_options[u'bodyfont']
1243 # Go through all font types and create fontname{-bold,-oblique,-boldoblique} entries.
1244 for fontname in self.valid_options[u'bodyfont'].keys():
1245 for fontstyle in [u'bold', u'oblique', u'boldoblique']:
1246 self.valid_options[u'headfootfont'][fontname + u'-' + fontstyle] = u'%s (%s)' % (self.valid_options[u'headfootfont'][fontname], self._(fontstyle),)
1247 # Set possible font sizes.
1248 self._set_fontsize(u'headfootsize', 6, 24, 5)
1249 self._set_fontsize(u'fontsize', 4, 24, 5)
1250 self._set_fontsize(u'fontspacing', 1, 3, 1)
1251
1252 # Set translated name of table of contents as default.
1253 self.default_values[u'toctitle'] = self._(u'Contents')
1254
1255 self.default_values[u'titletext'] = self.pagename
1256 self.default_values[u'extra-titledocnumber'] = u'%d' % self.request.page.get_rev()[1]
1257 page_editor = self.request.page.lastEditInfo().get(u'editor', u'')
1258 self.default_values[u'extra-titleauthor'] = wikiutil.escape(getEditorName(self.request))
1259
1260 # Make sure we create date and time strings in right format.
1261 if self.request.current_lang:
1262 self.default_values[u'language'] = self.request.current_lang
1263 elif self.request.page.language:
1264 self.default_values[u'language'] = self.request.page.language
1265 else:
1266 #self.cfg.language_default or "en"
1267 self.default_values[u'language'] = self.make_isolang(self.cfg.__dict__.get(u'default_language', u'en'))
1268
1269 self.values = {}
1270
1271 # If the configuration variable 'createpdfdocument_validoptions' exists we update our
1272 # self.valid_options dict with these values.
1273 if getattr (self.request.cfg, u'createpdfdocument_validoptions', None):
1274 self.valid_options.update (self.request.cfg.createpdfdocument_validoptions)
1275
1276 # If the configuration variable 'createpdfdocument_defaultvalues' exists we update our
1277 # self.default_values dict with these values.
1278 if getattr (self.request.cfg, u'createpdfdocument_defaultvalues', None):
1279 for key, value in self.request.cfg.createpdfdocument_defaultvalues.items():
1280 self.default_values[key] = value
1281
1282 # Scan page to extract default values.
1283 self.set_page_default_values()
1284
1285 # Create a PDF document direct without any user iteraction from default and page settings.
1286 if self.form.has_key(u'generate'):
1287 self.set_page_values()
1288 self.update_values(useform=False)
1289 return self.do_generate()
1290
1291 # Create a PDF document from form settings.
1292 if self.form.has_key(u'generate_from_form'):
1293 self.update_values()
1294 return self.do_generate()
1295
1296 if self.form.has_key(u'remember'):
1297 self.update_values()
1298 return self.do_remember()
1299
1300 self.set_page_values()
1301 self.update_values(useform=False)
1302 # Backward compatiblity with MoinMoin 1.5.
1303 if Page.send_page.func_code.co_varnames[1] == "request":
1304 return self.request.page.send_page(self.request, msg=self.makeform())
1305 else:
1306 return self.request.page.send_page(msg=self.makeform())
1307
1308 def update_values(self, useform=True):
1309 """Preset values with they form values or defaults."""
1310 for key, default in self.default_values.items():
1311 # Modify value only if not already set.
1312 if not key in self.values:
1313 # If the form does not contain the value (e.g. for checkboxes) set the
1314 # default value (e.g. for checkboxes unset by default).
1315 if not key in self.form:
1316 # Special processing for checkboxes in forms. If the key does not exists
1317 # within the form it is not checked.
1318 if key in self.form_checkbox and useform:
1319 self.values[key] = u'unchecked'
1320 elif useform:
1321 # Edit fields are missing if they are empty.
1322 self.values[key] = u''
1323 else:
1324 self.values[key] = default
1325 else:
1326 self.values[key] = self.form[key][0]
1327 # Check if revision is an integer value.
1328 try:
1329 self.values[u'rev'] = int(self.values.get(u'rev', self.request.page.rev))
1330 except:
1331 self.values[u'rev'] = self.request.page.rev
1332 # Check if page revision exists.
1333 (pagefname, realrev, exists) = self.request.page.get_rev(rev=self.values[u'rev'])
1334 if exists:
1335 self.values[u'rev'] = realrev
1336 else:
1337 # Determine latest revision number.
1338 (pagefname, self.values[u'rev'], exists) = self.request.page.get_rev()
1339
1340 def do_generate(self):
1341 """Create PDF document."""
1342 # Generate the HTML page using MoinMoin wiki engine.
1343 html = self.get_html()
1344 if html:
1345 pdfdata = self.html2pdf(html)
1346 if pdfdata:
1347 # Send as application/pdf the generated file by HTMLDOC
1348 self.send_pdf(pdfdata)
1349
1350 # MoinMoin1.6: send_closing_html() has to be called explicit.
1351 # MoinMoin1.5: raise MoinMoinNoFooter exception to forbit creation of HTML footer.
1352 if not 'send_closing_html' in self.request.theme.__dict__:
1353 from MoinMoin.util import MoinMoinNoFooter
1354 raise MoinMoinNoFooter
1355
1356 def do_remember(self):
1357 """Create a message containing information about how to save the form values for future reuse."""
1358 save = u''
1359 for key, value in self.values.items():
1360 if key in [u'user-password', u'owner-password', u'rev', u'debug']:
1361 continue
1362 if key in self.default_values and value == self.default_values[key]:
1363 continue
1364 save += u'##pdf %s %s' % (key, value,)
1365 if self.debug:
1366 if key in self.default_values:
1367 save += u' <-- default value is "%s" (without quotes)' % self.default_values[key]
1368 else:
1369 save += u' <-- keyword missing in defaults'
1370 save += u'\n'
1371 if save:
1372 msg = self._(u'Add follwing lines at the beginning of your page:') + u'<br/><pre>' + save + u'</pre>'
1373 else:
1374 msg = self._(u'All values correspond to they default. Nothing have to be saved.')
1375 # Backward compatiblity with MoinMoin 1.5.
1376 if Page.send_page.func_code.co_varnames[1] == "request":
1377 return self.request.page.send_page(self.request, msg)
1378 else:
1379 return self.request.page.send_page(msg)
1380
1381 def send_pdf (self, data):
1382 """Send PDF file to HTTP server."""
1383 filename = self.pagename.replace (u'/', u'-') + u'-v' + str(self.values[u'rev']) + u'.pdf'
1384
1385 # Send HTTP header.
1386 self.request.http_headers([
1387 'Content-Type: %s' % self.contenttype,
1388 'Content-Length: %d' % len(data),
1389 # TODO: fix the encoding here, plain 8 bit is not allowed
1390 # according to the RFCs There is no solution that is
1391 # compatible to IE except stripping non-ascii chars
1392 'Content-Disposition: inline; filename="%s"' % filename.encode(config.charset),
1393 ])
1394
1395 # Send binary data.
1396 sio = StringIO.StringIO(data)
1397 shutil.copyfileobj(sio, self.request, 8192)
1398
1399 def get_html(self):
1400 """Generate the HTML body of this page."""
1401 # Save page as HTML.
1402 newreq = RedirectOutputRequest(self.request)
1403 # Do not add edit information.
1404 # Add extra meta tags.
1405 orig_html_head = self.request.cfg.html_head
1406 # FIXME: self.values need to be quoted.
1407 self.request.cfg.html_head = self.request.cfg.html_head + u"""
1408 <meta name="docnumber" content="%(extra-titledocnumber)s">
1409 <meta name="author" content="%(extra-titleauthor)s">
1410 <meta name="copyright" content="%(extra-titlecopyright)s">
1411 """ % self.values
1412 (html, errmsg) = newreq.run(rev = self.values.get(u'rev', None))
1413 # Restore original HTML head configuration.
1414 self.request.cfg.html_head = orig_html_head
1415 if html:
1416 html = self.fixhtmlstr(html)
1417 # Make URLs absolute.
1418 # FIXME: Until MoinMoin is not XHTML compilant we can not use a XML parser
1419 # (e.g. expat) to transform the HTML document. In the meantime we try to
1420 # achive the same with regular expressions subtitution.
1421 base = self.request.getQualifiedURL()
1422 for htmlref in [u'src', u'href']:
1423 reurlref = r'(%s=[\'"])(/[^\'"]*)[\'"]' % (htmlref,)
1424 urlref = re.compile (reurlref, re.I)
1425 for match in urlref.finditer(html):
1426 foundref = match.groups()
1427 html = html.replace (foundref[0] + foundref[1], foundref[0] + base + foundref[1])
1428 # Rename title of the document.
1429 titletext_html = self.fixhtmlstr(wikiutil.escape(self.values['titletext']))
1430 html = re.compile(r'<title>[^<]+</title>').sub(u'<title>%s</title>' % titletext_html, html)
1431 if self.values[u'pageinfo'] == u'unchecked':
1432 # Remove pageinfo by regex. There is no standard way to do that yet.
1433 # Read the comment in ThemeBase.shouldShowPageinfo().
1434 html = re.compile(r'<p[^>]+id="pageinfo".*</p>').sub(u'', html)
1435 else:
1436 self.error_msg(self._(u'Could not redirect HTML output for further processing:') + errmsg)
1437
1438 return html
1439
1440 def make_isolang (self, language):
1441 return language + u'_' + language.upper()
1442
1443 def html2pdf (self, html):
1444 """Create a PDF document based on the current parameters."""
1445 # Not all os support environment variable definition in one line with the calling application.
1446 if os.name in ['posix', 'mac']:
1447 htmldocopts = [u'LANG=' + self.values[u'language'], u'HTMLDOC_NOCGI=1']
1448 else:
1449 htmldocopts = []
1450 # Determine UID to access ACL protected sites too (mandatory to download attached images).
1451 htmldocopts += [u'htmldoc', "--cookies", "MOIN_ID=" + self.request.user.id, u'--no-duplex']
1452
1453 for key in [u'header', u'footer', u'tocheader', u'tocfooter']:
1454 self.values[key] = self.values.get(key + u'left', u'.') + self.values.get(key + u'middle', u'.') + self.values.get(key + u'right', u'.')
1455
1456 permissions = []
1457 for opt, value in self.values.items():
1458 # Skip alle non-HTMLDOC configuration parameters.
1459 if opt in [u'language', u'debug', u'rev', u'titletext', u'pageinfo'] or opt[:6] == u'extra-':
1460 continue
1461
1462 # Skip options without values.
1463 value = value.strip()
1464 if not value:
1465 continue
1466
1467 # Skip options for header/footer configuration which differenciate between position (e.g. footerright or tocheadermiddle)
1468 if opt[:6] in [u'header', u'footer'] and opt[6:] or opt[:9] in [u'tocheader', u'tocfooter'] and opt[9:]:
1469 continue
1470
1471 if opt == u'titlefileimage':
1472 # Check if we have a --titlefile or --titleimage option.
1473 lower_value = value.lower()
1474 dotpos = lower_value.rfind(u'.')
1475 if lower_value[dotpos:] in [u'.bmp', u'.gif', u'.jpg', u'.jpeg', u'.png']:
1476 opt = u'titleimage'
1477 else:
1478 opt = u'titlefile'
1479 value = attachment_fsname(value, self.request.page, self.request)
1480 elif opt == u'bodyimage':
1481 value = attachment_fsname(value, self.request.page, self.request)
1482
1483 if opt in [u'style']:
1484 htmldocopts += [u'--' + value]
1485 elif opt in self.form_checkbox:
1486 if value == u'checked':
1487 if opt[:10] == u'permission':
1488 permissions += [opt[10:]]
1489 # Reverse meaning of 'no-' options.
1490 elif opt[:3] != u'no-':
1491 htmldocopts += [u'--' + opt]
1492 elif opt[:3] == u'no-':
1493 htmldocopts += [u'--' + opt]
1494 elif opt[:6] == u'margin':
1495 htmldocopts += [u'--' + opt[6:], value]
1496 else:
1497 htmldocopts += [u'--' + opt, value]
1498 if permissions:
1499 htmldocopts += [u'--permission', u','.join (permissions)]
1500 htmldocopts += [u'-']
1501 # Do not forget to escape all spaces!
1502 eschtmldocopts = [shell_quote(arg) for arg in htmldocopts]
1503 cmdstr = u' '.join(eschtmldocopts)
1504 errmsg = None
1505
1506 pdf = None
1507 if self.debug:
1508 self.request.http_headers()
1509 errmsg = self._(u'HTMLDOC command:') + u'<pre>' + wikiutil.escape(cmdstr) + u'</pre>'
1510 (htmldoc_help, htmldoc_err) = pipeCommand(u'HTMLDOC_NOCGI=1 htmldoc --help')
1511 errmsg += u'<p><pre>%s</pre></p>' % wikiutil.escape(htmldoc_help)
1512 if 'env' in self.request.__dict__:
1513 reqenv = u'%s' % wikiutil.escape(self.request.env)
1514 else:
1515 reqenv = u'None'
1516 errmsg += u'<p>MoinMoin release %s<br/>self.request = %s<br/>self.request.env = %s</p>' % (wikiutil.escape(moinmoin_release), wikiutil.escape(type(self.request)), reqenv,)
1517 errmsg += u'<hr/><hr/><hr/>'
1518 else:
1519 (pdf, htmldocmsg) = pipeCommand(cmdstr, html)
1520
1521 # Check for error message on STDOUT.
1522 if pdf[:8] == u'HTMLDOC ':
1523 htmldocmsg += pdf
1524 pdf = None
1525
1526 if htmldocmsg:
1527 errmsg = self._(u'Command:') + u'<pre>' + wikiutil.escape(cmdstr) + u'</pre>' + self._('returned:') + u'<pre>' + \
1528 wikiutil.escape(htmldocmsg).replace(u'\n', u'<br/>') + u'</pre>'
1529
1530 # As it is difficult to get the htmldoc return code, we check for
1531 # error by checking the produced pdf length
1532 if not pdf and errmsg:
1533 if self.debug:
1534 self.request.write(u'<html><body>%s</body></hftml>' % errmsg)
1535 self.request.write(html)
1536 else:
1537 self.error_msg(errmsg)
1538 elif pdf[:4] != '%PDF':
1539 self.error_msg(self._(u'Invalid PDF document generated') + wikiutil.escape(pdf[:80]))
1540 pdf = None
1541
1542 return pdf
1543
1544 def _set_fontsize(self, key, min, max, smallstep):
1545 self.valid_options[key] = {}
1546 for fontsize_big in range(min, max):
1547 for fontsize_step in range(0, 9, smallstep):
1548 fontsize = u'%d.%d' % (fontsize_big, fontsize_step,)
1549 self.valid_options[key][fontsize] = self._(fontsize)
1550 self.valid_options[key][u'%d.0' % max] = self._(u'%d.0' % max)
1551
1552
1553 def execute (pagename, request):
1554 try:
1555 CreatePdfDocument().run (pagename = pagename, request = request)
1556 except:
1557 raise
1558
1559 # vim:ts=4:et:sw=4:nu
Attached Files
To refer to attachments on a page, use attachment:filename, as shown below in the list of files. Do NOT use the URL of the [get] link, since this is subject to change and can break easily.You are not allowed to attach a file to this page.