Differences between revisions 6 and 7
Revision 6 as of 2005-10-20 23:44:51
Size: 29973
Editor: RobertSeeger
Comment:
Revision 7 as of 2005-10-21 00:35:28
Size: 34241
Editor: RobertSeeger
Comment: ready to publish this stuff
Deletions are marked like this. Additions are marked like this.
Line 2: Line 2:
I do not have the time to do this the proper way - isolate the minimum necessary core patches and test this extensively, write unit tests, document it...

However, as some people have asked for more, I have at least created some patch files for all the affected modules. You probably do not even need all those patches, as some are for other features.
I do not have the time to do this the proper way - isolate the minimum necessary core patches and test this extensively, write unit tests, document it, do i18n...

However, as some people have asked for more info, I have at least created some patch files for all the affected modules and decided to publish the whole thing as-is. You probably do not even need all those patches, as some are for other features.
Line 691: Line 691:
{{{
    #the physical path to your /wiki url, needed to export smiley images etc.
    url_prefix_dir = 'E:\\moin13\\share\\moin\\htdocs\\'
}}}

You also need to have a way to configure the target export directory - at the moment I have not bothered to integrate this int the config, but rather define it in the used action or command batch.

Line 719: Line 727:
You need to have Microsoft Word (2000) on your MoinMoin server and should have generated a python wrapper with {{{makepy}}} (oh yes, and you need Mark Hammonds win32 package (win32all build 163), of course). Due to some weird exceptions I have decided to serve the Word control with {{{Visible=1}}}, this means that you will watch Word "typing in" your target document!
Line 724: Line 734:
It needs the {{{reportlab}}} package.
Line 732: Line 742:
This can be used to batch export multiple pages matching PAGEPATTERN - be aware that Word may be slow, though ;-().

{{{
set GATEWAY_INTERFACE=CGI/1.1
set DUMPNAME=word
set PYTHONPATH=E:\moin13\Lib\site-packages
set TARGETROOT=E:\moin13
set WIKIURL="http://localhost/moin13/"
set DUMPPATH=%TARGETROOT%\dump_%DUMPNAME%
set PAGEPATTERN=SyntaxReference

set PAGEPATTERN="%PAGEPATTERN%"

E:\python23\python.exe -c "from MoinMoin.scripts.moin_export import run; run()" --wiki=%WIKIURL% --format=word --pattern=%PAGEPATTERN% %DUMPPATH% 1>dump.log 2>dumperr.log
pause
}}}

Line 733: Line 761:

To use this, you may have to modify the server to use a different request from {{{request_ors.py}}}, in my case I modified the {{{moin.cgi}}} (or you could try to create such a request inside the action?):

{{{
from MoinMoin.request_ors import RequestORSCGI

request = RequestORSCGI()
#disable my more critical modifications
request.ors_mod_active=0

}}}


Here is a test action that can be applied to a single page to trigger the export of this page (in Word format, but for PDF it looks very similar). We will later provide an action with a proper user dialog (select format, output name ...maybe even return a link to the created file). Right now, the output directory is hardcoded and you must pick up the result file from the server.

{{{#!python
# Imports
import string, time,sys,os,copy
from MoinMoin import user, webapi, wikiutil
from MoinMoin.PageEditor import PageEditor
from MoinMoin.scripts.moin_export import MoinExporter

def execute(pagename, request):
    _ = request.getText
    page = PageEditor(request,pagename)
    exp_format="word"
# exp_format="html"
# exp_format="plain"
# exp_format="pdf"
#must protect a function that is overrided by the exporter
    qfn_orig=wikiutil.quoteWikinameURL
    markup_orig=request.cfg.default_markup
    
    pname=pagename

#open exporter
    exporter=MoinExporter()
#assign a request (use a copy because we modify the request)
    exporter.request=copy.copy(request)
#set target output directory
    exporter.setOutputDir(r"E:\moin13\dump_word")
#redirect the request to action=print and indicate "export" mode
    exporter.request.user.show_topbottom=0
    exporter.request.form['action']='print'
    exporter.request.form['export']='1'
#assign the export formatter
    exporter.setFormatter(exp_format)
#use special parser for some export formats
    if exp_format in ("word","pdf"):
        request.cfg.default_markup="wiki_word"
#create target file name - this is the plain output of the formatter, the "real" target file
#is different for word and pdf as they create it as a "side effect"
    file = wikiutil.quoteWikinameFS(pname) + exporter.ext
#create error log file (right now we always use the same file for all - this may be bad)
    errfile = os.path.join(exporter.outputdir, 'error.log')

    exporter.errlog = open(errfile, 'w')
#dump the page using the formatter
    output=exporter.dumpPage(pname,file)
#close the error log
    exporter.errlog.close()

    
#reset overrided function
    wikiutil.quoteWikinameURL = qfn_orig
    request.cfg.default_markup=markup_orig
#use this to test output
# return page.send_page(request, msg='<pre>%s</pre>' % output)

    return page.send_page(request,
        msg='<strong>%s</strong>' %
            _('Export of page %s completed.' % pagename))

}}}

