--- /dev/null
+# -*- coding: iso-8859-1 -*-
+"""
+    MoinMoin - Theme Package
+
+    @copyright: 2003-2008 MoinMoin:ThomasWaldmann
+    @license: GNU GPL, see COPYING for details.
+"""
+
+from MoinMoin import i18n, wikiutil, config, version, caching
+from MoinMoin.Page import Page
+from MoinMoin.util import pysupport
+
+modules = pysupport.getPackageModules(__file__)
+
+# Check whether we can emit a RSS feed.
+# RSS is broken on plain Python 2.3.x/2.4.x, and works only when installing PyXML.
+# News: A user reported that the RSS is valid when using Python 2.5.1 on Windows.
+import sys, xml
+rss_supported = sys.version_info[:3] >= (2, 5, 1) or '_xmlplus' in xml.__file__
+
+
+class ThemeBase:
+    """ Base class for themes
+
+    This class supply all the standard template that sub classes can
+    use without rewriting the same code. If you want to change certain
+    elements, override them.
+    """
+
+    name = 'base'
+
+    # fake _ function to get gettext recognize those texts:
+    _ = lambda x: x
+
+    # TODO: remove icons that are not used any more.
+    icons = {
+        # key         alt                        icon filename      w   h
+        # ------------------------------------------------------------------
+        # navibar
+        'help':        ("%(page_help_contents)s", "moin-help.png",   12, 11),
+        'find':        ("%(page_find_page)s",     "moin-search.png", 12, 12),
+        'diff':        (_("Diffs"),               "moin-diff.png",   15, 11),
+        'info':        (_("Info"),                "moin-info.png",   12, 11),
+        'edit':        (_("Edit"),                "moin-edit.png",   12, 12),
+        'unsubscribe': (_("Unsubscribe"),         "moin-unsubscribe.png", 14, 10),
+        'subscribe':   (_("Subscribe"),           "moin-subscribe.png", 14, 10),
+        'raw':         (_("Raw"),                 "moin-raw.png",    12, 13),
+        'xml':         (_("XML"),                 "moin-xml.png",    20, 13),
+        'print':       (_("Print"),               "moin-print.png",  16, 14),
+        'view':        (_("View"),                "moin-show.png",   12, 13),
+        'home':        (_("Home"),                "moin-home.png",   13, 12),
+        'up':          (_("Up"),                  "moin-parent.png", 15, 13),
+        # FileAttach
+        'attach':     ("%(attach_count)s",       "moin-attach.png",  7, 15),
+        'attachimg':  ("",                       "attach.png",      32, 32),
+        # RecentChanges
+        'rss':        (_("[RSS]"),               "moin-rss.png",    24, 24),
+        'deleted':    (_("[DELETED]"),           "moin-deleted.png", 60, 12),
+        'updated':    (_("[UPDATED]"),           "moin-updated.png", 60, 12),
+        'renamed':    (_("[RENAMED]"),           "moin-renamed.png", 60, 12),
+        'conflict':   (_("[CONFLICT]"),          "moin-conflict.png", 60, 12),
+        'new':        (_("[NEW]"),               "moin-new.png",    31, 12),
+        'diffrc':     (_("[DIFF]"),              "moin-diff.png",   15, 11),
+        # General
+        'bottom':     (_("[BOTTOM]"),            "moin-bottom.png", 14, 10),
+        'top':        (_("[TOP]"),               "moin-top.png",    14, 10),
+        'www':        ("[WWW]",                  "moin-www.png",    11, 11),
+        'mailto':     ("[MAILTO]",               "moin-email.png",  14, 10),
+        'news':       ("[NEWS]",                 "moin-news.png",   10, 11),
+        'telnet':     ("[TELNET]",               "moin-telnet.png", 10, 11),
+        'ftp':        ("[FTP]",                  "moin-ftp.png",    11, 11),
+        'file':       ("[FILE]",                 "moin-ftp.png",    11, 11),
+        # search forms
+        'searchbutton': ("[?]",                  "moin-search.png", 12, 12),
+        'interwiki':  ("[%(wikitag)s]",          "moin-inter.png",  16, 16),
+
+        # smileys (this is CONTENT, but good looking smileys depend on looking
+        # adapted to the theme background color and theme style in general)
+        #vvv    ==      vvv  this must be the same for GUI editor converter
+        'X-(':        ("X-(",                    'angry.png',       15, 15),
+        ':D':         (":D",                     'biggrin.png',     15, 15),
+        '<:(':        ("<:(",                    'frown.png',       15, 15),
+        ':o':         (":o",                     'redface.png',     15, 15),
+        ':(':         (":(",                     'sad.png',         15, 15),
+        ':)':         (":)",                     'smile.png',       15, 15),
+        'B)':         ("B)",                     'smile2.png',      15, 15),
+        ':))':        (":))",                    'smile3.png',      15, 15),
+        ';)':         (";)",                     'smile4.png',      15, 15),
+        '/!\\':       ("/!\\",                   'alert.png',       15, 15),
+        '<!>':        ("<!>",                    'attention.png',   15, 15),
+        '(!)':        ("(!)",                    'idea.png',        15, 15),
+
+        # copied 2001-11-16 from http://pikie.darktech.org/cgi/pikie.py?EmotIcon
+        ':-?':        (":-?",                    'tongue.png',      15, 15),
+        ':\\':        (":\\",                    'ohwell.png',      15, 15),
+        '>:>':        (">:>",                    'devil.png',       15, 15),
+        '|)':         ("|)",                     'tired.png',       15, 15),
+
+        # some folks use noses in their emoticons
+        ':-(':        (":-(",                    'sad.png',         15, 15),
+        ':-)':        (":-)",                    'smile.png',       15, 15),
+        'B-)':        ("B-)",                    'smile2.png',      15, 15),
+        ':-))':       (":-))",                   'smile3.png',      15, 15),
+        ';-)':        (";-)",                    'smile4.png',      15, 15),
+        '|-)':        ("|-)",                    'tired.png',       15, 15),
+
+        # version 1.0
+        '(./)':       ("(./)",                   'checkmark.png',   20, 15),
+        '{OK}':       ("{OK}",                   'thumbs-up.png',   14, 12),
+        '{X}':        ("{X}",                    'icon-error.png',  16, 16),
+        '{i}':        ("{i}",                    'icon-info.png',   16, 16),
+        '{1}':        ("{1}",                    'prio1.png',       15, 13),
+        '{2}':        ("{2}",                    'prio2.png',       15, 13),
+        '{3}':        ("{3}",                    'prio3.png',       15, 13),
+
+        # version 1.3.4 (stars)
+        # try {*}{*}{o}
+        '{*}':        ("{*}",                    'star_on.png',     15, 15),
+        '{o}':        ("{o}",                    'star_off.png',    15, 15),
+    }
+    del _
+
+    # Style sheets - usually there is no need to override this in sub
+    # classes. Simply supply the css files in the css directory.
+
+    # Standard set of style sheets
+    stylesheets = (
+        # media         basename
+        ('all',         'common'),
+        ('screen',      'screen'),
+        ('print',       'print'),
+        ('projection',  'projection'),
+        )
+
+    # Used in print mode
+    stylesheets_print = (
+        # media         basename
+        ('all',         'common'),
+        ('all',         'print'),
+        )
+
+    # Used in slide show mode
+    stylesheets_projection = (
+        # media         basename
+        ('all',         'common'),
+        ('all',         'projection'),
+       )
+
+    stylesheetsCharset = 'utf-8'
+
+    def __init__(self, request):
+        """
+        Initialize the theme object.
+
+        @param request: the request object
+        """
+        self.request = request
+        self.cfg = request.cfg
+        self._cache = {} # Used to cache elements that may be used several times
+        self._status = []
+        self._send_title_called = False
+
+    def img_url(self, img):
+        """ Generate an image href
+
+        @param img: the image filename
+        @rtype: string
+        @return: the image href
+        """
+        return "%s/%s/img/%s" % (self.cfg.url_prefix_static, self.name, img)
+
+    def emit_custom_html(self, html):
+        """
+        generate custom HTML code in `html`
+
+        @param html: a string or a callable object, in which case
+                     it is called and its return value is used
+        @rtype: string
+        @return: string with html
+        """
+        if html:
+            if callable(html):
+                html = html(self.request)
+        return html
+
+    def logo(self):
+        """ Assemble logo with link to front page
+
+        The logo contain an image and or text or any html markup the
+        admin inserted in the config file. Everything it enclosed inside
+        a div with id="logo".
+
+        @rtype: unicode
+        @return: logo html
+        """
+        html = u''
+        if self.cfg.logo_string:
+            page = wikiutil.getFrontPage(self.request)
+            logo = page.link_to_raw(self.request, self.cfg.logo_string)
+            html = u'''<div id="logo">%s</div>''' % logo
+        return html
+
+    def interwiki(self, d):
+        """ Assemble the interwiki name display, linking to page_front_page
+
+        @param d: parameter dictionary
+        @rtype: string
+        @return: interwiki html
+        """
+        if self.request.cfg.show_interwiki:
+            page = wikiutil.getFrontPage(self.request)
+            text = self.request.cfg.interwikiname or 'Self'
+            link = page.link_to(self.request, text=text, rel='nofollow')
+            html = u'<div id="interwiki"><span>%s</span></div>' % link
+        else:
+            html = u''
+        return html
+
+    def title(self, d):
+        """ Assemble the title (now using breadcrumbs)
+
+        @param d: parameter dictionary
+        @rtype: string
+        @return: title html
+        """
+        _ = self.request.getText
+        content = []
+        if d['title_text'] == d['page'].split_title(): # just showing a page, no action
+            curpage = ''
+            segments = d['page_name'].split('/') # was: title_text
+            for s in segments[:-1]:
+                curpage += s
+                content.append("<li>%s</li>" % Page(self.request, curpage).link_to(self.request, s))
+                curpage += '/'
+            link_text = segments[-1]
+            link_title = _('Click to do a full-text search for this title')
+            link_query = {
+                'action': 'fullsearch',
+                'value': 'linkto:"%s"' % d['page_name'],
+                'context': '180',
+            }
+            # we dont use d['title_link'] any more, but make it ourselves:
+            link = d['page'].link_to(self.request, link_text, querystr=link_query, title=link_title, css_class='backlink', rel='nofollow')
+            content.append(('<li>%s</li>') % link)
+        else:
+            content.append('<li>%s</li>' % wikiutil.escape(d['title_text']))
+
+        html = '''
+<ul id="pagelocation">
+%s
+</ul>
+''' % "".join(content)
+        return html
+
+    def username(self, d):
+        """ Assemble the username / userprefs link
+
+        @param d: parameter dictionary
+        @rtype: unicode
+        @return: username html
+        """
+        request = self.request
+        _ = request.getText
+
+        userlinks = []
+        # Add username/homepage link for registered users. We don't care
+        # if it exists, the user can create it.
+        if request.user.valid and request.user.name:
+            interwiki = wikiutil.getInterwikiHomePage(request)
+            name = request.user.name
+            aliasname = request.user.aliasname
+            if not aliasname:
+                aliasname = name
+            title = "%s @ %s" % (aliasname, interwiki[0])
+            # link to (interwiki) user homepage
+            homelink = (request.formatter.interwikilink(1, title=title, id="userhome", generated=True, *interwiki) +
+                        request.formatter.text(name) +
+                        request.formatter.interwikilink(0, title=title, id="userhome", *interwiki))
+            userlinks.append(homelink)
+            # link to userprefs action
+            if 'userprefs' not in self.request.cfg.actions_excluded:
+                userlinks.append(d['page'].link_to(request, text=_('Settings'),
+                                               querystr={'action': 'userprefs'}, id='userprefs', rel='nofollow'))
+
+        if request.user.valid:
+            if request.user.auth_method in request.cfg.auth_can_logout:
+                userlinks.append(d['page'].link_to(request, text=_('Logout'),
+                                                   querystr={'action': 'logout', 'logout': 'logout'}, id='logout', rel='nofollow'))
+        else:
+            query = {'action': 'login'}
+            # special direct-login link if the auth methods want no input
+            if request.cfg.auth_login_inputs == ['special_no_input']:
+                query['login'] = '1'
+            if request.cfg.auth_have_login:
+                userlinks.append(d['page'].link_to(request, text=_("Login"),
+                                                   querystr=query, id='login', rel='nofollow'))
+
+        userlinks = [u'<li>%s</li>' % link for link in userlinks]
+        html = u'<ul id="username">%s</ul>' % ''.join(userlinks)
+        return html
+
+    def splitNavilink(self, text, localize=1):
+        """ Split navibar links into pagename, link to page
+
+        Admin or user might want to use shorter navibar items by using
+        the [[page|title]] or [[url|title]] syntax. In this case, we don't
+        use localization, and the links goes to page or to the url, not
+        the localized version of page.
+
+        Supported syntax:
+            * PageName
+            * WikiName:PageName
+            * wiki:WikiName:PageName
+            * url
+            * all targets as seen above with title: [[target|title]]
+
+        @param text: the text used in config or user preferences
+        @rtype: tuple
+        @return: pagename or url, link to page or url
+        """
+        request = self.request
+        fmt = request.formatter
+        title = None
+
+        # Handle [[pagename|title]] or [[url|title]] formats
+        if text.startswith('[[') and text.endswith(']]'):
+            text = text[2:-2]
+            try:
+                pagename, title = text.split('|', 1)
+                pagename = pagename.strip()
+                title = title.strip()
+                localize = 0
+            except (ValueError, TypeError):
+                # Just use the text as is.
+                pagename = text.strip()
+        else:
+            pagename = text
+
+        if wikiutil.is_URL(pagename):
+            if not title:
+                title = pagename
+            link = fmt.url(1, pagename) + fmt.text(title) + fmt.url(0)
+            return pagename, link
+
+        # remove wiki: url prefix
+        if pagename.startswith("wiki:"):
+            pagename = pagename[5:]
+
+        # try handling interwiki links
+        try:
+            interwiki, page = wikiutil.split_interwiki(pagename)
+            thiswiki = request.cfg.interwikiname
+            if interwiki == thiswiki or interwiki == 'Self':
+                pagename = page
+            else:
+                if not title:
+                    title = page
+                link = fmt.interwikilink(True, interwiki, page) + fmt.text(title) + fmt.interwikilink(False, interwiki, page)
+                return pagename, link
+        except ValueError:
+            pass
+
+        # Handle regular pagename like "FrontPage"
+        pagename = request.normalizePagename(pagename)
+
+        # Use localized pages for the current user
+        if localize:
+            page = wikiutil.getLocalizedPage(request, pagename)
+        else:
+            page = Page(request, pagename)
+
+        pagename = page.page_name # can be different, due to i18n
+
+        if not title:
+            title = page.split_title()
+            title = self.shortenPagename(title)
+
+        link = page.link_to(request, title)
+
+        return pagename, link
+
+    def shortenPagename(self, name):
+        """ Shorten page names
+
+        Shorten very long page names that tend to break the user
+        interface. The short name is usually fine, unless really stupid
+        long names are used (WYGIWYD).
+
+        If you don't like to do this in your theme, or want to use
+        different algorithm, override this method.
+
+        @param name: page name, unicode
+        @rtype: unicode
+        @return: shortened version.
+        """
+        maxLength = self.maxPagenameLength()
+        # First use only the sub page name, that might be enough
+        if len(name) > maxLength:
+            name = name.split('/')[-1]
+            # If it's not enough, replace the middle with '...'
+            if len(name) > maxLength:
+                half, left = divmod(maxLength - 3, 2)
+                name = u'%s...%s' % (name[:half + left], name[-half:])
+        return name
+
+    def maxPagenameLength(self):
+        """ Return maximum length for shortened page names """
+        return 25
+
+    def navibar(self, d):
+        """ Assemble the navibar
+
+        @param d: parameter dictionary
+        @rtype: unicode
+        @return: navibar html
+        """
+        request = self.request
+        found = {} # pages we found. prevent duplicates
+        items = [] # navibar items
+        item = u'<li class="%s">%s</li>'
+        current = d['page_name']
+
+        # Process config navi_bar
+        if request.cfg.navi_bar:
+            for text in request.cfg.navi_bar:
+                pagename, link = self.splitNavilink(text)
+                if pagename == current:
+                    cls = 'wikilink current'
+                else:
+                    cls = 'wikilink'
+                items.append(item % (cls, link))
+                found[pagename] = 1
+
+        # Add user links to wiki links, eliminating duplicates.
+        userlinks = request.user.getQuickLinks()
+        for text in userlinks:
+            # Split text without localization, user knows what he wants
+            pagename, link = self.splitNavilink(text, localize=0)
+            if not pagename in found:
+                if pagename == current:
+                    cls = 'userlink current'
+                else:
+                    cls = 'userlink'
+                items.append(item % (cls, link))
+                found[pagename] = 1
+
+        # Add current page at end of local pages
+        if not current in found:
+            title = d['page'].split_title()
+            title = self.shortenPagename(title)
+            link = d['page'].link_to(request, title)
+            cls = 'current'
+            items.append(item % (cls, link))
+
+        # Add sister pages.
+        for sistername, sisterurl in request.cfg.sistersites:
+            if sistername == request.cfg.interwikiname: # it is THIS wiki
+                cls = 'sisterwiki current'
+                items.append(item % (cls, sistername))
+            else:
+                # TODO optimize performance
+                cache = caching.CacheEntry(request, 'sisters', sistername, 'farm', use_pickle=True)
+                if cache.exists():
+                    data = cache.content()
+                    sisterpages = data['sisterpages']
+                    if current in sisterpages:
+                        cls = 'sisterwiki'
+                        url = sisterpages[current]
+                        link = request.formatter.url(1, url) + \
+                               request.formatter.text(sistername) +\
+                               request.formatter.url(0)
+                        items.append(item % (cls, link))
+
+        # Assemble html
+        items = u''.join(items)
+        html = u'''
+<ul id="navibar">
+%s
+</ul>
+''' % items
+        return html
+
+    def get_icon(self, icon):
+        """ Return icon data from self.icons
+
+        If called from <<Icon(file)>> we have a filename, not a
+        key. Using filenames is deprecated, but for now, we simulate old
+        behavior.
+
+        @param icon: icon name or file name (string)
+        @rtype: tuple
+        @return: alt (unicode), href (string), width, height (int)
+        """
+        if icon in self.icons:
+            alt, icon, w, h = self.icons[icon]
+        else:
+            # Create filenames to icon data mapping on first call, then
+            # cache in class for next calls.
+            if not getattr(self.__class__, 'iconsByFile', None):
+                d = {}
+                for data in self.icons.values():
+                    d[data[1]] = data
+                self.__class__.iconsByFile = d
+
+            # Try to get icon data by file name
+            if icon in self.iconsByFile:
+                alt, icon, w, h = self.iconsByFile[icon]
+            else:
+                alt, icon, w, h = '', icon, '', ''
+
+        return alt, self.img_url(icon), w, h
+
+    def make_icon(self, icon, vars=None, **kw):
+        """
+        This is the central routine for making <img> tags for icons!
+        All icons stuff except the top left logo and search field icons are
+        handled here.
+
+        @param icon: icon id (dict key)
+        @param vars: ...
+        @rtype: string
+        @return: icon html (img tag)
+        """
+        if vars is None:
+            vars = {}
+        alt, img, w, h = self.get_icon(icon)
+        try:
+            alt = vars['icon-alt-text'] # if it is possible we take the alt-text from 'page_icons_table'
+        except KeyError, err:
+            try:
+                alt = alt % vars # if not we just leave the  alt-text from 'icons'
+            except KeyError, err:
+                alt = 'KeyError: %s' % str(err)
+        alt = self.request.getText(alt)
+        tag = self.request.formatter.image(src=img, alt=alt, width=w, height=h, **kw)
+        return tag
+
+    def make_iconlink(self, which, d):
+        """
+        Make a link with an icon
+
+        @param which: icon id (dictionary key)
+        @param d: parameter dictionary
+        @rtype: string
+        @return: html link tag
+        """
+        qs = {}
+        pagekey, querystr, title, icon = self.cfg.page_icons_table[which]
+        qs.update(querystr) # do not modify the querystr dict in the cfg!
+        d['icon-alt-text'] = d['title'] = title % d
+        d['i18ntitle'] = self.request.getText(d['title'])
+        img_src = self.make_icon(icon, d)
+        rev = d['rev']
+        if rev and which in ['raw', 'print', ]:
+            qs['rev'] = str(rev)
+        attrs = {'rel': 'nofollow', 'title': d['i18ntitle'], }
+        page = d[pagekey]
+        if isinstance(page, unicode):
+            # e.g. d['page_parent_page'] is just the unicode pagename
+            # while d['page'] will give a page object
+            page = Page(self.request, page)
+        return page.link_to_raw(self.request, text=img_src, querystr=qs, **attrs)
+
+    def msg(self, d):
+        """ Assemble the msg display
+
+        Display a message with a widget or simple strings with a clear message link.
+
+        @param d: parameter dictionary
+        @rtype: unicode
+        @return: msg display html
+        """
+        _ = self.request.getText
+        msgs = d['msg']
+
+        result = u""
+        close = d['page'].link_to(self.request, text=_('Clear message'), css_class="clear-link")
+        for msg, msg_class in msgs:
+            try:
+                result += u'<p>%s</p>' % msg.render()
+                close = ''
+            except AttributeError:
+                if msg and msg_class:
+                    result += u'<p><div class="%s">%s</div></p>' % (msg_class, msg)
+                elif msg:
+                    result += u'<p>%s</p>\n' % msg
+        if result:
+            html = result + close
+            return u'<div id="message">\n%s\n</div>\n' % html
+        else:
+            return u''
+
+        return u'<div id="message">\n%s\n</div>\n' % html
+
+    def trail(self, d):
+        """ Assemble page trail
+
+        @param d: parameter dictionary
+        @rtype: unicode
+        @return: trail html
+        """
+        request = self.request
+        user = request.user
+        html = ''
+        if not user.valid or user.show_page_trail:
+            trail = user.getTrail()
+            if trail:
+                items = []
+                for pagename in trail:
+                    try:
+                        interwiki, page = wikiutil.split_interwiki(pagename)
+                        if interwiki != request.cfg.interwikiname and interwiki != 'Self':
+                            link = (self.request.formatter.interwikilink(True, interwiki, page) +
+                                    self.shortenPagename(page) +
+                                    self.request.formatter.interwikilink(False, interwiki, page))
+                            items.append('<li>%s</li>' % link)
+                            continue
+                        else:
+                            pagename = page
+
+                    except ValueError:
+                        pass
+                    page = Page(request, pagename)
+                    title = page.split_title()
+                    title = self.shortenPagename(title)
+                    link = page.link_to(request, title)
+                    items.append('<li>%s</li>' % link)
+                html = '''
+<ul id="pagetrail">
+%s
+</ul>''' % ''.join(items)
+        return html
+
+    def html_stylesheets(self, d):
+        """ Assemble html head stylesheet links
+
+        @param d: parameter dictionary
+        @rtype: string
+        @return: stylesheets links
+        """
+        link = '<link rel="stylesheet" type="text/css" charset="%s" media="%s" href="%s">'
+
+        # Check mode
+        if d.get('print_mode'):
+            media = d.get('media', 'print')
+            stylesheets = getattr(self, 'stylesheets_' + media)
+        else:
+            stylesheets = self.stylesheets
+        usercss = self.request.user.valid and self.request.user.css_url
+
+        # Create stylesheets links
+        html = []
+        prefix = self.cfg.url_prefix_static
+        csshref = '%s/%s/css' % (prefix, self.name)
+        for media, basename in stylesheets:
+            href = '%s/%s.css' % (csshref, basename)
+            html.append(link % (self.stylesheetsCharset, media, href))
+
+            # Don't add user css url if it matches one of ours
+            if usercss and usercss == href:
+                usercss = None
+
+        # admin configurable additional css (farm or wiki level)
+        for media, csshref in self.request.cfg.stylesheets:
+            html.append(link % (self.stylesheetsCharset, media, csshref))
+
+        csshref = '%s/%s/css/msie.css' % (prefix, self.name)
+        html.append("""
+<!-- css only for MSIE browsers -->
+<!--[if IE]>
+   %s
+<![endif]-->
+""" % link % (self.stylesheetsCharset, 'all', csshref))
+
+        # Add user css url (assuming that user css uses same charset)
+        if usercss and usercss.lower() != "none":
+            html.append(link % (self.stylesheetsCharset, 'all', usercss))
+
+        return '\n'.join(html)
+
+    def shouldShowPageinfo(self, page):
+        """ Should we show page info?
+
+        Should be implemented by actions. For now, we check here by action
+        name and page.
+
+        @param page: current page
+        @rtype: bool
+        @return: true if should show page info
+        """
+        if page.exists() and self.request.user.may.read(page.page_name):
+            # These  actions show the  page content.
+            # TODO: on new action, page info will not show.
+            # A better solution will be if the action itself answer the question: showPageInfo().
+            contentActions = [u'', u'show', u'refresh', u'preview', u'diff',
+                              u'subscribe', u'RenamePage', u'CopyPage', u'DeletePage',
+                              u'SpellCheck', u'print']
+            return self.request.action in contentActions
+        return False
+
+    def pageinfo(self, page):
+        """ Return html fragment with page meta data
+
+        Since page information uses translated text, it uses the ui
+        language and direction. It looks strange sometimes, but
+        translated text using page direction looks worse.
+
+        @param page: current page
+        @rtype: unicode
+        @return: page last edit information
+        """
+        _ = self.request.getText
+        html = ''
+        if self.shouldShowPageinfo(page):
+            info = page.lastEditInfo()
+            if info:
+                if info['editor']:
+                    info = _("last edited %(time)s by %(editor)s") % info
+                else:
+                    info = _("last modified %(time)s") % info
+                pagename = page.page_name
+                if self.request.cfg.show_interwiki:
+                    pagename = "%s: %s" % (self.request.cfg.interwikiname, pagename)
+                info = "%s  (%s)" % (wikiutil.escape(pagename), info)
+                html = '<p id="pageinfo" class="info"%(lang)s>%(info)s</p>\n' % {
+                    'lang': self.ui_lang_attr(),
+                    'info': info
+                    }
+        return html
+
+    def searchform(self, d):
+        """
+        assemble HTML code for the search forms
+
+        @param d: parameter dictionary
+        @rtype: unicode
+        @return: search form html
+        """
+        _ = self.request.getText
+        form = self.request.form
+        updates = {
+            'search_label': _('Search:'),
+            'search_value': wikiutil.escape(form.get('value', [''])[0], 1),
+            'search_full_label': _('Text'),
+            'search_title_label': _('Titles'),
+            'baseurl': self.request.getScriptname(),
+            'pagename_quoted': wikiutil.quoteWikinameURL(d['page'].page_name),
+            }
+        d.update(updates)
+
+        html = u'''
+<form id="searchform" method="get" action="%(baseurl)s/%(pagename_quoted)s">
+<div>
+<input type="hidden" name="action" value="fullsearch">
+<input type="hidden" name="context" value="180">
+<label for="searchinput">%(search_label)s</label>
+<input id="searchinput" type="text" name="value" value="%(search_value)s" size="20"
+    onfocus="searchFocus(this)" onblur="searchBlur(this)"
+    onkeyup="searchChange(this)" onchange="searchChange(this)" alt="Search">
+<input id="titlesearch" name="titlesearch" type="submit"
+    value="%(search_title_label)s" alt="Search Titles">
+<input id="fullsearch" name="fullsearch" type="submit"
+    value="%(search_full_label)s" alt="Search Full Text">
+</div>
+</form>
+<script type="text/javascript">
+<!--// Initialize search form
+var f = document.getElementById('searchform');
+f.getElementsByTagName('label')[0].style.display = 'none';
+var e = document.getElementById('searchinput');
+searchChange(e);
+searchBlur(e);
+//-->
+</script>
+''' % d
+        return html
+
+    def showversion(self, d, **keywords):
+        """
+        assemble HTML code for copyright and version display
+
+        @param d: parameter dictionary
+        @rtype: string
+        @return: copyright and version display html
+        """
+        html = ''
+        if self.cfg.show_version and not keywords.get('print_mode', 0):
+            html = (u'<div id="version">MoinMoin Release %s [Revision %s], '
+                     'Copyright by Juergen Hermann et al.</div>') % (version.release, version.revision, )
+        return html
+
+    def headscript(self, d):
+        """ Return html head script with common functions
+
+        @param d: parameter dictionary
+        @rtype: unicode
+        @return: script for html head
+        """
+        # Don't add script for print view
+        if self.request.action == 'print':
+            return u''
+
+        _ = self.request.getText
+        script = u"""
+<script type="text/javascript">
+<!--
+var search_hint = "%(search_hint)s";
+//-->
+</script>
+""" % {
+    'search_hint': _('Search Wiki'),
+    }
+        return script
+
+    def shouldUseRSS(self, page):
+        """ Return True if RSS feature is available and we are on the
+            RecentChanges page, or False.
+
+            Currently rss is broken on plain Python, and works only when
+            installing PyXML. Return true if PyXML is installed.
+        """
+        if not rss_supported:
+            return False
+        return page.page_name == u'RecentChanges' or \
+           page.page_name == self.request.getText(u'RecentChanges')
+
+    def rsshref(self, page):
+        """ Create rss href, used for rss button and head link
+
+        @rtype: unicode
+        @return: rss href
+        """
+        request = self.request
+        url = page.url(request, querystr={
+                'action': 'rss_rc', 'ddiffs': '1', 'unique': '1', }, escape=0)
+        return url
+
+    def rsslink(self, d):
+        """ Create rss link in head, used by FireFox
+
+        RSS link for FireFox. This shows an rss link in the bottom of
+        the page and let you subscribe to the wiki rss feed.
+
+        @rtype: unicode
+        @return: html head
+        """
+        link = u''
+        page = d['page']
+        if self.shouldUseRSS(page):
+            link = (u'<link rel="alternate" title="%s Recent Changes" '
+                    u'href="%s" type="application/rss+xml">') % (
+                        wikiutil.escape(self.cfg.sitename, True),
+                        wikiutil.escape(self.rsshref(page), True) )
+        return link
+
+    def html_head(self, d):
+        """ Assemble html head
+
+        @param d: parameter dictionary
+        @rtype: unicode
+        @return: html head
+        """
+        html = [
+            u'<title>%(title)s - %(sitename)s</title>' % {
+                'title': wikiutil.escape(d['title']),
+                'sitename': wikiutil.escape(d['sitename']),
+            },
+            self.externalScript('common'),
+            self.headscript(d), # Should move to separate .js file
+            self.guiEditorScript(d),
+            self.html_stylesheets(d),
+            self.rsslink(d),
+            self.universal_edit_button(d),
+            ]
+        return '\n'.join(html)
+
+    def externalScript(self, name):
+        """ Format external script html """
+        src = '%s/common/js/%s.js' % (self.request.cfg.url_prefix_static, name)
+        return '<script type="text/javascript" src="%s"></script>' % src
+
+    def universal_edit_button(self, d, **keywords):
+        """ Generate HTML for an edit link in the header."""
+        page = d['page']
+        if 'edit' in self.request.cfg.actions_excluded:
+            return ""
+        if not (page.isWritable() and
+                self.request.user.may.write(page.page_name)):
+            return ""
+        _ = self.request.getText
+        querystr = {'action': 'edit'}
+        text = _(u'Edit')
+        url = page.url(self.request, querystr=querystr, escape=0)
+        return (u'<link rel="alternate" type="application/wiki" '
+                u'title="%s" href="%s" />' % (text, url))
+
+    def credits(self, d, **keywords):
+        """ Create credits html from credits list """
+        if isinstance(self.cfg.page_credits, (list, tuple)):
+            items = ['<li>%s</li>' % i for i in self.cfg.page_credits]
+            html = '<ul id="credits">\n%s\n</ul>\n' % ''.join(items)
+        else:
+            # Old config using string, output as is
+            html = self.cfg.page_credits
+        return html
+
+    def actionsMenu(self, page):
+        """ Create actions menu list and items data dict
+
+        The menu will contain the same items always, but items that are
+        not available will be disabled (some broken browsers will let
+        you select disabled options though).
+
+        The menu should give best user experience for javascript
+        enabled browsers, and acceptable behavior for those who prefer
+        not to use Javascript.
+
+        TODO: Move actionsMenuInit() into body onload - requires that the theme will render body,
+              it is currently done in wikiutil/page.
+
+        @param page: current page, Page object
+        @rtype: unicode
+        @return: actions menu html fragment
+        """
+        request = self.request
+        _ = request.getText
+        rev = request.rev
+
+        menu = [
+            'raw',
+            'print',
+            'RenderAsDocbook',
+            'refresh',
+            '__separator__',
+            'SpellCheck',
+            'LikePages',
+            'LocalSiteMap',
+            '__separator__',
+            'RenamePage',
+            'CopyPage',
+            'DeletePage',
+            '__separator__',
+            'MyPages',
+            'SubscribeUser',
+            '__separator__',
+            'Despam',
+            'revert',
+            'PackagePages',
+            'SyncPages',
+            ]
+
+        titles = {
+            # action: menu title
+            '__title__': _("More Actions:"),
+            # Translation may need longer or shorter separator
+            '__separator__': _('------------------------'),
+            'raw': _('Raw Text'),
+            'print': _('Print View'),
+            'refresh': _('Delete Cache'),
+            'SpellCheck': _('Check Spelling'), # rename action!
+            'RenamePage': _('Rename Page'),
+            'CopyPage': _('Copy Page'),
+            'DeletePage': _('Delete Page'),
+            'LikePages': _('Like Pages'),
+            'LocalSiteMap': _('Local Site Map'),
+            'MyPages': _('My Pages'),
+            'SubscribeUser': _('Subscribe User'),
+            'Despam': _('Remove Spam'),
+            'revert': _('Revert to this revision'),
+            'PackagePages': _('Package Pages'),
+            'RenderAsDocbook': _('Render as Docbook'),
+            'SyncPages': _('Sync Pages'),
+            }
+
+        options = []
+        option = '<option value="%(action)s"%(disabled)s>%(title)s</option>'
+        # class="disabled" is a workaround for browsers that ignore
+        # "disabled", e.g IE, Safari
+        # for XHTML: data['disabled'] = ' disabled="disabled"'
+        disabled = ' disabled class="disabled"'
+
+        # Format standard actions
+        available = request.getAvailableActions(page)
+        for action in menu:
+            data = {'action': action, 'disabled': '', 'title': titles[action]}
+            # removes excluded actions from the more actions menu
+            if action in request.cfg.actions_excluded:
+                continue
+
+            # Enable delete cache only if page can use caching
+            if action == 'refresh':
+                if not page.canUseCache():
+                    data['action'] = 'show'
+                    data['disabled'] = disabled
+
+            # revert action enabled only if user can revert
+            if action == 'revert' and not request.user.may.revert(page.page_name):
+                data['action'] = 'show'
+                data['disabled'] = disabled
+
+            # SubscribeUser action enabled only if user has admin rights
+            if action == 'SubscribeUser' and not request.user.may.admin(page.page_name):
+                data['action'] = 'show'
+                data['disabled'] = disabled
+
+            # PackagePages action only if user has write rights
+            if action == 'PackagePages' and not request.user.may.write(page.page_name):
+                data['action'] = 'show'
+                data['disabled'] = disabled
+
+            # Despam action enabled only for superusers
+            if action == 'Despam' and not request.user.isSuperUser():
+                data['action'] = 'show'
+                data['disabled'] = disabled
+
+            # Special menu items. Without javascript, executing will
+            # just return to the page.
+            if action.startswith('__'):
+                data['action'] = 'show'
+
+            # Actions which are not available for this wiki, user or page
+            if (action == '__separator__' or
+                (action[0].isupper() and not action in available)):
+                data['disabled'] = disabled
+
+            options.append(option % data)
+
+        # Add custom actions not in the standard menu, except for
+        # some actions like AttachFile (we have them on top level)
+        more = [item for item in available if not item in titles and not item in ('AttachFile', )]
+        more.sort()
+        if more:
+            # Add separator
+            separator = option % {'action': 'show', 'disabled': disabled,
+                                  'title': titles['__separator__']}
+            options.append(separator)
+            # Add more actions (all enabled)
+            for action in more:
+                data = {'action': action, 'disabled': ''}
+                # Always add spaces: AttachFile -> Attach File
+                # XXX do not create page just for using split_title -
+                # creating pages for non-existent does 2 storage lookups
+                #title = Page(request, action).split_title(force=1)
+                title = action
+                # Use translated version if available
+                data['title'] = _(title)
+                options.append(option % data)
+
+        data = {
+            'label': titles['__title__'],
+            'options': '\n'.join(options),
+            'rev_field': rev and '<input type="hidden" name="rev" value="%d">' % rev or '',
+            'do_button': _("Do"),
+            'baseurl': self.request.getScriptname(),
+            'pagename_quoted': wikiutil.quoteWikinameURL(page.page_name),
+            }
+        html = '''
+<form class="actionsmenu" method="GET" action="%(baseurl)s/%(pagename_quoted)s">
+<div>
+    <label>%(label)s</label>
+    <select name="action"
+        onchange="if ((this.selectedIndex != 0) &&
+                      (this.options[this.selectedIndex].disabled == false)) {
+                this.form.submit();
+            }
+            this.selectedIndex = 0;">
+        %(options)s
+    </select>
+    <input type="submit" value="%(do_button)s">
+    %(rev_field)s
+</div>
+<script type="text/javascript">
+<!--// Init menu
+actionsMenuInit('%(label)s');
+//-->
+</script>
+</form>
+''' % data
+
+        return html
+
+    def editbar(self, d):
+        """ Assemble the page edit bar.
+
+        Create html on first call, then return cached html.
+
+        @param d: parameter dictionary
+        @rtype: unicode
+        @return: iconbar html
+        """
+        page = d['page']
+        if not self.shouldShowEditbar(page):
+            return ''
+
+        html = self._cache.get('editbar')
+        if html is None:
+            # Remove empty items and format as list
+            items = ''.join(['<li>%s</li>' % item
+                             for item in self.editbarItems(page) if item])
+            html = u'<ul class="editbar">%s</ul>\n' % items
+            self._cache['editbar'] = html
+
+        return html
+
+    def shouldShowEditbar(self, page):
+        """ Should we show the editbar?
+
+        Actions should implement this, because only the action knows if
+        the edit bar makes sense. Until it goes into actions, we do the
+        checking here.
+
+        @param page: current page
+        @rtype: bool
+        @return: true if editbar should show
+        """
+        # Show editbar only for existing pages, including deleted pages,
+        # that the user may read. If you may not read, you can't edit,
+        # so you don't need editbar.
+        if (page.exists(includeDeleted=1) and
+            self.request.user.may.read(page.page_name)):
+            form = self.request.form
+            action = self.request.action
+            # Do not show editbar on edit but on save/cancel
+            return not (action == 'edit' and
+                        not form.has_key('button_save') and
+                        not form.has_key('button_cancel'))
+        return False
+
+    def editbarItems(self, page):
+        """ Return list of items to show on the editbar
+
+        This is separate method to make it easy to customize the
+        edtibar in sub classes.
+        """
+        _ = self.request.getText
+        editbar_actions = []
+        for editbar_item in self.request.cfg.edit_bar:
+            if editbar_item == 'Discussion':
+                if not self.request.cfg.supplementation_page and self.request.getPragma('supplementation-page', 1) in ('on', '1'):
+                    editbar_actions.append(self.supplementation_page_nameLink(page))
+                elif self.request.cfg.supplementation_page and not self.request.getPragma('supplementation-page', 1) in ('off', '0'):
+                    editbar_actions.append(self.supplementation_page_nameLink(page))
+            elif editbar_item == 'Comments':
+                # we just use <a> to get same style as other links, but we add some dummy
+                # link target to get correct mouseover pointer appearance. return false
+                # keeps the browser away from jumping to the link target::
+                editbar_actions.append('<a href="#" class="toggleCommentsButton" style="display:none;" onClick="toggleComments();return false;">%s</a>' % _('Comments'))
+            elif editbar_item == 'Edit':
+                editbar_actions.append(self.editorLink(page))
+            elif editbar_item == 'Info':
+                editbar_actions.append(self.infoLink(page))
+            elif editbar_item == 'Subscribe':
+                editbar_actions.append(self.subscribeLink(page))
+            elif editbar_item == 'Quicklink':
+                editbar_actions.append(self.quicklinkLink(page))
+            elif editbar_item == 'Attachments':
+                editbar_actions.append(self.attachmentsLink(page))
+            elif editbar_item == 'ActionsMenu':
+                editbar_actions.append(self.actionsMenu(page))
+        return editbar_actions
+
+    def supplementation_page_nameLink(self, page):
+        """Return a link to the discussion page
+
+           If the discussion page doesn't exist and the user
+           has no right to create it, show a disabled link.
+        """
+        _ = self.request.getText
+        suppl_name = self.request.cfg.supplementation_page_name
+        suppl_name_full = "%s/%s" % (page.page_name, suppl_name)
+
+        test = Page(self.request, suppl_name_full)
+        if not test.exists() and not self.request.user.may.write(suppl_name_full):
+            return ('<span class="disabled">%s</span>' % _(suppl_name))
+        else:
+            return page.link_to(self.request, text=_(suppl_name),
+                                querystr={'action': 'supplementation'}, css_class='nbsupplementation', rel='nofollow')
+
+    def guiworks(self, page):
+        """ Return whether the gui editor / converter can work for that page.
+
+            The GUI editor currently only works for wiki format.
+            For simplicity, we also tell it does not work if the admin forces the text editor.
+        """
+        is_wiki = page.pi['format'] == 'wiki'
+        gui_disallowed = self.cfg.editor_force and self.cfg.editor_default == 'text'
+        return is_wiki and not gui_disallowed
+
+
+    def editorLink(self, page):
+        """ Return a link to the editor
+
+        If the user can't edit, return a disabled edit link.
+
+        If the user want to show both editors, it will display "Edit
+        (Text)", otherwise as "Edit".
+        """
+        if 'edit' in self.request.cfg.actions_excluded:
+            return ""
+
+        if not (page.isWritable() and
+                self.request.user.may.write(page.page_name)):
+            return self.disabledEdit()
+
+        _ = self.request.getText
+        querystr = {'action': 'edit'}
+
+        guiworks = self.guiworks(page)
+        if self.showBothEditLinks() and guiworks:
+            text = _('Edit (Text)')
+            querystr['editor'] = 'text'
+            attrs = {'name': 'texteditlink', 'rel': 'nofollow', }
+        else:
+            text = _('Edit')
+            if guiworks:
+                # 'textonly' will be upgraded dynamically to 'guipossible' by JS
+                querystr['editor'] = 'textonly'
+                attrs = {'name': 'editlink', 'rel': 'nofollow', }
+            else:
+                querystr['editor'] = 'text'
+                attrs = {'name': 'texteditlink', 'rel': 'nofollow', }
+
+        return page.link_to(self.request, text=text, querystr=querystr, **attrs)
+
+    def showBothEditLinks(self):
+        """ Return True if both edit links should be displayed """
+        editor = self.request.user.editor_ui
+        if editor == '<default>':
+            editor = self.request.cfg.editor_ui
+        return editor == 'freechoice'
+
+    def guiEditorScript(self, d):
+        """ Return a script that set the gui editor link variables
+
+        The link will be created only when javascript is enabled and
+        the browser is compatible with the editor.
+        """
+        page = d['page']
+        if not (page.isWritable() and
+                self.request.user.may.write(page.page_name) and
+                self.showBothEditLinks() and
+                self.guiworks(page)):
+            return ''
+
+        _ = self.request.getText
+        return """\
+<script type="text/javascript">
+<!-- // GUI edit link and i18n
+var gui_editor_link_href = "%(url)s";
+var gui_editor_link_text = "%(text)s";
+//-->
+</script>
+""" % {'url': page.url(self.request, querystr={'action': 'edit', 'editor': 'gui', }),
+       'text': _('Edit (GUI)'),
+      }
+
+    def disabledEdit(self):
+        """ Return a disabled edit link """
+        _ = self.request.getText
+        return ('<span class="disabled">%s</span>'
+                % _('Immutable Page'))
+
+    def infoLink(self, page):
+        """ Return link to page information """
+        if 'info' in self.request.cfg.actions_excluded:
+            return ""
+
+        _ = self.request.getText
+        return page.link_to(self.request,
+                            text=_('Info'),
+                            querystr={'action': 'info'}, css_class='nbinfo', rel='nofollow')
+
+    def subscribeLink(self, page):
+        """ Return subscribe/unsubscribe link to valid users
+
+        @rtype: unicode
+        @return: subscribe or unsubscribe link
+        """
+        if not ((self.cfg.mail_enabled or self.cfg.jabber_enabled) and self.request.user.valid):
+            return ''
+
+        _ = self.request.getText
+        if self.request.user.isSubscribedTo([page.page_name]):
+            action, text = 'unsubscribe', _("Unsubscribe")
+        else:
+            action, text = 'subscribe', _("Subscribe")
+        if action in self.request.cfg.actions_excluded:
+            return ""
+        return page.link_to(self.request, text=text, querystr={'action': action}, css_class='nbsubscribe', rel='nofollow')
+
+    def quicklinkLink(self, page):
+        """ Return add/remove quicklink link
+
+        @rtype: unicode
+        @return: link to add or remove a quicklink
+        """
+        if not self.request.user.valid:
+            return ''
+
+        _ = self.request.getText
+        if self.request.user.isQuickLinkedTo([page.page_name]):
+            action, text = 'quickunlink', _("Remove Link")
+        else:
+            action, text = 'quicklink', _("Add Link")
+        if action in self.request.cfg.actions_excluded:
+            return ""
+        return page.link_to(self.request, text=text, querystr={'action': action}, css_class='nbquicklink', rel='nofollow')
+
+    def attachmentsLink(self, page):
+        """ Return link to page attachments """
+        if 'AttachFile' in self.request.cfg.actions_excluded:
+            return ""
+
+        _ = self.request.getText
+        return page.link_to(self.request,
+                            text=_('Attachments'),
+                            querystr={'action': 'AttachFile'}, css_class='nbattachments', rel='nofollow')
+
+    def startPage(self):
+        """ Start page div with page language and direction
+
+        @rtype: unicode
+        @return: page div with language and direction attribtues
+        """
+        return u'<div id="page"%s>\n' % self.content_lang_attr()
+
+    def endPage(self):
+        """ End page div
+
+        Add an empty page bottom div to prevent floating elements to
+        float out of the page bottom over the footer.
+        """
+        return '<div id="pagebottom"></div>\n</div>\n'
+
+    # Public functions #####################################################
+
+    def header(self, d, **kw):
+        """ Assemble page header
+
+        Default behavior is to start a page div. Sub class and add
+        footer items.
+
+        @param d: parameter dictionary
+        @rtype: string
+        @return: page header html
+        """
+        return self.startPage()
+
+    editorheader = header
+
+    def footer(self, d, **keywords):
+        """ Assemble page footer
+
+        Default behavior is to end page div. Sub class and add
+        footer items.
+
+        @param d: parameter dictionary
+        @keyword ...:...
+        @rtype: string
+        @return: page footer html
+        """
+        return self.endPage()
+
+    # RecentChanges ######################################################
+
+    def recentchanges_entry(self, d):
+        """
+        Assemble a single recentchanges entry (table row)
+
+        @param d: parameter dictionary
+        @rtype: string
+        @return: recentchanges entry html
+        """
+        _ = self.request.getText
+        html = []
+        html.append('<tr>\n')
+
+        html.append('<td class="rcicon1">%(icon_html)s</td>\n' % d)
+
+        html.append('<td class="rcpagelink">%(pagelink_html)s</td>\n' % d)
+
+        html.append('<td class="rctime">')
+        if d['time_html']:
+            html.append("%(time_html)s" % d)
+        html.append('</td>\n')
+
+        html.append('<td class="rcicon2">%(info_html)s</td>\n' % d)
+
+        html.append('<td class="rceditor">')
+        if d['editors']:
+            html.append('<br>'.join(d['editors']))
+        html.append('</td>\n')
+
+        html.append('<td class="rccomment">')
+        if d['comments']:
+            if d['changecount'] > 1:
+                notfirst = 0
+                for comment in d['comments']:
+                    html.append('%s<tt>#%02d</tt> %s' % (
+                        notfirst and '<br>' or '', comment[0], comment[1]))
+                    notfirst = 1
+            else:
+                comment = d['comments'][0]
+                html.append('%s' % comment[1])
+        html.append('</td>\n')
+
+        html.append('</tr>\n')
+
+        return ''.join(html)
+
+    def recentchanges_daybreak(self, d):
+        """
+        Assemble a rc daybreak indication (table row)
+
+        @param d: parameter dictionary
+        @rtype: string
+        @return: recentchanges daybreak html
+        """
+        if d['bookmark_link_html']:
+            set_bm = '  %(bookmark_link_html)s' % d
+        else:
+            set_bm = ''
+        return ('<tr class="rcdaybreak"><td colspan="%d">'
+                '<strong>%s</strong>'
+                '%s'
+                '</td></tr>\n') % (6, d['date'], set_bm)
+
+    def recentchanges_header(self, d):
+        """
+        Assemble the recentchanges header (intro + open table)
+
+        @param d: parameter dictionary
+        @rtype: string
+        @return: recentchanges header html
+        """
+        _ = self.request.getText
+
+        # Should use user interface language and direction
+        html = '<div class="recentchanges"%s>\n' % self.ui_lang_attr()
+        html += '<div>\n'
+        page = d['page']
+        if self.shouldUseRSS(page):
+            link = [
+                u'<div class="rcrss">',
+                self.request.formatter.url(1, self.rsshref(page)),
+                self.request.formatter.rawHTML(self.make_icon("rss")),
+                self.request.formatter.url(0),
+                u'</div>',
+                ]
+            html += ''.join(link)
+        html += '<p>'
+        # Add day selector
+        if d['rc_days']:
+            days = []
+            for day in d['rc_days']:
+                if day == d['rc_max_days']:
+                    days.append('<strong>%d</strong>' % day)
+                else:
+                    days.append(
+                        wikiutil.link_tag(self.request,
+                            '%s?max_days=%d' % (d['q_page_name'], day),
+                            str(day),
+                            self.request.formatter, rel='nofollow'))
+            days = ' | '.join(days)
+            html += (_("Show %s days.") % (days, ))
+
+        if d['rc_update_bookmark']:
+            html += " %(rc_update_bookmark)s %(rc_curr_bookmark)s" % d
+
+        html += '</p>\n</div>\n'
+
+        html += '<table>\n'
+        return html
+
+    def recentchanges_footer(self, d):
+        """
+        Assemble the recentchanges footer (close table)
+
+        @param d: parameter dictionary
+        @rtype: string
+        @return: recentchanges footer html
+        """
+        _ = self.request.getText
+        html = ''
+        html += '</table>\n'
+        if d['rc_msg']:
+            html += "<br>%(rc_msg)s\n" % d
+        html += '</div>\n'
+        return html
+
+    # Language stuff ####################################################
+
+    def ui_lang_attr(self):
+        """Generate language attributes for user interface elements
+
+        User interface elements use the user language (if any), kept in
+        request.lang.
+
+        @rtype: string
+        @return: lang and dir html attributes
+        """
+        lang = self.request.lang
+        return ' lang="%s" dir="%s"' % (lang, i18n.getDirection(lang))
+
+    def content_lang_attr(self):
+        """Generate language attributes for wiki page content
+
+        Page content uses the page language or the wiki default language.
+
+        @rtype: string
+        @return: lang and dir html attributes
+        """
+        lang = self.request.content_lang
+        return ' lang="%s" dir="%s"' % (lang, i18n.getDirection(lang))
+
+    def add_msg(self, msg, msg_class=None):
+        """ Adds a message to a list which will be used to generate status
+        information.
+
+        @param msg: additional message
+        @param msg_class: html class for the div of the additional message.
+        """
+        if not msg_class:
+            msg_class = 'dialog'
+        if self._send_title_called:
+            raise Exception("You cannot call add_msg() after send_title()")
+        self._status.append((msg, msg_class))
+
+    # stuff from wikiutil.py
+    def send_title(self, text, **keywords):
+        """
+        Output the page header (and title).
+
+        @param text: the title text
+        @keyword page: the page instance that called us - using this is more efficient than using pagename..
+        @keyword pagename: 'PageName'
+        @keyword print_mode: 1 (or 0)
+        @keyword editor_mode: 1 (or 0)
+        @keyword media: css media type, defaults to 'screen'
+        @keyword allow_doubleclick: 1 (or 0)
+        @keyword html_head: additional <head> code
+        @keyword body_attr: additional <body> attributes
+        @keyword body_onload: additional "onload" JavaScript code
+        """
+        request = self.request
+        _ = request.getText
+        rev = request.rev
+
+        if keywords.has_key('page'):
+            page = keywords['page']
+            pagename = page.page_name
+        else:
+            pagename = keywords.get('pagename', '')
+            page = Page(request, pagename)
+        if keywords.get('msg', ''):
+            raise DeprecationWarning("Using send_page(msg=) is deprecated! Use theme.add_msg() instead!")
+        scriptname = request.getScriptname()
+        pagename_quoted = wikiutil.quoteWikinameURL(pagename)
+
+        # get name of system pages
+        page_front_page = wikiutil.getFrontPage(request).page_name
+        page_help_contents = wikiutil.getLocalizedPage(request, 'HelpContents').page_name
+        page_title_index = wikiutil.getLocalizedPage(request, 'TitleIndex').page_name
+        page_site_navigation = wikiutil.getLocalizedPage(request, 'SiteNavigation').page_name
+        page_word_index = wikiutil.getLocalizedPage(request, 'WordIndex').page_name
+        page_help_formatting = wikiutil.getLocalizedPage(request, 'HelpOnFormatting').page_name
+        page_find_page = wikiutil.getLocalizedPage(request, 'FindPage').page_name
+        home_page = wikiutil.getInterwikiHomePage(request) # sorry theme API change!!! Either None or tuple (wikiname,pagename) now.
+        page_parent_page = getattr(page.getParentPage(), 'page_name', None)
+
+        # Prepare the HTML <head> element
+        user_head = [request.cfg.html_head]
+
+        # include charset information - needed for moin_dump or any other case
+        # when reading the html without a web server
+        user_head.append('''<meta http-equiv="Content-Type" content="%s;charset=%s">\n''' % (page.output_mimetype, page.output_charset))
+
+        meta_keywords = request.getPragma('keywords')
+        meta_desc = request.getPragma('description')
+        if meta_keywords:
+            user_head.append('<meta name="keywords" content="%s">\n' % wikiutil.escape(meta_keywords, 1))
+        if meta_desc:
+            user_head.append('<meta name="description" content="%s">\n' % wikiutil.escape(meta_desc, 1))
+
+        # search engine precautions / optimization:
+        # if it is an action or edit/search, send query headers (noindex,nofollow):
+        if request.query_string:
+            user_head.append(request.cfg.html_head_queries)
+        elif request.request_method == 'POST':
+            user_head.append(request.cfg.html_head_posts)
+        # we don't want to have BadContent stuff indexed:
+        elif pagename in ['BadContent', 'LocalBadContent', ]:
+            user_head.append(request.cfg.html_head_posts)
+        # if it is a special page, index it and follow the links - we do it
+        # for the original, English pages as well as for (the possibly
+        # modified) frontpage:
+        elif pagename in [page_front_page, request.cfg.page_front_page,
+                          page_title_index, 'TitleIndex',
+                          page_find_page, 'FindPage',
+                          page_site_navigation, 'SiteNavigation',
+                          'RecentChanges', ]:
+            user_head.append(request.cfg.html_head_index)
+        # if it is a normal page, index it, but do not follow the links, because
+        # there are a lot of illegal links (like actions) or duplicates:
+        else:
+            user_head.append(request.cfg.html_head_normal)
+
+        if 'pi_refresh' in keywords and keywords['pi_refresh']:
+            user_head.append('<meta http-equiv="refresh" content="%d;URL=%s">' % keywords['pi_refresh'])
+
+        # output buffering increases latency but increases throughput as well
+        output = []
+        # later: <html xmlns=\"http://www.w3.org/1999/xhtml\">
+        output.append("""\
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
+<html>
+<head>
+%s
+%s
+%s
+""" % (
+            ''.join(user_head),
+            self.html_head({
+                'page': page,
+                'title': text,
+                'sitename': request.cfg.html_pagetitle or request.cfg.sitename,
+                'print_mode': keywords.get('print_mode', False),
+                'media': keywords.get('media', 'screen'),
+            }),
+            keywords.get('html_head', ''),
+        ))
+
+        # Links
+        output.append('<link rel="Start" href="%s/%s">\n' % (scriptname, wikiutil.quoteWikinameURL(page_front_page)))
+        if pagename:
+            output.append('<link rel="Alternate" title="%s" href="%s/%s?action=raw">\n' % (
+                _('Wiki Markup'), scriptname, pagename_quoted, ))
+            output.append('<link rel="Alternate" media="print" title="%s" href="%s/%s?action=print">\n' % (
+                _('Print View'), scriptname, pagename_quoted, ))
+
+            # !!! currently disabled due to Mozilla link prefetching, see
+            # http://www.mozilla.org/projects/netlib/Link_Prefetching_FAQ.html
+            #~ all_pages = request.getPageList()
+            #~ if all_pages:
+            #~     try:
+            #~         pos = all_pages.index(pagename)
+            #~     except ValueError:
+            #~         # this shopuld never happend in theory, but let's be sure
+            #~         pass
+            #~     else:
+            #~         request.write('<link rel="First" href="%s/%s">\n' % (request.getScriptname(), quoteWikinameURL(all_pages[0]))
+            #~         if pos > 0:
+            #~             request.write('<link rel="Previous" href="%s/%s">\n' % (request.getScriptname(), quoteWikinameURL(all_pages[pos-1])))
+            #~         if pos+1 < len(all_pages):
+            #~             request.write('<link rel="Next" href="%s/%s">\n' % (request.getScriptname(), quoteWikinameURL(all_pages[pos+1])))
+            #~         request.write('<link rel="Last" href="%s/%s">\n' % (request.getScriptname(), quoteWikinameURL(all_pages[-1])))
+
+            if page_parent_page:
+                output.append('<link rel="Up" href="%s/%s">\n' % (scriptname, wikiutil.quoteWikinameURL(page_parent_page)))
+
+        # write buffer because we call AttachFile
+        request.write(''.join(output))
+        output = []
+
+        # XXX maybe this should be removed completely. moin emits all attachments as <link rel="Appendix" ...>
+        # and it is at least questionable if this fits into the original intent of rel="Appendix".
+        if pagename and request.user.may.read(pagename):
+            from MoinMoin.action import AttachFile
+            AttachFile.send_link_rel(request, pagename)
+
+        output.extend([
+            '<link rel="Search" href="%s/%s">\n' % (scriptname, wikiutil.quoteWikinameURL(page_find_page)),
+            '<link rel="Index" href="%s/%s">\n' % (scriptname, wikiutil.quoteWikinameURL(page_title_index)),
+            '<link rel="Glossary" href="%s/%s">\n' % (scriptname, wikiutil.quoteWikinameURL(page_word_index)),
+            '<link rel="Help" href="%s/%s">\n' % (scriptname, wikiutil.quoteWikinameURL(page_help_formatting)),
+                      ])
+
+        output.append("</head>\n")
+        request.write(''.join(output))
+        output = []
+        request.flush()
+
+        # start the <body>
+        bodyattr = []
+        if keywords.has_key('body_attr'):
+            bodyattr.append(' ')
+            bodyattr.append(keywords['body_attr'])
+
+        # Add doubleclick edit action
+        if (pagename and keywords.get('allow_doubleclick', 0) and
+            not keywords.get('print_mode', 0) and
+            request.user.edit_on_doubleclick):
+            if request.user.may.write(pagename): # separating this gains speed
+                url = page.url(request, {'action': 'edit'})
+                bodyattr.append(''' ondblclick="location.href='%s'" ''' % wikiutil.escape(url, True))
+
+        # Set body to the user interface language and direction
+        bodyattr.append(' %s' % self.ui_lang_attr())
+
+        body_onload = keywords.get('body_onload', '')
+        if body_onload:
+            bodyattr.append(''' onload="%s"''' % body_onload)
+        output.append('\n<body%s>\n' % ''.join(bodyattr))
+
+        # Output -----------------------------------------------------------
+
+        # If in print mode, start page div and emit the title
+        if keywords.get('print_mode', 0):
+            d = {
+                'title_text': text,
+                'page': page,
+                'page_name': pagename or '',
+                'rev': rev,
+            }
+            request.themedict = d
+            output.append(self.startPage())
+            output.append(self.interwiki(d))
+            output.append(self.title(d))
+
+        # In standard mode, emit theme.header
+        else:
+            exists = pagename and page.exists(includeDeleted=True)
+            # prepare dict for theme code:
+            d = {
+                'theme': self.name,
+                'script_name': scriptname,
+                'title_text': text,
+                'logo_string': request.cfg.logo_string,
+                'site_name': request.cfg.sitename,
+                'page': page,
+                'rev': rev,
+                'pagesize': pagename and page.size() or 0,
+                # exists checked to avoid creation of empty edit-log for non-existing pages
+                'last_edit_info': exists and page.lastEditInfo() or '',
+                'page_name': pagename or '',
+                'page_find_page': page_find_page,
+                'page_front_page': page_front_page,
+                'home_page': home_page,
+                'page_help_contents': page_help_contents,
+                'page_help_formatting': page_help_formatting,
+                'page_parent_page': page_parent_page,
+                'page_title_index': page_title_index,
+                'page_word_index': page_word_index,
+                'user_name': request.user.name,
+                'user_valid': request.user.valid,
+                'msg': self._status,
+                'trail': keywords.get('trail', None),
+                # Discontinued keys, keep for a while for 3rd party theme developers
+                'titlesearch': 'use self.searchform(d)',
+                'textsearch': 'use self.searchform(d)',
+                'navibar': ['use self.navibar(d)'],
+                'available_actions': ['use self.request.availableActions(page)'],
+            }
+
+            # add quoted versions of pagenames
+            newdict = {}
+            for key in d:
+                if key.startswith('page_'):
+                    if not d[key] is None:
+                        newdict['q_'+key] = wikiutil.quoteWikinameURL(d[key])
+                    else:
+                        newdict['q_'+key] = None
+            d.update(newdict)
+            request.themedict = d
+
+            # now call the theming code to do the rendering
+            if keywords.get('editor_mode', 0):
+                output.append(self.editorheader(d))
+            else:
+                output.append(self.header(d))
+
+        # emit it
+        request.write(''.join(output))
+        output = []
+        request.flush()
+        self._send_title_called = True
+
+    def send_footer(self, pagename, **keywords):
+        """
+        Output the page footer.
+
+        @param pagename: WikiName of the page
+        @keyword print_mode: true, when page is displayed in Print mode
+        """
+        request = self.request
+        d = request.themedict
+
+        # Emit end of page in print mode, or complete footer in standard mode
+        if keywords.get('print_mode', 0):
+            request.write(self.pageinfo(d['page']))
+            request.write(self.endPage())
+        else:
+            request.write(self.footer(d, **keywords))
+
+    # stuff moved from request.py
+    def send_closing_html(self):
+        """ generate timing info html and closing html tag,
+            everyone calling send_title must call this at the end to close
+            the body and html tags.
+        """
+        request = self.request
+
+        # as this is the last chance to emit some html, we stop the clocks:
+        request.clock.stop('run')
+        request.clock.stop('total')
+
+        # Close html code
+        if request.cfg.show_timings and request.action != 'print':
+            request.write('<ul id="timings">\n')
+            for t in request.clock.dump():
+                request.write('<li>%s</li>\n' % t)
+            request.write('</ul>\n')
+        #request.write('<!-- auth_method == %s -->' % repr(request.user.auth_method))
+        request.write('</body>\n</html>\n\n')
+
+
 
