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