MoinMoin Exporter Package - Work in Progress

I do not have the time to do this the proper way - isolate the minimum necessary core patches and test this extensively, write unit tests, document it, do i18n...

However, as some people have asked for more info, I have at least created some patch files for all the affected modules and decided to publish the whole thing as-is. You probably do not even need all those patches, as some are for other features.

TableOfContents

Patches to core MoinMoin 1.3.5

I put them here as code snippets rather than attachments, so I can comment on questions later on.

MoinMoin.Page

--- E:\Install\MoinMoin\moin-1.3.5.tar\moin-1.3.5\MoinMoin\Page.py      Sun Jul 24 13:06:26 2005
+++ E:\moin13\Lib\site-packages\MoinMoin\Page.py        Wed Sep 28 17:56:41 2005
@@ -1,21 +1,45 @@
 # -*- coding: iso-8859-1 -*-
 """
     MoinMoin - Page class
 
     @copyright: 2000-2004 by Jürgen Hermann <jh@web.de>
     @license: GNU GPL, see COPYING for details.
+    ORS modifications:
+        02.01.04 RS implemented old "#locked" syntax as "#acl All:Read"
+        05.01.04 RS no caching of "new Page" page
+        05.01.04 RS activate IIS-patch for getScriptname,
+        13.01.04 RS extensions for export mode
+        20.01.04 RS fix for "extensions for export mode" in send_page
+        05.02.04 RS more versions of "#locked_*" syntax using config.acl_line_locked_*
+        24.02.04 RS fix for export mode in link_to
+        28.10.04 RS patch to enable #pragma title for a display title 
+        04.11.04 RS patch to allow interwiki and relative links in #REDIRECT
+        30.11.04 RS patch for acl handling of remotely created pages (they may have an extra \r after #locked_hard)
+        17.10.04 RS patch to force #pragma section-numbers 0 in export mode 
+     1.3.3 re-migration of patches started 26.01.05
+       * keywords and export_mode tricks - export_mode now a property of request, no tricks here -done
+       * acl specials - done 
+       * IIS-patch - encapsulated in special request class, modify calls - done
+       * interwiki enhancements - needs code review, some code moved to themeing -open
+       * pragma enhancements - title done, var_* re-designed (request_ors.py,wiki.py) - done
+       * remotely created pages - needs redesign based on globaledit utilities! -open
+
+        03.02.05 RS all critical ORS modifications now check for request.ors_mod_active
+     
 """
 
-import StringIO, os, re, urllib, random, codecs
+import sys, os, re, urllib, random, codecs,StringIO 
 
 from MoinMoin import config, caching, user, util, wikiutil
 from MoinMoin.logfile import eventlog
 from MoinMoin.util import filesys, web
 
+_debug=0
+
 class Page:
     """Page - Manage an (immutable) page associated with a WikiName.
        To change a page's content, use the PageEditor class.
     """
 
     # Header regular expression, used to get header boundaries
     header_re = r'(^#+.*(?:\n\s*)+)+'
