Attachment 'CreatePdfDocument2_4_1.py'

Download

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