changeset 635:9e17ec23650c

moved wikimacro.py to macro/__init__.py
author Thomas Waldmann <tw AT waldmann-edv DOT de>
date Fri, 12 May 2006 19:53:53 +0200
parents 31797904ee5a
children b77ab6ea0c18
files MoinMoin/Page.py MoinMoin/_tests/test_macro.py MoinMoin/_tests/test_wikimacro.py MoinMoin/action/fckdialog.py MoinMoin/macro/SystemInfo.py MoinMoin/macro/__init__.py MoinMoin/parser/wiki.py MoinMoin/wikimacro.py docs/CHANGES
diffstat 9 files changed, 679 insertions(+), 665 deletions(-) [+]
line wrap: on
line diff
--- a/MoinMoin/Page.py	Fri May 12 17:35:50 2006 +0200
+++ b/MoinMoin/Page.py	Fri May 12 19:53:53 2006 +0200
@@ -1356,8 +1356,8 @@
     def execute(self, request, parser, code):
         """ Write page content by executing cache code """            
         formatter = self.formatter
-        from MoinMoin import wikimacro        
-        macro_obj = wikimacro.Macro(parser)        
+        from MoinMoin.macro import Macro
+        macro_obj = Macro(parser)        
         # Fix __file__ when running from a zip package
         import MoinMoin
         if hasattr(MoinMoin, '__loader__'):
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MoinMoin/_tests/test_macro.py	Fri May 12 19:53:53 2006 +0200
@@ -0,0 +1,33 @@
+# -*- coding: iso-8859-1 -*-
+"""
+    MoinMoin - MoinMoin.macro Tests
+
+    @copyright: 2003-2004 by Jürgen Hermann <jh@web.de>,
+                2006 MoinMoin:ThomasWaldmann
+    @license: GNU GPL, see COPYING for details.
+"""
+
+import unittest, os
+
+from MoinMoin import macro, wikiutil
+from MoinMoin.parser.plain import Parser
+from MoinMoin.formatter.text_html import Formatter
+
+
+class MacroTestCase(unittest.TestCase):
+    def testTrivialMacro(self):
+        """macro: trivial macro works"""
+        m = self._make_macro()
+        expected = m.formatter.linebreak(0)
+        result = m.execute("BR", "")
+        self.assertEqual(result, expected,
+            'Expected "%(expected)s" but got "%(result)s"' % locals())        
+
+    def _make_macro(self):
+        """Test helper"""
+        p = Parser("##\n", self.request)
+        p.formatter = Formatter(self.request)
+        self.request.formatter = p.formatter
+        p.form = self.request.form
+        m = macro.Macro(p)
+        return m
--- a/MoinMoin/_tests/test_wikimacro.py	Fri May 12 17:35:50 2006 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,32 +0,0 @@
-# -*- coding: iso-8859-1 -*-
-"""
-    MoinMoin - MoinMoin.wikimacro Tests
-
-    @copyright: 2003-2004 by Jürgen Hermann <jh@web.de>
-    @license: GNU GPL, see COPYING for details.
-"""
-
-import unittest, os
-
-from MoinMoin import wikimacro, wikiutil
-from MoinMoin.parser.plain import Parser
-from MoinMoin.formatter.text_html import Formatter
-
-
-class MacroTestCase(unittest.TestCase):
-    def testTrivialMacro(self):
-        """wikimacro: trivial macro works"""
-        m = self._make_macro()
-        expected = m.formatter.linebreak(0)
-        result = m.execute("BR", "")
-        self.assertEqual(result, expected,
-            'Expected "%(expected)s" but got "%(result)s"' % locals())        
-
-    def _make_macro(self):
-        """Test helper"""
-        p = Parser("##\n", self.request)
-        p.formatter = Formatter(self.request)
-        self.request.formatter = p.formatter
-        p.form = self.request.form
-        m = wikimacro.Macro(p)
-        return m
--- a/MoinMoin/action/fckdialog.py	Fri May 12 17:35:50 2006 +0200
+++ b/MoinMoin/action/fckdialog.py	Fri May 12 19:53:53 2006 +0200
@@ -138,8 +138,8 @@
 ''')
         
 def macro_list(request):
-    from MoinMoin import wikimacro
-    macros = wikimacro.getNames(request.cfg)
+    from MoinMoin import macro
+    macros = macro.getNames(request.cfg)
     macros.sort()
     return macros
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MoinMoin/macro/SystemInfo.py	Fri May 12 19:53:53 2006 +0200
@@ -0,0 +1,123 @@
+# -*- coding: iso-8859-1 -*-
+"""
+    MoinMoin - SystemInfo Macro
+
+    This macro shows some info about your wiki, wiki software and your system.
+
+    @copyright: 2006 MoinMoin:ThomasWaldmann
+    @license: GNU GPL, see COPYING for details.
+"""
+
+Dependencies = ['pages']
+
+import operator, sys, os
+from StringIO import StringIO
+
+from MoinMoin import wikiutil, version
+from MoinMoin import action, wikiaction, macro, parser, processor
+from MoinMoin.logfile import editlog, eventlog
+from MoinMoin.Page import Page
+
+def execute(Macro, args):
+    """ show SystemInfo: wiki infos, wiki sw version, space usage infos """
+    def _formatInReadableUnits(size):
+        size = float(size)
+        unit = u' Byte'
+        if size > 9999:
+            unit = u' KiB'
+            size /= 1024
+        if size > 9999:
+            unit = u' MiB'
+            size /= 1024
+        if size > 9999:
+            unit = u' GiB'
+            size /= 1024
+        return u"%.1f %s" % (size, unit)
+
+    def _getDirectorySize(path):
+        try:
+            dirsize = 0
+            for root, dirs, files in os.walk(path):
+                dirsize += sum([os.path.getsize(os.path.join(root, name)) for name in files])
+        except EnvironmentError, e:
+            dirsize = -1
+        return dirsize
+
+    _ = Macro._
+    request = Macro.request
+    
+    # check for 4XSLT
+    try:
+        import Ft
+        ftversion = Ft.__version__
+    except ImportError:
+        ftversion = None
+    except AttributeError:
+        ftversion = 'N/A'
+
+    t_count = None
+    try:
+        from threading import activeCount
+        t_count = activeCount()
+    except ImportError:
+        pass
+
+    # Get the full pagelist in the wiki
+    pagelist = request.rootpage.getPageList(user='')
+    totalsize = reduce(operator.add, [Page(request, name).size()
+                                      for name in pagelist])
+
+    buf = StringIO()
+    row = lambda label, value, buf=buf: buf.write(
+        u'<dt>%s</dt><dd>%s</dd>' % (label, value))
+
+    buf.write(u'<dl>')
+    row(_('Python Version'), sys.version)
+    row(_('MoinMoin Version'), _('Release %s [Revision %s]') % (version.release, version.revision))
+    if ftversion:
+        row(_('4Suite Version'), ftversion)
+
+    systemPages = [page for page in pagelist
+                   if wikiutil.isSystemPage(request, page)]
+    row(_('Number of pages'), str(len(pagelist)-len(systemPages)))
+    row(_('Number of system pages'), str(len(systemPages)))
+
+    row(_('Accumulated page sizes'), _formatInReadableUnits(totalsize))
+    data_dir = request.cfg.data_dir
+    row(_('Disk usage of %(data_dir)s/pages/') % {'data_dir': data_dir},
+        _formatInReadableUnits(_getDirectorySize(os.path.join(data_dir, 'pages'))))
+    row(_('Disk usage of %(data_dir)s/') % {'data_dir': data_dir},
+        _formatInReadableUnits(_getDirectorySize(data_dir)))
+
+    edlog = editlog.EditLog(request)
+    row(_('Entries in edit log'), "%s (%s)" % (edlog.lines(), _formatInReadableUnits(edlog.size())))
+
+    # This puts a heavy load on the server when the log is large
+    eventlogger = eventlog.EventLog(request)
+    nonestr = _("NONE")
+    row('Event log', _formatInReadableUnits(eventlogger.size()))
+    
+    row(_('Global extension macros'), ', '.join(macro.extension_macros) or nonestr)
+    row(_('Local extension macros'), 
+        ', '.join(wikiutil.wikiPlugins('macro', Macro.cfg)) or nonestr)
+    
+    ext_actions = [x for x in action.extension_actions
+                   if not x in request.cfg.actions_excluded]
+    row(_('Global extension actions'), ', '.join(ext_actions) or nonestr)
+    row(_('Local extension actions'), 
+        ', '.join(wikiaction.getPlugins(request)[1]) or nonestr)
+    
+    row(_('Global parsers'), ', '.join(parser.modules) or nonestr)
+    row(_('Local extension parsers'), 
+        ', '.join(wikiutil.wikiPlugins('parser', Macro.cfg)) or nonestr)
+    row(_('Installed processors (DEPRECATED -- use Parsers instead)'), 
+        ', '.join(processor.processors) or nonestr)
+    
+    state = (_('Disabled'), _('Enabled'))
+    row(_('Lupy search'), state[request.cfg.lupy_search])
+    
+    row(_('Active threads'), t_count or 'N/A')
+    buf.write(u'</dl>')
+
+    return Macro.formatter.rawHTML(buf.getvalue())
+
--- a/MoinMoin/macro/__init__.py	Fri May 12 17:35:50 2006 +0200
+++ b/MoinMoin/macro/__init__.py	Fri May 12 19:53:53 2006 +0200
@@ -1,19 +1,18 @@
 # -*- coding: iso-8859-1 -*-
 """
-    MoinMoin - Macro Package
+    MoinMoin - Macro Implementation
 
-    The canonical interface to macros is their execute() function,
+    These macros are used by the parser/wiki.py module
+    to implement complex and/or dynamic page content.
+
+    The canonical interface to plugin macros is their execute() function,
     which gets passed an instance of the Macro class. Such an instance
     has the four members parser, formatter, form and request.
 
-    Using "form" directly is deprecated and should be replaced
-    by "request.form".
+    Using "form" directly is deprecated and should be replaced by "request.form".
 
-    Besides the execute() function, macros can export additional
-    functions to offer services to other macros or actions. A few
-    actually do that, e.g. AttachFile.
-
-    @copyright: 2000 by Jürgen Hermann <jh@web.de>
+    @copyright: 2000-2004 by Jürgen Hermann <jh@web.de>,
+                2006 MoinMoin:ThomasWaldmann
     @license: GNU GPL, see COPYING for details.
 """
 
@@ -21,3 +20,509 @@
 
 extension_macros = pysupport.getPackageModules(__file__)
 modules = extension_macros
+
+import re, time, os
+from MoinMoin import action, config, util
+from MoinMoin import wikiutil, wikiaction, i18n
+from MoinMoin.Page import Page
+from MoinMoin.util import pysupport
+
+names = ["TitleSearch", "WordIndex", "TitleIndex",
+         "GoTo", "InterWiki", "PageCount", "UserPreferences",
+         # Macros with arguments
+         "Icon", "PageList", "Date", "DateTime", "Anchor", "MailTo", "GetVal",
+         "TemplateList",
+]
+names.extend(i18n.languages.keys())
+
+#############################################################################
+### Helpers
+#############################################################################
+
+def getNames(cfg):
+    if hasattr(cfg, 'macro_names'):
+        return cfg.macro_names
+    else:
+        lnames = names[:]
+        lnames.extend(wikiutil.getPlugins('macro', cfg))
+        return lnames
+
+def _make_index_key(index_letters, additional_html=""):
+    index_letters.sort()
+    links = map(lambda ch:
+                    '<a href="#%s">%s</a>' %
+                    (wikiutil.quoteWikinameURL(ch), ch.replace('~', 'Others')),
+                index_letters)
+    return "<p>%s%s</p>" % (' | '.join(links), additional_html)
+
+
+#############################################################################
+### Macros - Handlers for [[macroname]] markup
+#############################################################################
+
+class Macro:
+    """ Macro handler 
+    
+    There are three kinds of macros: 
+     * Builtin Macros - implemented in this file and named _macro_[name]
+     * Language Pseudo Macros - any lang the wiki knows can be use as
+       macro and is implemented here by _m_lang() 
+     * External macros - implemented in either MoinMoin.macro package, or
+       in the specific wiki instance in the plugin/macro directory
+    """
+    defaultDependency = ["time"]
+
+    Dependencies = {
+        "TitleSearch" : ["namespace"],
+        "Goto"        : [],
+        "WordIndex"   : ["namespace"],
+        "TitleIndex"  : ["namespace"],
+        "InterWiki"   : ["pages"],  # if interwikimap is editable
+        "PageCount"   : ["namespace"],
+        "Icon"        : ["user"], # users have different themes and user prefs
+        "PageList"    : ["namespace"],
+        "Date"        : ["time"],
+        "DateTime"    : ["time"],
+        "UserPreferences" :["time"],
+        "Anchor"      : [],
+        "Mailto"      : ["user"],
+        "GetVal"      : ["pages"],
+        "TemplateList": ["namespace"],
+        }
+
+    # we need the lang macros to execute when html is generated,
+    # to have correct dir and lang html attributes
+    for lang in i18n.languages.keys():
+        Dependencies[lang] = []
+    
+
+    def __init__(self, parser):
+        self.parser = parser
+        self.form = self.parser.form
+        self.request = self.parser.request
+        self.formatter = self.request.formatter
+        self._ = self.request.getText
+        self.cfg = self.request.cfg
+        
+        # Initialized on execute
+        self.name = None
+
+    def execute(self, macro_name, args):
+        """ Get and execute a macro 
+        
+        Try to get a plugin macro, or a builtin macro or a language
+        macro, or just raise ImportError. 
+        """
+        self.name = macro_name
+        try:
+            execute = wikiutil.importPlugin(self.cfg, 'macro', macro_name)
+        except wikiutil.PluginMissingError:
+            try:
+                builtins = self.__class__
+                execute = getattr(builtins, '_macro_' + macro_name)
+            except AttributeError:
+                if macro_name in i18n.languages:
+                    execute = builtins._m_lang
+                else:
+                    raise ImportError("Cannot load macro %s" % macro_name)
+        return execute(self, args)
+
+    def _m_lang(self, text):
+        """ Set the current language for page content.
+        
+            Language macro are used in two ways:
+             * [lang] - set the current language until next lang macro
+             * [lang(text)] - insert text with specific lang inside page
+        """
+        if text:
+            return (self.formatter.lang(1, self.name) +
+                    self.formatter.text(text) +
+                    self.formatter.lang(0, self.name))
+        
+        self.request.current_lang = self.name
+        return ''
+  
+    def get_dependencies(self, macro_name):
+        if macro_name in self.Dependencies:
+            return self.Dependencies[macro_name]
+        try:
+            return wikiutil.importPlugin(self.request.cfg, 'macro',
+                                         macro_name, 'Dependencies')
+        except wikiutil.PluginError:
+            return self.defaultDependency
+
+    def _macro_TitleSearch(self, args):
+        return self._m_search("titlesearch")
+
+    def _m_search(self, type):
+        """ Make a search box
+
+        Make both Title Search and Full Search boxes, according to type.
+
+        @param type: search box type: 'titlesearch' or 'fullsearch'
+        @rtype: unicode
+        @return: search box html fragment
+        """
+        _ = self._
+        if self.form.has_key('value'):
+            default = wikiutil.escape(self.form["value"][0], quote=1)
+        else:
+            default = ''
+
+        # Title search settings
+        boxes = ''
+        button = _("Search Titles")
+
+        # Special code for fullsearch
+        if type == "fullsearch":
+            boxes = [
+                u'<br>',
+                u'<input type="checkbox" name="context" value="160" checked="checked">',
+                _('Display context of search results'),
+                u'<br>',
+                u'<input type="checkbox" name="case" value="1">',
+                _('Case-sensitive searching'),
+                ]
+            boxes = u'\n'.join(boxes)
+            button = _("Search Text")
+            
+        # Format
+        type = (type == "titlesearch")
+        html = [
+            u'<form method="get" action="">',
+            u'<div>',
+            u'<input type="hidden" name="action" value="fullsearch">',
+            u'<input type="hidden" name="titlesearch" value="%i">' % type,
+            u'<input type="text" name="value" size="30" value="%s">' % default,
+            u'<input type="submit" value="%s">' % button,
+            boxes,
+            u'</div>',
+            u'</form>',    
+            ]
+        html = u'\n'.join(html)
+        return self.formatter.rawHTML(html)
+    
+    def _macro_GoTo(self, args):
+        """ Make a goto box
+
+        @param args: macro arguments
+        @rtype: unicode
+        @return: goto box html fragment
+        """
+        _ = self._
+        html = [
+            u'<form method="get" action="">',
+            u'<div>',
+            u'<input type="hidden" name="action" value="goto">',
+            u'<input type="text" name="target" size="30">',
+            u'<input type="submit" value="%s">' % _("Go To Page"),
+            u'</div>',
+            u'</form>',
+            ]
+        html = u'\n'.join(html)
+        return self.formatter.rawHTML(html)
+
+    def _macro_WordIndex(self, args):
+        _ = self._
+        allpages = int(self.form.get('allpages', [0])[0]) != 0
+        # Get page list readable by current user
+        # Filter by isSystemPage if needed
+        if allpages:
+            # TODO: make this fast by caching full page list
+            pages = self.request.rootpage.getPageList()
+        else:
+            def filter(name):
+                return not wikiutil.isSystemPage(self.request, name)
+            pages = self.request.rootpage.getPageList(filter=filter)
+        map = {}
+        word_re = re.compile(u'[%s][%s]+' % (config.chars_upper, config.chars_lower), re.UNICODE)
+        for name in pages:
+            for word in word_re.findall(name):
+                try:
+                    if not map[word].count(name):
+                        map[word].append(name)
+                except KeyError:
+                    map[word] = [name]
+
+        all_words = map.keys()
+        all_words.sort()
+        index_letters = []
+        current_letter = None
+        html = []
+        for word in all_words:
+            letter = wikiutil.getUnicodeIndexGroup(word)
+            if letter != current_letter:
+                #html.append(self.formatter.anchordef()) # XXX no text param available!
+                html.append(u'<a name="%s"><h3>%s</h3></a>' % (
+                    wikiutil.quoteWikinameURL(letter), letter.replace('~', 'Others')))
+                current_letter = letter
+            if letter not in index_letters:
+                index_letters.append(letter)
+
+            html.append(self.formatter.strong(1))
+            html.append(word)
+            html.append(self.formatter.strong(0))
+            html.append(self.formatter.bullet_list(1))
+            links = map[word]
+            links.sort()
+            last_page = None
+            for name in links:
+                if name == last_page:
+                    continue
+                html.append(self.formatter.listitem(1))
+                html.append(Page(self.request, name).link_to(self.request))
+                html.append(self.formatter.listitem(0))
+            html.append(self.formatter.bullet_list(0))
+        
+        qpagename = wikiutil.quoteWikinameURL(self.formatter.page.page_name)
+        index = _make_index_key(index_letters, u"""<br>
+<a href="%s?allpages=%d">%s</a>
+""" % (qpagename, not allpages, (_('Include system pages'), _('Exclude system pages'))[allpages]) )
+        return u'%s%s' % (index, u''.join(html)) 
+
+
+    def _macro_TitleIndex(self, args):
+        _ = self._
+        html = []
+        index_letters = []
+        allpages = int(self.form.get('allpages', [0])[0]) != 0
+        # Get page list readable by current user
+        # Filter by isSystemPage if needed
+        if allpages:
+            # TODO: make this fast by caching full page list
+            pages = self.request.rootpage.getPageList()
+        else:
+            def filter(name):
+                return not wikiutil.isSystemPage(self.request, name)
+            pages = self.request.rootpage.getPageList(filter=filter)
+
+        # Sort ignoring case
+        tmp = [(name.upper(), name) for name in pages]
+        tmp.sort()
+        pages = [item[1] for item in tmp]
+                
+        current_letter = None
+        for name in pages:
+            letter = wikiutil.getUnicodeIndexGroup(name)
+            if letter not in index_letters:
+                index_letters.append(letter)
+            if letter != current_letter:
+                html.append(u'<a name="%s"><h3>%s</h3></a>' % (
+                    wikiutil.quoteWikinameURL(letter), letter.replace('~', 'Others')))
+                current_letter = letter
+            else:
+                html.append(u'<br>')
+            html.append(u'%s\n' % Page(self.request, name).link_to(self.request, attachment_indicator=1))
+
+        # add rss link
+        index = ''
+        if 0: # if wikixml.ok: # XXX currently switched off (not implemented)
+            from MoinMoin import wikixml
+            index = (index + self.formatter.url(1, 
+                wikiutil.quoteWikinameURL(self.formatter.page.page_name) + "?action=rss_ti", do_escape=0) +
+                     self.formatter.icon("rss") +
+                     self.formatter.url(0))
+
+        qpagename = wikiutil.quoteWikinameURL(self.formatter.page.page_name)
+        index = index + _make_index_key(index_letters, u"""<br>
+<a href="%s?allpages=%d">%s</a>&nbsp;|
+<a href="%s?action=titleindex">%s</a>&nbsp;|
+<a href="%s?action=titleindex&amp;mimetype=text/xml">%s</a>
+""" % (qpagename, not allpages, (_('Include system pages'), _('Exclude system pages'))[allpages],
+       qpagename, _('Plain title index'),
+       qpagename, _('XML title index')) )
+
+        return u'%s%s' % (index, u''.join(html)) 
+
+
+    def _macro_InterWiki(self, args):
+        from StringIO import StringIO
+
+        # load interwiki list
+        dummy = wikiutil.resolve_wiki(self.request, '')
+
+        buf = StringIO()
+        buf.write('<dl>')
+        list = self.cfg._interwiki_list.items() # this is where we cached it
+        list.sort()
+        for tag, url in list:
+            buf.write('<dt><tt><a href="%s">%s</a></tt></dt>' % (
+                wikiutil.join_wiki(url, 'RecentChanges'), tag))
+            if url.find('$PAGE') == -1:
+                buf.write('<dd><tt><a href="%s">%s</a></tt></dd>' % (url, url))
+            else:
+                buf.write('<dd><tt>%s</tt></dd>' % url)
+        buf.write('</dl>')
+
+        return self.formatter.rawHTML(buf.getvalue())
+
+    def _macro_PageCount(self, args):
+        """ Return number of pages readable by current user
+        
+        Return either an exact count (slow!) or fast count including
+        deleted pages.
+        """
+        # Check input
+        options = {None: 0, '': 0, 'exists': 1}
+        try:
+            exists = options[args]
+        except KeyError:
+            # Wrong argument, return inline error message
+            arg = self.formatter.text(args)
+            return (self.formatter.span(1, css_class="error") +
+                    'Wrong argument: %s' % arg +
+                    self.formatter.span(0))
+        
+        count = self.request.rootpage.getPageCount(exists=exists)
+        return self.formatter.text("%d" % count)
+
+    def _macro_Icon(self, args):
+        icon = args.lower()
+        return self.formatter.icon(icon)
+
+    def _macro_PageList(self, needle):
+        from MoinMoin import search
+        _ = self._
+        literal=0
+        case=0
+
+        # If called with empty or no argument, default to regex search for .+,
+        # the full page list.
+        if not needle:
+            needle = 'regex:.+'
+
+        # With whitespace argument, return same error message as FullSearch
+        elif needle.isspace():
+            err = _('Please use a more selective search term instead of '
+                    '{{{"%s"}}}') %  needle
+            return '<span class="error">%s</span>' % err
+            
+        # Return a title search for needle, sorted by name.
+        query = search.QueryParser(literal=literal, titlesearch=1,
+                                   case=case).parse_query(needle)
+        results = search.searchPages(self.request, query)
+        results.sortByPagename()
+        return results.pageList(self.request, self.formatter)
+        
+    def _macro_TemplateList(self, args):
+        _ = self._
+        try:
+            needle_re = re.compile(args or '', re.IGNORECASE)
+        except re.error, e:
+            return "<strong>%s: %s</strong>" % (
+                _("ERROR in regex '%s'") % (args,), e)
+
+        # Get page list readable by current user, filtered by needle
+        hits = self.request.rootpage.getPageList(filter=needle_re.search)
+        hits.sort()
+        
+        result = []
+        result.append(self.formatter.bullet_list(1))
+        for pagename in hits:
+            result.append(self.formatter.listitem(1))
+            result.append(self.formatter.pagelink(1, pagename, generated=1))
+            result.append(self.formatter.text(pagename))
+            result.append(self.formatter.pagelink(0, pagename))
+            result.append(self.formatter.listitem(0))
+        result.append(self.formatter.bullet_list(0))
+        return ''.join(result)
+
+
+    def __get_Date(self, args, format_date):
+        _ = self._
+        if not args:
+            tm = time.time() # always UTC
+        elif len(args) >= 19 and args[4] == '-' and args[7] == '-' \
+                and args[10] == 'T' and args[13] == ':' and args[16] == ':':
+            # we ignore any time zone offsets here, assume UTC,
+            # and accept (and ignore) any trailing stuff
+            try:
+                year, month, day = int(args[0:4]), int(args[5:7]), int(args[8:10]) 
+                hour, minute, second = int(args[11:13]), int(args[14:16]), int(args[17:19]) 
+                tz = args[19:] # +HHMM, -HHMM or Z or nothing (then we assume Z)
+                tzoffset = 0 # we assume UTC no matter if there is a Z
+                if tz:
+                    sign = tz[0]
+                    if sign in '+-':
+                        tzh, tzm = int(tz[1:3]), int(tz[3:])
+                        tzoffset = (tzh*60+tzm)*60
+                        if sign == '-':
+                            tzoffset = -tzoffset
+                tm = (year, month, day, hour, minute, second, 0, 0, 0)
+            except ValueError, e:
+                return "<strong>%s: %s</strong>" % (
+                    _("Bad timestamp '%s'") % (args,), e)
+            # as mktime wants a localtime argument (but we only have UTC),
+            # we adjust by our local timezone's offset
+            try:
+                tm = time.mktime(tm) - time.timezone - tzoffset
+            except (OverflowError, ValueError), err:
+                tm = 0 # incorrect, but we avoid an ugly backtrace
+        else:
+            # try raw seconds since epoch in UTC
+            try:
+                tm = float(args)
+            except ValueError, e:
+                return "<strong>%s: %s</strong>" % (
+                    _("Bad timestamp '%s'") % (args,), e)
+        return format_date(tm)
+
+    def _macro_Date(self, args):
+        return self.__get_Date(args, self.request.user.getFormattedDate)
+
+    def _macro_DateTime(self, args):
+        return self.__get_Date(args, self.request.user.getFormattedDateTime)
+
+
+    def _macro_UserPreferences(self, args):
+        from MoinMoin import userform
+
+        create_only = False
+        if isinstance(args, unicode):
+            args = args.strip(" '\"")
+            create_only = (args.lower()=="createonly")
+
+        return self.formatter.rawHTML(userform.getUserForm(
+            self.request,
+            create_only=create_only))
+
+    def _macro_Anchor(self, args):
+        return self.formatter.anchordef(args or "anchor")
+
+    def _macro_MailTo(self, args):
+        from MoinMoin.util.mail import decodeSpamSafeEmail
+
+        args = args or ''
+        if args.find(',') == -1:
+            email = args
+            text = ''
+        else:
+            email, text = args.split(',', 1)
+
+        email, text = email.strip(), text.strip()
+
+        if self.request.user.valid:
+            # decode address and generate mailto: link
+            email = decodeSpamSafeEmail(email)
+            result = (self.formatter.url(1, 'mailto:' + email, css='mailto', do_escape=0) +
+                      self.formatter.text(text or email) +
+                      self.formatter.url(0))
+        else:
+            # unknown user, maybe even a spambot, so
+            # just return text as given in macro args
+            email = self.formatter.code(1) + \
+                self.formatter.text("<%s>" % email) + \
+                self.formatter.code(0)
+            if text:
+                result = self.formatter.text(text) + " " + email
+            else:
+                result = email
+
+        return result
+
+    def _macro_GetVal(self, args):
+        page,key = args.split(',')
+        d = self.request.dicts.dict(page)
+        result = d.get(key,'')
+        return self.formatter.text(result)
+
--- a/MoinMoin/parser/wiki.py	Fri May 12 17:35:50 2006 +0200
+++ b/MoinMoin/parser/wiki.py	Fri May 12 19:53:53 2006 +0200
@@ -7,7 +7,7 @@
 """
 
 import os, re
-from MoinMoin import config, wikimacro, wikiutil
+from MoinMoin import config, wikiutil, macro
 from MoinMoin.Page import Page
 from MoinMoin.util import web
 
@@ -134,7 +134,7 @@
         self.list_indents = []
         self.list_types = []
         
-        self.formatting_rules = self.formatting_rules % {'macronames': u'|'.join(wikimacro.getNames(self.cfg))}
+        self.formatting_rules = self.formatting_rules % {'macronames': u'|'.join(macro.getNames(self.cfg))}
 
     def _close_item(self, result):
         #result.append("<!-- close item begin -->\n")
@@ -846,7 +846,7 @@
 
         # create macro instance
         if self.macro is None:
-            self.macro = wikimacro.Macro(self)
+            self.macro = macro.Macro(self)
         return self.formatter.macro(self.macro, macro_name, args)
 
     def scan(self, scan_re, line):
--- a/MoinMoin/wikimacro.py	Fri May 12 17:35:50 2006 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,617 +0,0 @@
-# -*- coding: iso-8859-1 -*-
-"""
-    MoinMoin - Macro Implementation
-
-    These macros are used by the parser/wiki.py module
-    to implement complex and/or dynamic page content.
-
-    The sub-package "MoinMoin.macro" contains external
-    macros, you can place your extensions there.
-
-    @copyright: 2000-2004 by Jürgen Hermann <jh@web.de>
-    @license: GNU GPL, see COPYING for details.
-"""
-
-import re, time, os
-from MoinMoin import action, config, macro, util
-from MoinMoin import wikiutil, wikiaction, i18n
-from MoinMoin.Page import Page
-from MoinMoin.util import pysupport
-
-names = ["TitleSearch", "WordIndex", "TitleIndex",
-         "GoTo", "InterWiki", "SystemInfo", "PageCount", "UserPreferences",
-         # Macros with arguments
-         "Icon", "PageList", "Date", "DateTime", "Anchor", "MailTo", "GetVal",
-         "TemplateList",
-]
-names.extend(i18n.languages.keys())
-
-#############################################################################
-### Helpers
-#############################################################################
-
-def getNames(cfg):
-    if hasattr(cfg, 'macro_names'):
-        return cfg.macro_names
-    else:
-        lnames = names[:]
-        lnames.extend(wikiutil.getPlugins('macro', cfg))
-        return lnames
-
-def _make_index_key(index_letters, additional_html=""):
-    index_letters.sort()
-    links = map(lambda ch:
-                    '<a href="#%s">%s</a>' %
-                    (wikiutil.quoteWikinameURL(ch), ch.replace('~', 'Others')),
-                index_letters)
-    return "<p>%s%s</p>" % (' | '.join(links), additional_html)
-
-
-#############################################################################
-### Macros - Handlers for [[macroname]] markup
-#############################################################################
-
-class Macro:
-    """ Macro handler 
-    
-    There are three kinds of macros: 
-     * Builtin Macros - implemented in this file and named _macro_[name]
-     * Language Pseudo Macros - any lang the wiki knows can be use as
-       macro and is implemented here by _m_lang() 
-     * External macros - implemented in either MoinMoin.macro package, or
-       in the specific wiki instance in the plugin/macro directory
-    """
-    defaultDependency = ["time"]
-
-    Dependencies = {
-        "TitleSearch" : ["namespace"],
-        "Goto"        : [],
-        "WordIndex"   : ["namespace"],
-        "TitleIndex"  : ["namespace"],
-        "InterWiki"   : ["pages"],  # if interwikimap is editable
-        "SystemInfo"  : ["pages"],
-        "PageCount"   : ["namespace"],
-        "Icon"        : ["user"], # users have different themes and user prefs
-        "PageList"    : ["namespace"],
-        "Date"        : ["time"],
-        "DateTime"    : ["time"],
-        "UserPreferences" :["time"],
-        "Anchor"      : [],
-        "Mailto"      : ["user"],
-        "GetVal"      : ["pages"],
-        "TemplateList": ["namespace"],
-        }
-
-    # we need the lang macros to execute when html is generated,
-    # to have correct dir and lang html attributes
-    for lang in i18n.languages.keys():
-        Dependencies[lang] = []
-    
-
-    def __init__(self, parser):
-        self.parser = parser
-        self.form = self.parser.form
-        self.request = self.parser.request
-        self.formatter = self.request.formatter
-        self._ = self.request.getText
-        self.cfg = self.request.cfg
-        
-        # Initialized on execute
-        self.name = None
-
-    def execute(self, macro_name, args):
-        """ Get and execute a macro 
-        
-        Try to get a plugin macro, or a builtin macro or a language
-        macro, or just raise ImportError. 
-        """
-        self.name = macro_name
-        try:
-            execute = wikiutil.importPlugin(self.cfg, 'macro', macro_name)
-        except wikiutil.PluginMissingError:
-            try:
-                builtins = self.__class__
-                execute = getattr(builtins, '_macro_' + macro_name)
-            except AttributeError:
-                if macro_name in i18n.languages:
-                    execute = builtins._m_lang
-                else:
-                    raise ImportError("Cannot load macro %s" % macro_name)
-        return execute(self, args)
-
-    def _m_lang(self, text):
-        """ Set the current language for page content.
-        
-            Language macro are used in two ways:
-             * [lang] - set the current language until next lang macro
-             * [lang(text)] - insert text with specific lang inside page
-        """
-        if text:
-            return (self.formatter.lang(1, self.name) +
-                    self.formatter.text(text) +
-                    self.formatter.lang(0, self.name))
-        
-        self.request.current_lang = self.name
-        return ''
-  
-    def get_dependencies(self, macro_name):
-        if macro_name in self.Dependencies:
-            return self.Dependencies[macro_name]
-        try:
-            return wikiutil.importPlugin(self.request.cfg, 'macro',
-                                         macro_name, 'Dependencies')
-        except wikiutil.PluginError:
-            return self.defaultDependency
-
-    def _macro_TitleSearch(self, args):
-        return self._m_search("titlesearch")
-
-    def _m_search(self, type):
-        """ Make a search box
-
-        Make both Title Search and Full Search boxes, according to type.
-
-        @param type: search box type: 'titlesearch' or 'fullsearch'
-        @rtype: unicode
-        @return: search box html fragment
-        """
-        _ = self._
-        if self.form.has_key('value'):
-            default = wikiutil.escape(self.form["value"][0], quote=1)
-        else:
-            default = ''
-
-        # Title search settings
-        boxes = ''
-        button = _("Search Titles")
-
-        # Special code for fullsearch
-        if type == "fullsearch":
-            boxes = [
-                u'<br>',
-                u'<input type="checkbox" name="context" value="160" checked="checked">',
-                _('Display context of search results'),
-                u'<br>',
-                u'<input type="checkbox" name="case" value="1">',
-                _('Case-sensitive searching'),
-                ]
-            boxes = u'\n'.join(boxes)
-            button = _("Search Text")
-            
-        # Format
-        type = (type == "titlesearch")
-        html = [
-            u'<form method="get" action="">',
-            u'<div>',
-            u'<input type="hidden" name="action" value="fullsearch">',
-            u'<input type="hidden" name="titlesearch" value="%i">' % type,
-            u'<input type="text" name="value" size="30" value="%s">' % default,
-            u'<input type="submit" value="%s">' % button,
-            boxes,
-            u'</div>',
-            u'</form>',    
-            ]
-        html = u'\n'.join(html)
-        return self.formatter.rawHTML(html)
-    
-    def _macro_GoTo(self, args):
-        """ Make a goto box
-
-        @param args: macro arguments
-        @rtype: unicode
-        @return: goto box html fragment
-        """
-        _ = self._
-        html = [
-            u'<form method="get" action="">',
-            u'<div>',
-            u'<input type="hidden" name="action" value="goto">',
-            u'<input type="text" name="target" size="30">',
-            u'<input type="submit" value="%s">' % _("Go To Page"),
-            u'</div>',
-            u'</form>',
-            ]
-        html = u'\n'.join(html)
-        return self.formatter.rawHTML(html)
-
-    def _macro_WordIndex(self, args):
-        _ = self._
-        allpages = int(self.form.get('allpages', [0])[0]) != 0
-        # Get page list readable by current user
-        # Filter by isSystemPage if needed
-        if allpages:
-            # TODO: make this fast by caching full page list
-            pages = self.request.rootpage.getPageList()
-        else:
-            def filter(name):
-                return not wikiutil.isSystemPage(self.request, name)
-            pages = self.request.rootpage.getPageList(filter=filter)
-        map = {}
-        word_re = re.compile(u'[%s][%s]+' % (config.chars_upper, config.chars_lower), re.UNICODE)
-        for name in pages:
-            for word in word_re.findall(name):
-                try:
-                    if not map[word].count(name):
-                        map[word].append(name)
-                except KeyError:
-                    map[word] = [name]
-
-        all_words = map.keys()
-        all_words.sort()
-        index_letters = []
-        current_letter = None
-        html = []
-        for word in all_words:
-            letter = wikiutil.getUnicodeIndexGroup(word)
-            if letter != current_letter:
-                #html.append(self.formatter.anchordef()) # XXX no text param available!
-                html.append(u'<a name="%s"><h3>%s</h3></a>' % (
-                    wikiutil.quoteWikinameURL(letter), letter.replace('~', 'Others')))
-                current_letter = letter
-            if letter not in index_letters:
-                index_letters.append(letter)
-
-            html.append(self.formatter.strong(1))
-            html.append(word)
-            html.append(self.formatter.strong(0))
-            html.append(self.formatter.bullet_list(1))
-            links = map[word]
-            links.sort()
-            last_page = None
-            for name in links:
-                if name == last_page:
-                    continue
-                html.append(self.formatter.listitem(1))
-                html.append(Page(self.request, name).link_to(self.request))
-                html.append(self.formatter.listitem(0))
-            html.append(self.formatter.bullet_list(0))
-        
-        qpagename = wikiutil.quoteWikinameURL(self.formatter.page.page_name)
-        index = _make_index_key(index_letters, u"""<br>
-<a href="%s?allpages=%d">%s</a>
-""" % (qpagename, not allpages, (_('Include system pages'), _('Exclude system pages'))[allpages]) )
-        return u'%s%s' % (index, u''.join(html)) 
-
-
-    def _macro_TitleIndex(self, args):
-        _ = self._
-        html = []
-        index_letters = []
-        allpages = int(self.form.get('allpages', [0])[0]) != 0
-        # Get page list readable by current user
-        # Filter by isSystemPage if needed
-        if allpages:
-            # TODO: make this fast by caching full page list
-            pages = self.request.rootpage.getPageList()
-        else:
-            def filter(name):
-                return not wikiutil.isSystemPage(self.request, name)
-            pages = self.request.rootpage.getPageList(filter=filter)
-
-        # Sort ignoring case
-        tmp = [(name.upper(), name) for name in pages]
-        tmp.sort()
-        pages = [item[1] for item in tmp]
-                
-        current_letter = None
-        for name in pages:
-            letter = wikiutil.getUnicodeIndexGroup(name)
-            if letter not in index_letters:
-                index_letters.append(letter)
-            if letter != current_letter:
-                html.append(u'<a name="%s"><h3>%s</h3></a>' % (
-                    wikiutil.quoteWikinameURL(letter), letter.replace('~', 'Others')))
-                current_letter = letter
-            else:
-                html.append(u'<br>')
-            html.append(u'%s\n' % Page(self.request, name).link_to(self.request, attachment_indicator=1))
-
-        # add rss link
-        index = ''
-        if 0: # if wikixml.ok: # XXX currently switched off (not implemented)
-            from MoinMoin import wikixml
-            index = (index + self.formatter.url(1, 
-                wikiutil.quoteWikinameURL(self.formatter.page.page_name) + "?action=rss_ti", do_escape=0) +
-                     self.formatter.icon("rss") +
-                     self.formatter.url(0))
-
-        qpagename = wikiutil.quoteWikinameURL(self.formatter.page.page_name)
-        index = index + _make_index_key(index_letters, u"""<br>
-<a href="%s?allpages=%d">%s</a>&nbsp;|
-<a href="%s?action=titleindex">%s</a>&nbsp;|
-<a href="%s?action=titleindex&amp;mimetype=text/xml">%s</a>
-""" % (qpagename, not allpages, (_('Include system pages'), _('Exclude system pages'))[allpages],
-       qpagename, _('Plain title index'),
-       qpagename, _('XML title index')) )
-
-        return u'%s%s' % (index, u''.join(html)) 
-
-
-    def _macro_InterWiki(self, args):
-        from StringIO import StringIO
-
-        # load interwiki list
-        dummy = wikiutil.resolve_wiki(self.request, '')
-
-        buf = StringIO()
-        buf.write('<dl>')
-        list = self.cfg._interwiki_list.items() # this is where we cached it
-        list.sort()
-        for tag, url in list:
-            buf.write('<dt><tt><a href="%s">%s</a></tt></dt>' % (
-                wikiutil.join_wiki(url, 'RecentChanges'), tag))
-            if url.find('$PAGE') == -1:
-                buf.write('<dd><tt><a href="%s">%s</a></tt></dd>' % (url, url))
-            else:
-                buf.write('<dd><tt>%s</tt></dd>' % url)
-        buf.write('</dl>')
-
-        return self.formatter.rawHTML(buf.getvalue())
-
-    def _macro_SystemInfo(self, args):
-        import operator, sys
-        from StringIO import StringIO
-        from MoinMoin import parser, processor, version
-        from MoinMoin.logfile import editlog, eventlog
-        def _formatInReadableUnits(size):
-            size = float(size)
-            unit = u' Byte'
-            if size > 9999:
-                unit = u' KiB'
-                size /= 1024
-            if size > 9999:
-                unit = u' MiB'
-                size /= 1024
-            if size > 9999:
-                unit = u' GiB'
-                size /= 1024
-            return u"%.1f %s" % (size, unit)
-
-        def _getDirectorySize(path):
-            try:
-                dirsize = 0
-                for root, dirs, files in os.walk(path):
-                    dirsize += sum([os.path.getsize(os.path.join(root, name)) for name in files])
-            except EnvironmentError, e:
-                dirsize = -1
-            return dirsize
-
-        _ = self._
-        # check for 4XSLT
-        try:
-            import Ft
-            ftversion = Ft.__version__
-        except ImportError:
-            ftversion = None
-        except AttributeError:
-            ftversion = 'N/A'
-
-        t_count = None
-        try:
-            from threading import activeCount
-            t_count = activeCount()
-        except ImportError:
-            pass
-
-        # Get the full pagelist in the wiki
-        pagelist = self.request.rootpage.getPageList(user='')
-        totalsize = reduce(operator.add, [Page(self.request, name).size()
-                                          for name in pagelist])
-
-        buf = StringIO()
-        row = lambda label, value, buf=buf: buf.write(
-            u'<dt>%s</dt><dd>%s</dd>' % (label, value))
-
-        buf.write(u'<dl>')
-        row(_('Python Version'), sys.version)
-        row(_('MoinMoin Version'), _('Release %s [Revision %s]') % (version.release, version.revision))
-        if ftversion:
-            row(_('4Suite Version'), ftversion)
-        systemPages = [page for page in pagelist
-                       if wikiutil.isSystemPage(self.request, page)]
-        row(_('Number of pages'), str(len(pagelist)-len(systemPages)))
-        row(_('Number of system pages'), str(len(systemPages)))
-        row(_('Accumulated page sizes'), _formatInReadableUnits(totalsize))
-        data_dir = self.request.cfg.data_dir
-        row(_('Disk usage of %(data_dir)s/pages/') % {'data_dir': data_dir},
-            _formatInReadableUnits(_getDirectorySize(os.path.join(data_dir, 'pages'))))
-        row(_('Disk usage of %(data_dir)s/') % {'data_dir': data_dir},
-            _formatInReadableUnits(_getDirectorySize(data_dir)))
-
-        edlog = editlog.EditLog(self.request)
-        row(_('Entries in edit log'), "%s (%s)" % (edlog.lines(), _formatInReadableUnits(edlog.size())))
-
-        # This puts a heavy load on the server when the log is large
-        eventlogger = eventlog.EventLog(self.request)
-        nonestr = _("NONE")
-        row('Event log', _formatInReadableUnits(eventlogger.size()))
-        row(_('Global extension macros'), ', '.join(macro.extension_macros) or nonestr)
-        row(_('Local extension macros'), 
-            ', '.join(wikiutil.wikiPlugins('macro', self.cfg)) or nonestr)
-        ext_actions = [x for x in action.extension_actions
-                       if not x in self.request.cfg.actions_excluded]
-        row(_('Global extension actions'), ', '.join(ext_actions) or nonestr)
-        row(_('Local extension actions'), 
-            ', '.join(wikiaction.getPlugins(self.request)[1]) or nonestr)
-        row(_('Global parsers'), ', '.join(parser.modules) or nonestr)
-        row(_('Local extension parsers'), 
-            ', '.join(wikiutil.wikiPlugins('parser', self.cfg)) or nonestr)
-        row(_('Installed processors (DEPRECATED -- use Parsers instead)'), 
-            ', '.join(processor.processors) or nonestr)
-        state = (_('Disabled'), _('Enabled'))
-        row(_('Lupy search'), state[self.request.cfg.lupy_search])
-        row(_('Active threads'), t_count or 'N/A')
-        buf.write(u'</dl>')
-
-        return self.formatter.rawHTML(buf.getvalue())
-
-    def _macro_PageCount(self, args):
-        """ Return number of pages readable by current user
-        
-        Return either an exact count (slow!) or fast count including
-        deleted pages.
-        """
-        # Check input
-        options = {None: 0, '': 0, 'exists': 1}
-        try:
-            exists = options[args]
-        except KeyError:
-            # Wrong argument, return inline error message
-            arg = self.formatter.text(args)
-            return (self.formatter.span(1, css_class="error") +
-                    'Wrong argument: %s' % arg +
-                    self.formatter.span(0))
-        
-        count = self.request.rootpage.getPageCount(exists=exists)
-        return self.formatter.text("%d" % count)
-
-    def _macro_Icon(self, args):
-        icon = args.lower()
-        return self.formatter.icon(icon)
-
-    def _macro_PageList(self, needle):
-        from MoinMoin import search
-        _ = self._
-        literal=0
-        case=0
-
-        # If called with empty or no argument, default to regex search for .+,
-        # the full page list.
-        if not needle:
-            needle = 'regex:.+'
-
-        # With whitespace argument, return same error message as FullSearch
-        elif needle.isspace():
-            err = _('Please use a more selective search term instead of '
-                    '{{{"%s"}}}') %  needle
-            return '<span class="error">%s</span>' % err
-            
-        # Return a title search for needle, sorted by name.
-        query = search.QueryParser(literal=literal, titlesearch=1,
-                                   case=case).parse_query(needle)
-        results = search.searchPages(self.request, query)
-        results.sortByPagename()
-        return results.pageList(self.request, self.formatter)
-        
-    def _macro_TemplateList(self, args):
-        _ = self._
-        try:
-            needle_re = re.compile(args or '', re.IGNORECASE)
-        except re.error, e:
-            return "<strong>%s: %s</strong>" % (
-                _("ERROR in regex '%s'") % (args,), e)
-
-        # Get page list readable by current user, filtered by needle
-        hits = self.request.rootpage.getPageList(filter=needle_re.search)
-        hits.sort()
-        
-        result = []
-        result.append(self.formatter.bullet_list(1))
-        for pagename in hits:
-            result.append(self.formatter.listitem(1))
-            result.append(self.formatter.pagelink(1, pagename, generated=1))
-            result.append(self.formatter.text(pagename))
-            result.append(self.formatter.pagelink(0, pagename))
-            result.append(self.formatter.listitem(0))
-        result.append(self.formatter.bullet_list(0))
-        return ''.join(result)
-
-
-    def __get_Date(self, args, format_date):
-        _ = self._
-        if not args:
-            tm = time.time() # always UTC
-        elif len(args) >= 19 and args[4] == '-' and args[7] == '-' \
-                and args[10] == 'T' and args[13] == ':' and args[16] == ':':
-            # we ignore any time zone offsets here, assume UTC,
-            # and accept (and ignore) any trailing stuff
-            try:
-                year, month, day = int(args[0:4]), int(args[5:7]), int(args[8:10]) 
-                hour, minute, second = int(args[11:13]), int(args[14:16]), int(args[17:19]) 
-                tz = args[19:] # +HHMM, -HHMM or Z or nothing (then we assume Z)
-                tzoffset = 0 # we assume UTC no matter if there is a Z
-                if tz:
-                    sign = tz[0]
-                    if sign in '+-':
-                        tzh, tzm = int(tz[1:3]), int(tz[3:])
-                        tzoffset = (tzh*60+tzm)*60
-                        if sign == '-':
-                            tzoffset = -tzoffset
-                tm = (year, month, day, hour, minute, second, 0, 0, 0)
-            except ValueError, e:
-                return "<strong>%s: %s</strong>" % (
-                    _("Bad timestamp '%s'") % (args,), e)
-            # as mktime wants a localtime argument (but we only have UTC),
-            # we adjust by our local timezone's offset
-            try:
-                tm = time.mktime(tm) - time.timezone - tzoffset
-            except (OverflowError, ValueError), err:
-                tm = 0 # incorrect, but we avoid an ugly backtrace
-        else:
-            # try raw seconds since epoch in UTC
-            try:
-                tm = float(args)
-            except ValueError, e:
-                return "<strong>%s: %s</strong>" % (
-                    _("Bad timestamp '%s'") % (args,), e)
-        return format_date(tm)
-
-    def _macro_Date(self, args):
-        return self.__get_Date(args, self.request.user.getFormattedDate)
-
-    def _macro_DateTime(self, args):
-        return self.__get_Date(args, self.request.user.getFormattedDateTime)
-
-
-    def _macro_UserPreferences(self, args):
-        from MoinMoin import userform
-
-        create_only = False
-        if isinstance(args, unicode):
-            args = args.strip(" '\"")
-            create_only = (args.lower()=="createonly")
-
-        return self.formatter.rawHTML(userform.getUserForm(
-            self.request,
-            create_only=create_only))
-
-    def _macro_Anchor(self, args):
-        return self.formatter.anchordef(args or "anchor")
-
-    def _macro_MailTo(self, args):
-        from MoinMoin.util.mail import decodeSpamSafeEmail
-
-        args = args or ''
-        if args.find(',') == -1:
-            email = args
-            text = ''
-        else:
-            email, text = args.split(',', 1)
-
-        email, text = email.strip(), text.strip()
-
-        if self.request.user.valid:
-            # decode address and generate mailto: link
-            email = decodeSpamSafeEmail(email)
-            result = (self.formatter.url(1, 'mailto:' + email, css='mailto', do_escape=0) +
-                      self.formatter.text(text or email) +
-                      self.formatter.url(0))
-        else:
-            # unknown user, maybe even a spambot, so
-            # just return text as given in macro args
-            email = self.formatter.code(1) + \
-                self.formatter.text("<%s>" % email) + \
-                self.formatter.code(0)
-            if text:
-                result = self.formatter.text(text) + " " + email
-            else:
-                result = email
-
-        return result
-
-    def _macro_GetVal(self, args):
-        page,key = args.split(',')
-        d = self.request.dicts.dict(page)
-        result = d.get(key,'')
-        return self.formatter.text(result)
-
--- a/docs/CHANGES	Fri May 12 17:35:50 2006 +0200
+++ b/docs/CHANGES	Fri May 12 19:53:53 2006 +0200
@@ -37,6 +37,8 @@
   Developer notes:
     * refactored some actions to use ActionBase base class
     * moved "test" action from wikiaction to MoinMoin/action/ (and use ActionBase)
+    * moved "SystemInfo" macro from wikimacro to MoinMoin/macro/
+    * moved wikimacro.py stuff to MoinMoin/macro/__init__.py
 
   Bugfixes:
     * on action "info" page, "revert" link will not be displayed for empty page