@@ -760,17 +784,21 @@
         @param querystr: the query string to add after a "?" after the url
             (str or dict, see util.web.makeQueryString)
         @param escpae: escape url for html, to be backward compatible
             with old code (bool)
         @rtype: str
         @return: complete url of this page, including scriptname
         """
-        url = '%s/%s' % (request.getScriptname(),
+#RS activate IIS-patch for getScriptname
+##        url = '%s/%s' % (request.getScriptname(),
+##                     wikiutil.quoteWikinameURL(self.page_name))
+        url = '%s/%s' % (request.getScriptname(patch=1),
                      wikiutil.quoteWikinameURL(self.page_name))
-
+#RS end
+        
         if querystr:
             querystr = web.makeQueryString(querystr)
 
             # TODO: remove in 1.4
             # Escape query string to be compatible with old 3rd party code
             # New code should call with escape=0 to prevent the warning.
             if escape:
@@ -898,14 +926,15 @@
         from MoinMoin import i18n
         request.clock.start('send_page')
         _ = request.getText
 
         # determine modes
         print_mode = request.form.has_key('action') and request.form['action'][0] == 'print'
         if print_mode:
+            #raise AttributeError,"exporter: %s" % keywords
             media = request.form.has_key('media') and request.form['media'][0] or 'print'
         else:
             media = 'screen'
         content_only = keywords.get('content_only', 0)
         content_id = keywords.get('content_id', 'content')
         do_cache = keywords.get('do_cache', 1)
         send_missing_page = keywords.get('send_missing_page', 0)
@@ -920,22 +949,26 @@
             eventlog.EventLog(request).add(request, 'VIEWPAGE', {'pagename': self.page_name})
 
         # load the text
         body = self.get_raw_body()
 
         # if necessary, load the default formatter
         if self.default_formatter:
+            if _debug:
+                sys.stderr.write("\nreset formatter to default!")
             from MoinMoin.formatter.text_html import Formatter
             self.formatter = Formatter(request, store_pagelinks=1)
         self.formatter.setPage(self)
         if self.hilite_re: self.formatter.set_highlight_re(self.hilite_re)
         request.formatter = self.formatter
 
         # default is wiki markup
         pi_format = self.cfg.default_markup or "wiki"
+        if _debug:
+            sys.stderr.write("\nMarkup: %s,frmat=%s" % (str(self.cfg.default_markup),pi_format))
         pi_formatargs = ''
         pi_redirect = None
         pi_refresh = None
         pi_formtext = []
         pi_formfields = []
         wikiform = None
 
@@ -960,21 +993,32 @@
             # skip comments (lines with two hash marks)
             if line[1] == '#': continue
 
             # parse the PI
             verb, args = (line[1:]+' ').split(' ', 1)
             verb = verb.lower()
             args = args.strip()
+#RS TOCHECK protect against remotely created pages (they may have an extra \r after #locked_hard
+            verb = verb.replace('\r','')
+#RS end
 
             # check the PIs
             if verb == "format":
                 # markup format
-                pi_format, pi_formatargs = (args+' ').split(' ',1)
-                pi_format = pi_format.lower()
-                pi_formatargs = pi_formatargs.strip()
+#RS avoid reset if format=='wiki' (maybe we're in 'wiki_word' mode?
+##                pi_format, pi_formatargs = (args+' ').split(' ',1)
+##                pi_format = pi_format.lower()
+##                pi_formatargs = pi_formatargs.strip()
+                m_pi_format, m_pi_formatargs = (args+' ').split(' ',1)
+                if m_pi_format!="wiki":
+                    pi_format=m_pi_format
+                    pi_format = pi_format.lower()
+                    pi_formatargs = m_pi_formatargs.strip()
+                    sys.stderr.write("\nFORMAT Markup: %s,frmat=%s" % (str(self.cfg.default_markup),pi_format))
+#RS end
             elif verb == "refresh":
                 if self.cfg.refresh:
                     try:
                         mindelay, targetallowed = self.cfg.refresh
                         args = args.split()
                         if len(args) >= 1:
                             delay = max(int(args[0]), mindelay)
@@ -995,19 +1039,26 @@
             elif verb == "redirect":
                 # redirect to another page
                 # note that by including "action=show", we prevent
                 # endless looping (see code in "request") or any
                 # cascaded redirection
                 pi_redirect = args
                 if request.form.has_key('action') or request.form.has_key('redirect') or content_only: continue
+#RS interwiki and anchor handling
+                (link,xanchor)=wikiutil.resolve_interwiki(request,self.page_name,pi_redirect)
 
-                request.http_redirect('%s/%s?action=show&redirect=%s' % (
-                    request.getScriptname(),
-                    wikiutil.quoteWikinameURL(pi_redirect),
-                    urllib.quote_plus(self.page_name.encode(config.charset), ''),))
+##                request.http_redirect('%s/%s?action=show&redirect=%s' % (
+##                    request.getScriptname(),
+##                    wikiutil.quoteWikinameURL(pi_redirect),
+##                    urllib.quote_plus(self.page_name.encode(config.charset), ''),))
+                request.http_redirect('%s?action=show&redirect=%s%s' % (
+                    link,
+#                    urllib.quote_plus(self.page_name.encode(config.charset), '')))
+                    urllib.quote_plus(self.page_name.encode(config.charset), ''),xanchor))
+#RS end
                 return
             elif verb == "deprecated":
                 # deprecated page, append last backup version to current contents
                 # (which should be a short reason why the page is deprecated)
                 msg = '%s<strong>%s</strong><br>%s' % (
                     self.formatter.smiley('/!\\'),
                     _('The backupped content of this page is deprecated and will not be included in search results!'),
@@ -1038,37 +1089,51 @@
                     pi_formtext.append('<table border="1" cellspacing="1" cellpadding="3">\n'
                         '<form method="POST" action="%s">\n'
                         '<input type="hidden" name="action" value="formtest">\n' % self.url(request))
                 pi_formtext.append(wikiform.parseDefinition(request, args, pi_formfields))
             elif verb == "acl":
                 # We could build it here, but there's no request.
                 pass
+#RS 05.02.04
+#old-style ORS locking to be interpreted like an ACL
+            elif verb in ("locked","locked_hard","locked_test"):
+                # We could build it here, but there's no request.
+                pass
+#RS end            
             elif verb == "language":
                 # Page language. Check if args is a known moin language
                 if args in i18n.wikiLanguages():
                     self.language = args
                     request.setContentLanguage(self.language)
             else:
                 # unknown PI ==> end PI parsing, and show invalid PI as text
                 body = line + '\n' + body
                 break
 
         # Save values for later use
         self.pi_format = pi_format
+        if _debug:
+            sys.stderr.write("\nMarkup: %s,.frmat=%s" % (str(self.cfg.default_markup),self.pi_format))
 
         # start document output
         doc_leader = self.formatter.startDocument(self.page_name)
         page_exists = self.exists()
         if not content_only:
             # send the document leader
+#RS disable caching
+            if request.ors_mod_active==1:
+                request.disableHttpCaching()
+
+#RS end           
             if page_exists:
                 request.http_headers()
             else:
                 request.http_headers(['Status: 404 NOTFOUND'])
                 request.setResponseCode(404)
+
             request.write(doc_leader)
 
             # send the page header
             if self.default_formatter:
                 full_text_query = 'linkto:"%s"' % self.page_name
                 link = '%s/%s?action=fullsearch&amp;value=%s&amp;context=180' % (
                     request.getScriptname(),
@@ -1071,15 +1136,43 @@
             if self.default_formatter:
                 full_text_query = 'linkto:"%s"' % self.page_name
                 link = '%s/%s?action=fullsearch&amp;value=%s&amp;context=180' % (
                     request.getScriptname(),
                     wikiutil.quoteWikinameURL(self.page_name),
                     urllib.quote_plus(full_text_query.encode(config.charset)))
 
+                def quote_whitespace(x):
+                    if x.find(" ")!=-1:
+                        return "'%s'" % x
+                    else:
+                        return x
+                page_needle = quote_whitespace(self.page_name)
+                if config.allow_subpages and page_needle.count('/'):
+                    #parts = page_needle.split('/')
+                    #for level in range(1, len(parts)):
+                    #    page_needle += (" !" + quote_whitespace(
+                    #        "/".join(parts[:level])) + " " +
+                    #                    quote_whitespace(
+                    #        "/" + "/".join(parts[level:])))   
+                    page_needle = '/' + page_needle.split('/')[-1]
+                    
+#RS activate IIS-patch for getScriptname
+##                link = '%s/%s?action=fullsearch&amp;value=%s&amp;literal=1&amp;case=1&amp;context=180' % (
+##                    request.getScriptname(),
+##                    wikiutil.quoteWikinameURL(self.page_name),
+##                    urllib.quote_plus(page_needle.encode(config.charset), ''))
+                link = '%s/%s?action=fullsearch&amp;value=%s&amp;literal=1&amp;case=1&amp;context=180' % (
+                    request.getScriptname(patch=1),
+                    wikiutil.quoteWikinameURL(self.page_name),
+                    urllib.quote_plus(page_needle.encode(config.charset), ''))
+#RS end
                 title = self.split_title(request)
+#RS overwrite with nice title from pragma
+                title= request.getPragma('title', title)
+#RS end
                 if self.rev:
                     msg = "<strong>%s</strong><br>%s" % (
                         _('Revision %(rev)d as of %(date)s') % {
                             'rev': self.rev,
                             'date': self.mtime_printable(request)
                         }, msg)
 
@@ -1118,23 +1211,44 @@
                     request.write(''.join(pi_formtext))
-
+#RS extensions for export mode
+            else:
+#nearly same as default formatter but used in export
+                if self.formatter.mimetype=="text/html":
+                    title = self.split_title(request)
+##                    wikiutil.send_title(request, title,  page=self, link=link, msg=msg,
+##                                        pagename=self.page_name, print_mode=print_mode,
+##                                        media=media, pi_refresh=pi_refresh,
+##                                        allow_doubleclick=1, trail=trail,
+##                                        )
+                    wikiutil.send_title(request, title,  page=self, link='', msg='',
+                                        pagename=self.page_name, print_mode=1,
+                                        media='print', pi_refresh=None,
+                                        allow_doubleclick=0,trail=None
+                                        )
+#RS end
+
+        if _debug:
+            sys.stderr.write("\nMarkup: %s,self.pi_format=%s" % (str(self.cfg.default_markup),self.pi_format))
+            sys.stderr.write("\nloading Parser for %s" % str(self.pi_format))
         # try to load the parser
         Parser = wikiutil.importPlugin(self.request.cfg, "parser",
                                        self.pi_format, "Parser")
         if Parser is None:
             # default to plain text formatter (i.e. show the page source)
             del Parser
             from parser.plain import Parser
 
+        if _debug:
+            sys.stderr.write("\nParser=%s" % str(Parser))
         # start wiki content div
         request.write(self.formatter.startContent(content_id))
 
         # new page?
         if not page_exists and (not content_only or (content_only
                                                      and send_missing_page)):
             if self.default_formatter and not content_only:
@@ -1157,23 +1271,36 @@
             if getattr(request, 'footnotes', None):
                 from MoinMoin.macro.FootNote import emit_footnotes
                 request.write(emit_footnotes(request, self.formatter))
 
         # end wiki content div
         request.write(self.formatter.endContent())
 
+#RS do this later for proper sequence!        
+#        doc_trailer = self.formatter.endDocument()
+#
         # end document output
-        doc_trailer = self.formatter.endDocument()
         if not content_only:
             # send the page footer
             if self.default_formatter:
                 wikiutil.send_footer(request, self.page_name, print_mode=print_mode)
-
-            request.write(doc_trailer)
-
+#RS extensions for export mode
+                doc_trailer = self.formatter.endDocument()
+                request.write(doc_trailer)
+            elif self.formatter.mimetype=="text/html":
+                wikiutil.send_footer(request, self.page_name, print_mode=print_mode)
+                doc_trailer = self.formatter.endDocument()
+                request.write(doc_trailer)
+            else:
+                doc_trailer = self.formatter.endDocument()
+                request.write(doc_trailer)
+                
+##            request.write(doc_trailer)
+#RS end
+        
         # cache the pagelinks
         if do_cache and self.default_formatter and page_exists:
             cache = caching.CacheEntry(request, self, 'pagelinks')
             if cache.needsUpdate(self._text_filename()):
                 links = self.formatter.pagelinks
                 cache.update('\n'.join(links) + '\n', True)
 
@@ -1223,14 +1350,15 @@
         Output the formatted wiki page, using caching, if possible.
 
         @param request: the request object
         @param Parser: the Parser
         @param body: text of the wiki page
         @param needsupdate: if 1, force update of the cached compiled page
         """
