Attachment 'CreatePdfDocument2_0_3.py'

Download

   1 # -*- coding: iso-8859-1 -*-
   2 """
   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     @copyright: (C) 2006  Pascal Bauermeister
  17     @copyright: (C) 2006  Raphael Bossek <raphael.bossek@solutions4linux.de>
  18     @license: GNU GPL, see COPYING for details
  19 
  20     2006-09-06  RaphaelBossek
  21     * Release v2.0.3
  22     * Fixed FastCGI support by removing stdout redirect output code. The
  23       same functionality will be done by the RedirectOutputRequest class.
  24     * Renamed to CreatePdfDocument (for better translation possibilities).
  25     * Fixed FastCGI support.
  26     * Fixed encoding of title page.
  27     * Added charset set option.
  28     * Fixed waiting for HTMLDOC.
  29 
  30     2006-09-02  RaphaelBossek
  31     * Release v2.0.2
  32     * Added createpdfdocument_validoptions and createpdfdocument_defaultvalues
  33       configuration parameter support. You are not able to preset available
  34       options and which defaults are used in your configuration file.
  35 
  36     2006-08-30  RaphaelBossek
  37     * Release v2.0.1
  38     * Fixed issue with page revision number forwarding (traceback).
  39 
  40     2006-08-30  RaphaelBossek
  41     * Release v2.0.0
  42     * Feature enchanced and bug fixed version.
  43 
  44     2006-05-26  PascalBauermeister
  45     * Release v1.0.2
  46     * Relative image URLs turned absolute was bogus. It is less bogus now.
  47 
  48     2006-05-26  PascalBauermeister
  49     * Release v1.0.1
  50     * Set env var HTMLDOC_NOCGI to solve CGI issue
  51 
  52     2006-05-24  PascalBauermeister
  53     * Initial release v1.0.0
  54 """
  55 
  56 import os, stat
  57 import re, copy, sys, StringIO, tempfile, traceback
  58 import shutil, StringIO
  59 from MoinMoin import config, util, wikiutil, packages, error
  60 from MoinMoin.Page import Page
  61 from MoinMoin.util import MoinMoinNoFooter, filesys
  62 from MoinMoin.request import RequestModPy, RequestBase
  63 from MoinMoin.parser import wiki
  64 from MoinMoin.widget.dialog import Dialog
  65 
  66 class ActionError(Exception):
  67     pass
  68 
  69 class RedirectOutputRequest(RequestBase):
  70     """Redirect output to string without HTTP headers."""
  71     def __init__ (self, req):
  72         self.path_info = req.path_info.encode(config.charset)
  73         self.request_uri = req.request_uri
  74         self.url = req.url
  75         self.http_user_agent = req.http_user_agent
  76         self.query_string = req.query_string
  77         #self.request_method = req.request_method
  78         self.request_method = None
  79         self.saved_cookie = req.saved_cookie
  80         self.remote_addr = req.remote_addr
  81 
  82         # RequestModPy
  83         if getattr (req, u'script_name', None):
  84             self.script_name = req.script_name
  85 
  86         self.req = req
  87         RequestBase.__init__(self)
  88 
  89     def run(self):
  90         self.output_string = u''
  91         self.error_string = u''
  92         self.sent_headers = False
  93         self.fail = False
  94         RequestBase.run(self)
  95         return (self.fixhtmlstr(self.output_string), self.error_string)
  96 
  97     def fixhtmlstr (self, str):
  98         """Convert utf-8 encoded multi-byte sequences into &#XXXX; format."""
  99         htmlstr = u''
 100         for c in str:
 101             if ord(c) >= 128:
 102                 htmlstr = htmlstr + '&#%d;' % ord(c)
 103             else:
 104                 htmlstr = htmlstr + c
 105         return htmlstr
 106 
 107     def http_headers (self, more_headers=[]):
 108         self.sent_headers = True
 109 
 110     def fail (self, err):
 111         RequestBase.fail (self, err)
 112         if not self.error_string:
 113             self.error_string = str(err)
 114 
 115     def write (self, *data):
 116         if self.sent_headers:
 117             if self.fail:
 118                 self.error_string += data[0]
 119             else:
 120                 self.output_string += data[0]
 121 
 122     def flush(self):
 123         pass
 124 
 125 class CreatePdfDocument:
 126     """Implementation of the PDF document generator."""
 127 
 128     version = u'2.0.3'
 129 
 130     def __init__(self):
 131         self.action_name = self.__class__.__name__
 132         self.pagename = None
 133         self.request = None
 134         self._ = None
 135         self.debug = False
 136         self.msg = None
 137         self.errormsgsent = False
 138         self.default_values = {
 139             'style': u'webpage',
 140             'format': u'pdf13',
 141             'linkstyle': u'underline',
 142             'headerleft': u't',
 143             'headermiddle': u'.',
 144             'headerright': u'D',
 145             'footerleft': u'.',
 146             'footermiddle': u'/',
 147             'footerright': u'.',
 148             'tocheaderleft': u'.',
 149             'tocheadermiddle': u't',
 150             'tocheaderright': u'.',
 151             'tocfooterleft': u'.',
 152             'tocfootermiddle': u'.',
 153             'tocfooterright': u'i',
 154             'linkcolor': u'0000E0',
 155             'size': u'legal',
 156             'user-password': u'',
 157             'owner-password': u'',
 158             'toclevels': u'3',
 159             'grayscale': u'unchecked',
 160             'title': u'checked',
 161             'duplex': u'unchecked',
 162             'landscape': u'unchecked',
 163             'usersize': u'',
 164             'margintop': u'0.50in',
 165             'marginbottom': u'0.50in',
 166             'marginleft': u'1.00in',
 167             'marginright': u'0.50in',
 168             'no-toc': u'checked',
 169             'no-links': u'checked',
 170             'firstpage': u'p1',
 171             'jpeg': u'0',
 172             'compression': u'0',
 173             'pagemode': u'outline',
 174             'pagelayout': u'single',
 175             'firstpage': u'c1',
 176             'numbered': u'checked',
 177             'encryption': u'unchecked',
 178             'permissioncopy': u'checked',
 179             'permissionprint': u'checked',
 180             'permissionannotate': u'checked',
 181             'permissionmodify': u'checked',
 182             'charset': u'iso-8859-1',
 183             'debug': u'',
 184             'rev': 0,
 185         }
 186         # We have to know which values are checkboxes within the form. If a key does
 187         # not exists wihtin the form the corresponding checkbox is not checked.
 188         self.form_checkbox = []
 189         for key, value in self.default_values.items():
 190             if value in [u'checked', u'unchecked']:
 191                 self.form_checkbox += [key]
 192         self.contenttype = u'application/pdf'
 193 
 194     def error_msg (self, msg):
 195         """Display error message."""
 196         if not self.errormsgsent:
 197             Page (self.request, self.pagename).send_page (self.request, msg=msg)
 198             self.errormsgsent = True
 199 
 200     def set_page_values(self):
 201         """Scan raw page for additional information relating PDF generation."""
 202         #pdflines = False
 203         for line in self.request.page.get_raw_body().split(u'\n'):
 204             if line[:6] == u'##pdf ':
 205                 cols = line[6:].split()
 206                 # Only accept known values/settings.
 207                 if len(cols) > 1 and cols[0] in self.default_values:
 208                     self.values[cols[0]] = u' '.join(cols[1:])
 209                     #pdflines = True
 210                 continue
 211             # Stop parsing with first non-pdf line (if detected at least one).
 212             #elif pdflines and not line:
 213             #    break
 214 
 215     def set_page_default_values(self):
 216         # We are not able to recognise if this string is part of a verbatim area.
 217         matchtoclvl = re.compile(r'^\[\[TableOfContents\(\s*(\d+)\s*\)\]\]')
 218         matchtoc = re.compile(r'^\[\[TableOfContents\(*\)*\]\]')
 219         toc = False
 220         for line in self.request.page.get_raw_body().split(u'\n'):
 221             if line[:10] == u'#language ' and not u'language' in self.values:
 222                 lang = self.make_isolang(line[10:])
 223                 if lang:
 224                     self.default_values[u'language'] = lang
 225             elif not u'toclevels' in self.values and not toc:
 226                 result = matchtoclvl.match(line)
 227                 if result:
 228                     toclevels = int(result.group(1).strip())
 229                     if toclevels > 4:
 230                         toclevels = 4
 231                     self.default_values[u'toclevels'] = str(toclevels)
 232                     toc = True
 233                 elif matchtoc.match(line):
 234                     toc = True
 235         # We assume if table-of-contents is used we intent to generate a book.
 236         if toc:
 237             self.default_values[u'style'] = u'book'
 238         else:
 239             # Do not generate a table of contents page.
 240             self.default_values[u'no-toc'] = u'unchecked'
 241  
 242     def escape (self, str):
 243         return str.replace (u'&', u'&amp;').replace (u'<', u'&lt;').replace (u'>', u'&gt;')
 244 
 245     def _select (self, name, description=None):
 246         """Helper function to create a selection control."""
 247         str = u'<select name="%s" size="1">' % (name,)
 248         if not description:
 249             description = self.valid_options[name]
 250         keys = description.keys()
 251         keys.sort()
 252         for value in keys:
 253             if value == self.values[name]:
 254                 selected = u'selected'
 255             else:
 256                 selected = u''
 257             str += u'<option value="%s" %s>%s</option>' % (value, selected, description[value],)
 258         str += u'</select>'
 259         return str
 260 
 261     def _chooseformat (self, name):
 262         """Helper function to create left/middle/right selection controls."""
 263         str = u"""    <tr>
 264         <td class="label"><label>%s&nbsp;:</label></td>
 265         <td><table>
 266                 <tr>
 267                     <td>%s&nbsp;:</td>
 268                     <td>%s</td>
 269                 </tr>
 270                 <tr>
 271                     <td>%s&nbsp;:</td>
 272                     <td>%s</td>
 273                 </tr>
 274                 <tr>
 275                     <td>%s&nbsp;:</td>
 276                     <td>%s</td>
 277                 </tr>
 278             </table>
 279         </td>
 280         <td>%s</td>
 281     </tr>""" % (self.fields[u'label_' + name],
 282               self.fields[u'label_left'], self._select(name + u'left', self.valid_options[u'tocformats']),
 283               self.fields[u'label_middle'], self._select(name + u'middle', self.valid_options[u'tocformats']),
 284               self.fields[u'label_right'], self._select(name + u'right', self.valid_options[u'tocformats']),
 285               self.fields[u'help_' + name],)
 286         return str
 287 
 288     def makeform (self, errormsg=u''):
 289         self.fields = {
 290             'error': errormsg,
 291             'pagename': wikiutil.escape(self.pagename),
 292             'action': self.action_name,
 293             'seperator': u'<tr><td colspan="3"><hr/></td></tr>',
 294             'space': u'<tr><td colspan="3">&nbsp;</td></tr>',
 295 
 296             'label_input': self._(u'Input'),
 297             'label_output': self._(u'Output'),
 298             'label_page': self._(u'Page'),
 299             'label_tableofcontents': self._(u'Contents'),
 300             'label_pdf': self._(u'PDF'),
 301             'label_security': self._(u'Security'),
 302 
 303             'label_choose_style': self._(u'Choose style'),
 304             'help_choose_style': self._(u'book: Create a structured PDF document with headings, chapters, etc.') + u'<br/>' + \
 305                                  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/>' + \
 306                                  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.'),
 307 
 308             'label_format': self._(u'Output format'),
 309             'help_format': self._(u'Specifies the output format.'),
 310 
 311             'label_outputoptions': self._(u'Output options'),
 312             'label_grayscale': self._(u'Grayscale document'),
 313             'label_title': self._(u'Title page'),
 314             'label_jpeg': self._(u'JPEG big images'),
 315             'label_compression': self._(u'Compression'),
 316 
 317             'label_no-toc': self._(u'Generate a table of contents'),
 318             'help_no-toc': self._(u''),
 319 
 320             'label_toclevels': self._(u'Limit the number of levels in the table-of-contents'),
 321             'help_toclevels': self._(u'Sets the number of levels in the table-of-contents.') + u' ' + self._(u'Empty for unlimited levels.'),
 322 
 323             'label_numbered': self._(u'Numbered headings'),
 324             'help_numbered': self._(u'Check to number all of the headings in the document.'),
 325 
 326             'label_toctitle': self._(u'Table-of-contents title'),
 327             'help_toctitle': self._(u'Sets the title for the table-of-contents.') + u' ' + self._(u'Empty for default title.'),
 328 
 329             'label_left': self._(u'Left'),
 330             'label_middle': self._(u'Middle'),
 331             'label_right': self._(u'Right'),
 332 
 333             'label_tocheader': self._(u'Header of table-of-contantes page'),
 334             'help_tocheader': self._(u'Sets the page header to use on table-of-contents pages.'),
 335 
 336             'label_tocfooter': self._(u'Footer of table-of-contantes page'),
 337             'help_tocfooter': self._(u'Sets the page footer to use on table-of-contents pages.'),
 338 
 339             'label_header': self._(u'Page header'),
 340             'help_header': self._(u'Sets the page header to use on body pages.'),
 341 
 342             'label_footer': self._(u'Page footer'),
 343             'help_footer': self._(u'Sets the page footer to use on body pages.'),
 344 
 345             'label_no-links': self._(u'Create HTTP links'),
 346             'help_no-links': self._(u'Enables generation of links in PDF files.'),
 347             
 348             'label_linkstyle': self._(u'Style of HTTP links'),
 349             'help_linkstyle': self._(u''),
 350 
 351             'label_linkcolor': self._(u'HTTP links color'),
 352             'help_linkcolor': self._(u'Sets the color of links.'),
 353 
 354             'label_duplex': self._(u'2-Sided'),
 355             'help_duplex': self._(u'Specifies that the output should be formatted for double-sided printing.'),
 356 
 357             'label_landscape': self._(u'Landscape'),
 358 
 359             'label_choose_size': self._(u'Choose page size'),
 360             'help_choose_size': self._(u'Choose one of the predefined standard sizes or select user defined.'),
 361 
 362             'label_usersize': self._(u'User defined page size'),
 363             '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).'),
 364 
 365             'label_margin': self._(u'User defined margin'),
 366             'label_margintop': self._(u'Top'),
 367             'label_marginbottom': self._(u'Bottom'),
 368             'label_marginleft': self._(u'Left'),
 369             'label_marginright': self._(u'Right'),
 370             '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.'),
 371 
 372             'label_pagemode': self._(u'Page mode'),
 373             '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.'),
 374 
 375             'label_pagelayout': self._(u'Page layout'),
 376             '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.'),
 377 
 378             'label_firstpage': self._(u'First page'),
 379             'help_firstpage': self._(u'Choose the initial page that will be shown.'),
 380 
 381             'label_encryption': self._(u'Encryption'),
 382             'help_encryptin': self._(u'Enables encryption and security features for PDF output.'),
 383 
 384             'label_permissions': self._(u'Permissions'),
 385             'help_permissions': self._(u'Specifies the document permissions.'),
 386 
 387             'label_permissionannotate': self._(u'Annotate'),
 388             'label_permissionprint': self._(u'Print'),
 389             'label_permissionmodify': self._(u'Modify'),
 390             'label_permissioncopy': self._(u'Copy'),
 391 
 392             'label_owner-password': self._(u'Owner password'),
 393             '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.'),
 394 
 395             'label_user-password': self._(u'User password'),
 396             'help_user-password': self._(u'Specifies the user password to restrict viewing permissions on this PDF document.') + u' ' + self._(u'Empty for no encryption.'),
 397 
 398             'label_expert': self._(u'Expert'),
 399             'label_language': self._(u'Language translation'),
 400             'help_language': self._(u'Specify language to use for date and time format.'),
 401 
 402             'label_charset': self._(u'Charset set'),
 403             'help_charset': self._(u'Change the encoding of the text in document.'),
 404 
 405             'button_generate': self._(u'Generate PDF'),
 406             'button_remember': self._(u'Remember form'),
 407             'button_cancel': self._(u'Cancel'),
 408             'button_reset': self._(u'Reset'),
 409             }
 410         self.fields.update(self.values)
 411 
 412         # Go through all format strings.
 413         for name in [u'tocheader', u'tocfooter', u'header', u'footer']:
 414             self.fields[u'choose_' + name] = self._chooseformat(name)
 415 
 416         self.fields[u'select_style'] = self._select (u'style')
 417         self.fields[u'select_format'] = self._select (u'format')
 418         self.fields[u'select_linkstyle'] = self._select (u'linkstyle')
 419         self.fields[u'select_size'] = self._select (u'size')
 420         self.fields[u'select_jpeg'] = self._select (u'jpeg')
 421         self.fields[u'select_compression'] = self._select (u'compression')
 422         self.fields[u'select_toclevels'] = self._select (u'toclevels')
 423         self.fields[u'select_pagemode'] = self._select (u'pagemode')
 424         self.fields[u'select_pagelayout'] = self._select (u'pagelayout')
 425         self.fields[u'select_firstpage'] = self._select (u'firstpage')
 426         self.fields[u'select_charset'] = self._select (u'charset')
 427 
 428         self.fields[u'pdfpydownloadlink'] = u'http://einstein.speech-design.de/~br/pdf.py'
 429         self.fields[u'pdfpyversion'] = self.version
 430 
 431         form = """<p class="error">%(error)s</p>
 432 <form method="post" action="">
 433 <input type="hidden" name="action" value="%(action)s"/>
 434 <table>
 435     <tr><td colspan="3">%(label_input)s</td></tr>
 436     <tr>
 437         <td class="label"><label>%(label_choose_style)s&nbsp;:</label></td>
 438         <td class="content">%(select_style)s</td>
 439         <td>%(help_choose_style)s</td>
 440     </tr>
 441     %(seperator)s
 442     <tr><td colspan="3">%(label_output)s</td></tr>
 443     <tr>
 444         <td class="label"><label>%(label_format)s&nbsp;:</label></td>
 445         <td class="content">%(select_format)s</td>
 446         <td>%(help_format)s</td>
 447     </tr>
 448     <tr>
 449         <td class="label"><label>%(label_outputoptions)s&nbsp;:</label></td>
 450         <td colspan="2"><input type="checkbox" name="grayscale" value="checked" %(grayscale)s />%(label_grayscale)s&nbsp;
 451             <input type="checkbox" name="title" value="checked" %(title)s />%(label_title)s<br />
 452             %(label_compression)s&nbsp:&nbsp;%(select_compression)s&nbsp;
 453             %(label_jpeg)s&nbsp;:&nbsp;%(select_jpeg)s</td>
 454     </tr>
 455     %(seperator)s
 456     <tr><td colspan="3">%(label_page)s</td></tr>
 457     <tr>
 458         <td class="label"><label>%(label_choose_size)s&nbsp;:</label></td>
 459         <td>%(select_size)s&nbsp;<br /><nobr>%(label_usersize)s&nbsp;:&nbsp;<input type="text" size="15" name="usersize" value="%(usersize)s" /></nobr></td>
 460         <td>%(help_choose_size)s<br />%(help_usersize)s</td>
 461     </tr>
 462     <tr>
 463         <td>&nbsp;</td>
 464         <td colspan="2"><input type="checkbox" name="duplex" value="checked" %(duplex)s />&nbsp;%(label_duplex)s&nbsp;
 465             <input type="checkbox" name="landscape" value="checked" %(landscape)s />&nbsp;%(label_landscape)s</td>
 466     </tr>
 467     <tr>
 468         <td class="label"><label>%(label_margin)s&nbsp;:</label></td>
 469         <td><table><tr><td>&nbsp;</td><td><nobr><label>%(label_margintop)s&nbsp;:</label>&nbsp;<input type="text" name="margintop" value="%(margintop)s" size="7" /></nobr></td><td>&nbsp;</td></tr>
 470             <tr><td><nobr><label>%(label_marginleft)s&nbsp;:</label>&nbsp;<input type="text" name="marginleft" value="%(marginleft)s" size="7" /></nobr></td><td>&nbsp;</td><td><nobr><label>%(label_marginright)s&nbsp;:</label>&nbsp;<input type="text" name="marginright" value="%(marginright)s" size="7" /></nobr></td></tr>
 471             <tr><td>&nbsp;</td><td><nobr><label>%(label_marginbottom)s&nbsp;:</label>&nbsp;<input type="text" name="marginbottom" value="%(marginbottom)s" size="7" /></nobr></td><td>&nbsp;</td></tr></table>
 472         <td>%(help_margin)s</td>
 473     </tr>
 474     %(choose_header)s
 475     %(choose_footer)s
 476     %(seperator)s
 477     <tr><td colspan="3">%(label_tableofcontents)s</td></tr>
 478     <tr>
 479         <td class="label"><label>%(label_no-toc)s&nbsp;:</label></td>
 480         <td class="checkbox"><input type="checkbox" name="no-toc" value="checked" %(no-toc)s /></td>
 481         <td>%(help_no-toc)s</td>
 482     </tr>
 483     <tr>
 484         <td class="label"><label>%(label_toclevels)s&nbsp;:</label></td>
 485         <td class="content">%(select_toclevels)s</td>
 486         <td>%(help_toclevels)s</td>
 487     </tr>
 488     <tr>
 489         <td>&nbsp;</td>
 490         <td><input type="checkbox" name="numbered" value="checked" %(numbered)s />&nbsp;%(label_numbered)s</td>
 491         <td>%(help_numbered)s</td>
 492     </tr>
 493     <tr>
 494         <td class="label"><label>%(label_toctitle)s&nbsp;:</label></td>
 495         <td class="content"><input type="text" size="30" name="toctitle" value="%(toctitle)s" /></td>
 496         <td>%(help_toctitle)s</td>
 497     </tr>
 498     %(choose_tocheader)s
 499     %(choose_tocfooter)s
 500     %(seperator)s
 501     <tr><td colspan="3">%(label_pdf)s</td></tr>
 502     <tr>
 503         <td class="label"><label>%(label_pagemode)s&nbsp;:</label></td>
 504         <td class="content">%(select_pagemode)s</td>
 505         <td>%(help_pagemode)s</td>
 506     </tr>
 507     <tr>
 508         <td class="label"><label>%(label_pagelayout)s&nbsp;:</label></td>
 509         <td class="content">%(select_pagelayout)s</td>
 510         <td>%(help_pagelayout)s</td>
 511     </tr>
 512     <tr>
 513         <td class="label"><label>%(label_firstpage)s&nbsp;:</label></td>
 514         <td class="content">%(select_firstpage)s</td>
 515         <td>%(help_firstpage)s</td>
 516     </tr>
 517     <tr>
 518         <td class="label"><label>%(label_no-links)s&nbsp;:</label></td>
 519         <td><input type="checkbox" name="no-links" value="checked" %(no-links)s /></td>
 520         <td>%(help_no-links)s</td>
 521     </tr>
 522     <tr>
 523         <td class="label"><label>%(label_linkstyle)s&nbsp;:</label></td>
 524         <td class="content">%(select_linkstyle)s</td>
 525         <td>%(help_linkstyle)s</td>
 526     </tr>
 527     <tr>
 528         <td class="label"><label>%(label_linkcolor)s&nbsp;:</label></td>
 529         <td class="content"><input type="text" size="6" name="linkcolor" value="%(linkcolor)s" /></td>
 530         <td>%(help_linkcolor)s</td>
 531     </tr>
 532     %(seperator)s
 533     <tr><td colspan="3">%(label_security)s</td></tr>
 534     <tr>
 535         <td class="label"><label>%(label_encryption)s&nbsp;:</label></td>
 536         <td><input type="checkbox" name="encryption" value="checked" %(encryption)s /></td>
 537         <td>%(help_numbered)s</td>
 538     </tr>
 539     <tr>
 540         <td class="label"><label>%(label_permissions)s&nbsp;:</label></td>
 541         <td><nobr><input type="checkbox" name="permissionprint" value="checked" %(permissionprint)s />&nbsp;%(label_permissionprint)s</nobr>&nbsp;
 542             <nobr><input type="checkbox" name="permissionmodify" value="checked" %(permissionmodify)s />&nbsp;%(label_permissionmodify)s</nobr><br />
 543             <nobr><input type="checkbox" name="permissioncopy" value="checked" %(permissioncopy)s />&nbsp;%(label_permissioncopy)s</nobr>&nbsp;
 544             <nobr><input type="checkbox" name="permissionannotate" value="checked" %(permissionannotate)s />&nbsp;%(label_permissionannotate)s</nobr></td>
 545         <td>%(help_permissions)s</td>
 546     </tr>
 547     <tr>
 548         <td class="label"><label>%(label_user-password)s&nbsp;:</label></td>
 549         <td class="content"><input type="password" size="30" name="user-password" value="%(user-password)s" /></td>
 550         <td>%(help_user-password)s</td>
 551     </tr>
 552     <tr>
 553         <td class="label"><label>%(label_owner-password)s&nbsp;:</label></td>
 554         <td class="content"><input type="password" size="30" name="owner-password" value="%(owner-password)s" /></td>
 555         <td>%(help_owner-password)s</td>
 556     </tr>
 557     %(seperator)s
 558     <tr><td colspan="3">%(label_expert)s</td></tr>
 559     <tr>
 560         <td class="label"><label>%(label_language)s&nbsp;:</label></td>
 561         <td class="content"><input type="text" size="6" name="language" value="%(language)s" /></td>
 562         <td>%(help_language)s</td>
 563     </tr>
 564     <tr>
 565         <td class="label"><label>%(label_charset)s&nbsp;:</label></td>
 566         <td class="content">%(select_charset)s</td>
 567         <td>%(help_charset)s</td>
 568     </tr>
 569     %(space)s
 570     <tr>
 571         <td><input type="hidden" name="debug" value="%(debug)s" /><input type="hidden" name="rev" value="%(rev)s" /></td>
 572         <td class="buttons" colspan="2">
 573             <input type="submit" name="generate_from_form" value="%(button_generate)s" />&nbsp;
 574             <input type="submit" name="remember" value="%(button_remember)s" />&nbsp;
 575             <input type="submit" name="cancel" value="%(button_cancel)s" />
 576         </td>
 577     </tr>
 578 </table>
 579 </form>""" % self.fields
 580         return Dialog (self.request, content=form)
 581 
 582     def run (self, pagename, request):
 583         """ Main dispatcher for the action."""
 584         self.pagename = pagename
 585         self.request = request
 586         self._ = self.request.getText
 587         self.form = self.request.form
 588         self.cfg = self.request.cfg
 589 
 590         # Canceled by user.
 591         if self.form.has_key(u'cancel'):
 592             return self.request.page.send_page(self.request)
 593 
 594         # Determine calling parameters.
 595         if self.form.get(u'debug', [u'0']) == [u'1']:
 596             self.debug = True
 597 
 598         # This dict contains all possible values.
 599         self.valid_options = {}
 600 
 601         self.valid_options[u'tocformats'] = {
 602             u'/': self._(u'1/N,2/N Arabic page numbers'),
 603             u':': self._(u'1/C,2/C Arabic chapter page numbers'),
 604             u'1': self._(u'1,2,3,...'),
 605             u'a': self._(u'a,b,c,...'),
 606             u'A': self._(u'A,B,C,...'),
 607             u'c': self._(u'Chapter title'),
 608             u'C': self._(u'Chapter page number'),
 609             u'd': self._(u'Date'),
 610             u'D': self._(u'Date + Time'),
 611             u'h': self._(u'Heading'),
 612             u'i': self._(u'i,ii,iii,iv,...'),
 613             u'I': self._(u'I,II,III,IV,...'),
 614             u't': self._(u'Title'),
 615             u'T': self._(u'Time'),
 616             u'.': self._(u'Blank'),
 617             # TODO: Not supported yet; u'l': self._(u'Logo image'),
 618         }
 619         self.valid_options[u'style'] = {
 620             u'webpage': self._(u'webpage'),
 621             u'book': self._(u'book'),
 622             u'continuous': self._(u'continuous'),
 623         }
 624         self.valid_options[u'size'] = {
 625             u'legal': self._(u'Legal (8.5x14in)'),
 626             u'a4': self._(u'A4 (210x297mm)'),
 627             u'letter': self._(u'Letter (8.5x11in)'),
 628             u'universal': self._(u'Universal (8.27x11in)'),
 629             u'': self._(u'User defined'),
 630         }
 631         self.valid_options[u'format'] = {
 632             u'pdf11': self._(u'PDF 1.1 (Acrobat 2.0)'),
 633             u'pdf12': self._(u'PDF 1.2 (Acrobat 3.0)'),
 634             u'pdf13': self._(u'PDF 1.3 (Acrobat 4.0)'),
 635             u'pdf14': self._(u'PDF 1.4 (Acrobat 5.0)'),
 636             # TODO: Not supported yet:
 637             #u'ps1': self._(u'PostScript Level 1'),
 638             #u'ps2': self._(u'PostScript Level 2'),
 639             #u'ps3': self._(u'PostScript Level 3'),
 640         }
 641         self.valid_options[u'linkstyle'] = {
 642             u'underline': self._(u'Underline'),
 643             u'plain': self._(u'Plain'),
 644         }
 645         self.valid_options[u'firstpage'] = {
 646             u'c1': self._(u'1st chapter'),
 647             u'p1': self._(u'1st page'),
 648             u'toc': self._(u'Contents'),
 649         }
 650         self.valid_options[u'jpeg'] = {
 651             u'0': self._(u'None'),
 652             u'50': self._(u' 50% (Good)'),
 653             u'55': u'55%', u' 60': u' 60%', u' 65': ' 65%', u' 70': ' 70%', u' 75': ' 75%',
 654             u'80': ' 80%', u' 85': ' 85%', u' 90': ' 90%', u' 95': ' 95%',
 655             u'100': self._(u'100% (Best)'),
 656         }
 657         self.valid_options[u'compression'] = {
 658             u'0': self._(u'None'),
 659             u'1': self._(u'1 (Fast)'),
 660             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',
 661             u'9': self._(u'9 (Best)'),
 662         }
 663         self.valid_options[u'toclevels'] = {
 664             u'0': self._(u'None'),
 665             u'1': u'1', u'2': '2', u'3': '3', u'4': '4'
 666         }
 667         self.valid_options[u'pagemode'] = {
 668             u'outline': self._(u'Outline'),
 669             u'document': self._(u'Document'),
 670             u'fullscreen': self._(u'Full-screen'),
 671         }
 672         self.valid_options[u'pagelayout'] = {
 673             u'single': self._(u'Single'),
 674             u'one': self._(u'One column'),
 675             u'twoleft': self._(u'Two column left'),
 676             u'tworight': self._(u'Two column right'),
 677         }
 678         self.valid_options[u'charset'] = {
 679             u'iso-8859-1': self._(u'ISO 8859-1'),
 680             u'iso-8859-2': self._(u'ISO 8859-2'),
 681             u'iso-8859-3': self._(u'ISO 8859-3'),
 682             u'iso-8859-4': self._(u'ISO 8859-4'),
 683             u'iso-8859-5': self._(u'ISO 8859-5'),
 684             u'iso-8859-6': self._(u'ISO 8859-6'),
 685             u'iso-8859-7': self._(u'ISO 8859-7'),
 686             u'iso-8859-8': self._(u'ISO 8859-8'),
 687             u'iso-8859-9': self._(u'ISO 8859-9'),
 688             u'iso-8859-14': self._(u'ISO 8859-14'),
 689             u'iso-8859-15': self._(u'ISO 8859-15'),
 690             u'cp-874': self._(u'cp-847'),
 691             u'cp-1250': self._(u'cp-1250'),
 692             u'cp-1251': self._(u'cp-1251'),
 693             u'cp-1252': self._(u'cp-1252'),
 694             u'cp-1253': self._(u'cp-1253'),
 695             u'cp-1254': self._(u'cp-1254'),
 696             u'cp-1255': self._(u'cp-1255'),
 697             u'cp-1256': self._(u'cp-1256'),
 698             u'cp-1257': self._(u'cp-1257'),
 699             u'cp-1258': self._(u'cp-1258'),
 700             u'koi-8r': self._(u'koi-8r'),
 701         }
 702 
 703         # Set translated name of table of contents as default.
 704         self.default_values[u'toctitle'] = self._(u'Contents')
 705 
 706         # Make sure we create date and time strings in right format.
 707         if self.request.page.language:
 708             self.default_values[u'language'] = self.request.page.language
 709         else:
 710             self.default_values[u'language'] = self.make_isolang(self.cfg.__dict__.get (u'default_language', u'en'))
 711 
 712         self.values = {}
 713 
 714         # If the configuration variable 'createpdfdocument_validoptions' exists we update our
 715         # self.valid_options dict with these values.
 716         if getattr (self.request.cfg, u'createpdfdocument_validoptions', None):
 717             self.valid_options.update (self.request.cfg.createpdfdocument_validoptions)
 718 
 719         # If the configuration variable 'createpdfdocument_defaultvalues' exists we update our
 720         # self.default_values dict with these values.
 721         if getattr (self.request.cfg, u'createpdfdocument_defaultvalues', None):
 722             for key, value in self.request.cfg.createpdfdocument_defaultvalues.items():
 723                 self.default_values[key] = value
 724 
 725         # Scan page to extract default values.
 726         self.set_page_default_values()
 727 
 728         # Create a PDF document direct without any user iteraction from default and page settings.
 729         if self.form.has_key(u'generate'):
 730             self.set_page_values()
 731             self.update_values(useform=False)
 732             return self.do_generate()
 733 
 734         # Create a PDF document from form settings.
 735         if self.form.has_key(u'generate_from_form'):
 736             self.update_values()
 737             return self.do_generate()
 738 
 739         if self.form.has_key(u'remember'):
 740             self.update_values()
 741             return self.do_remember()
 742 
 743         self.set_page_values()
 744         self.update_values(useform=False)
 745         return self.request.page.send_page (self.request, msg=self.makeform())
 746 
 747     def update_values(self, useform=True):
 748         """Preset values with they form values or defaults."""
 749         for key, default in self.default_values.items():
 750             # Modify value only if not already set.
 751             if not key in self.values:
 752                 # If the form does not contain the value (e.g. for checkboxes) set the
 753                 # default value (e.g. for checkboxes unset by default).
 754                 if not key in self.form:
 755                     # Special processing for checkboxes in forms. If the key does not exists
 756                     # within the form it is not checked.
 757                     if key in self.form_checkbox and useform:
 758                         self.values[key] = u'unchecked'
 759                     else:
 760                         self.values[key] = default
 761                 else:
 762                     self.values[key] = self.form[key][0]
 763         # Check if revision is an integer value.
 764         try:
 765             self.values[u'rev'] = int(self.values.get(u'rev', self.request.page.rev))
 766         except:
 767             self.values[u'rev'] = self.request.page.rev
 768         # Check if page revision exists.
 769         (pagefname, realrev, exists) = self.request.page.get_rev (rev=self.values[u'rev'])
 770         if exists:
 771             self.values[u'rev'] = realrev
 772         else:
 773             # Determine latest revision number.
 774             (pagefname, self.values[u'rev'], exists) = self.request.page.get_rev()
 775 
 776     def do_generate(self):
 777         """Create PDF document."""
 778         # Generate the HTML page using MoinMoin wiki engine.
 779         html = self.get_html()
 780         if html:
 781             if self.debug:
 782                 self.request.http_headers()
 783                 self.request.write(html)
 784             else:
 785                 pdfdata = self.html2pdf(html)
 786                 if pdfdata:
 787                     # Send as application/pdf the generated file by HTMLDOC
 788                     self.send_pdf(pdfdata)
 789                     raise MoinMoinNoFooter
 790 
 791     def do_remember(self):
 792         """Create a message containing information about how to save the form values for future reuse."""
 793         save = u''
 794         for key, value in self.values.items():
 795             if key in [u'user-password', u'owner-password', u'rev', u'debug']:
 796                 continue
 797             if key in self.default_values and value == self.default_values[key]:
 798                 continue
 799             save += u'##pdf %s %s\n' % (key, value,)
 800         if save:
 801             msg = self._(u'Add follwing lines at the beginning of your page:') + u'<br/><pre>' + save + u'</pre>'
 802         else:
 803             msg = self._(u'All values correspond to they default. Nothing have to be saved.')
 804         return self.request.page.send_page (self.request, msg)
 805 
 806     def send_pdf (self, data):
 807         filename = self.pagename.replace (u'/', u'-') + u'-v' + str(self.values[u'rev']) + u'.pdf'
 808 
 809         # Send HTTP header.
 810         self.request.http_headers([
 811             'Content-Type: %s' % self.contenttype,
 812             'Content-Length: %d' % len(data),
 813             # TODO: fix the encoding here, plain 8 bit is not allowed
 814             # according to the RFCs There is no solution that is
 815             # compatible to IE except stripping non-ascii chars
 816             'Content-Disposition: inline; filename="%s"' % filename.encode(config.charset),
 817             ])
 818 
 819         # Send binary data.
 820         sio = StringIO.StringIO(data)
 821         shutil.copyfileobj (sio, self.request, 8192)
 822 
 823     def get_html(self):
 824         """Generate the HTML body of this page."""
 825         newreq = RedirectOutputRequest(self.request)
 826 
 827         newreq.form[u'action'] = [u'print']
 828         if self.values.get(u'rev', None):
 829             newreq.form[u'rev'] = [self.values[u'rev']]
 830 
 831         # Save page as HTML.
 832         (html, errmsg) = newreq.run()
 833         if html:
 834             # Make URLs absolute.
 835             # Until MoinMoin is not XHTML compilant we can not use a XML parser (e.g. expat)
 836             # to transform the HTML document. In the meantime we try to achive the same with
 837             # regular expressions subtitution.
 838             base = self.request.getQualifiedURL()
 839             for htmlref in [u'src', u'href']:
 840                 reurlref = r'(%s=[\'"])(/[^\'"]*)[\'"]' % (htmlref,)
 841                 urlref = re.compile (reurlref, re.I)
 842                 for match in urlref.finditer(html):
 843                     foundref = match.groups()
 844                     html = html.replace (foundref[0] + foundref[1], foundref[0] + base + foundref[1])
 845         else:
 846             self.error_msg(self._(u'Could not redirect HTML output for further processing:') + errmsg)
 847 
 848         return html
 849 
 850     def make_isolang (self, language):
 851         return language + u'_' + language.upper()
 852 
 853     def html2pdf (self, html):
 854         """Create a PDF document based on the current parameters."""
 855         # Determine UID to access ACL protected sites too (mandatory to download attached images).
 856         htmldocopts = [u'LANG=' + self.values[u'language'], u'HTMLDOC_NOCGI=1', u'htmldoc', "--cookies", "MOIN_ID=" + self.request.user.id, u'--no-duplex']
 857 
 858         for key in [u'header', u'footer', u'tocheader', u'tocfooter']:
 859             self.values[key] = self.values.get (key + u'left', u'.') + self.values.get (key + u'middle', u'.') + self.values.get (key + u'right', u'.')
 860 
 861         permissions = []
 862         for opt, value in self.values.items():
 863             if opt in [u'language', u'debug', u'rev']:
 864                 continue
 865             value = value.strip()
 866             if not value:
 867                 continue
 868             # Skip options for header/footer configuration which differenciate between position (e.g. footerright or tocheadermiddle)
 869             if opt[:6] in [u'header', u'footer'] and opt[6:] or opt[:9] in [u'tocheader', u'tocfooter'] and opt[9:]:
 870                 continue
 871             if opt in [u'style']:
 872                 htmldocopts += [u'--' + value]
 873             elif opt in self.form_checkbox:
 874                 if value == u'checked':
 875                     if opt[:10] == u'permission':
 876                         permissions += [opt[10:]]
 877                     # Reverse meaning of 'no-' options.
 878                     elif opt[:3] != u'no-':
 879                         htmldocopts += [u'--' + opt]
 880                 elif opt[:3] == u'no-':
 881                     htmldocopts += [u'--' + opt]
 882             elif opt[:6] == u'margin':
 883                 htmldocopts += [u'--' + opt[6:], value]
 884             else:
 885                 htmldocopts += [u'--' + opt, value]
 886         if permissions:
 887             htmldocopts += [u'--permission', u','.join (permissions)]
 888         htmldocopts += [u'-']
 889         # Do not forget to escape all spaces!
 890         eschtmldocopts = [arg.replace(u' ', u'\\ ') for arg in htmldocopts]
 891         cmdstr = u' '.join(eschtmldocopts)
 892         errmsg = None
 893 
 894         pdf = None
 895         if self.debug:
 896             errmsg = self._(u'Command:') + u'<pre>' + self.escape(cmdstr) + u'</pre>'
 897         else:
 898             inp, out, err = os.popen3 (cmdstr, u'b')
 899             try:
 900                 inp.write(html)
 901                 inp.close()
 902             except:
 903                 pass
 904 
 905             pdf = out.read()
 906             out.close()
 907 
 908             htmldocmsg = err.read()
 909             err.close()
 910 
 911             try:
 912                 # REMARK: Otherwise we get <defunct> processes.
 913                 os.wait()
 914             except OSError, e:
 915                 # 10: No child processes.
 916                 if e.errno != 10:
 917                     raise
 918 
 919             # Check for error message on STDOUT.
 920             if pdf[:8] == u'HTMLDOC ':
 921                 htmldocmsg += pdf
 922                 pdf = None
 923 
 924             if htmldocmsg:
 925                 errmsg = self._(u'Command:') + u'<pre>' + self.escape(cmdstr) + u'</pre>' + self._('returned:') + u'<pre>' + \
 926                       self.escape(htmldocmsg).replace(u'\n', u'<br/>') + u'</pre>'
 927 
 928         # As it is difficult to get the htmldoc return code, we check for
 929         # error by checking the produced pdf length
 930         if not pdf and errmsg:
 931             self.error_msg(errmsg)
 932             return None
 933         elif pdf[:4] != '%PDF':
 934             self.error_msg(self._(u'Invalid PDF document generated') + self.escape(pdf[:80]))
 935             return None
 936 
 937         return pdf
 938 
 939 def execute (pagename, request):
 940     CreatePdfDocument().run (pagename = pagename, request = request)
 941 
 942 # vim:ts=4:et:sw=4:nu

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.