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

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