+#        sys.stderr.write("Parser=%s" % str(Parser))
         request.clock.start('send_page_content')
 
         formatter_name = self.getFormatterName()
 
         # if we should not or can not use caching
         if not (do_cache and self.canUseCache(Parser)):
             # parse the text and send the page content
@@ -1335,14 +1463,28 @@
 
     def _emptyPageText(self, request):
         """
         Output the default page content for new pages.
 
         @param request: the request object
         """
+##RS deny creation of illegal pages
+
+        if request.ors_mod_active==1:
+            import re
+            _ = request.getText
+            word_re=re.compile(ur"^(((?:[%(u)s][%(l)s]+){2,}/{0,1}))(?:[%(u)s][%(l)s]+){2,}$" % {'u':"A-Z", 'l':"0-9a-z"})
+
+            word_match = word_re.match(self.page_name)
+            if not word_match:
+                request.http_headers()
+                request.write('<p>' + _("This page name is illegal at ORS wikis and you can't create page") + ' "<tt>' + self.page_name + '</tt>".'+_('Use only CapitalizedWords and no special characters!'))
+                return
+#RS end
+        
         missingpage = wikiutil.getSysPage(request, 'MissingPage')
         missingpagefn = missingpage._text_filename()
         missingpage.page_name = self.page_name
         missingpage._text_filename_force = missingpagefn
         missingpage.send_page(request, content_only=1, send_missing_page=1)
 
  • note that I had to remove an (insignificant) line containing '}'}'} from the generated diff, so the positions may be wrong, but you should apply those patches manually, anyway.

