Attachment 'explorer-2.1.1alpha.py'

Download

   1 # -*- coding: iso-8859-1 -*-
   2 """
   3     MoinMoin - explorer theme
   4     
   5     @copyright: 2007, 2008 Wolfgang Fischer
   6     @license: GNU GPL, see COPYING for details.
   7 """
   8 
   9 from MoinMoin.theme import ThemeBase
  10 
  11 class Theme(ThemeBase):
  12     from MoinMoin.action import AttachFile
  13     from MoinMoin import i18n, wikiutil
  14     from MoinMoin.Page import Page
  15 
  16     name = 'explorer'
  17 
  18     
  19     # ========================================
  20     # Iconbar and UI text definition
  21     # ========================================
  22     # fake _ function to get gettext recognize those texts:
  23     _ = lambda x: x
  24 
  25     ui_text = { 'splitbar_title' : _('Drag to resize. Double click to show or hide tree.') }
  26 
  27     iconbar_list = ['home', 'changes', 'separator', 'login', 'separator', 'showcomments', 'subscribe', 'quicklink', 'separator', 'edit', 'rename', 'copy', 'load', 'save', 'delete', 'separator', 'attach', 'spellcheck', 'diff', 'info', 'print', 'raw', 'refresh', 'separator', 'help', 'separator', 'find', 'likepages', 'sitemap', 'separator', 'searchform']
  28 
  29     button_table = {
  30         # key           page, query dict, title, icon-key
  31         'separator':   ("", {}, _(""), "separator"),
  32         'searchform':  ("", {}, _(""), "searchform"),
  33         'login':       ("", {'action': 'login'}, _("Login"), "login"),
  34         'logout':      ("", {'action': 'logout', 'logout': 'logout'}, _("Logout"), "logout"),
  35         'preferences': ("", {'action': 'userprefs'}, _("Preferences"), "preferences"),
  36         'showcomments':  ("", {}, _("Comments"), "showcomments"),
  37         'hidecomments':  ("", {}, _("Comments"), "hidecomments"),
  38         'subscribe':   ("", {'action': 'subscribe'}, _("Subscribe"), "subscribe"),
  39         'unsubscribe': ("", {'action': 'unsubscribe'}, _("UnSubscribe"), "unsubscribe"),
  40         'quicklink':   ("", {'action': 'quicklink'}, _("Add Link"), "quicklink"),
  41         'unquicklink': ("", {'action': 'quickunlink'}, _("Remove Link"), "unquicklink"),
  42         'home':        ("%(page_front_page)s", {}, _("Home"), "home"),
  43         'find':        ("%(page_find_page)s", {}, "%(page_find_page)s", "find"),
  44         'changes':     ("RecentChanges", {}, "RecentChanges", "changes"),
  45         'likepages':   ("", {'action': 'LikePages'}, _("Like Pages"), "likepages"),
  46         'sitemap':     ("", {'action': 'LocalSiteMap'}, _("Local Site Map"), "sitemap"),
  47         'edit_text':   ("", {'action': 'edit', 'editor': 'text'}, _("Edit (Text)"), "edit_text"),
  48         'edit_gui':    ("", {'action': 'edit', 'editor': 'gui'}, _("Edit (GUI)"), "edit_gui"),
  49         'attach':      ("", {'action': 'AttachFile'}, _("Attachments"), "attach"),
  50         'spellcheck':  ("", {'action': 'SpellCheck'}, _("Check Spelling"), "spellcheck"),
  51         'rename':      ("", {'action': 'RenamePage'}, _("Rename Page"), "rename"),
  52         'delete':      ("", {'action': 'DeletePage'}, _("Delete Page"), "delete"),
  53         'copy':        ("", {'action': 'CopyPage'}, _("Copy Page"), "copy"),
  54         'load':        ("", {'action': 'Load'}, _("Load Page"), "load"),
  55         'save':        ("", {'action': 'Save'}, _("Save Page"), "save"),
  56         'diff':        ("", {'action': 'diff'}, _("Diffs"), "diff"),
  57         'info':        ("", {'action': 'info'}, _("Info"), "info"),
  58         'print':       ("", {'action': 'print'}, _("Print View"), "print"),
  59         'raw':         ("", {'action': 'raw'}, _("Raw Text"), "raw"),
  60         'refresh':     ("", {'action': 'refresh'}, _("Delete Cache"), "refresh"),
  61         'xml':         ("", {'action': 'format', 'mimetype': 'text/xml'}, _("XML"), "xml"),
  62         'docbook':     ("", {'action': 'RenderAsDocbook'}, _("Render as Docbook"), "docbook"),
  63         'help':        ("%(page_help_contents)s", {}, "%(page_help_contents)s", "help"),
  64         'subscribeuser': ("", {'action': 'SubscribeUser'}, _("Subscribe User"), "subscribeuser"),
  65         'despam':      ("", {'action': 'Despam'}, _("Remove Spam"), "despam"),
  66         'packagepages':  ("", {'action': 'PackagePages'}, _("Package Pages"), "packagepages")
  67         }
  68 
  69 
  70     icons_updates = {
  71         # key         alt                        icon filename              w   h
  72         # ------------------------------------------------------------------------
  73         # iconbar
  74         'login':      (_("Login"),               "moin-login.png",          16, 16),
  75         'logout':     (_("Logout"),              "moin-login.png",          16, 16),
  76         'preferences':   (_("Preferences"),      "moin-preferences.png",    16, 16),
  77         'showcomments':  (_("Comments"),         "moin-comments.png",       16, 16),
  78         'hidecomments':  (_("Comments"),         "moin-comments.png",       16, 16),
  79         'subscribe':  (_("Subscribe"),           "moin-subscribe.png",      16, 16),
  80         'unsubscribe':(_("Unsubscribe"),         "moin-subscribe.png",      16, 16),
  81         'quicklink':  (_("Add Link"),            "moin-quicklink.png",      16, 16),
  82         'unquicklink':  (_("Remove Link"),       "moin-quicklink.png",      16, 16),
  83         'home':       (_("Home"),                "moin-home.png",           16, 16),
  84         'find':       ("%(page_find_page)s",     "moin-search.png",         16, 16),
  85         'likepages':  (_("Like Pages"),          "moin-likepages.png",      16, 16),
  86         'sitemap':    (_("Local Site Map"),      "moin-sitemap.png",        16, 16),
  87         'changes':    (_("RecentChanges"),       "moin-rc.png",             16, 16),
  88         'edit':       (_("Edit"),                "moin-edit-text.png",      16, 16),
  89         'edit_text':  (_("Edit (Text)"),         "moin-edit-text.png",      16, 16),
  90         'edit_text_disabled':  (_("Edit (Text)"),         "moin-edit-text-disabled.png",      16, 16),
  91         'edit_gui':   (_("Edit (GUI)"),          "moin-edit-gui.png",       16, 16),
  92         'edit_gui_disabled':   (_("Edit (GUI)"),          "moin-edit-gui-disabled.png",       16, 16),
  93         'attach':     (_("Attachments"),         "moin-attach.png",         16, 16),
  94         'spellcheck': (_("Check Spelling"),      "moin-check-spelling.png", 16, 16),
  95         'rename':     (_("Rename Page"),         "moin-rename.png",         16, 16),
  96         'rename_disabled':     (_("Rename Page"), "moin-rename-disabled.png", 16, 16),
  97         'copy':       (_("Copy Page"),           "moin-copy.png",           16, 16),
  98         'copy_disabled': (_("Copy Page"),        "moin-copy-disabled.png",  16, 16),
  99         'load':       (_("Load Page"),           "moin-load.png",           16, 16),
 100         'load_disabled': (_("Load Page"),        "moin-load-disabled.png",           16, 16),
 101         'save':       (_("Save Page"),           "moin-save.png",           16, 16),
 102         'save_disabled':       (_("Save Page"),           "moin-save-disabled.png",           16, 16),
 103         'delete':     (_("Delete Page"),         "moin-delete.png",         16, 16),
 104         'delete_disabled':     (_("Delete Page"),         "moin-delete-disabled.png",         16, 16),
 105         'diff':       (_("Diffs"),               "moin-diff.png",           16, 16),
 106         'info':       (_("Info"),                "moin-info.png",           16, 16),
 107         'print':      (_("Print View"),          "moin-print.png",          16, 16),
 108         'raw':        (_("Raw Text"),            "moin-raw.png",            16, 16),
 109         'refresh':    (_("Delete Cache"),        "moin-refresh.png",        16, 16),
 110         'xml':        (_("XML"),                 "moin-xml.png",            16, 16),
 111         'docbook':    (_("Render as Docbook"),   "moin-docbook.png",        16, 16),
 112         'help':       ("%(page_help_contents)s", "moin-help.png",           16, 16),
 113         'subscribeuser': (_("Subscribe User"),   "moin-subscribe.png",      16, 16),
 114         'view':       (_("View"),                "moin-show.png",           16, 16),
 115         'up':         (_("Up"),                  "moin-parent.png",         16, 16),
 116         'mypages':    (_("My Pages"),            "moin-mypages.png",        16, 16),
 117         'despam':     (_("Remove Spam"),         "moin-despam.png",         16, 16),
 118         'packagepages':  (_("Package Pages"),    "moin-package.png",        16, 16),
 119         # RecentChanges
 120         'deleted':    (_("[DELETED]"),           "moin-delete.png",         16, 16),
 121         'updated':    (_("[UPDATED]"),           "moin-edit-text.png",      16, 16),
 122         'renamed':    (_("[RENAMED]"),           "moin-rename.png",         16, 16),
 123         'conflict':   (_("[CONFLICT]"),          "moin-conflict.png",       16, 16),
 124         'new':        (_("[NEW]"),               "moin-new.png",            16, 16),
 125         'diffrc':     (_("[DIFF]"),              "moin-diff.png",           16, 16),
 126         }
 127    
 128     del _
 129 
 130 
 131     def __init__(self, request):
 132         """ Initialize the explorer theme
 133         
 134         @param request: the request object
 135         """
 136         ThemeBase.__init__(self, request)
 137         # Get the cookies
 138         self.cookies = request.parse_cookie()
 139         # Add them especific icons
 140         self.icons.update(self.icons_updates)
 141         # Get module name of the theme
 142         self.module_name = module_name = __name__.rsplit('.', 1)[1]
 143         cfg = self.cfg
 144         # Determine if theme should be displayed in site mode or desktop mode
 145         # Default: desktop mode <=> request is from localhost
 146         self.site_mode = getattr(cfg, '%s_site_mode' % module_name, not request.http_host.startswith('localhost'))
 147         # Get admin configured iconbar
 148         self.iconbar_list = getattr(cfg, '%s_iconbar' % module_name, self.iconbar_list)
 149         # Get admin configured default tree width
 150         self.default_sidebar_width = getattr(cfg, '%s_default_sidebar_width' % module_name, "20em")
 151         # Get admin configured page_header status
 152         # Default: show page header <=> theme is in desktop mode
 153         self.page_header = getattr(cfg, '%s_page_header' % module_name, not self.site_mode)
 154         # Get admin configured attachments status
 155         # Default: show attachments <=> theme is in desktop mode
 156         self.attachments = getattr(cfg, '%s_attachments' % module_name, not self.site_mode)
 157         if not self.site_mode:
 158             # Add additional stylesheet for desktop mode
 159             self.stylesheets += (('screen', 'desktop'),)
 160 
 161 
 162     def header(self, d, **kw):
 163         """ Assemble the wiki header
 164         
 165         @param d: parameter dictionary
 166         @rtype: unicode
 167         @return: page header html
 168         """
 169         # Init the wiki tree
 170         self.wiki_tree = WikiTree(self, d)
 171         self.page = d['page']
 172         self.page_name = d['page_name']
 173         module_name = self.module_name
 174 
 175         # Initialize default settings
 176         self.sidebar_width, main_height, page_content_height = self.default_sidebar_width, "auto", "auto"
 177 
 178         # Apply setings from cookie
 179         if self.cookies:
 180             if self.cookies.has_key('%s_hide_sidebar' % module_name):
 181                 self.sidebar_width = "0px"
 182             elif self.cookies.has_key('%s_sidebar_width' % module_name):
 183                 self.sidebar_width = self.cookies['%s_sidebar_width' % module_name].value
 184             if self.cookies.has_key('%s_main_height' % module_name):
 185                 main_height = self.cookies['%s_main_height' % module_name].value
 186             if self.cookies.has_key('%s_page_content_height' % module_name):
 187                 page_content_height = self.cookies['%s_page_content_height' % module_name].value
 188 
 189         is_ltr = self.i18n.getDirection(self.request.lang) == "ltr"
 190 
 191         header_html = page_header_html = page_header_html = []
 192         page_title_html = attachment_html = u''
 193         if self.site_mode:
 194             # Build site header
 195             header_html = [
 196                 u'<div id="header">',
 197                 u'<div></div>',  # IE Fix: Logo and searchform float over the header
 198                 self.logo(),
 199                 u'<span id="searchform_container">',  # IE 6 Fix: To enable searchform to float
 200                 self.searchform(d),
 201                 u'</span>',
 202                 self.username(d),
 203                 u'<div style="clear:both;"></div>',  # IE Fix: Navibar tabs move at hover
 204                 self.navibar(d),
 205                 u'</div>',
 206                 u'<div id="pageline"><hr style="display:none;"></div>',
 207             ]
 208         if self.attachments:
 209             # Build attachment list
 210             attachment_html = self.attachment_list()
 211         if self.page_header:
 212             # Build wiki page header
 213             page_header_html = [
 214                 u'<div id="page_header">',
 215                 self.wiki_tree.page_summary_html(),
 216                 self.wiki_tree.parents_html(),
 217                 self.interwiki(d),
 218                 self.title(d),
 219                 self.page_header_info(self.page),
 220                 u'<div class="bottom"></div>'
 221                 u'</div>',  # page_header
 222             ]
 223         else:
 224             # Build wiki page title
 225             page_title_html = [
 226                 u'<div id="page_title">',
 227                 self.interwiki(d),
 228                 self.title(d),
 229                 u'</div>',  # page_title
 230             ]
 231 
 232         html = [
 233             # Pre header custom html
 234             self.emit_custom_html(self.cfg.page_header1),
 235             u'',
 236             u'\n'.join(header_html),
 237             self.trail(d),
 238             [self.iconbar(d), u''][self.site_mode],
 239             u'',
 240             # u'<div id="main" style="height:%s;">' % main_height,
 241             u'<div id="main">',
 242             u'',
 243             u'<div id="page_area">',
 244             u'',
 245             [u'', self.iconbar(d)][self.site_mode],
 246             u'\n'.join(page_header_html),
 247             self.msg(d),
 248             u'',
 249             # u'<div id="page_content" style="height:%s;">' % page_content_height,
 250             u'<div id="page_content">',
 251             attachment_html,
 252             u'\n'.join(page_title_html),
 253 
 254             # Post header custom html (not recommended)
 255             self.emit_custom_html(self.cfg.page_header2),
 256             
 257             # Start of page
 258             self.startPage(),
 259         ]
 260         return u'\n'.join(html)
 261         
 262         
 263     def editorheader(self, d, **kw):
 264         """ Assemble wiki header for editor
 265         
 266         @param d: parameter dictionary
 267         @rtype: unicode
 268         @return: page header html
 269         """
 270         return self.header(d, **kw)
 271 
 272         
 273     def footer(self, d, **keywords):
 274         """ Assemble wiki footer
 275         
 276         @param d: parameter dictionary
 277         @keyword ...:...
 278         @rtype: unicode
 279         @return: page footer html
 280         """
 281         # Uncomment to display the cookies (for test purpose)
 282         # cookies = ['<li>%s</li>' % self.cookies[i] for i in self.cookies]
 283         wiki_tree_html = self.wiki_tree.wiki_tree_html()
 284         _ = self.ui_text
 285         pageinfo_html = [self.pageinfo(self.page), u''][self.page_header]
 286 
 287         html = [
 288             pageinfo_html,
 289             # End of page
 290             self.endPage(),
 291             
 292             # Pre footer custom html (not recommended!)
 293             self.emit_custom_html(self.cfg.page_footer1),
 294 
 295             u'</div>',  # page_content
 296             u'</div>',  # page
 297             u'',
 298             u'<div id="splitbar" title="%s"></div>' % _['splitbar_title'],
 299             u'',
 300             u'<div id="sidebar" style="width:%s;">' % self.sidebar_width,
 301             wiki_tree_html,
 302             u'</div>',  # sidebar
 303             u'</div>',  # main
 304             u'',
 305             # Footer
 306             u'<div id="footer">',
 307             self.wiki_tree.wiki_summary_html(),
 308             self.credits(d),
 309             self.showversion(d, **keywords),
 310             # Uncomment to display the cookies (for test purpose)
 311             # u'<ul id="cookies">\n%s\n</ul>\n' % ''.join(cookies),
 312             u'</div>',
 313             u'',
 314             # Post footer custom html
 315             self.emit_custom_html(self.cfg.page_footer2),
 316             ]
 317         return u'\n'.join(html)
 318 
 319 
 320     
 321     # =============================
 322     # Iconbar
 323     # =============================
 324 
 325     def iconbar(self, d):
 326         """
 327         Assemble the iconbar
 328         
 329         @param d: parameter dictionary
 330         @rtype: string
 331         @return: iconbar html
 332         """
 333         request = self.request
 334         available_actions = request.getAvailableActions(self.page)
 335         iconbar = []
 336         iconbar.append('<div id="iconbar">')
 337         iconbar.append('<ul class="iconbar">')
 338         icons = self.iconbar_list[:]
 339         for icon in icons:
 340             if icon == "separator":
 341                 if iconbar[-1] != '<ul class="iconbar">':
 342                     iconbar.append('</ul>')
 343                     iconbar.append('<ul class="iconbar">')
 344             elif icon == "home" and self.site_mode:
 345                 # Don't include the home icon in site mode
 346                 icon = "invalid"
 347             elif icon == "edit":
 348                 iconbar.append(self.editor_link(d))
 349             elif icon == "rename":
 350                 if (self.page.isWritable()
 351                     and self.request.user.may.read(self.page_name)
 352                     and self.request.user.may.write(self.page_name)
 353                     and self.request.user.may.delete(self.page_name)):
 354                     iconbar.append('<li>%s</li>' % self.make_iconlink(icon, d))
 355                 else:
 356                     iconbar.append('<li>%s</li>' % self.make_icon('rename_disabled', d))
 357             elif icon == "delete":
 358                 if (self.page.isWritable()
 359                     and self.request.user.may.delete(self.page_name)):
 360                     iconbar.append('<li>%s</li>' % self.make_iconlink(icon, d))
 361                 else:
 362                     iconbar.append('<li>%s</li>' % self.make_icon('delete_disabled', d))
 363             elif icon == "copy":
 364                 if self.request.user.may.read(self.page_name):
 365                     iconbar.append('<li>%s</li>' % self.make_iconlink(icon, d))
 366                 else:
 367                     iconbar.append('<li>%s</li>' % self.make_icon('copy_disabled', d))
 368             elif icon == "load":
 369                 if self.request.user.may.read(self.page_name):
 370                     iconbar.append('<li>%s</li>' % self.make_iconlink(icon, d))
 371                 else:
 372                     iconbar.append('<li>%s</li>' % self.make_icon('load_disabled', d))
 373             elif icon == "save":
 374                 if self.request.user.may.read(self.page_name):
 375                     iconbar.append('<li>%s</li>' % self.make_iconlink(icon, d))
 376                 else:
 377                     iconbar.append('<li>%s</li>' % self.make_icon('save_disabled', d))
 378             elif icon == "login":
 379                 if not self.site_mode:
 380                     if request.user.valid and request.user.name:
 381                         iconbar.append('<li>%s&nbsp;</li>' % self.username_link(d))
 382                     if self.cfg.show_login:
 383                         if request.user.valid:
 384                             iconbar.append('<li class="ib_selected">%s</li>' % self.make_iconlink("logout", d))
 385                             iconbar.append('<li>%s</li>' % self.make_iconlink("preferences", d))
 386                         else:
 387                             iconbar.append('<li>%s</li>' % self.make_iconlink(icon, d))
 388             elif icon == "quicklink" and request.user.valid:
 389                 # Only display for logged in users
 390                 iconbar.append('<li%s>%s</li>' % [('', self.make_iconlink(icon, d)), (' class="ib_selected"', self.make_iconlink("unquicklink", d))][request.user.isQuickLinkedTo([self.page_name])])
 391             elif icon == "showcomments":
 392                 iconbar.append('<li%s><a href="#" onClick="toggle_comments(this);return false;">%s</a></li>' % [('', self.make_icon(icon, d)), (' class="ib_selected"', self.make_icon("hidecomments", d))][self.request.user.show_comments])
 393             elif icon == "subscribe" and self.cfg.mail_enabled:
 394                 iconbar.append('<li%s>%s</li>' % [('', self.make_iconlink(icon, d)), (' class="ib_selected"', self.make_iconlink("unsubscribe", d))][self.request.user.isSubscribedTo([self.page_name])])
 395             elif icon == "searchform":
 396                 if not self.site_mode:
 397                     iconbar.append('<li>%s</li>' % self.searchform(d))
 398             else:
 399                 page_name, querystr, title, icon = self.button_table[icon]
 400                 if not (self.site_mode and page_name and self.cfg.navi_bar and ((page_name % d) in self.cfg.navi_bar)):
 401                     iconbar.append('<li>%s</li>' % self.make_iconlink(icon, d))
 402         iconbar.append('</ul></div>\n')
 403         return ''.join(iconbar)
 404 
 405         
 406     def editor_link(self, d):
 407         """ Return links to the editor if the user can edit
 408         """
 409         page = self.page
 410         enabled = page.isWritable() and self.request.user.may.write(page.page_name)
 411         guiworks = self.guiworks(page)
 412         editor = self.editor_to_show()
 413         if editor == 'freechoice' and guiworks:
 414             if enabled:
 415                 return '<li>%s</li><li>%s</li>' % (self.make_iconlink('edit_gui', d), self.make_iconlink('edit_text', d))
 416             else:
 417                 return '<li>%s</li><li>%s</li>' % (self.make_icon('edit_gui_disabled', d), self.make_icon('edit_text_disabled', d))
 418         else:
 419             if enabled:
 420                 icon = ['edit_text', 'edit_gui'][editor == 'gui' and guiworks]
 421                 return '<li>%s</li>' % self.make_iconlink(icon, d)
 422             else:
 423                 icon = ['edit_text_disabled', 'edit_gui_disabled'][editor == 'gui' and guiworks]
 424                 return '<li>%s</li>' % self.make_icon(icon, d)
 425 
 426 
 427     def editor_to_show(self):
 428         """ Returns the editor to show
 429         depending on global or user configuration
 430         
 431         @return: 'freechoice', 'gui' or 'text'
 432         """
 433         cfg = self.cfg
 434         user = self.request.user
 435         if cfg.editor_force:
 436             editor = cfg.editor_ui
 437             if editor == 'theonepreferred':
 438                 editor = cfg.editor_default
 439         else:
 440             editor = user.editor_ui
 441             if editor == '<default>':
 442                 editor = cfg.editor_ui
 443                 if editor == 'theonepreferred':
 444                     editor = user.editor_default
 445                     if editor == '<default>':
 446                         editor = cfg.editor_default
 447             elif editor == 'theonepreferred':
 448                 editor = user.editor_default
 449                 if editor == '<default>':
 450                     editor = cfg.editor_default
 451         return editor
 452 
 453                     
 454     def username_link(self, d):
 455         """ Assemble the username link
 456         
 457         @param d: parameter dictionary
 458         @rtype: unicode
 459         @return: username html
 460         """
 461         request = self.request
 462 
 463         html = u''
 464         # Add username/homepage link for registered users. We don't care
 465         # if it exists, the user can create it.
 466         if request.user.valid and request.user.name:
 467             interwiki = self.wikiutil.getInterwikiHomePage(request)
 468             name = request.user.name
 469             aliasname = request.user.aliasname
 470             if not aliasname:
 471                 aliasname = name
 472             title = "%s @ %s" % (aliasname, interwiki[0])
 473             # link to (interwiki) user homepage
 474             html = (request.formatter.interwikilink(1, title=title, id="userhome", generated=True, *interwiki) +
 475                         request.formatter.text(name) +
 476                         request.formatter.interwikilink(0, title=title, id="userhome", *interwiki))
 477         return html
 478 
 479         
 480     def make_iconlink(self, which, d):
 481         """
 482         Make a link with an icon
 483 
 484         @param which: icon id (dictionary key)
 485         @param d: parameter dictionary
 486         @rtype: string
 487         @return: html link tag
 488         """
 489         page_name, querystr, title, icon = self.button_table[which]
 490         d['title'] = title % d
 491         d['i18ntitle'] = self.request.getText(d['title'], formatted=False)
 492         img_src = self.make_icon(icon, d)
 493         # rev = d['rev']
 494         # if rev and which in ['raw', 'print', ]:
 495         #    querystr['rev'] = str(rev)
 496         attrs = {'rel': 'nofollow', 'title': d['i18ntitle'], }
 497         if page_name:
 498             page = self.Page(self.request, page_name % d)
 499         else:
 500             page = self.page
 501         return page.link_to_raw(self.request, text=img_src, querystr=querystr, **attrs)
 502 
 503 
 504 
 505     # =============================
 506     # Page Area
 507     # =============================
 508 
 509     def page_header_info(self, page):
 510         """ Return html fragment with page meta data
 511 
 512         Based on pageinfo function.
 513         
 514         Since page information uses translated text, it uses the ui
 515         language and direction. It looks strange sometimes, but
 516         translated text using page direction looks worse.
 517         
 518         @param page: current page
 519         @rtype: unicode
 520         @return: page last edit information
 521         """
 522         _ = self.request.getText
 523         html = ''
 524         if self.shouldShowPageinfo(page):
 525             info = page.lastEditInfo()
 526             if info:
 527                 if info['editor']:
 528                     info = _("last edited %(time)s by %(editor)s", formatted=False) % info
 529                 else:
 530                     info = _("last modified %(time)s", formatted=False) % info
 531                 html = '<p id="page_header_info"%(lang)s>%(info)s, page size: %(size)s</p>' % {
 532                     'lang': self.ui_lang_attr(),
 533                     'info': info,
 534                     'size': self.wiki_tree.human_readable_size(page.size())
 535                     }
 536         return html
 537 
 538 
 539     def attachment_list(self):
 540         html = self.AttachFile._build_filelist(self.request, self.page_name, showheader=0, readonly=0)
 541         if html:
 542             html = u'<div id="attachments">\n%s\n</div>' % html
 543         return html
 544 
 545 
 546 
 547     # ========================================
 548     # Include explorer.js
 549     # ========================================
 550 
 551     def externalScript(self, name):
 552         # Overwritten from ThemeBase
 553         """ Format external script html """
 554         # modified to supply an additional script file
 555         url_prefix_static = self.cfg.url_prefix_static
 556         html = [ ThemeBase.externalScript(self, name) ]
 557         
 558         html.append('''
 559 <script type="text/javascript">
 560 <!--
 561 var url_prefix_static = "%s";
 562 var DEFAULT_SIDEBAR_WIDTH = "%s";
 563 //-->
 564 </script>
 565 ''' % (url_prefix_static, self.default_sidebar_width))
 566 
 567         html.append('<script type="text/javascript" src="%s/explorer/js/explorer.js"></script>' % url_prefix_static)
 568 
 569         return '\n'.join(html)
 570 
 571 
 572 
 573 def execute(request):
 574     """
 575     Generate and return a theme object
 576         
 577     @param request: the request object
 578     @rtype: MoinTheme
 579     @return: Theme object
 580     """
 581     return Theme(request)
 582 
 583 
 584 class WikiNode:
 585     """
 586     A WikiNode is an object representing a page (resp. category)
 587     or an attachment and has the following attributes:
 588         display_name  : displayed name of the node
 589         type          : node type (0 = category, 1 = page or 2 = attachment)
 590         exists        : flag indicating that a node exists
 591         url           : url of this node
 592         html          : html code representing the node
 593         parents       : list of parents of the node
 594         size          : size of the node
 595         subnodes_size : size of all sub pages and attachments
 596         categories    : list of sub categories of this category
 597         pages         : list of pages in this category or subpages of page
 598         attachments   : list of attachments of this node
 599     """
 600     import re
 601     
 602     url = ''
 603     html = ''
 604     exists = False
 605     size = 0
 606     subnodes_size = 0
 607 
 608     
 609     def __init__(self, request, name, is_attachment=False):
 610         """ Init the wiki node
 611 
 612         @param request: the request object
 613         @param name: string
 614         @param is_attachment: boolean
 615         """
 616         self.name = name
 617         self.display_name = name
 618         self.parents, self.categories, self.pages, self.attachments = [], [], [], []
 619         self.subnodes = [ self.categories, self.pages, self.attachments ]
 620         if is_attachment:
 621             self.type = 2  # attachment
 622         else:  # Identify the node type
 623             match_object = self.re.match(request.cfg.cache.page_category_regex, name)
 624             if match_object:
 625                 self.type = 0  # category
 626                 # On categories remove the key string identifying a category (default 'Category')
 627                 if match_object.lastindex:
 628                     self.display_name = match_object.group(1).lstrip()
 629             else:
 630                 self.type = 1  # page
 631 
 632 
 633     def add_sub_node(self, node):
 634         """ Add a sub node of the given type
 635         """
 636         node.parents.append(self.name)
 637         self.subnodes[node.type].append(node.name)
 638         self.subnodes_size += node.size
 639 
 640 
 641     def remove_sub_node(self, node):
 642         """ Remove a sub node
 643         """
 644         node.parents.remove(self.name)
 645         self.subnodes[node.type].remove(node.name)
 646         self.subnodes_size -= node.size
 647 
 648 
 649     def reset(self):
 650         self.exists = False
 651         self.html = ''
 652         self.size = 0
 653 
 654 
 655 
 656 class WikiTree:
 657     """
 658     The wiki tree represents the tree of all pages (resp. categories) and
 659     attachments of the wiki. It has the following attributes:
 660         wiki_tree   : a dictionary of WikiNodes { node_name : node, ... }
 661         root        : name of the root category
 662         orphaned    : name of the orphaned category
 663         missing     : name of the missing category
 664         underlay    : name of the underlay category
 665         root_category, orphaned_category, missing_category, underlay_category
 666         type_counts : list of total counts of categories, pages and attachments
 667         total_size  : total size of all nodes
 668         
 669     These are cached in the meta cache server object and are updated on changes.
 670     """
 671     # import time
 672     import thread, math, os
 673     from MoinMoin.action import AttachFile
 674     from MoinMoin import wikiutil, config
 675     from MoinMoin.Page import Page
 676 
 677     # fake _ function to get gettext recognize those texts:
 678     _ = lambda x: x
 679     ui_text = {
 680         'toggle_title' : _("Toggle display"),
 681         # used in node_description
 682         'categories' :   _('categories'),
 683         'pages' :        _('pages'),
 684         'attachments' :  _('attachments'),
 685         'size' :         _('size'),
 686         }
 687     del _
 688 
 689     orphaned_category = missing_category = underlay_category = None
 690 
 691     wiki_tree = { }
 692     # Special categories of the wiki tree (generally these should be a categories) 
 693     root = u'CategoryRoot'
 694     orphaned = u'CategoryOrphaned'
 695     missing = u'CategoryMissing'
 696     underlay = u'CategoryUnderlay'
 697 
 698     type_counts = [ 0, 0, 0 ]  # categories, pages, attachments
 699     total_size = 0  # Total size of all nodes
 700 
 701     touched = set([])  # Nodes changed since last tree update
 702 
 703 
 704     def __init__(self, theme, d):
 705         """ Inits the wiki tree structure and wiki tree info
 706             or loads it from cache
 707         """
 708         self.request = theme.request
 709         # Get the cookies of the request
 710         self.cookies = self.request.parse_cookie()
 711         self.page_name = d['page_name']
 712 
 713         # Define image tags for node icons
 714         self.node_icon_html = [
 715             u'<img src="%s">' % theme.img_url('category.png'),
 716             u'<img src="%s">' % theme.img_url('page.png'),
 717             u'<img src="%s">' % theme.img_url('attachment.png'),
 718             u'<img src="%s">' % theme.img_url('category-missing.png'),
 719             u'<img src="%s">' % theme.img_url('page-missing.png'),
 720             ]
 721         self.expand_icon_url = theme.img_url('expand.png')
 722         self.collapse_icon_url = theme.img_url('collapse.png')
 723 
 724         # Get cached lock on wiki tree
 725         self.lock = self.request.cfg.cache.meta.getItem(self.request, u'', u'wiki_tree_lock')
 726         if not self.lock:  # If lock doesn't exist build one
 727             self.lock = self.thread.allocate_lock()
 728             self.request.cfg.cache.meta.putItem(self.request, u'', u'wiki_tree_lock', self.lock)
 729 
 730         # Get cached wiki tree
 731         self.cache = self.request.cfg.cache.meta.getItem(self.request, u'', u'wiki_tree')
 732         if not self.cache and self.lock.acquire(0):
 733             # No cached tree exists: Set lock and build tree.
 734             try:
 735                 self.categories_formatter = CategoriesFormatter(self.request, store_pagelinks=1)
 736                 self.build_wiki_tree()
 737                 self.log_pos, items = self.request.editlog.news(None)
 738                 self.request.cfg.cache.meta.putItem(self.request, u'', u'wiki_tree',
 739                         [ self.log_pos, self.wiki_tree, self.type_counts, self.total_size,
 740                             self.root, self.root_category,
 741                             self.orphaned, self.orphaned_category,
 742                             self.missing, self.missing_category,
 743                             self.underlay, self.underlay_category,
 744                             self.categories_formatter
 745                         ])
 746             finally:
 747                 self.lock.release()
 748         else:
 749             # If cached tree exists: Set lock and refresh tree.
 750             self.lock.acquire()
 751             try:
 752                 self.refresh_wiki_tree()            
 753             finally:
 754                 self.lock.release()
 755 
 756 
 757     def refresh_wiki_tree(self):
 758         """ Refresh the wiki nodes changed
 759             if anything has changed in the wiki, we see it
 760             in the edit-log and update the wiki_tree accordingly
 761         """
 762         # Get wiki tree data from cache.
 763         if not self.cache:
 764             self.cache = self.request.cfg.cache.meta.getItem(self.request, u'', u'wiki_tree')
 765         self.log_pos, self.wiki_tree, self.type_counts, self.total_size, self.root, self.root_category, self.orphaned, self.orphaned_category, self.missing, self.missing_category, self.underlay, self.underlay_category, self.categories_formatter = self.cache
 766         elog = self.request.editlog
 767         old_pos = self.log_pos
 768         new_pos, items = elog.news(old_pos)
 769         if self.request.action == 'refresh':
 770             if self.page_name not in items:
 771                 items.append(self.page_name)
 772             page = self.Page(self.request, self.page_name, formatter=self.categories_formatter)
 773             parent_categories = self.categories_formatter.getCategories(page, update_cache=True)
 774         if items:
 775             for item in items:
 776                 self.remove_page(item)
 777             for item in items:
 778                 self.add_page(item)
 779             self.finalize_touched()
 780         self.log_pos = new_pos  # important to do this at the end -
 781                                 # avoids threading race conditions
 782         self.cache[0] = self.log_pos
 783 
 784 
 785     def build_wiki_tree(self):
 786         """ Builds the wiki tree structure and wiki tree info
 787         """
 788         cfg = self.request.cfg
 789         if hasattr(cfg, 'wiki_tree_root'):
 790             self.root = cfg.wiki_tree_root
 791         if hasattr(cfg, 'wiki_tree_orphaned'):
 792             self.orphaned = cfg.wiki_tree_orphaned
 793         if hasattr(cfg, 'wiki_tree_missing'):
 794             self.missing = cfg.wiki_tree_missing
 795         if hasattr(cfg, 'wiki_tree_underlay'):
 796             self.underlay = cfg.wiki_tree_underlay
 797 
 798         self.root_category = self.get_assured_node(self.root)
 799         if self.orphaned:
 800             self.orphaned_category = self.get_assured_node(self.orphaned)
 801         if self.missing:
 802             self.missing_category = self.get_assured_node(self.missing)
 803         if self.underlay:
 804             self.underlay_category = self.get_assured_node(self.underlay)
 805 
 806         # print '>>>>>> Start Build WikiTree: ', self.time.clock()
 807         # Add all pages from the wiki
 808         for page_name in self.request.rootpage.getPageList(user=''):
 809             self.add_page(page_name)
 810         # print '>>>>>> Finish Build WikiTree: ', self.time.clock()
 811         self.finalize_touched()
 812 
 813         
 814     def add_page(self, page_name):
 815         """ Add a page to the wiki tree
 816         """
 817         request = self.request
 818         page = self.Page(self.request, page_name, formatter=self.categories_formatter)
 819         if page.exists():
 820             node = self.get_assured_node(page_name)
 821             node.exists = True
 822             node.size = page.size()
 823             node.url = page.url(request, relative=False)
 824             self.type_counts[node.type] += 1
 825             self.total_size += node.size
 826 
 827             # Add attachments to the wiki tree
 828             attachments = self.get_attachment_dict(page_name)
 829             for (attachment_name, attachment_info) in attachments.iteritems():
 830                 attachment_key = page_name + '/' + attachment_name
 831                 attachment_node = self.get_assured_node(attachment_key, is_attachment=True)
 832                 attachment_node.display_name = attachment_name
 833                 attachment_node.exists = True
 834                 attachment_node.size = attachment_info[0]
 835                 attachment_node.url = attachment_info[1]
 836                 self.add_to_parent(attachment_node, page_name, node)
 837                 self.type_counts[2] += 1
 838                 self.total_size += attachment_node.size
 839                 self.touched.add(attachment_key)
 840 
 841             pos = page_name.rfind('/')
 842             if pos > 0:  # page is subpage
 843                 node.display_name = page_name[pos+1:]
 844                 self.add_to_parent(node, page_name[:pos])
 845             else:
 846                 # Add the page to the categories it belongs to
 847                 parent_categories = self.categories_formatter.getCategories(page)
 848                 if self.underlay_category and page.isUnderlayPage():
 849                     parent_categories.append(self.underlay)
 850                 for parent_category in parent_categories:
 851                     self.add_to_parent(node, parent_category)
 852 
 853             self.touched.add(page_name)
 854 
 855 
 856     def get_assured_node(self, node_name, is_attachment=False):
 857         """ Get a wiki node with the specified name.
 858         If the node doesn't exist it is created.
 859         """
 860         if node_name in self.wiki_tree:
 861             node = self.wiki_tree[node_name]
 862         else:
 863             node = WikiNode(self.request, node_name, is_attachment=is_attachment)
 864             self.wiki_tree[node_name] = node
 865             self.touched.add(node_name)
 866         return node
 867 
 868 
 869     def add_to_parent(self, node, parent_name, parent = None):
 870         """ Add a node to a parent node
 871         """
 872         if not parent:
 873             parent = self.get_assured_node(parent_name)
 874         parent.add_sub_node(node)
 875         self.touched.add(parent_name)
 876 
 877 
 878     def get_attachment_dict(self, page_name):
 879         """ Returns a dict of attachments
 880 
 881         The structure of the dictionary is:
 882         { file_name : [file_size, get_url], ... }
 883         
 884         @param page_name:
 885         @rtype: attachments dictionary
 886         @return: attachments dictionary
 887         """
 888         attach_dir = self.AttachFile.getAttachDir(self.request, page_name)
 889         files = self.AttachFile._get_files(self.request, page_name)
 890         attachments = {}
 891         for file in files:
 892             fsize = float(self.os.stat(self.os.path.join(attach_dir,file).encode(self.config.charset))[6])
 893             get_url = self.AttachFile.getAttachUrl(page_name, file, self.request, escaped=1)
 894             attachments[file] = [fsize, get_url]
 895         return attachments
 896 
 897 
 898     def remove_page(self, page_name):
 899         """ Remove a node from the wiki tree
 900         """
 901         if page_name in self.wiki_tree:
 902             node = self.wiki_tree[page_name]
 903             self.type_counts[node.type] -= 1
 904             self.total_size -= node.size
 905 
 906             while node.attachments:
 907                 self.remove_page(node.attachments[0])
 908 
 909             while node.parents:
 910                 self.remove_from_parent(node, node.parents[0])
 911 
 912             if node.categories or node.pages or (page_name in [self.root, self.orphaned, self.missing, self.underlay]):
 913                 node.reset()
 914                 self.touched.add(page_name)
 915             else:
 916                 del self.wiki_tree[page_name]
 917 
 918 
 919     def remove_from_parent(self, node, parent_name):
 920         """ Remove a node from the parent node
 921         """
 922         parent = self.wiki_tree[parent_name]
 923         parent.remove_sub_node(node)
 924         if parent.exists or parent.categories or parent.pages or (node.name in [self.root, self.orphaned, self.missing, self.underlay]):
 925             self.touched.add(parent_name)
 926         else:
 927             self.remove_page(parent_name)
 928 
 929 
 930     def finalize_touched(self):
 931         """ Calculate totals, prepare the html code for each node
 932         that has changed (these nodes are stored in the touched set)
 933         """
 934         first_step_touched = self.touched
 935         self.touched = set([])
 936         for node_name in first_step_touched:
 937             self.finalize_node(node_name)
 938         second_step_touched = self.touched
 939         self.touched = set([])
 940         for node_name in second_step_touched:
 941             self.finalize_node(node_name)
 942 
 943 
 944     def finalize_node(self, node_name):
 945         """ Finalize the wiki tree node
 946         
 947         Calculates the totals and the html code for the node.
 948         
 949         @param node name:
 950         @param path: list of nodes up to this one
 951         """
 952         if node_name in self.wiki_tree:
 953             node = self.wiki_tree[node_name]
 954             # Sort sub nodes
 955             node.categories.sort()
 956             node.pages.sort()
 957             node.attachments.sort()
 958             # Calculate subnode counts
 959             node.categories_count = len(node.categories)
 960             node.pages_count = len(node.pages)
 961             node.attachments_count = len(node.attachments)
 962             icon_type = node.type
 963             if not node.exists:
 964                 # Page represented by node doesn't exist
 965                 icon_type += 3
 966                 node.url = '%s/%s' % (self.request.getScriptname(), self.wikiutil.quoteWikinameURL(node_name))
 967                 if node_name not in [self.missing, self.orphaned]:
 968                     if not node.parents and self.missing:
 969                         # Add page to category missing
 970                         self.add_to_parent(node, self.missing)
 971             if not node.parents and self.orphaned and node_name != self.root:
 972                 # Page is orphaned but not root, add it to orphaned category
 973                 self.add_to_parent(node, self.orphaned)
 974                 if node_name == self.orphaned:
 975                     # If orphaned category is orphaned add it to root
 976                     self.add_to_parent(node, self.root)
 977             # Build the html code for the link
 978             title = self.node_description(node)
 979             link_html = u'<a class="node" href="%s" title="%s">%s</a>' % (node.url, title, node.display_name)
 980             node.html = self.node_icon_html[icon_type] + link_html
 981 
 982 
 983     def node_description(self, node, include_name = 1, separator = u', '):
 984         """ Return a description of the node
 985         
 986         The description contains information about the size and
 987         counters of the sub tree of the node
 988         
 989         @param node: 
 990         @param include_name: integer denoting if the display_name should be included
 991         @param separator: separator unicode string
 992         @rtype: unicode
 993         @return: html describing the sub tree
 994         """
 995         description = u''
 996         categories_count = node.categories_count
 997         pages_count = node.pages_count
 998         attachments_count = node.attachments_count
 999         total_size = node.subnodes_size + node.size
