Attachment 'CreatePdfDocument2_4_1+p1.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     Copy this script in your's MoinMoin action script plugin directory.
  10 
  11     Thanks goes to Pascal Bauermeister who initiated the implementaion.
  12     Lot of things changes since then but the idear using HTMLDOC is the
  13     main concept of this implementation.
  14     
  15     Please visit the homepage for further informations:
  16     http://moinmo.in/ActionMarket/PdfAction
  17 
  18     @copyright: (C) 2006  Pascal Bauermeister
  19     @copyright: (C) 2006-2010  Raphael Bossek <raphael.bossek@solutions4linux.de>
  20     @license: GNU GPL, see COPYING for details
  21 """
  22 
  23 __version__ = u'2.4.1+p1'
  24 
  25 release_notes = """
  26 2010-09-17  RaphaelBossek
  27 * Release v2.4.1+p1
  28 * FIX: Missing pictures for ACL protected pages with MoinMoin
  29 
  30 2009-02-30  RaphaelBossek
  31 * Release v2.4.1
  32 * FIX: Support for PDFIcon v1.1.0
  33 
  34 2009-02-29  RaphaelBossek
  35 * Release v2.4.0
  36 * NEW: Initial support for MoinMoin 1.9 including 1.8, 1.7, 1.6, 1.5!
  37 * NEW: htmldoc_options is an new configuration parameter. htmldoc_options is
  38        an array of parameters which will by applied before calling htmldoc_cmd.
  39 * WIP: ERR012: Unable to write document file - Permission denied
  40        Define an alternative output directory with -d parameter using the
  41        htmldoc_options configuration variable.
  42 * FIX: Title image with MoinMoin 1.5.
  43 
  44 2009-01-12  RaphaelBossek
  45 * Releae v2.3.6
  46 * FIX: Error message, e.b. ERR017: Unknown style property, are surpressed
  47   as long PAGES: and BYTES: lines exists.
  48 
  49 2009-01-11  RaphaelBossek
  50 * Release v2.3.5
  51 * Supports MoinMoin 1.8.
  52 * FIX: Fixed support for anonymous users in CGI servers where
  53   no cookies exists for this page on browser site.
  54 * FIX: JPEG compression level.
  55 * FIX: In case where extra-headingastitle is checked but no
  56   heading is defined at the page, we use the form entry.
  57 
  58 2008-09-07  RaphaelBossek
  59 * Release v2.3.4
  60 * FIX: Value of expert's form field `extra-dynamiccodeblock-break` will be considered.
  61 
  62 2008-07-09  RaphaelBossek
  63 * Release v2.3.3
  64 * FIX: Recognission of newline character on non-UNIX systems.
  65 
  66 2008-07-02  RaphaelBossek
  67 * Release v2.3.2
  68 * NEW: Added support for HTTP PROXY server (set createpdfdocument_defaultvalues['proxy'] = u'http://myproxy:3128')
  69 * FIX: Access to ACL protected attachments for MoinMoin 1.6 and newer (MOIN_SESSION).
  70 * FIX: Table borders.
  71 * FIX: Do not show top/bottom links in headings (show_topbottom = False).
  72 
  73 2008-06-25  RaphaelBossek
  74 * Release v2.3.1
  75 * FIX: Remove interwiki part of author name.
  76 * FIX: RedirectOutputRequest is able to catch error messages while initialisation, e.g. if forbidden.
  77 * FIX: Suppress of line numbers in code blocks.
  78 * FIX: Processing of form values (checkbox).
  79 * NEW: Added debugging support in expert mode.
  80 * NEW: Possibility to set in expert tab if line breaks and middle dot character is used in dynamic blocks.
  81 
  82 2008-06-04  RaphaelBossek
  83 * Release v2.3.0
  84 * Supports MoinMoin v1.7.0, v1.6.3, v1.5.8
  85 * Preselect document style to webpage if no heading and table of contents is found.
  86 * FIX: Missing table borders where '<table style=' was used.
  87 * FIX: Cut off blocks.
  88 
  89 2008-01-27  RaphaelBossek
  90 * Release v2.2.1
  91 * Remove page information for MoinMoin v1.5 (interwiki).
  92 * Added support to set page title using first heading.
  93 * Fix for htmldoc_cmd parameter in remember form.
  94 * Style of document will be set to book if TableOfContents is found by default (if
  95   not overwritten).
  96 
  97 2007-09-25  RaphaelBossek
  98 * Release v2.2.0
  99 * Fixed debug information for Windows.
 100 * Added support for Windows where HTMLDOC_NOCGI environment variable was missing.
 101 * Added preview mode added. It's now possible to see what HTMLDOC will process.
 102 
 103 2007-09-23  RaphaelBossek
 104 * Release v2.1.5
 105 * Added support for line numbers in code blocks.
 106 * Remove page information so it's not printed for webpage style.
 107 * Added table cell padding to 5 pixels.
 108 
 109 2007-09-21  RaphaelBossek
 110 * Release v2.1.4
 111 * HTMLDOC supports only HTML 4.1 (http://www.htmldoc.org/documentation.php/Elements.html)
 112 * Fix for table borders and cell background.
 113 * Added support for code blocks (break on page boundries).
 114 
 115 2007-09-18  RaphaelBossek
 116 * Release v2.1.3
 117 * Added support for scaling images (--browserwidth).
 118 
 119 2007-08-21  RaphaelBossek
 120 * Release v2.1.2
 121 * Fixed support for python 2.3.
 122 
 123 2007-08-21  RaphaelBossek
 124 * Release v2.1.1
 125 * Applied speed improvemtn patch from BrianDickman.
 126 * Fixed font size values where x.9 was missing.
 127 * Some cosmetic changes in the form.
 128 
 129 2007-08-20  RaphaelBossek
 130 * Release v2.1.0
 131 * Configuration is seperated by tabbs.
 132 * Added support for font style and colors.
 133 * Fixed save/load of configuration variables within page (may be empty).
 134 
 135 2007-08-17  RaphaelBossek
 136 * Release v2.0.11
 137 * Added support for alternative title page and title page logo.
 138 * Added support for additional fields for the title page author, docnumber
 139   and copyright.
 140 
 141 2006-12-18  RaphaelBossek
 142 * Release v2.0.10
 143 * Improved support for webpage/book styles if the HTML documents does not.
 144 
 145 2006-12-17  RaphaelBossek
 146 * Release v2.0.9
 147 * Added AUTH_TYPE for RedirectOutputRequest()
 148 
 149 2006-11-16  RaphaelBossek
 150 * Release v2.0.8
 151 * Fixed another missing configuration for RedirectOutputRequest()
 152 
 153 2006-11-15  RaphaelBossek
 154 * Release v2.0.7
 155 * Fixed support for MoinMoin 1.5.3
 156 * Fixed SSL support.
 157 
 158 2006-11-14  RaphaelBossek
 159 * Release v2.0.6
 160 * MoinMoin 1.6 support added.
 161 * Fixed Windows(TM) platform support.
 162 * Added support for alternative title text.
 163 
 164 2006-09-13  RaphaelBossek
 165 * Release v2.0.5
 166 * Fixed RedirectOutputRequest class where definition of script_name
 167   was missing.
 168 
 169 2006-09-12  RaphaelBossek
 170 * Release v2.0.4
 171 * Fixed RedirectOutputRequest class where function was redifined
 172   to boolean value.
 173 
 174 2006-09-06  RaphaelBossek
 175 * Release v2.0.3
 176 * Fixed FastCGI support by removing stdout redirect output code. The
 177   same functionality will be done by the RedirectOutputRequest class.
 178 * Renamed to CreatePdfDocument (for better translation possibilities).
 179 * Fixed encoding of title page.
 180 * Added charset set option.
 181 * Fixed waiting for HTMLDOC.
 182 
 183 2006-09-02  RaphaelBossek
 184 * Release v2.0.2
 185 * Added createpdfdocument_validoptions and createpdfdocument_defaultvalues
 186   configuration parameter support. You are not able to preset available
 187   options and which defaults are used in your configuration file.
 188 
 189 2006-08-30  RaphaelBossek
 190 * Release v2.0.1
 191 * Fixed issue with page revision number forwarding (traceback).
 192 
 193 2006-08-30  RaphaelBossek
 194 * Release v2.0.0
 195 * Feature enchanced and bug fixed version.
 196 
 197 2006-05-26  PascalBauermeister
 198 * Release v1.0.2
 199 * Relative image URLs turned absolute was bogus. It is less bogus now.
 200 
 201 2006-05-26  PascalBauermeister
 202 * Release v1.0.1
 203 * Set env var HTMLDOC_NOCGI to solve CGI issue
 204 
 205 2006-05-24  PascalBauermeister
 206 * Initial release v1.0.0
 207 """
 208 
 209 import os
 210 import sys
 211 import stat
 212 import re
 213 import copy
 214 import shutil
 215 import StringIO
 216 import array
 217 
 218 from MoinMoin import config
 219 from MoinMoin import util
 220 from MoinMoin import wikiutil
 221 from MoinMoin import packages
 222 from MoinMoin import error
 223 from MoinMoin.Page import Page
 224 from MoinMoin.widget.dialog import Dialog
 225 from MoinMoin.version import release as moinmoin_release
 226 from MoinMoin.action import ActionBase
 227 from MoinMoin.action import AttachFile
 228 
 229 from MoinMoin.version import release as moinmoin_release
 230 if moinmoin_release[:4] in ['1.5.', '1.6.', '1.7.', '1.8.']:
 231     class Version:
 232         def __init__(self, version):
 233             VERSION_RE = re.compile(r"(?P<major>\d+)\.(?P<minor>\d+)\.(?P<release>\d+)(-(?P<additional>.+))?", re.VERBOSE)
 234             match = VERSION_RE.match(version)
 235             v = match.groupdict()
 236             self.major = int(v['major'])
 237             self.minor = int(v['minor'])
 238             self.release = int(v['release'])
 239     moinmoin_version = Version(moinmoin_release)
 240 else:
 241     # Since MoinMoin 1.9
 242     moinmoin_version = wikiutil.Version(version=moinmoin_release)
 243 
 244 def is_moinmoin_version_eqless(major, minor):
 245     return moinmoin_version.major <= major and moinmoin_version.minor <= minor
 246 
 247 def is_moinmoin_version_eqhigher(major, minor):
 248     return moinmoin_version.major >= major and moinmoin_version.minor >= minor
 249 
 250 if is_moinmoin_version_eqless(1,8):
 251     from MoinMoin.request import RequestBase
 252 else:
 253     # Since MoinMoin 1.9
 254     from MoinMoin.web.request import Request as RequestBase
 255     from MoinMoin.web.session import get_cookie_name
 256 
 257 # http://www.barelyfitz.com/projects/tabber/
 258 tabber_minimized = '''
 259 // Version 1.9 stripped by Creativyst SS & JavaScript Compressor v2.2c (http://www.creativyst.com/Prod/3/)
 260 function tabberObj(argsObj)
 261 { 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];}
 262 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;}
 263 }
 264 tabberObj.prototype.init = function(e)
 265 { var
 266 childNodes, i, i2, t, defaultTab=0, DOM_ul, DOM_li, DOM_a, aId, headingElement; if (!document.getElementsByTagName) { return false;}
 267 if (e.id) { this.id = e.id;}
 268 this.tabs.length = 0; childNodes = e.childNodes; for(i=0; i < childNodes.length; i++) { if(childNodes[i].className &&
 269 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;}
 270 }
 271 }
 272 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 = '';}
 273 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,"");}
 274 break;}
 275 }
 276 }
 277 if (!t.headingText) { t.headingText = i + 1;}
 278 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;}
 279 DOM_li.appendChild(DOM_a); DOM_ul.appendChild(DOM_li);}
 280 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});}
 281 return this;}; tabberObj.prototype.navClick = function(event)
 282 { var
 283 rVal, a, self, tabberIndex, onClickArgs; a = this; if (!a.tabber) { return false;}
 284 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;}
 285 rVal = self.onClick(onClickArgs); if (rVal === false) { return false;}
 286 }
 287 self.tabShow(tabberIndex); return false;}; tabberObj.prototype.tabHideAll = function()
 288 { var i; for (i = 0; i < this.tabs.length; i++) { this.tabHide(i);}
 289 }; tabberObj.prototype.tabHide = function(tabberIndex)
 290 { var div; if (!this.tabs[tabberIndex]) { return false;}
 291 div = this.tabs[tabberIndex].div; if (!div.className.match(this.REclassTabHide)) { div.className += ' ' + this.classTabHide;}
 292 this.navClearActive(tabberIndex); return this;}; tabberObj.prototype.tabShow = function(tabberIndex)
 293 { var div; if (!this.tabs[tabberIndex]) { return false;}
 294 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});}
 295 return this;}; tabberObj.prototype.navSetActive = function(tabberIndex)
 296 { this.tabs[tabberIndex].li.className = this.classNavActive; return this;}; tabberObj.prototype.navClearActive = function(tabberIndex)
 297 { this.tabs[tabberIndex].li.className = ''; return this;}; function tabberAutomatic(tabberArgs)
 298 { var
 299 tempObj, divs, i; if (!tabberArgs) { tabberArgs = {};}
 300 tempObj = new tabberObj(tabberArgs); divs = document.getElementsByTagName("div"); for (i=0; i < divs.length; i++) { if (divs[i].className &&
 301 divs[i].className.match(tempObj.REclassMain)) { tabberArgs.div = divs[i]; divs[i].tabber = new tabberObj(tabberArgs);}
 302 }
 303 return this;}
 304 function tabberAutomaticOnLoad(tabberArgs)
 305 { var oldOnLoad; if (!tabberArgs) { tabberArgs = {};}
 306 oldOnLoad = window.onload; if (typeof window.onload != 'function') { window.onload = function() { tabberAutomatic(tabberArgs);};} else { window.onload = function() { oldOnLoad(); tabberAutomatic(tabberArgs);};}
 307 }
 308 if (typeof tabberOptions == 'undefined') { tabberAutomaticOnLoad();} else { if (!tabberOptions['manualStartup']) { tabberAutomaticOnLoad(tabberOptions);}
 309 }
 310 '''
 311 
 312 tabber_minimized_css = '''
 313 <style type="text/css">
 314 /*--------------------------------------------------
 315   REQUIRED to hide the non-active tab content.
 316   But do not hide them in the print stylesheet!
 317   --------------------------------------------------*/
 318 .tabberlive .tabbertabhide {
 319  display:none;
 320 }
 321 
 322 /*--------------------------------------------------
 323   .tabber = before the tabber interface is set up
 324   .tabberlive = after the tabber interface is set up
 325   --------------------------------------------------*/
 326 .tabber {
 327 }
 328 .tabberlive {
 329  margin-top:1em;
 330 }
 331 
 332 /*--------------------------------------------------
 333   ul.tabbernav = the tab navigation list
 334   li.tabberactive = the active tab
 335   --------------------------------------------------*/
 336 ul.tabbernav
 337 {
 338  margin:0;
 339  padding: 3px 0;
 340  border-bottom: 1px solid #778;
 341  font: bold 12px Verdana, sans-serif;
 342 }
 343 
 344 ul.tabbernav li
 345 {
 346  list-style: none;
 347  margin: 0;
 348  display: inline;
 349 }
 350 
 351 ul.tabbernav li a
 352 {
 353  padding: 3px 0.5em;
 354  margin-left: 3px;
 355  border: 1px solid #778;
 356  border-bottom: none;
 357  background: #DDE;
 358  text-decoration: none;
 359 }
 360 
 361 ul.tabbernav li a:link { color: #448; }
 362 ul.tabbernav li a:visited { color: #667; }
 363 
 364 ul.tabbernav li a:hover
 365 {
 366  color: #000;
 367  background: #AAE;
 368  border-color: #227;
 369 }
 370 
 371 ul.tabbernav li.tabberactive a
 372 {
 373  background-color: #fff;
 374  border-bottom: 1px solid #fff;
 375 }
 376 
 377 ul.tabbernav li.tabberactive a:hover
 378 {
 379  color: #000;
 380  background: white;
 381  border-bottom: 1px solid white;
 382 }
 383 
 384 /*--------------------------------------------------
 385   .tabbertab = the tab content
 386   Add style only after the tabber interface is set up (.tabberlive)
 387   --------------------------------------------------*/
 388 .tabberlive .tabbertab {
 389  padding:5px;
 390  border:1px solid #aaa;
 391  border-top:0;
 392 
 393  /* If you don't want the tab size changing whenever a tab is changed
 394     you can set a fixed height */
 395 
 396  /* height:200px; */
 397 
 398  /* If you set a fix height set overflow to auto and you will get a
 399     scrollbar when necessary */
 400 
 401  /* overflow:auto; */
 402 }
 403 
 404 /* If desired, hide the heading since a heading is provided by the tab */
 405 .tabberlive .tabbertab h2 {
 406  display:none;
 407 }
 408 .tabberlive .tabbertab h3 {
 409  display:none;
 410 }
 411 
 412 /* Example of using an ID to set different styles for the tabs on the page */
 413 .tabberlive#tab1 {
 414 }
 415 .tabberlive#tab2 {
 416 }
 417 .tabberlive#tab2 .tabbertab {
 418  height:200px;
 419  overflow:auto;
 420 }
 421 </style>
 422 '''
 423 
 424 def merge_form_query(request):
 425     """
 426     This function intent to unifiy the way MoinMoin 1.9 differenciante
 427     between form and query key/value pairs.
 428     Since MoinMoin 1.9 key/value pairs are stored it two different dictonaries:
 429      -- key/value which are set by query/url are within request.values 
 430      -- key/value which are set by from are within request.form
 431     Until MoinMoin 1.8 key/value paires are stored only within request.form. Also
 432     values are arrays; we use only the first array entry.
 433     """
 434     ret = {}
 435     if is_moinmoin_version_eqless(1,8):
 436         for k,v in request.form.iteritems():
 437             ret[k] = v[0]
 438     else:
 439         # Since MoinMoin 1.9 werkzeug.datastructures.dict is used as datatype.
 440         for k,v in request.form.iteritems():
 441             ret[k] = v
 442         # Sine MoinMoin 1.9 self.request.values is of type CombinedMultiDict
 443         for k,v in request.values.iteritems():
 444             ret[k] = v
 445     return ret
 446 
 447 
 448 def attachment_fsname(attachment, page, request):
 449     """Return location of attament on the file system. current_page is the relative location
 450     where attachment is used.
 451     """
 452     fname = None
 453     pagename, filename = AttachFile.absoluteName(attachment, page.page_name)
 454     #self.request.log("attachment_link: url %s pagename %s filename %s" % (url, pagename, filename))
 455     fname = wikiutil.taintfilename(filename)
 456     fname = AttachFile.getFilename(request, pagename, fname)
 457     if not os.path.exists(fname):
 458         fname = None
 459     return fname
 460 
 461 
 462 def shell_quote(parameter):
 463     o = parameter
 464     for c in [u' ', u'(', u')']:
 465         o = o.replace(c, u'\\' + c)
 466     return o
 467 
 468 
 469 def getEditorName(request):
 470     """Return name of the last editor."""
 471     editorname = u''
 472     if 'edit_info' in dir(request.page):
 473         editorname = request.page.edit_info().get('editor', u':').split(u':')[-1]
 474     else:
 475         # Backward compatibility before MoinMoin 1.7.0
 476         log = request.page._last_edited(request)
 477         if log:
 478             if request.cfg.show_hosts:
 479                 title = " @ %s[%s]" % (log.hostname, log.addr)
 480             else:
 481                 title = ""
 482             kind, info = log.getInterwikiEditorData(request)
 483             if kind in ['interwiki', 'email']:
 484                 if log._usercache[log.userid].__dict__.get('aliasname', u''):
 485                     editorname = log._usercache[log.userid].aliasname
 486                 else:
 487                     editorname = log._usercache[log.userid].name
 488             elif kind == 'ip':
 489                 try:
 490                     idx = info.index('.')
 491                 except ValueError:
 492                     idx = len(info)
 493                 editorname = wikiutil.escape(info[:idx])
 494     return editorname
 495 
 496 
 497 def pipeCommand(cmdstr, input=None):
 498     child_stdin, child_stdout, child_stderr = os.popen3(cmdstr, u'b')
 499     try:
 500         if input:
 501             child_stdin.write(input)
 502         child_stdin.close()
 503     except:
 504         pass
 505 
 506     child_output = child_stdout.read()
 507     child_stdout.close()
 508 
 509     child_error = child_stderr.read()
 510     child_stderr.close()
 511 
 512     if os.name in ['posix', 'mac']:
 513         try:
 514             # REMARK: Otherwise we get <defunct> processes.
 515             os.wait()
 516         except OSError, e:
 517             # 10: No child processes.
 518             if e.errno != 10:
 519                 raise
 520     return (child_output, child_error)
 521 
 522 
 523 class CreatePdfDocument(ActionBase):
 524     """Implementation of the PDF document generator."""
 525 
 526     def __init__(self, pagename, request):
 527         ActionBase.__init__(self, pagename, request)
 528         self.action_name = self.__class__.__name__
 529         self.debug = False
 530         self.msg = None
 531         self.errormsgsent = False
 532         # See merge_form_query function; support for MoinMoin 1.9 and older
 533         self.form_wrapper = {}
 534         self.default_values = {
 535             #'style': u'webpage',
 536             'style': None,
 537             'format': u'pdf13',
 538             'titlefileimage': u'',
 539             'linkstyle': u'underline',
 540             'headerleft': u't',
 541             'headermiddle': u'.',
 542             'headerright': u'D',
 543             'footerleft': u'.',
 544             'footermiddle': u'/',
 545             'footerright': u'.',
 546             'tocheaderleft': u'.',
 547             'tocheadermiddle': u't',
 548             'tocheaderright': u'.',
 549             'tocfooterleft': u'.',
 550             'tocfootermiddle': u'.',
 551             'tocfooterright': u'i',
 552             'bodycolor': u'FFFFFF',
 553             'bodyimage': u'',
 554             'textcolor': u'000000',
 555             'linkcolor': u'0000E0',
 556             'size': u'legal',
 557             'user-password': u'',
 558             'owner-password': u'',
 559             'toclevels': u'3',
 560             'grayscale': u'unchecked',
 561             'title': u'checked',
 562             'duplex': u'unchecked',
 563             'landscape': u'unchecked',
 564             'usersize': u'',
 565             'margintop': u'0.50in',
 566             'marginbottom': u'0.50in',
 567             'marginleft': u'1.00in',
 568             'marginright': u'0.50in',
 569             'no-toc': u'checked',
 570             'no-links': u'checked',
 571             'firstpage': u'p1',
 572             'jpeg': u'0',
 573             'compression': u'0',
 574             'pagemode': u'outline',
 575             'pagelayout': u'single',
 576             'firstpage': u'c1',
 577             'numbered': u'checked',
 578             'encryption': u'unchecked',
 579             'permissioncopy': u'checked',
 580             'permissionprint': u'checked',
 581             'permissionannotate': u'checked',
 582             'permissionmodify': u'checked',
 583             'charset': u'iso-8859-1',
 584             'debug': u'',
 585             'rev': 0,
 586             'extra-titledocnumber': u'',
 587             'extra-titleauthor': u'',
 588             'extra-titlecopyright': u'',
 589             'pageinfo': u'unchecked',
 590             'bodyfont': u'times',
 591             'headingfont': u'helvetica',
 592             'headfootfont': u'helvetica',
 593             'fontsize': u'11.0',
 594             'headfootsize': u'11.0',
 595             'fontspacing': u'1.2',
 596             'embedfonts': u'checked',
 597             'browserwidth': u'680',
 598             'extra-dynamiccodeblock': u'checked',
 599             'extra-codeblocklinenumbers': u'checked',
 600             'htmldoc_cmd': u'htmldoc',
 601             'htmldoc_options': [],
 602             'extra-headingastitle': u'unchecked',
 603             'extra-dynamiccodeblock-middot': u'checked',
 604             'extra-dynamiccodeblock-middotchar': u'&middot;',
 605             'extra-dynamiccodeblock-break': u'checked',
 606             'extra-dynamiccodeblock-breakchar': u'&para;',
 607         }
 608         # We have to know which values are checkboxes within the form. If a key does
 609         # not exists wihtin the form the corresponding checkbox is not checked.
 610         self.form_checkbox = []
 611         for key, value in self.default_values.items():
 612             if value in [u'checked', u'unchecked']:
 613                 self.form_checkbox += [key]
 614         self.contenttype = u'application/pdf'
 615 
 616         # This dict contains all possible values.
 617         self.valid_options = {}
 618 
 619         self.valid_options[u'tocformats'] = {
 620             u'/': self._(u'1/N,2/N Arabic page numbers'),
 621             u':': self._(u'1/C,2/C Arabic chapter page numbers'),
 622             u'1': self._(u'1,2,3,...'),
 623             u'a': self._(u'a,b,c,...'),
 624             u'A': self._(u'A,B,C,...'),
 625             u'c': self._(u'Chapter title'),
 626             u'C': self._(u'Chapter page number'),
 627             u'd': self._(u'Date'),
 628             u'D': self._(u'Date + Time'),
 629             u'h': self._(u'Heading'),
 630             u'i': self._(u'i,ii,iii,iv,...'),
 631             u'I': self._(u'I,II,III,IV,...'),
 632             u't': self._(u'Title'),
 633             u'T': self._(u'Time'),
 634             u'.': self._(u'Blank'),
 635             # TODO: Not supported yet; u'l': self._(u'Logo image'),
 636         }
 637         self.valid_options[u'style'] = {
 638             u'webpage': self._(u'webpage'),
 639             u'book': self._(u'book'),
 640             u'continuous': self._(u'continuous'),
 641         }
 642         self.valid_options[u'size'] = {
 643             u'legal': self._(u'Legal (8.5x14in)'),
 644             u'a4': self._(u'A4 (210x297mm)'),
 645             u'letter': self._(u'Letter (8.5x11in)'),
 646             u'universal': self._(u'Universal (8.27x11in)'),
 647             u'': self._(u'User defined'),
 648         }
 649         self.valid_options[u'format'] = {
 650             u'pdf11': self._(u'PDF 1.1 (Acrobat 2.0)'),
 651             u'pdf12': self._(u'PDF 1.2 (Acrobat 3.0)'),
 652             u'pdf13': self._(u'PDF 1.3 (Acrobat 4.0)'),
 653             u'pdf14': self._(u'PDF 1.4 (Acrobat 5.0)'),
 654             # TODO: Not supported yet:
 655             #u'ps1': self._(u'PostScript Level 1'),
 656             #u'ps2': self._(u'PostScript Level 2'),
 657             #u'ps3': self._(u'PostScript Level 3'),
 658         }
 659         self.valid_options[u'linkstyle'] = {
 660             u'underline': self._(u'Underline'),
 661             u'plain': self._(u'Plain'),
 662         }
 663         self.valid_options[u'firstpage'] = {
 664             u'c1': self._(u'1st chapter'),
 665             u'p1': self._(u'1st page'),
 666             u'toc': self._(u'Contents'),
 667         }
 668         self.valid_options[u'jpeg'] = {
 669             u'0': self._(u'None'),
 670             u'50': self._(u' 50% (Good)'),
 671             u'55': u'55%', u'60': u' 60%', u'65': ' 65%', u'70': ' 70%', u'75': ' 75%',
 672             u'80': ' 80%', u'85': ' 85%', u'90': ' 90%', u'95': ' 95%',
 673             u'100': self._(u'100% (Best)'),
 674         }
 675         self.valid_options[u'compression'] = {
 676             u'0': self._(u'None'),
 677             u'1': self._(u'1 (Fast)'),
 678             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',
 679             u'9': self._(u'9 (Best)'),
 680         }
 681         self.valid_options[u'toclevels'] = {
 682             u'0': self._(u'None'),
 683             u'1': u'1', u'2': '2', u'3': '3', u'4': '4'
 684         }
 685         self.valid_options[u'pagemode'] = {
 686             u'outline': self._(u'Outline'),
 687             u'document': self._(u'Document'),
 688             u'fullscreen': self._(u'Full-screen'),
 689         }
 690         self.valid_options[u'pagelayout'] = {
 691             u'single': self._(u'Single'),
 692             u'one': self._(u'One column'),
 693             u'twoleft': self._(u'Two column left'),
 694             u'tworight': self._(u'Two column right'),
 695         }
 696         self.valid_options[u'charset'] = {
 697             u'iso-8859-1': self._(u'ISO 8859-1'),
 698             u'iso-8859-2': self._(u'ISO 8859-2'),
 699             u'iso-8859-3': self._(u'ISO 8859-3'),
 700             u'iso-8859-4': self._(u'ISO 8859-4'),
 701             u'iso-8859-5': self._(u'ISO 8859-5'),
 702             u'iso-8859-6': self._(u'ISO 8859-6'),
 703             u'iso-8859-7': self._(u'ISO 8859-7'),
 704             u'iso-8859-8': self._(u'ISO 8859-8'),
 705             u'iso-8859-9': self._(u'ISO 8859-9'),
 706             u'iso-8859-14': self._(u'ISO 8859-14'),
 707             u'iso-8859-15': self._(u'ISO 8859-15'),
 708             u'cp-874': self._(u'cp-847'),
 709             u'cp-1250': self._(u'cp-1250'),
 710             u'cp-1251': self._(u'cp-1251'),
 711             u'cp-1252': self._(u'cp-1252'),
 712             u'cp-1253': self._(u'cp-1253'),
 713             u'cp-1254': self._(u'cp-1254'),
 714             u'cp-1255': self._(u'cp-1255'),
 715             u'cp-1256': self._(u'cp-1256'),
 716             u'cp-1257': self._(u'cp-1257'),
 717             u'cp-1258': self._(u'cp-1258'),
 718             u'koi-8r': self._(u'koi-8r'),
 719         }
 720         self.valid_options[u'bodyfont'] = {
 721             u'courier': self._(u'Courier'),
 722             u'helvetica': self._(u'Helvetica'),
 723             u'monospace': self._(u'Monospace'),
 724             u'sans': self._(u'Sans'),
 725             u'serif': self._(u'Serif'),
 726             u'times': self._(u'Times'),
 727         }
 728         self.valid_options[u'headingfont'] = self.valid_options[u'bodyfont']
 729         self.valid_options[u'headfootfont'] = self.valid_options[u'bodyfont']
 730         # Go through all font types and create fontname{-bold,-oblique,-boldoblique} entries.
 731         for fontname in self.valid_options[u'bodyfont'].keys():
 732             for fontstyle in [u'bold', u'oblique', u'boldoblique']:
 733                 self.valid_options[u'headfootfont'][fontname + u'-' + fontstyle] = u'%s (%s)' % (self.valid_options[u'headfootfont'][fontname], self._(fontstyle),)
 734         # Set possible font sizes.
 735         self._set_fontsize(u'headfootsize', 6, 24, 5)
 736         self._set_fontsize(u'fontsize', 4, 24, 5)
 737         self._set_fontsize(u'fontspacing', 1, 3, 1)
 738         
 739         # Set translated name of table of contents as default.
 740         self.default_values[u'toctitle'] = self._(u'Contents')
 741         
 742         self.default_values[u'titletext'] = self.pagename
 743         self.default_values[u'extra-titledocnumber'] = u'%d' % self.request.page.get_rev()[1]
 744         page_editor = self.request.page.lastEditInfo().get(u'editor', u'')
 745         self.default_values[u'extra-titleauthor'] = wikiutil.escape(getEditorName(self.request))
 746         
 747         # Make sure we create date and time strings in right format.
 748         if self.request.current_lang:
 749             self.default_values[u'language'] = self.request.current_lang
 750         elif self.request.page.language:
 751             self.default_values[u'language'] = self.request.page.language
 752         else:
 753             #self.cfg.language_default or "en"
 754             self.default_values[u'language'] = self.make_isolang(self.cfg.__dict__.get(u'default_language', u'en'))
 755 
 756         self.values = {}
 757 
 758         # If the configuration variable 'createpdfdocument_validoptions' exists we update our
 759         # self.valid_options dict with these values.
 760         if getattr (self.request.cfg, u'createpdfdocument_validoptions', None):
 761             self.valid_options.update (self.request.cfg.createpdfdocument_validoptions)
 762 
 763         # If the configuration variable 'createpdfdocument_defaultvalues' exists we update our
 764         # self.default_values dict with these values.
 765         if getattr (self.request.cfg, u'createpdfdocument_defaultvalues', None):
 766             for key, value in self.request.cfg.createpdfdocument_defaultvalues.items():
 767                 self.default_values[key] = value
 768         
 769         # Scan page to extract default values.
 770         self.set_page_default_values()
 771         self.set_page_values()
 772         self.update_values(useform=False)
 773         
 774         self.fields = {
 775             'pagename': wikiutil.escape(self.pagename),
 776             'action': self.action_name,
 777             'version': __version__,
 778             'moinmoin_release': moinmoin_release,
 779 
 780             'label_input': self._(u'Input'),
 781             'label_output': self._(u'Output'),
 782             'label_page': self._(u'Page'),
 783             'label_tableofcontents': self._(u'Contents'),
 784             'label_pdf': self._(u'PDF'),
 785             'label_security': self._(u'Security'),
 786 
 787             'label_choose_style': self._(u'Choose style'),
 788             'help_choose_style': self._(u'book: Create a structured PDF document with headings, chapters, etc.') + u'<br />' + \
 789                                  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/>' + \
 790                                  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.'),
 791 
 792             'help_titletext': self._(u'Title of the document for the front page.'),
 793             
 794             'label_extra-headingastitle': self._(u'Heading 1 as title'),
 795             'help_extra-headingastitle': self._(u'Extract the first heading of the document and use it as title. If checked the title field has no effect.'),
 796             
 797             'label_titlefileimage': self._(u'Title file/image'),
 798             'help_titlefileimage': self._(u'The title image or HTML page. These file has to be an attachments!'),
 799             
 800             'label_extra-titledocnumber': self._(u'Version'),
 801             'help_extra-titledocnumber': self._(u'Specify document version to be displayed on the title page.'),
 802             
 803             'label_extra-titleauthor': self._(u'Author'),
 804             'help_extra-titleauthor': self._(u'Intellectual property owner of this document.'),
 805             
 806             'label_extra-titlecopyright': self._(u'Copyright'),
 807             'help_extra-titlecopyright': self._(u'Copyright notice for this document.'),
 808             
 809             'label_pageinfo': self._(u'Apply page information'),
 810             'help_pageinfo': self._(u'Information about who and when modified the document are applied at the end.'),
 811             
 812             'label_format': self._(u'Output format'),
 813             'help_format': self._(u'Specifies the output format.'),
 814 
 815             'label_outputoptions': self._(u'Output options'),
 816             'label_grayscale': self._(u'Grayscale document'),
 817             'label_titlepage': self._(u'Title page'),
 818             'label_titletext': self._(u'Title'),
 819             'label_jpeg': self._(u'JPEG big images'),
 820             'label_compression': self._(u'Compression'),
 821 
 822             'label_no-toc': self._(u'Generate a table of contents'),
 823             'help_no-toc': self._(u''),
 824 
 825             'label_toclevels': self._(u'Limit the number of levels in the table-of-contents'),
 826             'help_toclevels': self._(u'Sets the number of levels in the table-of-contents.') + u' ' + self._(u'Empty for unlimited levels.'),
 827 
 828             'label_numbered': self._(u'Numbered headings'),
 829             'help_numbered': self._(u'Check to number all of the headings in the document.'),
 830 
 831             'label_toctitle': self._(u'Table-of-contents title'),
 832             'help_toctitle': self._(u'Sets the title for the table-of-contents.') + u' ' + self._(u'Empty for default title.'),
 833 
 834             'label_left': self._(u'Left'),
 835             'label_middle': self._(u'Middle'),
 836             'label_right': self._(u'Right'),
 837 
 838             'label_tocheader': self._(u'Header of table-of-contantes page'),
 839             'help_tocheader': self._(u'Sets the page header to use on table-of-contents pages.'),
 840 
 841             'label_tocfooter': self._(u'Footer of table-of-contantes page'),
 842             'help_tocfooter': self._(u'Sets the page footer to use on table-of-contents pages.'),
 843 
 844             'label_header': self._(u'Page header'),
 845             'help_header': self._(u'Sets the page header to use on body pages.'),
 846 
 847             'label_footer': self._(u'Page footer'),
 848             'help_footer': self._(u'Sets the page footer to use on body pages.'),
 849 
 850             'label_colors': self._(u'Colors'),
 851             'label_no-links': self._(u'Create HTTP links'),
 852             'help_no-links': self._(u'Enables generation of links in PDF files.'),
 853             
 854             'label_linkstyle': self._(u'Style of HTTP links'),
 855             'help_linkstyle': self._(u''),
 856 
 857             'label_linkcolor': self._(u'HTTP links color'),
 858             'help_linkcolor': self._(u'Sets the color of links.'),
 859             
 860             'label_bodycolor': self._(u'Body color'),
 861             'help_bodycolor': self._(u'Enter the HTML color for the body (background).'),
 862             
 863             'label_bodyimage': self._(u'Body image'),
 864             'help_bodyimage': self._(u'Enter the image file for the body (background). These file has to be an attachments!'),
 865             
 866             'label_textcolor': self._(u'Text color'),
 867             'help_textcolor': self._(u'Enter the HTML color for the text.'),
 868             
 869             'label_duplex': self._(u'2-Sided'),
 870             'help_duplex': self._(u'Specifies that the output should be formatted for double-sided printing.'),
 871 
 872             'label_landscape': self._(u'Landscape'),
 873 
 874             'label_choose_size': self._(u'Choose page size'),
 875             'help_choose_size': self._(u'Choose one of the predefined standard sizes or select user defined.'),
 876 
 877             'label_usersize': self._(u'User defined page size'),
 878             '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).'),
 879 
 880             'label_browserwidth': self._(u'Browser width'),
 881             'help_browserwidth': self._(u'Set the target browser width in pixels (400-1200). This determines the page scaling of images.'),
 882 
 883             'label_margin': self._(u'User defined margin'),
 884             'label_margintop': self._(u'Top'),
 885             'label_marginbottom': self._(u'Bottom'),
 886             'label_marginleft': self._(u'Left'),
 887             'label_marginright': self._(u'Right'),
 888             '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.'),
 889 
 890             'label_pagemode': self._(u'Page mode'),
 891             '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.'),
 892 
 893             'label_pagelayout': self._(u'Page layout'),
 894             '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.'),
 895 
 896             'label_firstpage': self._(u'First page'),
 897             'help_firstpage': self._(u'Choose the initial page that will be shown.'),
 898 
 899             'label_encryption': self._(u'Encryption'),
 900             'help_encryptin': self._(u'Enables encryption and security features for PDF output.'),
 901             'label_permissions': self._(u'Permissions'),
 902             'help_permissions': self._(u'Specifies the document permissions.'),
 903 
 904             'label_permissionannotate': self._(u'Annotate'),
 905             'label_permissionprint': self._(u'Print'),
 906             'label_permissionmodify': self._(u'Modify'),
 907             'label_permissioncopy': self._(u'Copy'),
 908 
 909             'label_owner-password': self._(u'Owner password'),
 910             '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.'),
 911 
 912             'label_user-password': self._(u'User password'),
 913             'help_user-password': self._(u'Specifies the user password to restrict viewing permissions on this PDF document.') + u' ' + self._(u'Empty for no encryption.'),
 914 
 915             'label_expert': self._(u'Expert'),
 916             'label_language': self._(u'Language translation'),
 917             'help_language': self._(u'Specify language to use for date and time format.'),
 918 
 919             'label_extra-dynamiccodeblock': self._(u'Dynamic code block'),
 920             'help_extra-dynamiccodeblock': self._(u'Shrink code blocks on page.'),
 921             
 922             'label_extra-codeblocklinenumbers': self._(u'Lines in code block'),
 923             'help_extra-codeblocklinenumbers': self._(u'Show line numbers for code blocks.'),
 924             
 925             'label_extra-dynamiccodeblock-middot': self._(u'Use dots instead of spaces in code blocks'),
 926             'help_extra-dynamiccodeblock-middot': self._(u'Make spaces visable by dots (%s) instead of white spaces.') % self.values['extra-dynamiccodeblock-middotchar'],
 927 
 928             'label_extra-dynamiccodeblock-break': self._(u'Use a para character for line breaks'),
 929             'help_extra-dynamiccodeblock-break': self._(u'Make line breaks visable by a extra character (%s) at the end.') % self.values['extra-dynamiccodeblock-breakchar'],
 930             
 931             'label_debug': self._(u'Enable debugging information'),
 932             'help_debug': self._(u'Enable this feature if you searching for problems or intent to report a bug report'),
 933 
 934             'label_fonts': self._(u'Fonts'),
 935             'label_fontsize': self._(u'Base font size'),
 936             'help_fontsize': self._(u'Set the default size of text.'),
 937             'label_fontspacing': self._(u'Line spacing'),
 938             'help_fontspacing': self._(u'Set the spacing between lines of text.'),
 939             'label_bodyfont': self._(u'Body typeface'),
 940             'help_bodyfont': self._(u'Choose the default typeface (font) of text.'),
 941             'label_headingfont': self._(u'Heading typeface'),
 942             'help_headingfont': self._(u'Choose the default typeface (font) of headings.'),
 943             'label_headfootsize': self._(u'Header/Footer size'),
 944             'help_headfootsize': self._(u'Set the size of header and footer text.'),
 945             'label_headfootfont': self._(u'Header/Footer font'),
 946             'help_headfootfont': self._(u'Choose the font for header and footer text.'),
 947             'label_charset': self._(u'Charset set'),
 948             'help_charset': self._(u'Change the encoding of the text in document.'),
 949             'label_embedfonts': self._(u'Embed fonts'),
 950             'help_embedfonts': self._(u'Check to embed font in the output file.'),
 951             
 952             'label_about': self._(u'About'),
 953             'copyright': u'',
 954             'version': self._(u'Version') + u' ' + __version__,
 955 
 956             'button_generate': self._(u'Generate PDF'),
 957             'button_preview': self._(u'Preview'),
 958             'button_remember': self._(u'Remember form'),
 959             'button_cancel': self._(u'Cancel'),
 960             'button_reset': self._(u'Reset'),
 961         }
 962         self.fields['copyright'] = u"<br/>\n".join(wikiutil.escape(__doc__).split(u"\n"))
 963         self.fields.update(self.values)
 964 
 965         # Status of debug.
 966         if self.debug:
 967             self.fields[u'debug'] = u'1'
 968         else:
 969             self.fields[u'debug'] = u'0'
 970 
 971         # Go through all format strings.
 972         for name in [u'tocheader', u'tocfooter', u'header', u'footer']:
 973             self.fields[u'choose_' + name] = self._chooseformat(name)
 974 
 975         self.fields[u'select_style'] = self._select(u'style')
 976         self.fields[u'select_format'] = self._select(u'format')
 977         self.fields[u'select_linkstyle'] = self._select(u'linkstyle')
 978         self.fields[u'select_size'] = self._select(u'size')
 979         self.fields[u'select_jpeg'] = self._select(u'jpeg')
 980         self.fields[u'select_compression'] = self._select(u'compression')
 981         self.fields[u'select_toclevels'] = self._select(u'toclevels')
 982         self.fields[u'select_pagemode'] = self._select(u'pagemode')
 983         self.fields[u'select_pagelayout'] = self._select(u'pagelayout')
 984         self.fields[u'select_firstpage'] = self._select(u'firstpage')
 985         self.fields[u'select_charset'] = self._select(u'charset')
 986         self.fields[u'select_fontsize'] = self._select(u'fontsize')
 987         self.fields[u'select_bodyfont'] = self._select(u'bodyfont')
 988         self.fields[u'select_headingfont'] = self._select(u'headingfont')
 989         self.fields[u'select_headfootsize'] = self._select(u'headfootsize')
 990         self.fields[u'select_headfootfont'] = self._select(u'headfootfont')
 991         self.fields[u'select_fontspacing'] = self._select(u'fontspacing')
 992 
 993         # Add tabber implementation.
 994         self.request.cfg.html_head += """
 995 <script type="text/javascript">
 996 <!-- //
 997 %s
 998 //-->
 999 </script>
1000 
1001 %s
1002 """ % (tabber_minimized, tabber_minimized_css,)
1003 
1004 
1005     def error_msg(self, msg):
1006         """Display error message."""
1007         self.error = msg
1008 
1009                 
1010     def fixhtmlstr(self, str):
1011         """Convert utf-8 encoded multi-byte sequences into &#XXXX; format."""
1012         htmlstr = array.array('c')
1013         for c in str:
1014             if ord(c) >= 128:
1015                 htmlstr.fromstring('&#%d;' % ord(c))
1016             else:
1017                 htmlstr.fromstring(c)
1018         return htmlstr.tostring()
1019 
1020     
1021     def set_page_values(self):
1022         """Scan raw page for additional information relating PDF generation.
1023         """
1024         #pdflines = False
1025         for line in self.request.page.get_raw_body().split(u'\n'):
1026             if line[:6] == u'##pdf ' and len(line[6:]):
1027                 line = line[6:]
1028                 key = line.split()[0]
1029                 value = line[len(key) + 1:]
1030                 # Only accept known values/settings.
1031                 if key in self.default_values:
1032                     # Check if there are any restrictions for key.
1033                     if key in self.valid_options:
1034                         # Set only the value if the restrictions are confirmed.
1035                         valid_values = self.valid_options[key].keys()
1036                         if value in valid_values:
1037                             self.values[key] = value
1038                     else:
1039                         # There are no restrictions for value.
1040                         self.values[key] = value
1041             elif not line:
1042                 break
1043 
1044             
1045     def set_page_default_values(self):
1046         """Collect as mutch as possible information about this page to assume some defaults.
1047         """
1048         # We are not able to recognise if this string is part of a verbatim area.
1049         # Support for MoinMoin v1.6 [[TableOfContentes]] and v1.7 <<TableOfContents>> syntax.
1050         matchtoclvl = re.compile(r'^[\[<]{2}TableOfContents\(\s*(\d+)\s*\)[\]>]{2}')
1051         matchtoc = re.compile(r'^[\[<]{2}TableOfContents\(*\)*[\]>]{2}')
1052         matchheading = re.compile(r'^[=]+ .*[=]+$')
1053         toc_found = False
1054         heading_found = False
1055         for line in self.request.page.get_raw_body().split(u'\n'):
1056             if line[:10] == u'#language ' and not u'language' in self.values:
1057                 lang = self.make_isolang(line[10:])
1058                 if lang:
1059                     self.default_values[u'language'] = lang
1060             elif not u'toclevels' in self.values and not toc_found:
1061                 result = matchtoclvl.match(line)
1062                 if result:
1063                     toclevels = int(result.group(1).strip())
1064                     if toclevels > 4:
1065                         toclevels = 4
1066                     self.default_values[u'toclevels'] = str(toclevels)
1067                     toc_found = True
1068                 elif matchtoc.match(line):
1069                     toc_found = True
1070             elif matchheading.match(line):
1071                 heading_found = True
1072         # We assume if table-of-contents is used we intent to generate a book.
1073         if toc_found and heading_found:
1074             # Do not change style if set manually or by configuration.
1075             if self.default_values.get(u'style', None) == None:
1076                 self.default_values[u'style'] = u'book'
1077         else:
1078             self.default_values[u'style'] = u'webpage'
1079         # Do not generate a table of contents page.
1080         if self.default_values[u'style'] != u'book':
1081             # Do not change style if set manually or by configuration.
1082             self.default_values[u'no-toc'] = self.default_values.get(u'no-toc', u'unchecked')
1083  
1084             
1085     def _select (self, name, description=None):
1086         """Helper function to create a selection control."""
1087         str = u'<select name="%s" size="1">' % (name,)
1088         if not description:
1089             description = self.valid_options[name]
1090         keys = description.keys()
1091         keys.sort()
1092         for value in keys:
1093             if value == self.values[name]:
1094                 selected = u'selected'
1095             else:
1096                 selected = u''
1097             str += u'<option value="%s" %s>%s</option>' % (value, selected, description[value],)
1098         str += u'</select>'
1099         return str
1100 
1101     def _chooseformat (self, name):
1102         """Helper function to create left/middle/right selection controls."""
1103         str = u"""    <tr>
1104         <td class="label"><label>%s</label></td>
1105         <td><table>
1106                 <tr>
1107                     <td>%s</td>
1108                     <td>%s</td>
1109                 </tr>
1110                 <tr>
1111                     <td>%s</td>
1112                     <td>%s</td>
1113                 </tr>
1114                 <tr>
1115                     <td>%s</td>
1116                     <td>%s</td>
1117                 </tr>
1118             </table>
1119         </td>
1120         <td>%s</td>
1121     </tr>""" % (self.fields[u'label_' + name],
1122               self.fields[u'label_left'], self._select(name + u'left', self.valid_options[u'tocformats']),
1123               self.fields[u'label_middle'], self._select(name + u'middle', self.valid_options[u'tocformats']),
1124               self.fields[u'label_right'], self._select(name + u'right', self.valid_options[u'tocformats']),
1125               self.fields[u'help_' + name],)
1126         return str
1127 
1128     def get_form_html(self, buttons_html):
1129         """MoinMoin.action.ActionBase interface function
1130         """
1131         form = u''
1132         if self.debug:
1133             form += u'<p class="warning">' + self._('Debug mode activated.') + u'</p>'
1134         if not is_moinmoin_version_eqless(1,9):
1135             form += u'<p class="warning">' + self._(u'This plugin was not verified with MoinMoin %(version)s.') % {u'version': moinmoin_release} + u'</p>'
1136         form += """
1137 <form method="post" action="">
1138 <input type="hidden" name="action" value="%(action)s"/>
1139 <div class="tabber">
1140 <div class="tabbertab">
1141     <h3>%(label_input)s</h3>
1142     <table>
1143     <tr>
1144         <td class="label"><label>%(label_choose_style)s</label></td>
1145         <td class="content">%(select_style)s</td>
1146         <td>%(help_choose_style)s</td>
1147     </tr>
1148     <tr>
1149         <td class="label"><label>%(label_titletext)s</label></td>
1150         <td class="content"><input type="text" size="30" name="titletext" value="%(titletext)s" /></td>
1151         <td>%(help_titletext)s</td>
1152     </tr>
1153     <tr>
1154         <td class="label"><label>%(label_extra-headingastitle)s</label></td>
1155         <td><input type="checkbox" name="extra-headingastitle" value="checked" %(extra-headingastitle)s /></td>
1156         <td>%(help_extra-headingastitle)s</td>
1157     </tr>
1158     <tr>
1159         <td class="label"><label>%(label_titlefileimage)s</label></td>
1160         <td class="content"><input type="text" size="30" name="titlefileimage" value="%(titlefileimage)s" /></td>
1161         <td>%(help_titlefileimage)s</td>
1162     </tr>
1163     <tr>
1164         <td class="label"><label>%(label_extra-titledocnumber)s</label></td>
1165         <td class="content"><input type="text" size="30" name="extra-titledocnumber" value="%(extra-titledocnumber)s" /></td>
1166         <td>%(help_extra-titledocnumber)s</td>
1167     </tr>
1168     <tr>
1169         <td class="label"><label>%(label_extra-titleauthor)s</label></td>
1170         <td class="content"><input type="text" size="30" name="extra-titleauthor" value="%(extra-titleauthor)s" /></td>
1171         <td>%(help_extra-titleauthor)s</td>
1172     </tr>
1173     <tr>
1174         <td class="label"><label>%(label_extra-titlecopyright)s</label></td>
1175         <td class="content"><input type="text" size="30" name="extra-titlecopyright" value="%(extra-titlecopyright)s" /></td>
1176         <td>%(help_extra-titlecopyright)s</td>
1177     </tr>
1178     <tr>
1179         <td class="label"><label>%(label_pageinfo)s</label></td>
1180         <td class="checkbox"><input type="checkbox" name="pageinfo" value="checked" %(pageinfo)s /></td>
1181         <td>%(help_pageinfo)s</td>
1182     </tr>
1183     </table>
1184 </div>
1185 <div class="tabbertab">
1186     <h3>%(label_output)s</h3>
1187     <table>
1188     <tr>
1189         <td class="label"><label>%(label_format)s</label></td>
1190         <td class="content">%(select_format)s</td>
1191         <td>%(help_format)s</td>
1192     </tr>
1193     <tr>
1194         <td class="label"><label>%(label_outputoptions)s</label></td>
1195         <td colspan="2"><input type="checkbox" name="grayscale" value="checked" %(grayscale)s />%(label_grayscale)s&nbsp;
1196             <input type="checkbox" name="title" value="checked" %(title)s />%(label_titlepage)s<br />
1197             %(label_compression)s&nbsp:&nbsp;%(select_compression)s&nbsp;
1198             %(label_jpeg)s&nbsp;%(select_jpeg)s</td>
1199     </tr>
1200     </table>
1201 </div>
1202 <div class="tabbertab">
1203     <h3>%(label_page)s</h3>
1204     <table>
1205     <tr>
1206         <td class="label"><label>%(label_choose_size)s</label></td>
1207         <td>%(select_size)s&nbsp;<br /><nobr>%(label_usersize)s&nbsp;<input type="text" size="15" name="usersize" value="%(usersize)s" /></nobr></td>
1208         <td>%(help_choose_size)s<br />%(help_usersize)s</td>
1209     </tr>
1210     <tr>
1211         <td class="label"><label>%(label_browserwidth)s</label></td>
1212         <td class="content"><input type="text" size="30" name="browserwidth" value="%(browserwidth)s" /></td>
1213         <td>%(help_browserwidth)s</td>
1214     </tr>
1215     <tr>
1216         <td>&nbsp;</td>
1217         <td colspan="2"><input type="checkbox" name="duplex" value="checked" %(duplex)s />&nbsp;%(label_duplex)s&nbsp;
1218             <input type="checkbox" name="landscape" value="checked" %(landscape)s />&nbsp;%(label_landscape)s</td>
1219     </tr>
1220     <tr>
1221         <td class="label"><label>%(label_margin)s</label></td>
1222         <td><table><tr><td>&nbsp;</td><td><nobr><label>%(label_margintop)s</label>&nbsp;<input type="text" name="margintop" value="%(margintop)s" size="7" /></nobr></td><td>&nbsp;</td></tr>
1223             <tr><td><nobr><label>%(label_marginleft)s</label>&nbsp;<input type="text" name="marginleft" value="%(marginleft)s" size="7" /></nobr></td><td>&nbsp;</td><td><nobr><label>%(label_marginright)s</label>&nbsp;<input type="text" name="marginright" value="%(marginright)s" size="7" /></nobr></td></tr>
1224             <tr><td>&nbsp;</td><td><nobr><label>%(label_marginbottom)s</label>&nbsp;<input type="text" name="marginbottom" value="%(marginbottom)s" size="7" /></nobr></td><td>&nbsp;</td></tr></table>
1225         <td>%(help_margin)s</td>
1226     </tr>
1227     %(choose_header)s
1228     %(choose_footer)s
1229     </table>
1230 </div>
1231 <div class="tabbertab">
1232     <h3>%(label_tableofcontents)s</h3>
1233     <table>
1234     <tr>
1235         <td class="label"><label>%(label_no-toc)s</label></td>
1236         <td class="checkbox"><input type="checkbox" name="no-toc" value="checked" %(no-toc)s /></td>
1237         <td>%(help_no-toc)s</td>
1238     </tr>
1239     <tr>
1240         <td class="label"><label>%(label_toclevels)s</label></td>
1241         <td class="content">%(select_toclevels)s</td>
1242         <td>%(help_toclevels)s</td>
1243     </tr>
1244     <tr>
1245         <td>&nbsp;</td>
1246         <td><input type="checkbox" name="numbered" value="checked" %(numbered)s />&nbsp;%(label_numbered)s</td>
1247         <td>%(help_numbered)s</td>
1248     </tr>
1249     <tr>
1250         <td class="label"><label>%(label_toctitle)s</label></td>
1251         <td class="content"><input type="text" size="30" name="toctitle" value="%(toctitle)s" /></td>
1252         <td>%(help_toctitle)s</td>
1253     </tr>
1254     %(choose_tocheader)s
1255     %(choose_tocfooter)s
1256     </table>
1257 </div>
1258 <div class="tabbertab">
1259     <h3>%(label_colors)s</h3>
1260     <table>
1261     <tr>
1262         <td class="label"><label>%(label_bodycolor)s</label></td>
1263         <td class="content"><input type="text" size="6" name="bodycolor" value="%(bodycolor)s" /></td>
1264         <td>%(help_bodycolor)s</td>
1265     </tr>
1266     <tr>
1267         <td class="label"><label>%(label_bodyimage)s</label></td>
1268         <td class="content"><input type="text" size="30" name="bodyimage" value="%(bodyimage)s" /></td>
1269         <td>%(help_bodyimage)s</td>
1270     </tr>
1271     <tr>
1272         <td class="label"><label>%(label_textcolor)s</label></td>
1273         <td class="content"><input type="text" size="6" name="textcolor" value="%(textcolor)s" /></td>
1274         <td>%(help_textcolor)s</td>
1275     </tr>
1276     <tr>
1277         <td class="label"><label>%(label_linkcolor)s</label></td>
1278         <td class="content"><input type="text" size="6" name="linkcolor" value="%(linkcolor)s" /></td>
1279         <td>%(help_linkcolor)s</td>
1280     </tr>
1281     <tr>
1282         <td class="label"><label>%(label_linkstyle)s</label></td>
1283         <td class="content">%(select_linkstyle)s</td>
1284         <td>%(help_linkstyle)s</td>
1285     </tr>
1286     <tr>
1287         <td class="label"><label>%(label_no-links)s</label></td>
1288         <td><input type="checkbox" name="no-links" value="checked" %(no-links)s /></td>
1289         <td>%(help_no-links)s</td>
1290     </tr>
1291     </table>
1292 </div>
1293 <div class="tabbertab">
1294     <h3>%(label_fonts)s</h3>
1295     <table>
1296     <tr>
1297         <td class="label"><label>%(label_fontsize)s</label></td>
1298         <td class="content">%(select_fontsize)s</td>
1299         <td>%(help_fontsize)s</td>
1300     </tr>
1301     <tr>
1302         <td class="label"><label>%(label_fontspacing)s</label></td>
1303         <td class="content">%(select_fontspacing)s</td>
1304         <td>%(help_fontspacing)s</td>
1305     </tr>
1306     <tr>
1307         <td class="label"><label>%(label_bodyfont)s</label></td>
1308         <td class="content">%(select_bodyfont)s</td>
1309         <td>%(help_bodyfont)s</td>
1310     </tr>
1311     <tr>
1312         <td class="label"><label>%(label_headingfont)s</label></td>
1313         <td class="content">%(select_headingfont)s</td>
1314         <td>%(help_headingfont)s</td>
1315     </tr>
1316     <tr>
1317         <td class="label"><label>%(label_headfootsize)s</label></td>
1318         <td class="content">%(select_headfootsize)s</td>
1319         <td>%(help_headfootsize)s</td>
1320     </tr>
1321     <tr>
1322         <td class="label"><label>%(label_headfootfont)s</label></td>
1323         <td class="content">%(select_headfootfont)s</td>
1324         <td>%(help_headfootfont)s</td>
1325     </tr>
1326     <tr>
1327         <td class="label"><label>%(label_charset)s</label></td>
1328         <td class="content">%(select_charset)s</td>
1329         <td>%(help_charset)s</td>
1330     </tr>
1331     <tr>
1332         <td class="label"><label>%(label_embedfonts)s</label></td>
1333         <td class="checkbox"><input type="checkbox" name="embedfonts" value="checked" %(embedfonts)s /></td>
1334         <td>%(help_embedfonts)s</td>
1335     </tr>
1336 </table>
1337 </div>
1338 <div class="tabbertab">
1339     <h3>%(label_pdf)s</h3>
1340     <table>
1341     <tr>
1342         <td class="label"><label>%(label_pagemode)s</label></td>
1343         <td class="content">%(select_pagemode)s</td>
1344         <td>%(help_pagemode)s</td>
1345     </tr>
1346     <tr>
1347         <td class="label"><label>%(label_pagelayout)s</label></td>
1348         <td class="content">%(select_pagelayout)s</td>
1349         <td>%(help_pagelayout)s</td>
1350     </tr>
1351     <tr>
1352         <td class="label"><label>%(label_firstpage)s</label></td>
1353         <td class="content">%(select_firstpage)s</td>
1354         <td>%(help_firstpage)s</td>
1355     </tr>
1356     </table>
1357 </div>
1358 <div class="tabbertab">
1359     <h3>%(label_security)s</h3>
1360     <table>
1361     <tr>
1362         <td class="label"><label>%(label_encryption)s</label></td>
1363         <td><input type="checkbox" name="encryption" value="checked" %(encryption)s /></td>
1364         <td>%(help_numbered)s</td>
1365     </tr>
1366     <tr>
1367         <td class="label"><label>%(label_permissions)s</label></td>
1368         <td><nobr><input type="checkbox" name="permissionprint" value="checked" %(permissionprint)s />&nbsp;%(label_permissionprint)s</nobr>&nbsp;
1369             <nobr><input type="checkbox" name="permissionmodify" value="checked" %(permissionmodify)s />&nbsp;%(label_permissionmodify)s</nobr><br />
1370             <nobr><input type="checkbox" name="permissioncopy" value="checked" %(permissioncopy)s />&nbsp;%(label_permissioncopy)s</nobr>&nbsp;
1371             <nobr><input type="checkbox" name="permissionannotate" value="checked" %(permissionannotate)s />&nbsp;%(label_permissionannotate)s</nobr></td>
1372         <td>%(help_permissions)s</td>
1373     </tr>
1374     <tr>
1375         <td class="label"><label>%(label_user-password)s</label></td>
1376         <td class="content"><input type="password" size="30" name="user-password" value="%(user-password)s" /></td>
1377         <td>%(help_user-password)s</td>
1378     </tr>
1379     <tr>
1380         <td class="label"><label>%(label_owner-password)s</label></td>
1381         <td class="content"><input type="password" size="30" name="owner-password" value="%(owner-password)s" /></td>
1382         <td>%(help_owner-password)s</td>
1383     </tr>
1384     </table>
1385 </div>
1386 <div class="tabbertab">
1387     <h3>%(label_expert)s</h3>
1388     <table>
1389     <tr>
1390         <td class="label"><label>%(label_language)s</label></td>
1391         <td class="content"><input type="text" size="6" name="language" value="%(language)s" /></td>
1392         <td>%(help_language)s</td>
1393     </tr>
1394     <tr>
1395         <td class="label"><label>%(label_extra-dynamiccodeblock)s</label></td>
1396         <td class="checkbox"><input type="checkbox" name="extra-dynamiccodeblock" value="checked" %(extra-dynamiccodeblock)s /></td>
1397         <td>%(help_extra-dynamiccodeblock)s</td>
1398     </tr>
1399     <tr>
1400         <td class="label"><label>%(label_extra-codeblocklinenumbers)s</label></td>
1401         <td class="checkbox"><input type="checkbox" name="extra-codeblocklinenumbers" value="checked" %(extra-codeblocklinenumbers)s /></td>
1402         <td>%(help_extra-codeblocklinenumbers)s</td>
1403     </tr>
1404     <tr>
1405         <td class="label"><label>%(label_extra-dynamiccodeblock-middot)s</label></td>
1406         <td class="checkbox"><input type="checkbox" name="extra-dynamiccodeblock-middot" value="checked" %(extra-dynamiccodeblock-middot)s /></td>
1407         <td>%(help_extra-dynamiccodeblock-middot)s</td>
1408     </tr>
1409     <tr>
1410         <td class="label"><label>%(label_extra-dynamiccodeblock-break)s</label></td>
1411         <td class="checkbox"><input type="checkbox" name="extra-dynamiccodeblock-break" value="checked" %(extra-dynamiccodeblock-break)s /></td>
1412         <td>%(help_extra-dynamiccodeblock-break)s</td>
1413     </tr>
1414     <tr>
1415         <td class="label"><label>%(label_debug)s</label></td>
1416         <td class="checkbox"><input type="checkbox" name="debug" value="checked" %(debug)s /></td>
1417         <td>%(help_debug)s</td>
1418     </tr>
1419     </table>
1420 </div>
1421 <div class="tabbertab">
1422     <h3>%(label_about)s</h3>
1423     <p>%(version)s (MoinMoin %(moinmoin_release)s)</p>
1424     <p>%(copyright)s</p>
1425 </div>
1426 </div>
1427 <p>
1428 <div class="buttons">
1429 <input type="hidden" name="debug" value="%(debug)s" /><input type="hidden" name="rev" value="%(rev)s" />
1430 <input type="submit" name="generate_from_form" value="%(button_generate)s" />&nbsp;
1431 <input type="submit" name="preview" value="%(button_preview)s" />&nbsp;
1432 <input type="submit" name="remember" value="%(button_remember)s" />&nbsp;
1433 <input type="submit" name="cancel" value="%(button_cancel)s" />&nbsp;
1434 </div>
1435 </p>
1436 </form>
1437 """ % self.fields
1438         return form
1439     
1440 
1441     def render(self):
1442         """MoinMoin.action.ActionBase: Extend support of multipple action buttons
1443         """
1444         self.form_wrapper = merge_form_query(self.request)
1445         for buttonname in ['generate', 'generate_from_form', 'preview', 'remember']:
1446             # We have multipple buttons which do not match the default
1447             # MoinMoin.action.ActionBase.form_trigger = 'doit'. We rename the default
1448             # to one of the pressed buttions.
1449             if buttonname in self.form_wrapper:
1450                 self.form_trigger = buttonname
1451                 break
1452         # Since MoinMoin 1.9:
1453         #   In order to generate a PDF document by URL like PDFIcon
1454         #   do, we have to manipulate the self.request.form and place the form_trigger
1455         #   manual; the form_trigger is stored in self.request.values only sine 1.9
1456         #   We do all this in order to reuse ActionBase.render function.
1457         #   Because self.form is immutable we need a workarround; we change the type
1458         #   of self.form!
1459         if self.form_trigger == 'generate' and is_moinmoin_version_eqhigher(1,9):
1460             # FIXME: Changing type of self.from is not the right solution but the only
1461             # one which works for me.
1462             self.form = self.form_wrapper.copy()
1463             self.form[self.form_trigger] = u'submit'
1464         # Continue with super function
1465         ActionBase.render(self)
1466 
1467 
1468     def do_action(self):
1469         """MoinMoin.action.ActionBase: Main dispatcher for the action.
1470         """
1471         # Determine calling parameters.
1472         if self.form_wrapper.get('debug', u'0') != u'0':
1473             self.debug = True
1474 
1475         # Create a PDF document direct without any user iteraction from default and page settings.
1476         if self.form_wrapper.has_key('generate'):
1477             self.set_page_values()
1478             self.update_values(useform=False)
1479             return self.do_action_generate()
1480             
1481         # Create a PDF document from form settings.
1482         if self.form_wrapper.has_key('generate_from_form') or self.form_wrapper.has_key('preview'):
1483             self.update_values()
1484             return self.do_action_generate()
1485         
1486         # Display a message with instructions.
1487         if self.form_wrapper.has_key('remember'):
1488             self.update_values()
1489             return self.do_action_remember()
1490     
1491         return (True, None)
1492 
1493 
1494     def do_action_finish(self, success):
1495         """Overwrite default processing in case of success where application/pdf mime document is send instead of default page."""
1496         if self.error:
1497             ActionBase.do_action_finish(self, False)
1498 
1499 
1500     def update_values(self, useform=True):
1501         """Preset values with they form values or defaults."""
1502         if useform:
1503             pass
1504         for key, default in self.default_values.items():
1505             # Skip settings which can't be modified by form.
1506             if useform and key in ['htmldoc_cmd', 'htmldoc_options', 'extra-dynamiccodeblock-middotchar', 'extra-dynamiccodeblock-breakchar']:
1507                 continue
1508             # Modify value only if not already set.
1509             if not key in self.values or useform:
1510                 # If the form does not contain the value (e.g. for checkboxes) set the
1511                 # default value (e.g. for checkboxes unset by default).
1512                 if not key in self.form_wrapper:
1513                     # Special processing for checkboxes in forms. If the key does not exists
1514                     # within the form it is not checked.
1515                     if key in self.form_checkbox and useform:
1516                         self.values[key] = u'unchecked'
1517                     elif useform:
1518                         # Edit fields are missing if they are empty.
1519                         self.values[key] = u''
1520                     else:
1521                         self.values[key] = default
1522                 else:
1523                     self.values[key] = self.form_wrapper[key]
1524         # Check if revision is an integer value.
1525         try:
1526             self.values[u'rev'] = int(self.values.get(u'rev', self.request.page.rev))
1527         except:
1528             self.values[u'rev'] = self.request.page.rev
1529         # Check if page revision exists.
1530         (pagefname, realrev, exists) = self.request.page.get_rev(rev=self.values[u'rev'])
1531         if exists:
1532             self.values[u'rev'] = realrev
1533         else:
1534             # Determine latest revision number.
1535             (pagefname, self.values[u'rev'], exists) = self.request.page.get_rev()
1536         # Check valid value range.
1537         try:
1538             self.values[u'browserwidth'] = int(self.values[u'browserwidth'])
1539             if self.values[u'browserwidth'] < 400 or self.values[u'browserwidth'] > 1200:
1540                 self.values[u'browserwidth'] = self.default_values[u'browserwidth']
1541         except:
1542             self.values[u'browserwidth'] = self.default_values[u'browserwidth']
1543         # We need a string.
1544         self.values[u'browserwidth'] = u'%d' % self.values[u'browserwidth']
1545 
1546         
1547     def do_action_generate(self):
1548         """Create PDF document."""
1549         # Generate the HTML page using MoinMoin wiki engine.
1550         html = self.get_html()
1551         if html:
1552             if self.form_wrapper.has_key('preview'):
1553                 self.wrapper_emit_http_headers()
1554                 self.request.write(html)
1555             else:
1556                 pdfdata = self.html2pdf(html)
1557                 if pdfdata:
1558                     # Send as application/pdf the generated file by HTMLDOC
1559                     self.send_pdf(pdfdata)
1560                     return (True, None)
1561         
1562         return (False, self.error)
1563 
1564                     
1565     def do_action_remember(self):
1566         """Create a message containing information about how to save the form values for future reuse."""
1567         save = u''
1568         for key, value in self.values.items():
1569             if key in ['user-password', 'owner-password', 'rev', 'debug', 'htmldoc_cmd', 'htmldoc_options']:
1570                 continue
1571             if key in self.default_values and value == self.default_values[key]:
1572                 continue
1573             save += u'##pdf %s %s' % (key, value,)
1574             if self.debug:
1575                 if key in self.default_values:
1576                     save += u' <-- default value is "%s" (without quotes)' % self.default_values[key]
1577                 else:
1578                     save += u' <-- keyword missing in defaults'
1579             save += u'\n'
1580         if save:
1581             msg = self._(u'Add follwing lines at the beginning of your page:') + u'<br/><pre>' + save + u'</pre>'
1582         else:
1583             msg = self._(u'All values correspond to they default. Nothing have to be saved.')
1584         
1585         return (True, msg)
1586 
1587 
1588     def wrapper_emit_http_headers(self, more_headers={}):
1589         if is_moinmoin_version_eqless(1,8):
1590             pre19 = []
1591             for k,v in more_headers.items():
1592                 pre19.append("%s: %s" % (k, v))
1593             if is_moinmoin_version_eqless(1,5):
1594                 self.request.http_headers(pre19)
1595             else:
1596                 self.request.emit_http_headers(pre19)
1597         else:
1598             for k,v in more_headers.items():
1599                 self.request.headers.add(k, v)
1600 
1601 
1602     def send_pdf(self, data):
1603         """Send PDF file to HTTP server as inline (not attachment).
1604         Refer to Page.send_raw() for an example of implementation.
1605         """
1606         filename = self.pagename.replace (u'/', u'-') + u'-v' + str(self.values[u'rev']) + u'.pdf'
1607 
1608         # Send HTTP header.
1609         self.wrapper_emit_http_headers({'Content-Type': '%s' % self.contenttype,
1610             'Content-Length': '%d' % len(data),
1611             # TODO: fix the encoding here, plain 8 bit is not allowed
1612             # according to the RFCs There is no solution that is
1613             # compatible to IE except stripping non-ascii chars
1614             'Content-Disposition': 'inline; filename="%s"' % filename.encode(config.charset),
1615             })
1616 
1617         # Send binary data.
1618         sio = StringIO.StringIO(data)
1619         shutil.copyfileobj(sio, self.request, 8192)
1620 
1621         
1622     def get_html(self):
1623         """Generate the HTML body of this page."""
1624         newtitle = None
1625         # Find out what the right title is.
1626         if self.values[u'extra-headingastitle'] == u'checked':
1627             matchheading = re.compile(r'^= (.*) =$')
1628             for line in self.request.page.get_raw_body().split(u'\n'):
1629                 result = matchheading.match(line)
1630                 if result:
1631                     newtitle = result.group(1).strip()
1632                     break
1633         # In case where extra-headingastitle is checked but no heading is defined
1634         # at the page, we use the form entry.
1635         if newtitle == None:
1636             newtitle = self.values['titletext']
1637 
1638         errmsg = None
1639         html = None
1640         if is_moinmoin_version_eqless(1,5):
1641             # Workarround: We need the pure HTML page without MoinMoin theme
1642             self.request.form['action'] = ['print']
1643             # Workarround: Do not sent HTTP header
1644             self.request.sent_headers = True
1645             html = self.request.redirectedOutput(self.request.page.send_page, request=self.request, do_cache=0)
1646             # Remove HTTP header
1647             i = html.find('<!DOCTYPE ')
1648             if i > 0:
1649                 html = html[i:]
1650             # Revert workarround
1651             self.request.sent_headers = False
1652         else:
1653             # Since MoinMoin 1.6
1654             html = self.request.redirectedOutput(self.request.page.send_page, emit_headers=0, print_mode=1, do_cache=0)
1655             # In case where `self.request.page.send_page` is redirected we can not
1656             # emit HTTP headers for the same request twice.
1657             if is_moinmoin_version_eqless(1,8):
1658                 if self.request.sent_headers:
1659                     errmsg = self._('HTTP header already sent. We are not able to change the Content-Type to application/pdf.')
1660         i = html.find(u'<body ')
1661         if i >= 0:
1662             html = html[:i] + u"""
1663 <meta name="docnumber" content="%s">
1664 <meta name="author" content="%s">
1665 <meta name="copyright" content="%s">
1666 """ % (wikiutil.escape(self.values['extra-titledocnumber']), wikiutil.escape(self.values['extra-titleauthor']), wikiutil.escape(self.values['extra-titlecopyright']),) \
1667     + html[i:]
1668         else:
1669             errmsg = self._(u'Can not find the right place to insert the HTML header.')
1670 
1671         if html:
1672             html = self.fixhtmlstr(html)
1673             # Make URLs absolute.
1674             # FIXME: Until MoinMoin is not XHTML compilant we can not use a XML parser
1675             # (e.g. expat) to transform the HTML document. In the meantime we try to
1676             # achive the same with regular expressions subtitution.
1677             base = self.request.getQualifiedURL()
1678             for htmlref in [u'src', u'href']:
1679                 reurlref = r'(%s=[\'"])(/[^\'"]*)[\'"]' % (htmlref,)
1680                 urlref = re.compile (reurlref, re.I)
1681                 for match in urlref.finditer(html):
1682                     foundref = match.groups()
1683                     html = html.replace (foundref[0] + foundref[1], foundref[0] + base + foundref[1])
1684 
1685             # Rename title of the document.
1686             titletext_html = self.fixhtmlstr(wikiutil.escape(newtitle))
1687             html = re.compile(r'<title>[^<]+</title>').sub(u'<title>%s</title>' % titletext_html, html)
1688 
1689             if self.values['pageinfo'] == u'unchecked':
1690                 # Remove pageinfo by regex. There is no standard way to do that yet.
1691                 # Read the comment in ThemeBase.shouldShowPageinfo().
1692                 html = re.compile(r'<p[^>]+id="pageinfo".*</p>').sub(u'', html)
1693                 html = re.compile(r'<div id="interwiki">.*?</div>').sub(u'', html)
1694             
1695             # HTMLDOC workarround: Add borders to tables. HTMLDOC assume border="0" if not defined.
1696             html = re.compile(r'<table').sub(u'<table border="1" cellpadding="2"', html)
1697             
1698             # Display line numbers for code blocks without &middot;.
1699             if self.values[u'extra-dynamiccodeblock'] == u'checked':
1700                 if self.values[u'extra-codeblocklinenumbers'] == u'checked':
1701                     codeblocknr = re.compile(r'<span[^>]+class="LineNumber">([^<]+)</span>', re.IGNORECASE)
1702                     for match in codeblocknr.finditer(html):
1703                         newlinenr = u'<font color="%s">%s</font>' % (self.values[u'linkcolor'], match.group(1).replace(u' ', u'&nbsp;'),)
1704                         html = html.replace(match.group(0), newlinenr, 1)
1705                 else:
1706                     html = re.compile(r'<span[^>]+class="LineNumber">[^<]+</span>', re.IGNORECASE).sub(u'', html)
1707             
1708             # HTMLDOC: Does not support <span> so we remove them.
1709             for spanmatch in [r'<span[^>]*>', r'</span>']:
1710                 html = re.compile(spanmatch).sub(u'', html)
1711 
1712             # HTMLDOC: Does not support JavaScript.
1713             html = re.compile(r'<script type="text/javascript".*?</script>', re.IGNORECASE | re.DOTALL).sub(u'', html)
1714             
1715             # HTMLDOC: Does not support CSS.
1716             html = re.compile(r'<style type="text/css".*?</style>', re.IGNORECASE | re.DOTALL).sub(u'', html)
1717             
1718             # HTMLDOC does not support stylesheets.
1719             html = re.compile(r'<link rel="stylesheet".*?>', re.IGNORECASE | re.DOTALL).sub(u'', html)
1720             
1721             # Remove page location added by &action=print.
1722             html = re.compile(r'<ul id="pagelocation">.*?</ul>', re.IGNORECASE | re.DOTALL).sub(u'', html)
1723             
1724             # HTMLDOC workarround: There is no CSS support in HTMLDOC.
1725             tablecolor = re.compile(r'<td.*?background-color: (#......).*?>')
1726             for match in tablecolor.finditer(html):
1727                 html = html.replace(match.group(0), u'<td bgcolor="%s">' % match.group(1), 1)
1728             
1729             # HTMLDOC workarround: Handle <pre> sections over page boundries.
1730             if self.values[u'extra-dynamiccodeblock'] == u'checked':
1731                 multiplespaces = re.compile(r'( {2,})')
1732                 # Which character should be used to fill out spaces.
1733                 if self.values[u'extra-dynamiccodeblock-middot'] == u'checked':
1734                     fillchar = self.values[u'extra-dynamiccodeblock-middotchar']
1735                 else:
1736                     fillchar = u'&nbsp;'
1737                 for regexstr in [r'(<pre>)(.*?)(</pre>)', r'(<div class="codearea"[^>]*>.*?<pre[^>]*>)(.*?)(</pre>.*?</div>)']:
1738                     codesections = re.compile(regexstr, re.IGNORECASE | re.DOTALL)
1739                     for match in codesections.finditer(html):
1740                         foundref = match.groups()
1741                         presection = foundref[1]
1742                         # Search for multiple spaces and replace them by `fillchar` except the last space.
1743                         for spaces in multiplespaces.finditer(presection):
1744                             newspaces = fillchar * (len(spaces.group(1)) - 1) + u' '
1745                             presection = presection.replace (spaces.group(1), newspaces, 1)
1746                         # Go through lines and add a &para; sign at the end of eatch line.
1747                         newprelines = []
1748                         prelines = presection.split(u"\n")
1749                         if len(prelines) > 1:
1750                             breakchar = self.values[u'extra-dynamiccodeblock-break'] == u'checked' and self.values[u'extra-dynamiccodeblock-breakchar'] or u''
1751                             for preline in prelines:
1752                                 preline = preline + breakchar + u'<br />'
1753                                 newprelines.append(preline)
1754                         else:
1755                             newprelines = prelines
1756                         # Create a table arround an multi-line block.
1757                         if newprelines:
1758                             tablestart = u'<table border="1" bgcolor="#F3F5F7" cellpadding="5"><tr><td>'
1759                             tableend = u'</td></tr></table><br />'
1760                         else:
1761                             newprelines.append(preline)
1762                             tablestart = u''
1763                             tableend = u''
1764                         # Replace the <pre> block with new dynamic text.
1765                         html = html.replace(u''.join(foundref), u'%s<font face="Courier,Monospace">%s</font>%s' % (tablestart, u"\n".join(newprelines), tableend,), 1)
1766             # Do not suppress error messages.
1767             if errmsg:
1768                 html = u'<pre>' + errmsg + '</pre>' + html
1769         else:
1770             self.error_msg(self._(u'Could not redirect HTML output for further processing:') + errmsg)
1771         return html
1772 
1773     
1774     def make_isolang (self, language):
1775         return language + u'_' + language.upper()
1776 
1777     
1778     def html2pdf(self, html):
1779         """Create a PDF document based on the current parameters."""
1780         # Set environment variables for HTMLDOC
1781         os.environ['LANG'] = self.values[u'language']
1782         os.environ['HTMLDOC_NOCGI'] = '1'
1783         htmldocopts = [self.default_values['htmldoc_cmd'], u'--no-duplex'] + self.default_values['htmldoc_options']
1784 
1785         for cookie in self.get_session_cookies():
1786             htmldocopts += ['--cookies', cookie]
1787         
1788         for key in [u'header', u'footer', u'tocheader', u'tocfooter']:
1789             self.values[key] = self.values.get(key + u'left', u'.') + self.values.get(key + u'middle', u'.') + self.values.get(key + u'right', u'.')
1790 
1791         permissions = []
1792         for opt, value in self.values.items():
1793             # Skip alle non-HTMLDOC configuration parameters.
1794             if opt in ['language', 'debug', 'rev', 'titletext', 'pageinfo', 'htmldoc_cmd', 'htmldoc_options'] or opt[:6] == u'extra-':
1795                 continue
1796             
1797             # Skip options without values.
1798             value = value.strip()
1799             if not value:
1800                 continue
1801             
1802             # Skip options for header/footer configuration which differenciate between position (e.g. footerright or tocheadermiddle)
1803             if opt[:6] in [u'header', u'footer'] and opt[6:] or opt[:9] in [u'tocheader', u'tocfooter'] and opt[9:]:
1804                 continue
1805             
1806             if u'proxy' in self.default_values:
1807                 htmldocopts += [u'--proxy', self.default_values[u'proxy']]
1808             
1809             if opt == u'titlefileimage':
1810                 # Check if we have a --titlefile or --titleimage option.
1811                 lower_value = value.lower()
1812                 dotpos = lower_value.rfind(u'.')
1813                 if lower_value[dotpos:] in [u'.gif', u'.jpg', u'.jpeg', u'.png']:
1814                     opt = u'titleimage'
1815                 else:
1816                     opt = u'titlefile'
1817                 value = attachment_fsname(value, self.request.page, self.request)
1818             elif opt == u'bodyimage':
1819                 value = attachment_fsname(value, self.request.page, self.request)
1820             
1821             if opt in [u'style']:
1822                 htmldocopts += [u'--' + value]
1823             elif opt in self.form_checkbox:
1824                 if value == u'checked':
1825                     if opt[:10] == u'permission':
1826                         permissions += [opt[10:]]
1827                     # Reverse meaning of 'no-' options.
1828                     elif opt[:3] != u'no-':
1829                         htmldocopts += [u'--' + opt]
1830                 elif opt[:3] == u'no-':
1831                     htmldocopts += [u'--' + opt]
1832             elif opt[:6] == u'margin' and value:
1833                 htmldocopts += [u'--' + opt[6:], value]
1834             elif opt in [u'jpeg']:
1835                 htmldocopts += [u'--' + opt + '=' + value]
1836             elif value:
1837                 htmldocopts += [u'--' + opt, value]
1838         if permissions:
1839             htmldocopts += [u'--permission', u','.join (permissions)]
1840         htmldocopts += [u'-']
1841         # Do not forget to escape all spaces!
1842         eschtmldocopts = [shell_quote(arg) for arg in htmldocopts]
1843         cmdstr = u' '.join(eschtmldocopts)
1844         errmsg = None
1845 
1846         pdf = None
1847         os.environ['HTMLDOC_NOCGI'] = '1'
1848         if self.debug:
1849             self.wrapper_emit_http_headers()
1850             errmsg = self._(u'HTMLDOC command:') + u'<pre>' + wikiutil.escape(cmdstr) + u'</pre>'
1851             cmdstr = self.default_values['htmldoc_cmd'] + u' --help'
1852             (htmldoc_help, htmldoc_err) = pipeCommand(cmdstr)
1853             errmsg += u'<p>Execute <tt>%s</tt><br /><pre>%s</pre></p>' % (wikiutil.escape(cmdstr), wikiutil.escape(htmldoc_help),)
1854             if 'env' in self.request.__dict__:
1855                 reqenv = u'%s' % wikiutil.escape(self.request.env)
1856             else:
1857                 reqenv = u'None'
1858             errmsg += u'<p>Python release %s<br />MoinMoin release <tt>%s</tt><br />CreatePdfDocument release <tt>%s</tt><br />self.request = <tt>%s</tt><br />self.request.env = <tt>%s</tt><br />os.environ = <tt>%s</tt></p>' % (wikiutil.escape(sys.version), wikiutil.escape(moinmoin_release), wikiutil.escape(__version__), wikiutil.escape(type(self.request)), wikiutil.escape(reqenv), wikiutil.escape(u"%s" % os.environ),)
1859         else:
1860             (pdf, htmldoc_err) = pipeCommand(cmdstr, html)
1861 
1862             # Check for error message on STDOUT.
1863             if pdf[:8] == u'HTMLDOC ':
1864                 htmldoc_err += pdf
1865                 pdf = None
1866             # The PAGES: and BYTES: indicate a successful generation of the PDF document.
1867             if htmldoc_err.find('PAGES: ') != -1 and htmldoc_err.find('BYTES: ') != -1:
1868                 htmldoc_err = None
1869             else:
1870                 pdf = None
1871             if htmldoc_err:
1872                 errmsg = self._(u'Command:') + u'<pre>' + wikiutil.escape(cmdstr) + u'</pre>' + self._('returned:') + u'<pre>' + \
1873                       wikiutil.escape(htmldoc_err).replace(u'\n', u'<br />') + u'</pre>'
1874 
1875         # As it is difficult to get the htmldoc return code, we check for
1876         # error by checking the produced pdf length
1877         if not pdf and errmsg:
1878             if self.debug:
1879                 self.request.write(u'<html><body>%s</body></hftml>' % errmsg)
1880                 self.request.write(html)
1881             else:
1882                 self.error_msg(errmsg)
1883         elif pdf[:4] != '%PDF':
1884             self.error_msg(self._(u'Invalid PDF document generated.') + u' ' + self._(u'Length: %(size)d Byte(s)') % {u'size': u'%d' % len(pdf)} + (len(pdf) or u'' and u' <pre>' + wikiutil.escape(pdf[:80]) + u'</pre>'))
1885             pdf = None
1886         
1887         return pdf
1888 
1889     
1890     def _set_fontsize(self, key, min, max, smallstep):
1891         self.valid_options[key] = {}
1892         for fontsize_big in range(min, max):
1893             for fontsize_step in range(0, 10, smallstep):
1894                 fontsize = u'%d.%d' % (fontsize_big, fontsize_step,)
1895                 self.valid_options[key][fontsize] = self._(fontsize)
1896         self.valid_options[key][u'%d.0' % max] = self._(u'%d.0' % max)
1897 
1898 
1899     def get_session_cookies(self):
1900         """It's important to set the right cookies to get access to ACL protected pages, especially their attachments
1901         because the HTML code itself is already there if we call HTMLDOC. HTMLDOC have to download the attachments
1902         itself (in his own socket connection) using theses cookies.
1903         """
1904         result=["MOIN_ID=" + self.request.user.id]
1905         cookie = None
1906         # With MoinMoin 1.9 the usage of cookies for authentication changed.
1907         # Please refer to MoinMoin/web/session.py:get_cookie_name() function.
1908         if is_moinmoin_version_eqhigher(1,9):
1909             cookie_name = get_cookie_name(self.request, name=self.request.cfg.cookie_name, usage=self.request.cfg.session_service.cookie_usage)
1910 
1911         # For MoinMoin 1.6 and newer MOIN_SESSION is mandatory to get access to ACL protected attachments.
1912         # The ongoing session id will be used for HTMLDOC request.
1913         elif is_moinmoin_version_eqhigher(1,6):
1914             cookie_name = "MOIN_SESSION"
1915 
1916         # For MoinMoin 1.5 the MOIN_SESSION* cookie is not required.
1917         else:
1918             cookie_name = None
1919 
1920         if cookie_name:
1921             if hasattr(self.request, 'cookie') and self.request.cookie.get(cookie_name, False):
1922                 cookie = cookie_name + "=" + self.request.cookie.get(cookie_name).value
1923             # Since MoinMoin 1.9 it's called "cookies".
1924             elif hasattr(self.request, 'cookies') and self.request.cookies.get(cookie_name, False):
1925                 cookie = cookie_name + "=" + self.request.cookies.get(cookie_name)
1926             # Backward compatibility before MoinMoin 1.7
1927             else:
1928                 # For the CGI request method there is no `headers` attribute for an site where
1929                 # no cookies exists on browser site and the user is anonymous.
1930                 cookies = ';'
1931                 if hasattr(self.request, 'headers') and hasattr(self.request.headers, 'cookie'):
1932                     cookies = self.request.headers.cookie
1933                 # Since MoinMoin 1.6 it's called 'saved_cookies'
1934                 elif hasattr(self.request, 'saved_cookie'):
1935                     cookies = self.request.saved_cookie
1936 
1937                 for cookie in cookies.split(';'):
1938                     # Remove trailing while spaces.
1939                     cookie = cookie.strip()
1940                     # All kinds of MOIN_SESSION.* willbe accepted
1941                     if cookie.startswith(cookie_name):
1942                         result.append(cookie)
1943                     cookie = None
1944         
1945             # If anonymous sessions are allowed the MOIN_SESSION is not required. Otherwise we stop with an error.
1946             if cookie:
1947                 result.append(cookie)
1948 
1949         return result
1950 
1951 
1952 def execute(pagename, request):
1953     CreatePdfDocument(pagename, request).render()

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.
  • [get | view] (2008-06-25 06:31:28, 12.9 KB) [[attachment:AttributeError_form.html]]
  • [get | view] (2007-09-23 21:24:21, 74.0 KB) [[attachment:CreatePdfDocument2_1_5+p1.py]]
  • [get | view] (2007-09-24 19:04:02, 73.9 KB) [[attachment:CreatePdfDocument2_1_5+p2.py]]
  • [get | view] (2008-06-25 01:39:15, 77.3 KB) [[attachment:CreatePdfDocument2_3_0+p1.py]]
  • [get | view] (2008-06-25 06:52:21, 77.5 KB) [[attachment:CreatePdfDocument2_3_0+p2.py]]
  • [get | view] (2008-06-26 16:18:43, 81.0 KB) [[attachment:CreatePdfDocument2_3_0+p3.py]]
  • [get | view] (2008-11-10 06:53:43, 84.3 KB) [[attachment:CreatePdfDocument2_3_4+p1.py]]
  • [get | view] (2008-11-10 20:22:57, 84.7 KB) [[attachment:CreatePdfDocument2_3_4+p2.py]]
  • [get | view] (2009-01-12 19:30:53, 84.3 KB) [[attachment:CreatePdfDocument2_3_5+p1.py]]
  • [get | view] (2010-09-17 13:22:57, 86.4 KB) [[attachment:CreatePdfDocument2_4_1+p1.py]]
  • [get | view] (2010-05-16 05:14:41, 8311.5 KB) [[attachment:HTMLDOC_1.8.27.msi]]
  • [get | view] (2006-05-25 08:59:55, 100.6 KB) [[attachment:PdfAction--book.pdf]]
  • [get | view] (2006-05-25 10:37:23, 13.2 KB) [[attachment:PdfAction--page.pdf]]
  • [get | view] (2006-09-12 09:11:10, 8.3 KB) [[attachment:RedirectOutputRequest.html]]
  • [get | view] (2007-09-20 15:15:05, 10.9 KB) [[attachment:WikiAdministration_DebugInfo.zip]]
  • [get | view] (2007-09-24 06:25:51, 3.6 KB) [[attachment:WikiAdministration_DebugInfo_20070924.zip]]
  • [get | view] (2008-06-24 09:56:40, 16.0 KB) [[attachment:_AttributeError20080624.html]]
  • [get | view] (2006-09-11 18:56:44, 6.0 KB) [[attachment:bool.html]]
  • [get | view] (2007-06-28 20:42:45, 13.8 KB) [[attachment:coded_w_wing_large.png]]
  • [get | view] (2007-06-28 20:48:21, 8.6 KB) [[attachment:coded_w_wing_medium.png]]
  • [get | view] (2007-06-28 20:44:02, 6.8 KB) [[attachment:coded_w_wing_small.png]]
  • [get | view] (2008-06-26 17:18:39, 16.2 KB) [[attachment:debug260208.html]]
  • [get | view] (2007-09-20 15:14:47, 211.6 KB) [[attachment:htmldoc_WikiAdministration_ClassicTheme.zip]]
  • [get | view] (2008-12-21 10:40:28, 31.5 KB) [[attachment:listings.png]]
  • [get | view] (2010-09-16 12:23:08, 5.4 KB) [[attachment:moin.log.failed.gz]]
  • [get | view] (2010-09-16 12:23:22, 9.6 KB) [[attachment:moin.log.ok.gz]]
  • [get | view] (2014-08-18 02:10:25, 2.6 KB) [[attachment:patch_htmldoc_escaping.patch]]
  • [get | view] (2010-04-07 17:29:57, 5.7 KB) [[attachment:pdfbug.txt]]
  • [get | view] (2006-10-09 21:59:00, 0.7 KB) [[attachment:skip-one-region.diff]]
  • [get | view] (2008-12-21 10:33:05, 11.6 KB) [[attachment:traceback.html]]
 All files | Selected Files: delete move to page copy to page

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