Attachment 'CreatePdfDocument2_3_2.py'

Download

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