changeset 1218:b3c2d87024c3

merge with main
author Franz Pletz <fpletz AT franz-pletz DOT org>
date Sat, 05 Aug 2006 20:24:25 +0200
parents 237ca54182a7 (current diff) 4d0f0ecc7880 (diff)
children d56eeab4e070
files MoinMoin/parser/ParserBase.py
diffstat 14 files changed, 393 insertions(+), 348 deletions(-) [+]
line wrap: on
line diff
--- a/MoinMoin/Page.py	Wed Aug 02 20:56:26 2006 +0200
+++ b/MoinMoin/Page.py	Sat Aug 05 20:24:25 2006 +0200
@@ -12,6 +12,10 @@
 from MoinMoin.logfile import eventlog
 from MoinMoin.util import filesys, timefuncs
 
+def is_cache_exception(e):
+    args = e.args
+    return not (len(args) != 1 or args[0] != 'CacheNeedsUpdate')
+
 class Page:
     """Page - Manage an (immutable) page associated with a WikiName.
        To change a page's content, use the PageEditor class.
@@ -1360,14 +1364,14 @@
             try:
                 code = self.loadCache(request)
                 self.execute(request, parser, code)
-            except Exception, (msg, ):
-                if msg != 'CacheNeedsUpdate':
+            except Exception, e:
+                if not is_cache_exception(e):
                     raise
                 try:
                     code = self.makeCache(request, parser)
                     self.execute(request, parser, code)
-                except Exception, (msg, ):
-                    if msg != 'CacheNeedsUpdate':
+                except Exception, e:
+                    if not is_cache_exception(e):
                         raise
                     request.log('page cache failed after creation')
                     self.format(parser)
--- a/MoinMoin/action/info.py	Wed Aug 02 20:56:26 2006 +0200
+++ b/MoinMoin/action/info.py	Sat Aug 05 20:24:25 2006 +0200
@@ -13,6 +13,7 @@
 from MoinMoin import config, wikiutil, action
 from MoinMoin.Page import Page
 from MoinMoin.logfile import editlog
+from MoinMoin.widget import html
 
 def execute(pagename, request):
     """ show misc. infos about a page """
@@ -88,38 +89,41 @@
 
         may_revert = request.user.may.revert(pagename)
 
+        def render_action(text, query, **kw):
+            kw.update(rel='nofollow')
+            if 0: # diff button doesnt work XXX
+                params_html = []
+                for k, v in query.items():
+                    params_html.append('<input type="hidden" name="%s" value="%s">' % (k, v))
+                params_html = ''.join(params_html)
+                html = '''
+<form>
+<input type="submit" value="%s">
+%s
+</form>
+''' % (text, params_html)
+            else:
+                html = page.link_to(request, text, querystr=query, **kw)
+            return html
+
         # read in the complete log of this page
         log = editlog.EditLog(request, rootpagename=pagename)
         count = 0
         for line in log.reverse():
             rev = int(line.rev)
-            actions = ""
+            actions = []
             if line.action in ['SAVE', 'SAVENEW', 'SAVE/REVERT', ]:
                 size = page.size(rev=rev)
                 if count == 0: # latest page
-                    actions = '%s&nbsp;%s' % (actions, page.link_to(request,
-                        text=_('view'),
-                        querystr=''))
-                    actions = '%s&nbsp;%s' % (actions, page.link_to(request,
-                        text=_('raw'),
-                        querystr='action=raw', rel='nofollow'))
-                    actions = '%s&nbsp;%s' % (actions, page.link_to(request,
-                        text=_('print'),
-                        querystr='action=print', rel='nofollow'))
+                    actions.append(render_action(_('view'), {'action': 'show'}))
+                    actions.append(render_action(_('raw'), {'action': 'raw'}))
+                    actions.append(render_action(_('print'), {'action': 'print'}))
                 else:
-                    actions = '%s&nbsp;%s' % (actions, page.link_to(request,
-                        text=_('view'),
-                        querystr='action=recall&rev=%d' % rev, rel='nofollow'))
-                    actions = '%s&nbsp;%s' % (actions, page.link_to(request,
-                        text=_('raw'),
-                        querystr='action=raw&rev=%d' % rev, rel='nofollow'))
-                    actions = '%s&nbsp;%s' % (actions, page.link_to(request,
-                        text=_('print'),
-                        querystr='action=print&rev=%d' % rev, rel='nofollow'))
+                    actions.append(render_action(_('view'), {'action': 'recall', 'rev': '%d' % rev}))
+                    actions.append(render_action(_('raw'), {'action': 'raw', 'rev': '%d' % rev}))
+                    actions.append(render_action(_('print'), {'action': 'print', 'rev': '%d' % rev}))
                     if may_revert and size: # you can only revert to nonempty revisions
-                        actions = '%s&nbsp;%s' % (actions, page.link_to(request,
-                            text=_('revert'),
-                            querystr='action=revert&rev=%d' % rev, rel='nofollow'))
+                        actions.append(render_action(_('revert'), {'action': 'revert', 'rev': '%d' % rev}))
                 if count == 0:
                     rchecked = ' checked="checked"'
                     lchecked = ''
@@ -149,20 +153,12 @@
                     except:
                         pass
                     if line.action == 'ATTNEW':
-                        actions = '%s&nbsp;%s' % (actions, page.link_to(request,
-                            text=_('view'),
-                            querystr='action=AttachFile&do=view&target=%s' % filename, rel='nofollow'))
+                        actions.append(render_action(_('view'), {'action': 'AttachFile', 'do': 'view', 'target': '%s' % filename}))
                     elif line.action == 'ATTDRW':
-                        actions = '%s&nbsp;%s' % (actions, page.link_to(request,
-                            text=_('edit'),
-                            querystr='action=AttachFile&drawing=%s' % filename.replace(".draw", ""), rel='nofollow'))
+                        actions.append(render_action(_('edit'), {'action': 'AttachFile', 'drawing': '%s' % filename.replace(".draw", "")}))
 
-                    actions = '%s&nbsp;%s' % (actions, page.link_to(request,
-                        text=_('get'),
-                        querystr='action=AttachFile&do=get&target=%s' % filename, rel='nofollow'))
-                    actions = '%s&nbsp;%s' % (actions, page.link_to(request,
-                        text=_('del'),
-                        querystr='action=AttachFile&do=del&target=%s' % filename, rel='nofollow'))
+                    actions.append(render_action(_('get'), {'action': 'AttachFile', 'do': 'get', 'target': '%s' % filename}))
+                    actions.append(render_action(_('del'), {'action': 'AttachFile', 'do': 'del', 'target': '%s' % filename}))
                     # XXX use?: wikiutil.escape(filename)
 
             history.addRow((
@@ -172,7 +168,7 @@
                 diff,
                 line.getEditor(request) or _("N/A"),
                 wikiutil.escape(comment) or '&nbsp;',
-                actions,
+                "&nbsp;".join(actions),
             ))
             count += 1
             if count >= 100:
@@ -181,24 +177,22 @@
         # print version history
         from MoinMoin.widget.browser import DataBrowserWidget
 
-        request.write('<h2>%s</h2>\n' % _('Revision History'))
+        request.write(unicode(html.H2().append(_('Revision History'))))
 
         if not count: # there was no entry in logfile
             request.write(_('No log entries found.'))
             return
 
-        # TODO: this form activates revert, which should use POST, but
-        # other actions should use get. Maybe we should put the revert
-        # into the page view itself, and not in this form.
-        request.write('<form method="GET" action="">\n')
-        request.write('<div id="page-history">\n')
-        request.write('<input type="hidden" name="action" value="diff">\n')
-
         history_table = DataBrowserWidget(request)
         history_table.setData(history)
-        history_table.render()
-        request.write('</div>\n')
-        request.write('</form>\n')
+
+        div = html.DIV(id="page-history")
+        div.append(html.INPUT(type="hidden", name="action", value="diff"))
+        div.append(history_table.toHTML())
+
+        form = html.FORM(method="GET", action="")
+        form.append(div)
+        request.write(unicode(form))
 
     # main function
     _ = request.getText
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MoinMoin/action/refresh.py	Sat Aug 05 20:24:25 2006 +0200
@@ -0,0 +1,24 @@
+# -*- coding: iso-8859-1 -*-
+"""
+    MoinMoin - refresh cache of a page
+
+    @copyright: 2000-2004 by Jürgen Hermann <jh@web.de>,
+                2006 by MoinMoin:ThomasWaldmann
+    @license: GNU GPL, see COPYING for details.
+"""
+from MoinMoin.Page import Page
+
+def execute(pagename, request):
+    """ Handle refresh action """
+    # Without arguments, refresh action will refresh the page text_html cache.
+    arena = request.form.get('arena', ['Page.py'])[0]
+    if arena == 'Page.py':
+        arena = Page(request, pagename)
+    key = request.form.get('key', ['text_html'])[0]
+
+    # Remove cache entry (if exists), and send the page
+    from MoinMoin import caching
+    caching.CacheEntry(request, arena, key, scope='item').remove()
+    caching.CacheEntry(request, arena, "pagelinks", scope='item').remove()
+    request.page.send_page(request)
+
--- a/MoinMoin/parser/ParserBase.py	Wed Aug 02 20:56:26 2006 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,270 +0,0 @@
-# -*- coding: iso-8859-1 -*-
-"""
-	MoinMoin - Base Source Parser
-
-    @copyright: 2002 by Taesu Pyo <bigflood@hitel.net>
-    @license: GNU GPL, see COPYING for details.
-
-    Docstrings and some refactoring by Oliver Graf <ograf@bitart.de>
-
-basic css:
-
-pre.codearea     { font-style: sans-serif; color: #000000; }
-
-pre.codearea span.ID       { color: #000000; }
-pre.codearea span.Char     { color: #004080; }
-pre.codearea span.Comment  { color: #808080; }
-pre.codearea span.Number   { color: #008080; font-weight: bold; }
-pre.codearea span.String   { color: #004080; }
-pre.codearea span.SPChar   { color: #0000C0; }
-pre.codearea span.ResWord  { color: #4040ff; font-weight: bold; }
-pre.codearea span.ConsWord { color: #008080; font-weight: bold; }
-
-"""
-
-import re, sys, sha
-from MoinMoin import config, wikiutil
-
-def parse_start_step(request, args):
-    """
-    Parses common Colorizer parameters start, step, numbers.
-    Uses L{wikiutil.parseAttributes} and sanitizes the results.
-
-    Start and step must be a non negative number and default to 1,
-    numbers might be on, off, or none and defaults to on. On or off
-    means that numbers are switchable via JavaScript (html formatter),
-    disabled means that numbers are disabled completely.
-
-    attrdict is returned as last element in the tuple, to enable the
-    calling parser to extract further arguments.
-
-    @param request: a request instance
-    @param args: the argument string
-
-    @returns: numbers, start, step, attrdict
-    """
-    nums, start, step = 1, 1, 1
-    attrs, msg = wikiutil.parseAttributes(request, args)
-    if not msg:
-        try:
-            start = int(attrs.get('start','"1"')[1:-1])
-        except ValueError:
-            pass
-        try:
-            step = int(attrs.get('step','"1"')[1:-1])
-        except ValueError:
-            pass
-        if attrs.get('numbers','"on"')[1:-1].lower() in ('off', 'false', 'no'):
-            nums = 0
-        elif attrs.get('numbers','"on"')[1:-1].lower() in ('none', 'disable'):
-            nums = -1
-    return nums, start, step, attrs
-
-class FormatTextBase:
-    pass
-
-class FormatText(FormatTextBase):
-    
-    def __init__(self, fmt):
-        self.fmt = fmt
-
-    def formatString(self, formatter, word):
-        return (formatter.code_token(1, self.fmt) +
-                formatter.text(word) +
-                formatter.code_token(0, self.fmt))
-
-class FormatTextID(FormatTextBase):
-    
-    def __init__(self, fmt, icase=0):
-        if not isinstance(fmt, FormatText):
-            self.def_fmt = FormatText(fmt)
-        else:
-            self.def_fmt = fmt
-        self._ignore_case = icase
-        self.fmt = {}
-
-    def addFormat(self, word, fmt):
-        if self._ignore_case:
-            word = word.lower()
-        self.fmt[word] = fmt
-        
-    def setDefaultFormat(self, fmt):
-        self.def_fmt = fmt
-        
-    def formatString(self, formatter, word):
-        if self._ignore_case:
-            sword = word.lower()
-        else:
-            sword = word
-        return self.fmt.get(sword,self.def_fmt).formatString(formatter, word)
-
-class FormattingRuleSingle:
-    
-    def __init__(self, name, str_re, icase=0):
-        self.name = name
-        self.str_re = str_re
-        
-    def getStartRe(self):
-        return self.str_re
-    
-    def getText(self, parser, hit):
-        return hit
-
-class FormattingRulePair:
-    
-    def __init__(self, name, str_begin, str_end, icase=0):
-        self.name = name
-        self.str_begin = str_begin
-        self.str_end = str_end
-        if icase:
-            self.end_re = re.compile(str_end, re.M|re.I)
-        else:
-            self.end_re = re.compile(str_end, re.M)
-        
-    def getStartRe(self):
-        return self.str_begin
-    
-    def getText(self, parser, hit):
-        match = self.end_re.search(parser.line, parser.lastpos)
-        if not match:
-            next_lastpos = len(parser.line)
-        else:
-            next_lastpos = match.end() + (match.end() == parser.lastpos)
-        r = parser.line[parser.lastpos:next_lastpos]
-        parser.lastpos = next_lastpos
-        return hit + r
-
-
-# ------------------------------------------------------------------------
-
-class ParserBase:
-
-    parsername = 'ParserBase'
-    
-    def __init__(self, raw, request, **kw):
-        self.raw = raw
-        self.request = request
-        self.show_nums, self.num_start, self.num_step, attrs = parse_start_step(request, kw.get('format_args',''))
-
-        self._ignore_case = 0
-        self._formatting_rules = []
-        self._formatting_rules_n2r = {}
-        self._formatting_rule_index = 0
-        self.rule_fmt = {}
-        self.line_count = len(raw.split('\n'))+1
-
-    def setupRules(self):
-        self.def_format = FormatText('Default')
-        self.ID_format = FormatTextID('ID', self._ignore_case)
-        self.addRuleFormat("ID",self.ID_format)
-        self.addRuleFormat("Operator")
-        self.addRuleFormat("Char")
-        self.addRuleFormat("Comment")
-        self.addRuleFormat("Number")
-        self.addRuleFormat("String")
-        self.addRuleFormat("SPChar")
-        self.addRuleFormat("ResWord")
-        self.addRuleFormat("ResWord2")
-        self.addRuleFormat("ConsWord")
-        self.addRuleFormat("Special")
-        self.addRuleFormat("Preprc")
-        self.addRuleFormat("Error")
-        self.reserved_word_format = FormatText('ResWord')
-        self.constant_word_format = FormatText('ConsWord')
-
-    def addRule(self, name, str_re):
-        self._formatting_rule_index += 1
-        n = "%s_%s" % (name, self._formatting_rule_index)
-        f = FormattingRuleSingle(name, str_re, self._ignore_case)
-        self._formatting_rules.append((n,f))
-        self._formatting_rules_n2r[n] = f
-
-    def addRulePair(self, name, start_re, end_re):
-        self._formatting_rule_index += 1
-        n = "%s_%s" % (name,self._formatting_rule_index)
-        f = FormattingRulePair(name, start_re, end_re, self._ignore_case)
-        self._formatting_rules.append((n,f))
-        self._formatting_rules_n2r[n] = f
-
-    def addWords(self, words, fmt):
-        if not isinstance(fmt,FormatTextBase):
-            fmt = FormatText(fmt)
-        for w in words:
-            self.ID_format.addFormat(w, fmt)
-
-    def addReserved(self, words):
-        self.addWords(words, self.reserved_word_format)
-
-    def addConstant(self, words):
-        self.addWords(words, self.constant_word_format)
-        
-    def addRuleFormat(self, name, fmt=None):
-        if fmt is None:
-            fmt = FormatText(name)
-        self.rule_fmt[name] = fmt
-
-    def format(self, formatter, form = None):
-        """ Send the text.
-        """
-
-        self.setupRules()
-
-        l = []
-        for n,f in self._formatting_rules:
-            l.append("(?P<%s>%s)" % (n,f.getStartRe()))
-        
-        if self._ignore_case:
-            scan_re = re.compile("|".join(l),re.M|re.I)
-        else:
-            scan_re = re.compile("|".join(l),re.M)
-
-        self.lastpos = 0
-        self.line = self.raw
-
-        self._code_id = sha.new(self.raw.encode(config.charset)).hexdigest()
-        self.request.write(formatter.code_area(1, self._code_id, self.parsername, self.show_nums, self.num_start, self.num_step))
-
-        self.request.write(formatter.code_line(1))
-            #formatter, len('%d' % (self.line_count,)))
-        
-        match = scan_re.search(self.line)
-
-        while match and self.lastpos < len(self.line):
-            # add the match we found
-            self.write_normal_text(formatter,
-                                   self.line[self.lastpos:match.start()])
-            self.lastpos = match.end() + (match.end() == self.lastpos)
-
-            self.write_match(formatter, match)
-
-            # search for the next one
-            match = scan_re.search(self.line, self.lastpos)
-
-        self.write_normal_text(formatter, self.line[self.lastpos:])
-
-        self.request.write(formatter.code_area(0, self._code_id))
-
-
-    def write_normal_text(self, formatter, text):
-        first = 1
-        for line in text.expandtabs(4).split('\n'):
-            if not first:
-                self.request.write(formatter.code_line(1))
-            else:
-                first = 0
-            self.request.write(formatter.text(line))
-
-    def write_match(self, formatter, match):
-        for n, hit in match.groupdict().items():
-            if not hit: continue
-            r = self._formatting_rules_n2r[n]
-            s = r.getText(self, hit)
-            c = self.rule_fmt.get(r.name,None)
-            if not c: c = self.def_format
-            first = 1
-            for line in s.expandtabs(4).split('\n'):
-                if not first:
-                    self.request.write(formatter.code_line(1))
-                else:
-                    first = 0
-                self.request.write(c.formatString(formatter, line))
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MoinMoin/parser/_ParserBase.py	Sat Aug 05 20:24:25 2006 +0200
@@ -0,0 +1,270 @@
+# -*- coding: iso-8859-1 -*-
+"""
+	MoinMoin - Base Source Parser
+
+    @copyright: 2002 by Taesu Pyo <bigflood@hitel.net>
+    @license: GNU GPL, see COPYING for details.
+
+    Docstrings and some refactoring by Oliver Graf <ograf@bitart.de>
+
+basic css:
+
+pre.codearea     { font-style: sans-serif; color: #000000; }
+
+pre.codearea span.ID       { color: #000000; }
+pre.codearea span.Char     { color: #004080; }
+pre.codearea span.Comment  { color: #808080; }
+pre.codearea span.Number   { color: #008080; font-weight: bold; }
+pre.codearea span.String   { color: #004080; }
+pre.codearea span.SPChar   { color: #0000C0; }
+pre.codearea span.ResWord  { color: #4040ff; font-weight: bold; }
+pre.codearea span.ConsWord { color: #008080; font-weight: bold; }
+
+"""
+
+import re, sys, sha
+from MoinMoin import config, wikiutil
+
+def parse_start_step(request, args):
+    """
+    Parses common Colorizer parameters start, step, numbers.
+    Uses L{wikiutil.parseAttributes} and sanitizes the results.
+
+    Start and step must be a non negative number and default to 1,
+    numbers might be on, off, or none and defaults to on. On or off
+    means that numbers are switchable via JavaScript (html formatter),
+    disabled means that numbers are disabled completely.
+
+    attrdict is returned as last element in the tuple, to enable the
+    calling parser to extract further arguments.
+
+    @param request: a request instance
+    @param args: the argument string
+
+    @returns: numbers, start, step, attrdict
+    """
+    nums, start, step = 1, 1, 1
+    attrs, msg = wikiutil.parseAttributes(request, args)
+    if not msg:
+        try:
+            start = int(attrs.get('start','"1"')[1:-1])
+        except ValueError:
+            pass
+        try:
+            step = int(attrs.get('step','"1"')[1:-1])
+        except ValueError:
+            pass
+        if attrs.get('numbers','"on"')[1:-1].lower() in ('off', 'false', 'no'):
+            nums = 0
+        elif attrs.get('numbers','"on"')[1:-1].lower() in ('none', 'disable'):
+            nums = -1
+    return nums, start, step, attrs
+
+class FormatTextBase:
+    pass
+
+class FormatText(FormatTextBase):
+    
+    def __init__(self, fmt):
+        self.fmt = fmt
+
+    def formatString(self, formatter, word):
+        return (formatter.code_token(1, self.fmt) +
+                formatter.text(word) +
+                formatter.code_token(0, self.fmt))
+
+class FormatTextID(FormatTextBase):
+    
+    def __init__(self, fmt, icase=0):
+        if not isinstance(fmt, FormatText):
+            self.def_fmt = FormatText(fmt)
+        else:
+            self.def_fmt = fmt
+        self._ignore_case = icase
+        self.fmt = {}
+
+    def addFormat(self, word, fmt):
+        if self._ignore_case:
+            word = word.lower()
+        self.fmt[word] = fmt
+        
+    def setDefaultFormat(self, fmt):
+        self.def_fmt = fmt
+        
+    def formatString(self, formatter, word):
+        if self._ignore_case:
+            sword = word.lower()
+        else:
+            sword = word
+        return self.fmt.get(sword,self.def_fmt).formatString(formatter, word)
+
+class FormattingRuleSingle:
+    
+    def __init__(self, name, str_re, icase=0):
+        self.name = name
+        self.str_re = str_re
+        
+    def getStartRe(self):
+        return self.str_re
+    
+    def getText(self, parser, hit):
+        return hit
+
+class FormattingRulePair:
+    
+    def __init__(self, name, str_begin, str_end, icase=0):
+        self.name = name
+        self.str_begin = str_begin
+        self.str_end = str_end
+        if icase:
+            self.end_re = re.compile(str_end, re.M|re.I)
+        else:
+            self.end_re = re.compile(str_end, re.M)
+        
+    def getStartRe(self):
+        return self.str_begin
+    
+    def getText(self, parser, hit):
+        match = self.end_re.search(parser.line, parser.lastpos)
+        if not match:
+            next_lastpos = len(parser.line)
+        else:
+            next_lastpos = match.end() + (match.end() == parser.lastpos)
+        r = parser.line[parser.lastpos:next_lastpos]
+        parser.lastpos = next_lastpos
+        return hit + r
+
+
+# ------------------------------------------------------------------------
+
+class ParserBase:
+
+    parsername = 'ParserBase'
+    
+    def __init__(self, raw, request, **kw):
+        self.raw = raw
+        self.request = request
+        self.show_nums, self.num_start, self.num_step, attrs = parse_start_step(request, kw.get('format_args',''))
+
+        self._ignore_case = 0
+        self._formatting_rules = []
+        self._formatting_rules_n2r = {}
+        self._formatting_rule_index = 0
+        self.rule_fmt = {}
+        self.line_count = len(raw.split('\n'))+1
+
+    def setupRules(self):
+        self.def_format = FormatText('Default')
+        self.ID_format = FormatTextID('ID', self._ignore_case)
+        self.addRuleFormat("ID",self.ID_format)
+        self.addRuleFormat("Operator")
+        self.addRuleFormat("Char")
+        self.addRuleFormat("Comment")
+        self.addRuleFormat("Number")
+        self.addRuleFormat("String")
+        self.addRuleFormat("SPChar")
+        self.addRuleFormat("ResWord")
+        self.addRuleFormat("ResWord2")
+        self.addRuleFormat("ConsWord")
+        self.addRuleFormat("Special")
+        self.addRuleFormat("Preprc")
+        self.addRuleFormat("Error")
+        self.reserved_word_format = FormatText('ResWord')
+        self.constant_word_format = FormatText('ConsWord')
+
+    def addRule(self, name, str_re):
+        self._formatting_rule_index += 1
+        n = "%s_%s" % (name, self._formatting_rule_index)
+        f = FormattingRuleSingle(name, str_re, self._ignore_case)
+        self._formatting_rules.append((n,f))
+        self._formatting_rules_n2r[n] = f
+
+    def addRulePair(self, name, start_re, end_re):
+        self._formatting_rule_index += 1
+        n = "%s_%s" % (name,self._formatting_rule_index)
+        f = FormattingRulePair(name, start_re, end_re, self._ignore_case)
+        self._formatting_rules.append((n,f))
+        self._formatting_rules_n2r[n] = f
+
+    def addWords(self, words, fmt):
+        if not isinstance(fmt,FormatTextBase):
+            fmt = FormatText(fmt)
+        for w in words:
+            self.ID_format.addFormat(w, fmt)
+
+    def addReserved(self, words):
+        self.addWords(words, self.reserved_word_format)
+
+    def addConstant(self, words):
+        self.addWords(words, self.constant_word_format)
+        
+    def addRuleFormat(self, name, fmt=None):
+        if fmt is None:
+            fmt = FormatText(name)
+        self.rule_fmt[name] = fmt
+
+    def format(self, formatter, form = None):
+        """ Send the text.
+        """
+
+        self.setupRules()
+
+        l = []
+        for n,f in self._formatting_rules:
+            l.append("(?P<%s>%s)" % (n,f.getStartRe()))
+        
+        if self._ignore_case:
+            scan_re = re.compile("|".join(l),re.M|re.I)
+        else:
+            scan_re = re.compile("|".join(l),re.M)
+
+        self.lastpos = 0
+        self.line = self.raw
+
+        self._code_id = sha.new(self.raw.encode(config.charset)).hexdigest()
+        self.request.write(formatter.code_area(1, self._code_id, self.parsername, self.show_nums, self.num_start, self.num_step))
+
+        self.request.write(formatter.code_line(1))
+            #formatter, len('%d' % (self.line_count,)))
+        
+        match = scan_re.search(self.line)
+
+        while match and self.lastpos < len(self.line):
+            # add the match we found
+            self.write_normal_text(formatter,
+                                   self.line[self.lastpos:match.start()])
+            self.lastpos = match.end() + (match.end() == self.lastpos)
+
+            self.write_match(formatter, match)
+
+            # search for the next one
+            match = scan_re.search(self.line, self.lastpos)
+
+        self.write_normal_text(formatter, self.line[self.lastpos:])
+
+        self.request.write(formatter.code_area(0, self._code_id))
+
+
+    def write_normal_text(self, formatter, text):
+        first = 1
+        for line in text.expandtabs(4).split('\n'):
+            if not first:
+                self.request.write(formatter.code_line(1))
+            else:
+                first = 0
+            self.request.write(formatter.text(line))
+
+    def write_match(self, formatter, match):
+        for n, hit in match.groupdict().items():
+            if not hit: continue
+            r = self._formatting_rules_n2r[n]
+            s = r.getText(self, hit)
+            c = self.rule_fmt.get(r.name,None)
+            if not c: c = self.def_format
+            first = 1
+            for line in s.expandtabs(4).split('\n'):
+                if not first:
+                    self.request.write(formatter.code_line(1))
+                else:
+                    first = 0
+                self.request.write(c.formatString(formatter, line))
--- a/MoinMoin/parser/text_cplusplus.py	Wed Aug 02 20:56:26 2006 +0200
+++ b/MoinMoin/parser/text_cplusplus.py	Sat Aug 05 20:24:25 2006 +0200
@@ -23,7 +23,7 @@
 
 """
 
-from MoinMoin.parser.ParserBase import ParserBase
+from MoinMoin.parser._ParserBase import ParserBase
 
 Dependencies = []
 
--- a/MoinMoin/parser/text_diff.py	Wed Aug 02 20:56:26 2006 +0200
+++ b/MoinMoin/parser/text_diff.py	Sat Aug 05 20:24:25 2006 +0200
@@ -7,7 +7,7 @@
     @license: GNU GPL, see COPYING for details.
 """
 
-from MoinMoin.parser.ParserBase import ParserBase
+from MoinMoin.parser._ParserBase import ParserBase
 
 class Parser(ParserBase):
     parsername = "ColorizedDiff"
--- a/MoinMoin/parser/text_java.py	Wed Aug 02 20:56:26 2006 +0200
+++ b/MoinMoin/parser/text_java.py	Sat Aug 05 20:24:25 2006 +0200
@@ -7,7 +7,7 @@
 
 """
 
-from MoinMoin.parser.ParserBase import ParserBase
+from MoinMoin.parser._ParserBase import ParserBase
 
 Dependencies = []
 
--- a/MoinMoin/parser/text_pascal.py	Wed Aug 02 20:56:26 2006 +0200
+++ b/MoinMoin/parser/text_pascal.py	Sat Aug 05 20:24:25 2006 +0200
@@ -6,7 +6,7 @@
     @license: GNU GPL, see COPYING for details.
 """
 
-from MoinMoin.parser.ParserBase import ParserBase
+from MoinMoin.parser._ParserBase import ParserBase
 
 Dependencies = []
 
--- a/MoinMoin/parser/text_python.py	Wed Aug 02 20:56:26 2006 +0200
+++ b/MoinMoin/parser/text_python.py	Sat Aug 05 20:24:25 2006 +0200
@@ -9,7 +9,7 @@
 import StringIO
 import keyword, token, tokenize, sha
 from MoinMoin import config, wikiutil
-from MoinMoin.parser.ParserBase import parse_start_step
+from MoinMoin.parser._ParserBase import parse_start_step
 
 _KEYWORD = token.NT_OFFSET + 1
 _TEXT    = token.NT_OFFSET + 2
--- a/MoinMoin/request/STANDALONE.py	Wed Aug 02 20:56:26 2006 +0200
+++ b/MoinMoin/request/STANDALONE.py	Sat Aug 05 20:24:25 2006 +0200
@@ -33,10 +33,8 @@
             self.http_user_agent = sa.headers.getheader('user-agent', '')
             co = filter(None, sa.headers.getheaders('cookie'))
             self.saved_cookie = ', '.join(co) or ''
-            self.if_modified_since = (sa.headers.getheader('if-modified-since')
-                                      or self.if_modified_since)
-            self.if_none_match = (sa.headers.getheader('if-none-match')
-                                  or self.if_none_match)
+            self.if_modified_since = sa.headers.getheader('if-modified-since')
+            self.if_none_match = sa.headers.getheader('if-none-match')
 
             # Copy rest from standalone request   
             self.server_name = sa.server.server_name
--- a/MoinMoin/widget/html.py	Wed Aug 02 20:56:26 2006 +0200
+++ b/MoinMoin/widget/html.py	Sat Aug 05 20:24:25 2006 +0200
@@ -272,6 +272,7 @@
 class DIV(CompositeElement):
     "generic language/style container"
     _ATTRS = {
+        'id': None,
         'class': None,
     }
 
--- a/MoinMoin/wikiutil.py	Wed Aug 02 20:56:26 2006 +0200
+++ b/MoinMoin/wikiutil.py	Sat Aug 05 20:24:25 2006 +0200
@@ -503,37 +503,59 @@
 #############################################################################
 ### InterWiki
 #############################################################################
+INTERWIKI_PAGE = "InterWikiMap"
+
+def generate_file_list(request):
+    """ generates a list of all files. for internal use. """
+
+    # order is important here, the local intermap file takes
+    # precedence over the shared one, and is thus read AFTER
+    # the shared one
+    intermap_files = request.cfg.shared_intermap
+    if not isinstance(intermap_files, list):
+        intermap_files = [intermap_files]
+    else:
+        intermap_files = intermap_files[:]
+    intermap_files.append(os.path.join(request.cfg.data_dir, "intermap.txt"))
+    request.cfg.shared_intermap_files = [filename for filename in intermap_files
+                                         if filename and os.path.isfile(filename)]
+
+
+def get_max_mtime(file_list, page):
+    """ Returns the highest modification time of the files in file_list and the
+    page page. """
+    return max([os.stat(filename).st_mtime for filename in file_list] +
+        [version2timestamp(page.mtime_usecs())])
+
+
 def load_wikimap(request):
     """ load interwiki map (once, and only on demand) """
+    from MoinMoin.Page import Page
 
     now = int(time.time())
+    if getattr(request.cfg, "shared_intermap_files", None) is None:
+        generate_file_list(request)
 
     try:
         _interwiki_list = request.cfg._interwiki_list
-        if request.cfg._interwiki_ts + (3*60) < now: # 3 minutes caching time
-            raise AttributeError # refresh cache
+        old_mtime = request.cfg._interwiki_mtime
+        if request.cfg._interwiki_ts + (1*60) < now: # 1 minutes caching time
+            max_mtime = get_max_mtime(request.cfg.shared_intermap_files, Page(request, INTERWIKI_PAGE))
+            if max_mtime > old_mtime:
+                raise AttributeError # refresh cache
+            else:
+                request.cfg._interwiki_ts = now
     except AttributeError:
-        from MoinMoin.Page import Page
-
         _interwiki_list = {}
         lines = []
 
-        # order is important here, the local intermap file takes
-        # precedence over the shared one, and is thus read AFTER
-        # the shared one
-        intermap_files = request.cfg.shared_intermap
-        if not isinstance(intermap_files, list):
-            intermap_files = [intermap_files]
-        intermap_files.append(os.path.join(request.cfg.data_dir, "intermap.txt"))
-
-        for filename in intermap_files:
-            if filename and os.path.isfile(filename):
-                f = open(filename, "r")
-                lines.extend(f.readlines())
-                f.close()
+        for filename in request.cfg.shared_intermap_files:
+            f = open(filename, "r")
+            lines.extend(f.readlines())
+            f.close()
 
         # add the contents of the InterWikiMap page
-        lines += Page(request, "InterWikiMap").get_raw_body().splitlines()
+        lines += Page(request, INTERWIKI_PAGE).get_raw_body().splitlines()
 
         for line in lines:
             if not line or line[0] == '#': continue
@@ -555,6 +577,7 @@
         # save for later
         request.cfg._interwiki_list = _interwiki_list
         request.cfg._interwiki_ts = now
+        request.cfg._interwiki_mtime = get_max_mtime(request.cfg.shared_intermap_files, Page(request, INTERWIKI_PAGE))
 
     return _interwiki_list
 
--- a/docs/CHANGES.aschremmer	Wed Aug 02 20:56:26 2006 +0200
+++ b/docs/CHANGES.aschremmer	Sat Aug 05 20:24:25 2006 +0200
@@ -27,7 +27,7 @@
     * XMLRPC method to get the pagelist in a special way (revnos,
       no system pages etc.)
     * IWID support - i.e. every instance has a unique ID
-    * InterWiki page editable in the wiki
+    * InterWiki page editable in the wiki, modification detection based on mtimes
 
   Bugfixes (only stuff that is buggy in moin/1.6 main branch):
     * Conflict resolution fixes. (merged into main)
@@ -82,6 +82,7 @@
 Week 31: Load the IWID and the meta dict lazily. Reworked RemotePage/SyncPage,
          fixed option handling again, refined semantics of options, introduced
          direction option, replaced "localMatch"/"remoteMatch" by "pageMatch".
+         Store mtime for InterWiki list updates and detect changes based on it.
 
 2006-07-18: the requested daily entry is missing here, see http://moinmoin.wikiwikiweb.de/GoogleSoc2006/BetterProgress
 2006-07-19: the requested daily entry is missing here, see http://moinmoin.wikiwikiweb.de/GoogleSoc2006/BetterProgress