MoinMoin.parser.wiki

--- E:\Install\MoinMoin\moin-1.3.5.tar\moin-1.3.5\MoinMoin\parser\wiki.py       Sat Jul 30 14:51:12 2005
+++ E:\moin13\Lib\site-packages\MoinMoin\parser\wiki.py Tue Sep 27 12:17:55 2005
@@ -1,13 +1,20 @@
 # -*- coding: iso-8859-1 -*-
 """
     MoinMoin - MoinMoin Wiki Markup Parser
 
     @copyright: 2000, 2001, 2002 by Jürgen Hermann <jh@web.de>
     @license: GNU GPL, see COPYING for details.
+    ORS modifications:
+        08.01.04 RS inline parser fix, makes inline extensions configurable
+        08.01.04 RS colorizer, makes colorizer extensions configurable
+        09.01.04 RS ignore character formatting inside DT
+        26.01.04 RS fix colorizer patch
+     1.3.3 re-migration of patches started 31.01.05
+       * page variables in interwiki
 """
 
 import os, re
 from MoinMoin import config, wikimacro, wikiutil
 from MoinMoin.Page import Page
 from MoinMoin.util import web
 
@@ -161,14 +169,20 @@
             # fancy link to subpage [wiki:/SubPage text]
             return self._word_repl(url, text)
         elif Page(self.request, url).exists():
             # fancy link to local page [wiki:LocalPage text]
             return self._word_repl(url, text)
 
         wikitag, wikiurl, wikitail, wikitag_bad = wikiutil.resolve_wiki(self.request, url)
