Attachment 'explorer_V19x.py'

Download

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

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