--- /dev/null
+# -*- coding: iso-8859-1 -*-
+"""
+    MoinMoin - Debian theme
+
+    @copyright: 2003-2005 Nir Soffer, Thomas Waldmann
+    @license: GNU GPL, see COPYING for details.
+       theme modified by Kalle Soderman
+"""
+
+from MoinMoin.theme import ThemeBase
+from MoinMoin import wikiutil
+from MoinMoin.Page import Page
+
+class Theme(ThemeBase):
+
+    name = "debwiki"
+
+    _ = lambda x: x     # We don't have gettext at this moment, so we fake it
+    icons = {
+        # key         alt                        icon filename      w   h
+        # FileAttach
+        'attach':     ("%(attach_count)s",       "moin-attach.png",   16, 16),
+        'info':       ("[INFO]",                 "moin-info.png",     16, 16),
+        'attachimg':  (_("[ATTACH]"),            "attach.png",        32, 32),
+        # RecentChanges
+        'rss':        (_("[RSS]"),               "moin-rss.png",      16, 16),
+        'deleted':    (_("[DELETED]"),           "moin-deleted.png",  16, 16),
+        'updated':    (_("[UPDATED]"),           "moin-updated.png",  16, 16),
+        'renamed':    (_("[RENAMED]"),           "moin-renamed.png",  16, 16),
+        'conflict':   (_("[CONFLICT]"),          "moin-conflict.png", 16, 16),
+        'new':        (_("[NEW]"),               "moin-new.png",      16, 16),
+        'diffrc':     (_("[DIFF]"),              "moin-diff.png",     16, 16),
+        # General
+        'bottom':     (_("[BOTTOM]"),            "moin-bottom.png",   16, 16),
+        'top':        (_("[TOP]"),               "moin-top.png",      16, 16),
+        'www':        ("[WWW]",                  "moin-www.png",      16, 16),
+        'mailto':     ("[MAILTO]",               "moin-email.png",    16, 16),
+        'news':       ("[NEWS]",                 "moin-news.png",     16, 16),
+        'telnet':     ("[TELNET]",               "moin-telnet.png",   16, 16),
+        'ftp':        ("[FTP]",                  "moin-ftp.png",      16, 16),
+        'file':       ("[FILE]",                 "moin-ftp.png",      16, 16),
+        # search forms
+        'searchbutton': ("[?]",                  "moin-search.png",   16, 16),
+        'interwiki':  ("[%(wikitag)s]",          "moin-inter.png",    16, 16),
+
+        # smileys (this is CONTENT, but good looking smileys depend on looking
+        # adapted to the theme background color and theme style in general)
+        #vvv    ==      vvv  this must be the same for GUI editor converter
+        'X-(':        ("X-(",                    'angry.png',         16, 16),
+        ':D':         (":D",                     'biggrin.png',       16, 16),
+        '<:(':        ("<:(",                    'frown.png',         16, 16),
+        ':o':         (":o",                     'redface.png',       16, 16),
+        ':(':         (":(",                     'sad.png',           16, 16),
+        ':)':         (":)",                     'smile.png',         16, 16),
+        'B)':         ("B)",                     'smile2.png',        16, 16),
+        ':))':        (":))",                    'smile3.png',        16, 16),
+        ';)':         (";)",                     'smile4.png',        16, 16),
+        '/!\\':       ("/!\\",                   'alert.png',         16, 16),
+        '<!>':        ("<!>",                    'attention.png',     16, 16),
+        '(!)':        ("(!)",                    'idea.png',          16, 16),
+        ':-?':        (":-?",                    'tongue.png',        16, 16),
+        ':\\':        (":\\",                    'ohwell.png',        16, 16),
+        '>:>':        (">:>",                    'devil.png',         16, 16),
+        '|)':         ("|)",                     'tired.png',         16, 16),
+        ':-(':        (":-(",                    'sad.png',           16, 16),
+        ':-)':        (":-)",                    'smile.png',         16, 16),
+        'B-)':        ("B-)",                    'smile2.png',        16, 16),
+        ':-))':       (":-))",                   'smile3.png',        16, 16),
+        ';-)':        (";-)",                    'smile4.png',        16, 16),
+        '|-)':        ("|-)",                    'tired.png',         16, 16),
+        '(./)':       ("(./)",                   'checkmark.png',     16, 16),
+        '{OK}':       ("{OK}",                   'thumbs-up.png',     16, 16),
+        '{X}':        ("{X}",                    'icon-error.png',    16, 16),
+        '{i}':        ("{i}",                    'icon-info.png',     16, 16),
+        '{1}':        ("{1}",                    'prio1.png',         15, 13),
+        '{2}':        ("{2}",                    'prio2.png',         15, 13),
+        '{3}':        ("{3}",                    'prio3.png',         15, 13),
+        '{*}':        ("{*}",                    'star_on.png',       16, 16),
+        '{o}':        ("{o}",                    'star_off.png',      16, 16),
+    }
+    del _
+    def header(self, d, **kw):
+        """ Assemble wiki header
+
+        @param d: parameter dictionary
+        @rtype: unicode
+        @return: page header html
+        """
+        html = [
+            # Pre header custom html
+            self.emit_custom_html(self.cfg.page_header1),
+
+            # Header
+            u'<div id="header">',
+            self.searchform(d),
+            #self.logo(),
+            u'<div id="domaintools"><p class="section">Wiki</p>',
+            self.username(d),
+            u'</div>',
+            u'<div id="navbar">',
+            self.navibar(d),
+            u'</div>',
+            u'<div id="breadcrumbs">',
+            u'<a href="/" title="Wiki Homepage">Wiki</a><span class="sep">/</span>',
+            #self.interwiki(d),
+            self.title(d),
+            u'</div>',
+
+            #self.trail(d),
+            #u'<hr id="pageline">',
+            self.msg(d),
+            u'<h1 id="locationline">',
+            self.title(d),
+            u'</h1>',
+            u'<div id="pageline"><hr style="display:none;"></div>',
+            #self.editbar(d),
+            u'</div>',
+
+            # Post header custom html (not recommended)
+            self.emit_custom_html(self.cfg.page_header2),
+
+            # Start of page
+            self.startPage(),
+        ]
+        return u'\n'.join(html)
+
+    def editorheader(self, d, **kw):
+        """ Assemble wiki header for editor
+
+        @param d: parameter dictionary
+        @rtype: unicode
+        @return: page header html
+        """
+        html = [
+            # Pre header custom html
+            self.emit_custom_html(self.cfg.page_header1),
+
+            # Header
+            u'<div id="header">',
+            u'<h1 id="locationline">',
+            self.title(d),
+            u'</h1>',
+            self.msg(d),
+            u'</div>',
+
+            # Post header custom html (not recommended)
+            self.emit_custom_html(self.cfg.page_header2),
+
+            # Start of page
+            self.startPage(),
+        ]
+        return u'\n'.join(html)
+
+    def footer(self, d, **keywords):
+        """ Assemble wiki footer
+
+        @param d: parameter dictionary
+        @keyword ...:...
+        @rtype: unicode
+        @return: page footer html
+        """
+        page = d['page']
+        html = [
+            # End of page
+            self.pageinfo(page),
+            self.endPage(),
+
+            # Pre footer custom html (not recommended!)
+            self.emit_custom_html(self.cfg.page_footer1),
+
+            # Footer
+            u'<div id="footer">',
+            self.editbar(d),
+            self.credits(d),
+            self.showversion(d, **keywords),
+            u'</div>',
+
+            # Post footer custom html
+            self.emit_custom_html(self.cfg.page_footer2),
+            ]
+        return u'\n'.join(html)
+
+    def title(self, d):
+        """ Assemble the title (now using breadcrumbs)
+
+        @param d: parameter dictionary
+        @rtype: string
+        @return: title html
+        """
+        _ = self.request.getText
+        content = []
+        if d['title_text'] == d['page'].split_title(): # just showing a page, no action
+            curpage = ''
+            segments = d['page_name'].split('/') # was: title_text
+            for s in segments[:-1]:
+                curpage += s
+                content.append(Page(self.request, curpage).link_to(self.request, s))
+                curpage += '/'
+            link_text = segments[-1]
+            link_title = _('Click to do a full-text search for this title')
+            link_query = {
+                'action': 'fullsearch',
+                'value': 'linkto:"%s"' % d['page_name'],
+                'context': '180',
+            }
+            # we dont use d['title_link'] any more, but make it ourselves:
+            link = d['page'].link_to(self.request, link_text, querystr=link_query, title=link_title, css_class='backlink', rel='nofollow')
+            content.append(link)
+        else:
+            content.append(wikiutil.escape(d['title_text']))
+
+        location_html = u'<span class="sep">/</span>'.join(content)
+        html = u'<span id="pagelocation">%s</span>' % location_html
+        return html
+
+    def username(self, d):
+        """ Assemble the username / userprefs link
+
+        @param d: parameter dictionary
+        @rtype: unicode
+        @return: username html
+        """
+        request = self.request
+        _ = request.getText
+
+        userlinks = []
+        # Add username/homepage link for registered users. We don't care
+        # if it exists, the user can create it.
+        if request.user.valid and request.user.name:
+            interwiki = wikiutil.getInterwikiHomePage(request)
+            name = request.user.name
+            aliasname = request.user.aliasname
+            if not aliasname:
+                aliasname = name
+            title = "%s @ %s" % (aliasname, interwiki[0])
+            # link to (interwiki) user homepage
+            homelink = (request.formatter.interwikilink(1, title=title, id="userhome", generated=True, *interwiki) +
+                        request.formatter.text(name) +
+                        request.formatter.interwikilink(0, title=title, id="userhome", *interwiki))
+            userlinks.append(homelink)
+            # link to userprefs action
+            if 'userprefs' not in self.request.cfg.actions_excluded:
+                userlinks.append(d['page'].link_to(request, text=_('Settings'),
+                                               querystr={'action': 'userprefs'}, id='userprefs', rel='nofollow'))
+
+        if request.user.valid:
+            if request.user.auth_method in request.cfg.auth_can_logout:
+                userlinks.append(d['page'].link_to(request, text=_('Logout'),
+                                                   querystr={'action': 'logout', 'logout': 'logout'}, id='logout', rel='nofollow'))
+        else:
+            query = {'action': 'login'}
+            # special direct-login link if the auth methods want no input
+            if request.cfg.auth_login_inputs == ['special_no_input']:
+                query['login'] = '1'
+            if request.cfg.auth_have_login:
+                userlinks.append(d['page'].link_to(request, text=_("Login"),
+                                                   querystr=query, id='login', rel='nofollow'))
+
+        userlinks_html = u'<span class="sep"> | </span>'.join(userlinks)
+        html = u'<div id="username">%s</div>' % userlinks_html
+        return html
+
+    def trail(self, d):
+        """ Assemble page trail
+
+        @param d: parameter dictionary
+        @rtype: unicode
+        @return: trail html
+        """
+        request = self.request
+        user = request.user
+        html = ''
+        if not user.valid or user.show_page_trail:
+            trail = user.getTrail()
+            if trail:
+                items = []
+                for pagename in trail:
+                    try:
+                        interwiki, page = wikiutil.split_interwiki(pagename)
+                        if interwiki != request.cfg.interwikiname and interwiki != 'Self':
+                            link = (self.request.formatter.interwikilink(True, interwiki, page) +
+                                    self.shortenPagename(page) +
+                                    self.request.formatter.interwikilink(False, interwiki, page))
+                            items.append(link)
+                            continue
+                        else:
+                            pagename = page
+
+                    except ValueError:
+                        pass
+                    page = Page(request, pagename)
+                    title = page.split_title()
+                    title = self.shortenPagename(title)
+                    link = page.link_to(request, title)
+                    items.append(link)
+                html = u'<div id="pagetrail">%s</div>' % u'<span class="sep">/</span>'.join(items)
+        return html
+
+    def interwiki(self, d):
+        """ Assemble the interwiki name display, linking to page_front_page
+
+        @param d: parameter dictionary
+        @rtype: string
+        @return: interwiki html
+        """
+        if self.request.cfg.show_interwiki:
+            page = wikiutil.getFrontPage(self.request)
+            text = self.request.cfg.interwikiname or 'Self'
+            link = page.link_to(self.request, text=text, rel='nofollow')
+            html = u'<span id="interwiki">%s<span class="sep">: </span></span>' % link
+        else:
+            html = u''
+        return html
+
+def execute(request):
+    """
+    Generate and return a theme object
+
+    @param request: the request object
+    @rtype: MoinTheme
+    @return: Theme object
+    """
+    return Theme(request)
+