+#RS evaluate some keywords that can be set by pragmas
+        for key in self.request.pagevars:
+           value=self.request.getPragma("var_%s" % key, "")
+           wikiurl=wikiurl.replace("$var_%s" % key, value)
+#RS        
+        wikiurl = wikiutil.mapURL(self.request,wikiurl)
         href = wikiutil.join_wiki(wikiurl, wikitail)
 
         # check for image URL, and possibly return IMG tag
         if not kw.get('pretty_url', 0) and wikiutil.isPicture(wikitail):
             return self.formatter.image(src=href)
 
         # link to self?
@@ -270,14 +284,15 @@
                         attrs='title="%s"' % (_('Edit drawing %(filename)s') % {'filename': self.formatter.text(fname)}))
             else:
                 return self.formatter.image(alt=url,
                     src=AttachFile.getAttachUrl(pagename, url, self.request, addts=1))
 
         # try to inline the attachment (parser know what they
         # can handle)
+        
         base, ext = os.path.splitext(url)
         if inline:
             Parser = wikiutil.getParserForExtension(self.cfg, ext)
             if Parser is not None:
                 content = file(fpath, 'r').read()
                 # Try to decode text. It might return junk, but we don't
                 # have enough information with attachments.
@@ -526,15 +541,18 @@
         """Handle definition lists."""
         result = []
         self._close_item(result)
         #self.inhibit_p = 1
         self.in_dd = 1
         result.extend([
             self.formatter.definition_term(1),
-            self.formatter.text(match[1:-3]),
+#RS ignore character formatting inside DT
+#            self.formatter.text(match[1:-3]),
+            self.formatter.text(match[1:-3].replace("'","")),
+#RS end
             self.formatter.definition_term(0),
             self.formatter.definition_desc(1),
             ## CHANGE: no automatic paragraph
             ##self.formatter.paragraph(1)
         ])
         return ''.join(result)
 

MoinMoin.formatter.base

--- E:\Install\MoinMoin\moin-1.3.5.tar\moin-1.3.5\MoinMoin\formatter\base.py    Tue Jul 26 20:46:52 2005
+++ E:\moin13\Lib\site-packages\MoinMoin\formatter\base.py      Tue Sep 06 23:52:30 2005
@@ -1,13 +1,15 @@
 # -*- coding: iso-8859-1 -*-
 """
     MoinMoin - Formatter Base Class
 
     @copyright: 2000 - 2004 by Jürgen Hermann <jh@web.de>
     @license: GNU GPL, see COPYING for details.
+    ORS modifications:
+        08.01.04 RS mimetype and pure-handler (used in special formatters for dump)
 """
 
 from MoinMoin import wikiutil
 import re, types
 
 class FormatterBase:
     """ This defines the output interface used all over the rest of the code.
@@ -18,15 +20,17 @@
     """
 
     hardspace = ' '
 
     def __init__(self, request, **kw):
         self.request = request
         self._ = request.getText
-
+#RS additional property "mimetype"
+        self.mimetype="text/base"
+#RS end
         self._store_pagelinks = kw.get('store_pagelinks', 0)
         self._terse = kw.get('terse', 0)
         self.pagelinks = []
         self.in_p = 0
         self.in_pre = 0
         self._highlight_re = None
         self._base_depth = 0
@@ -107,14 +111,27 @@
         return u'<img%s>' % attrstr
 
     def smiley(self, text):
         return text
 
     # Text and Text Attributes ########################################### 
     
+#RS additional handler for pure
+#may be obsolete as text is now properly used by parser??
+    def pure(self, text):
+        """
+        this handles the "not in any markup" case
+        used in formatters with "side effects"
+        """
+        return self._text(text)
+
+    def nbsp(self):
+        return self.hardspace
+
+#RS end
     def text(self, text):
         if not self._highlight_re:
             return self._text(text)
             
         result = []
         lastpos = 0
         match = self._highlight_re.search(text)

