Attachment 'CreatePdfDocument2_4_0.py'

Download

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