1000         if include_name:  # Should the node name be displayed?
1001             description = u'%s: ' % node.display_name
1002         _ = self.ui_text
1003         # if (categories_count + pages_count + attachments_count):  # Are there sub nodes?
1004         description = u'%s%i&nbsp;%s%s%i&nbsp;%s%s%i&nbsp;%s%s%s:&nbsp;%s' % (
1005                 description,
1006                 pages_count, _['pages'], separator,
1007                 categories_count, _['categories'], separator,
1008                 attachments_count, _['attachments'], separator,
1009                 _['size'], self.human_readable_size(total_size))
1010         # else:
1011         #    description = u'%s%s:&nbsp;%s' % (description, _['size'], self.human_readable_size(total_size))
1012         return description
1013 
1014 
1015     def human_readable_size(self, size):
1016         """ Return the size normalized with unit
1017         
1018         @param size: integer denoting a file size
1019         @rtype: unicode
1020         @return: html describing the file size
1021         """
1022         if size == 0:
1023             return u'0&nbsp;Bytes'
1024         file_size_name = [u'Bytes', u'KB', u'MB', u'GB', u'TB', u'PB', u'EB', u'ZB', u'YB']
1025         i = int(self.math.log(size, 1024))
1026         if i:
1027             return u'%.2f&nbsp;%s' % (round(size/pow(1024, i), 2), file_size_name[i])
1028         else:
1029             return u'%i&nbsp;Bytes' % size
1030 
1031             
1032             
1033     # =============================
1034     # UI
1035     # =============================
1036 
1037     def wiki_tree_html(self):
1038         """ Build the html code of the wiki tree
1039         """
1040         return self.wiki_subtree_html(self.root)
1041 
1042 
1043     def wiki_subtree_html(self, node_name, path=[]):
1044         """ Return wiki sub tree html code with the specified node as root
1045 
1046         The path contains the path of nodes from the root to the current node.
1047         This is used to avoid recursion in the wiki tree.
1048         
1049         @param node_name: root of the sub tree
1050         @param path: list of nodes up to this one
1051         @rtype: unicode
1052         @return: wiki tree html
1053         """
1054         items = []
1055         node = self.wiki_tree[node_name]
1056         if not node.exists or self.request.user.may.read(node_name):
1057             html = node.html
1058             if node_name == self.page_name:
1059                 html = html.replace('class="node"', 'class="node_selected"')
1060             sub_nodes = node.categories + node.pages + node.attachments
1061             if not sub_nodes:
1062                 if path:  #  If it's not the root node (which is not diplayed)
1063                     items = [u'<li class="leaf">' + html]  # Display the node
1064             else:
1065                 node_id = 'tree_%s' % self.wikiutil.url_quote(u''.join(path),'')
1066                 display_subtree = (not path) or (self.cookies and node_id in self.cookies) 
1067                 if path:  #  If it's not the root node (which is not diplayed)
1068                     if display_subtree:
1069                         toggle_icon_url = self.collapse_icon_url
1070                         toggle_icon_alt = "[-]"
1071                     else:
1072                         toggle_icon_url = self.expand_icon_url
1073                         toggle_icon_alt = "[+]"
1074                     items = [u'<li><img class="toggle" alt="%s" title="%s" src="%s" onclick="toggle_display_element(this, \'%s\');">%s' % (toggle_icon_alt, self.ui_text["toggle_title"], toggle_icon_url, node_id, html)]
1075                 if display_subtree:
1076                     items.append(u'<ul class="wiki_tree" id="%s">' % node_id)
1077                     for sub_node_name in sub_nodes:
1078                         items.append(self.wiki_subtree_html(sub_node_name, path+[sub_node_name]))
1079                     items.append(u'</ul>')
1080         return u'\n'.join(items)
1081 
1082 
1083     def wiki_summary_html(self):
1084         """ Return a description of the wiki (counters and size) """
1085         _ = self.ui_text
1086         return u'<ul id="wiki_summary"><li>%i&nbsp;%s<li>%i&nbsp;%s<li>%i&nbsp;%s<li>%s:&nbsp;%s</ul>' % (
1087                     self.type_counts[1], _['pages'],
1088                     self.type_counts[0], _['categories'],
1089                     self.type_counts[2], _['attachments'],
1090                     _['size'], self.human_readable_size(self.total_size))
1091                     
1092                     
1093     def page_summary_html(self):
1094         """ Return html fragment with summary of current page
1095         
1096         @rtype: unicode
1097         @return: page summary information
1098         """
1099         html = u''
1100         if self.page_name in self.wiki_tree:
1101             html = u'<ul id="page_summary"><li>%s</ul>' % self.node_description(self.wiki_tree[self.page_name], include_name=0, separator=u'<li>')
1102         return html
1103 
1104         
1105     def parents_html(self):
1106         """ Builds a html list of the parents of the current node
1107         """
1108         html = u''
1109         request = self.request
1110         # Get list of parents the page belongs to
1111         if self.page_name in self.wiki_tree:
1112             parents = self.wiki_tree[self.page_name].parents
1113             if parents:
1114                 items = [u'<ul id="parents">']
1115                 for parent in parents:
1116                     page = self.Page(request, parent)
1117                     title = page.split_title()
1118                     link = page.link_to(request, title)
1119                     items.append('<li>%s</li>' % link)
1120                 items.append(u'</ul>')
1121                 html = '\n'.join(items)
1122         return html        
1123 
1124 
1125 
1126 from MoinMoin.formatter import FormatterBase
1127     
1128 class CategoriesFormatter(FormatterBase):
1129     """
1130         categories Formatter
1131         @copyright: 2007 Wolfgang Fischer
1132 
1133         based on pagelinks formatter
1134         @copyright: 2005 Nir Soffer <nirs@freeshell.org>
1135         @license: GNU GPL, see COPYING for details.
1136     """
1137     """ Collect categories and format nothing :-) """
1138 
1139     def __init__(self, request, **kw):
1140         FormatterBase.__init__(self, request, **kw)
1141         import re
1142         self.page_category_regex = re.compile(request.cfg.page_category_regex, re.UNICODE)
1143 
1144     def pagelink(self, on, pagename='', page=None, **kw):
1145         if self.page_category_regex.search(pagename):
1146             FormatterBase.pagelink(self, on, pagename, page, **kw)
1147         return self.null()
1148 
1149 
1150     def getCategories(self, page, update_cache=False):
1151         """ Get a list of the links on this page.
1152         Based on getPageLinks function in Page module
1153 
1154         @page page: the page name
1155         @rtype: list
1156         @return: category names this page belongs to
1157         """
1158         request = self.request
1159         if page:
1160             self.setPage(page)
1161         page = self.page
1162         if page.exists():
1163             from MoinMoin import caching
1164             cache = caching.CacheEntry(request, page, 'categories', scope='item', do_locking=False, use_pickle=True)
1165             if update_cache or cache.needsUpdate(page._text_filename()):
1166                 links = self.parseCategories()
1167                 cache.update(links)
1168             else:
1169                 try:
1170                     links = cache.content()
1171                 except caching.CacheError:
1172                     links = self.parseCategories()
1173                     cache.update(links)
1174         else:
1175             links = []
1176 
1177         return links
1178 
1179 
1180     def parseCategories(self):
1181         """ Parse categories by formatting with a categories formatter 
1182         [Based on parsePageLinks function in Page module]
1183 
1184         This is a old hack to get the pagelinks by rendering the page
1185         with send_page. We can remove this hack after factoring
1186         send_page and send_page_content into small reuseable methods.
1187         
1188         More efficient now by using special pagelinks formatter and
1189         redirecting possible output into null file.
1190         """
1191         request = self.request
1192         page = self.page
1193         pagename = page.page_name
1194 
1195         request.clock.start('parseCategories')
1196 
1197         class Null:
1198             def write(self, data):
1199                 pass
1200 
1201         request.redirect(Null())
1202         try:
1203             self.pagelinks = []
1204             pi = page.pi
1205             page.send_page_content(request, page.data,
1206                                    format=pi['format'],
1207                                    format_args=pi['formatargs'],
1208                                    do_cache=1,
1209                                    start_line=pi['lines'])
1210         finally:
1211             request.redirect()
1212             # if hasattr(request, '_fmt_hd_counters'):
1213             #    del request._fmt_hd_counters
1214             request.clock.stop('parseCategories')
1215         return self.pagelinks
1216 
1217         
1218     def null(self, *args, **kw):
1219         return ''
1220         
1221     def macro(self, macro_obj, name, args, markup=None):
1222         return ''
1223         
1224     # All these must be overriden here because they raise
1225     # NotImplementedError!@#! or return html?! in the base class.
1226     set_highlight_re = rawHTML = url = image = smiley = text = null
1227     strong = emphasis = underline = highlight = sup = sub = strike = null
1228     code = preformatted = small = big = code_area = code_line = null
1229     code_token = linebreak = paragraph = rule = icon = null
1230     number_list = bullet_list = listitem = definition_list = null
1231     definition_term = definition_desc = heading = table = null
1232     table_row = table_cell = attachment_link = attachment_image = attachment_drawing = null
1233     transclusion = transclusion_param = null

Attached Files

To refer to attachments on a page, use attachment:filename, as shown below in the list of files. Do NOT use the URL of the [get] link, since this is subject to change and can break easily.
  • [get | view] (2008-03-14 14:13:47, 51.7 KB) [[attachment:explorer-2.1.1alpha.py]]
 All files | Selected Files: delete move to page copy to page

You are not allowed to attach a file to this page.