MoinMoin.formatter.text_html

--- E:\Install\MoinMoin\moin-1.3.5.tar\moin-1.3.5\MoinMoin\formatter\text_html.py       Tue Jul 26 20:46:52 2005
+++ E:\moin13\Lib\site-packages\MoinMoin\formatter\text_html.py Tue Sep 27 18:15:01 2005
@@ -1,13 +1,22 @@
 # -*- coding: iso-8859-1 -*-
 """
     MoinMoin - "text/html+css" Formatter
 
     @copyright: 2000 - 2004 by Jürgen Hermann <jh@web.de>
     @license: GNU GPL, see COPYING for details.
+    ORS modifications:
+        08.01.04 RS mimetype
+        08.01.04 RS fix for heading depth calculation (not yet active, check if still needed)
+        13.01.04 RS taken implementation of image from FormatterBase
+        05.02.04 RS allow 'class' attribute in table/tr/td tags
+        09.02.04 RS support for SVG viewer
+        24.02.04 RS in pagelink, store request to Page(pagename) so that export mode can be checked in Page.link_to()
+     1.3.3 re-migration of patches started 31.01.05
+        07.03.04 RS applied fix (patch 652++) for wiki:Self:../SubPage syntax
 """
 
 from MoinMoin.formatter.base import FormatterBase
 from MoinMoin import wikiutil, i18n, config
 from MoinMoin.Page import Page
 
 class Formatter(FormatterBase):
@@ -15,14 +24,17 @@
         Send HTML data.
     """
 
     hardspace = '&nbsp;'
 
     def __init__(self, request, **kw):
         apply(FormatterBase.__init__, (self, request), kw)
+#RS additional property "mimetype"
+        self.mimetype="text/html"
+#RS end
 
         # inline tags stack. When an inline tag is called, it goes into
         # the stack. When a block element starts, all inline tags in
         # the stack are closed.
         self._inlineStack = []
 
         self._in_li = 0
@@ -144,14 +156,22 @@
             # The code that calls us should keep correct calling order.
             if tag in self._inlineStack:
                 self._inlineStack.remove(tag)
             return '</%s>' % tag
 
 
     # Public methods ###################################################
+    def startDocument(self, pagename):
+        return ""
+
+    def endDocument(self):
+        if self.request.export_mode:
+            return "\n<!-- EOD -->\n</body>\n</html>\n"
+        return ""
+
 
     def startContent(self, content_id='content', **kwargs):
         """ Start page content div """
 
         # Setup id
         if content_id!='content':
             aid = 'top_%s' % (content_id,)
@@ -733,10 +753,33 @@
                 attrs = self._checkTableAttr(attrs, '')
             return self.open(tag, newline=1, attr=attrs)
         return self.close(tag)
 
     def escapedText(self, text):
         return wikiutil.escape(text)
 
+#RS taken from base
+    def image(self, **kw):
+        """ Take HTML <IMG> tag attributes in `attr`.
+
+            Attribute names have to be lowercase!
+        """
+        attrstr = u''
+        for attr, value in kw.items():
+            if attr=='html_class':
+                attr='class'
+            attrstr = attrstr + u' %s="%s"' % (attr, wikiutil.escape(value))
+
+#RS special coding for SVG images        
+        src=kw.get('src','')
+        if src.endswith('.svg') or src.endswith('.svgz'):
+            return u'<embed%s/>' % attrstr
+        else:    
+            return u'<img%s/>' % attrstr
+
+    def nbsp(self):
+        return '&nbsp;'
+
+#RS end
+
     def rawHTML(self, markup):
         return markup
-

Additions to the configuration

    #the physical path to your /wiki url, needed to export smiley images etc.
    url_prefix_dir = 'E:\\moin13\\share\\moin\\htdocs\\'

You also need to have a way to configure the target export directory - at the moment I have not bothered to integrate this int the config, but rather define it in the used action or command batch.

Additional files

MoinMoin.request_ors

I have derived from the core request classes to encapsulate some general patches and enhancements. Again, you do probably not want all of those.

  • attachment:request_ors.py

MoinMoin.parser.wiki_word

I had to create a modified wiki parser that is only used in the exporter context and only for the text_word and text_pdf formatters. Basically it parses also for unformatted text words and characters which are not needed by the normal parser-formatter combination.

  • attachment:wiki_word.py

The Exporter Files

MoinMoin.scripts.moin_export

This is based on moin_dump.py but has been completely refactored into a class and enhanced to fit my purpose.

  • attachment:moin_export.py

The new formatters

MoinMoin.formatter.text_word

The implementation is nearly finished for all the main markup features (however migration to 1.3.5 broke some tables etc.), see an export of page SyntaxReference: attachment:SyntaxReference.doc

You need to have Microsoft Word (2000) on your MoinMoin server and should have generated a python wrapper with makepy (oh yes, and you need Mark Hammonds win32 package (win32all build 163), of course). Due to some weird exceptions I have decided to serve the Word control with Visible=1, this means that you will watch Word "typing in" your target document!

  • attachment:text_word.py

MoinMoin.formatter.text_pdf

This is still in its early stages and will be published by Alexander Bormann in a few weeks. It needs the reportlab package.

  • attachment:text_pdf.py (not ready yet)

Usage Scenarios

Exporting from a command line batch

This can be used to batch export multiple pages matching PAGEPATTERN - be aware that Word may be slow, though ;-().

set GATEWAY_INTERFACE=CGI/1.1
set DUMPNAME=word
set PYTHONPATH=E:\moin13\Lib\site-packages 
set TARGETROOT=E:\moin13
set WIKIURL="http://localhost/moin13/"
set DUMPPATH=%TARGETROOT%\dump_%DUMPNAME%
set PAGEPATTERN=SyntaxReference

set PAGEPATTERN="%PAGEPATTERN%"

E:\python23\python.exe -c "from MoinMoin.scripts.moin_export import run; run()" --wiki=%WIKIURL% --format=word --pattern=%PAGEPATTERN% %DUMPPATH% 1>dump.log 2>dumperr.log
pause

Exporting from an Action

To use this, you may have to modify the server to use a different request from request_ors.py, in my case I modified the moin.cgi (or you could try to create such a request inside the action?):

from MoinMoin.request_ors import RequestORSCGI

request = RequestORSCGI()
#disable my more critical modifications
request.ors_mod_active=0

Here is a test action that can be applied to a single page to trigger the export of this page (in Word format, but for PDF it looks very similar). We will later provide an action with a proper user dialog (select format, output name ...maybe even return a link to the created file). Right now, the output directory is hardcoded and you must pick up the result file from the server.

Toggle line numbers
   1 # Imports
   2 import string, time,sys,os,copy
   3 from MoinMoin import user, webapi, wikiutil
   4 from MoinMoin.PageEditor import PageEditor
   5 from MoinMoin.scripts.moin_export import MoinExporter
   6 
   7 def execute(pagename, request):
   8     _ = request.getText
   9     page = PageEditor(request,pagename)
  10     exp_format="word"
  11 #    exp_format="html"
  12 #    exp_format="plain"
  13 #    exp_format="pdf"
  14 #must protect a function that is overrided by the exporter
  15     qfn_orig=wikiutil.quoteWikinameURL
  16     markup_orig=request.cfg.default_markup
  17     
  18     pname=pagename
  19 
  20 #open exporter
  21     exporter=MoinExporter()
  22 #assign a request (use a copy because we modify the request)
  23     exporter.request=copy.copy(request)
  24 #set target output directory
  25     exporter.setOutputDir(r"E:\moin13\dump_word")
  26 #redirect the request to action=print and indicate "export" mode
  27     exporter.request.user.show_topbottom=0
  28     exporter.request.form['action']='print'
  29     exporter.request.form['export']='1'
  30 #assign the export formatter
  31     exporter.setFormatter(exp_format)
  32 #use special parser for some export formats
  33     if exp_format in ("word","pdf"):    
  34         request.cfg.default_markup="wiki_word"
  35 #create target file name - this is the plain output of the formatter, the "real" target file
  36 #is different for word and pdf as they create it as a "side effect"
  37     file = wikiutil.quoteWikinameFS(pname) + exporter.ext
  38 #create error log file (right now we always use the same file for all - this may be bad)
  39     errfile = os.path.join(exporter.outputdir, 'error.log')
  40 
  41     exporter.errlog = open(errfile, 'w')
  42 #dump the page using the formatter
  43     output=exporter.dumpPage(pname,file)
  44 #close the error log
  45     exporter.errlog.close()
  46 
  47     
  48 #reset overrided function
  49     wikiutil.quoteWikinameURL = qfn_orig
  50     request.cfg.default_markup=markup_orig
  51 #use this to test output
  52 #    return page.send_page(request,        msg='<pre>%s</pre>' % output)
  53 
  54     return page.send_page(request,
  55         msg='<strong>%s</strong>' %
  56             _('Export of page %s completed.' % pagename))

MoinMoin: RobertSeeger/MoinExporter (last edited 2009-02-18 15:15:20 by DennisBenzinger)