* looking for arch@arch.thinkmo.de--2003-archives/moin--main--1.3--patch-538 to compare with
* comparing to arch@arch.thinkmo.de--2003-archives/moin--main--1.3--patch-538
M  MoinMoin/theme/__init__.py
M  MoinMoin/macro/EditedSystemPages.py
M  MoinMoin/action/RenamePage.py
M  MoinMoin/Page.py
M  MoinMoin/PageEditor.py
M  MoinMoin/request.py
M  MoinMoin/util/filesys.py

* modified files

--- orig/MoinMoin/Page.py
+++ mod/MoinMoin/Page.py
@@ -21,16 +21,16 @@
 _acl_cache = {}
 
 class Page:
-    """Page - Manage an (immutable) page associated with a WikiName.
-       To change a page's content, use the PageEditor class.
+    """ Page - manage immutable page associated with a WikiName.
+    
+    To change a page's content, use the PageEditor class.
     """
 
     # Header regular expression, used to get header boundaries
     header_re = r'(^#+.*(?:\n\s*)+)+'
 
     def __init__(self, request, page_name, **keywords):
-        """
-        Create page object.
+        """ Init page object.
 
         Note that this is a 'lean' operation, since the text for the page
         is loaded on demand. Thus, things like `Page(name).link_to()` are
@@ -38,6 +38,7 @@
 
         @param page_name: WikiName of the page
         @keyword rev: number of older revision
+        @keyword is_rootpage: does this page is the wiki rootpage
         @keyword formatter: formatter instance
         """
         self.rev = keywords.get('rev', 0) # revision of this page
@@ -62,134 +63,143 @@
         self._raw_body_modified = 0
         self.hilite_re = None
         self.language = None
-
-        self.reset()
-
-    def reset(self):
-        """ Reset page state """
-        page_name = self.page_name
-        # page_name quoted for file system usage, needs to be reset to
-        # None when pagename changes
-        
-        qpagename = wikiutil.quoteWikinameFS(page_name)
-        self.page_name_fs = qpagename
-
-        # the normal and the underlay path used for this page
-        if not self.cfg.data_underlay_dir is None:
-            underlaypath = os.path.join(self.cfg.data_underlay_dir, "pages", qpagename)
-        else:
-            underlaypath = None
-        if self.is_rootpage: # we have no request.rootpage yet!
-            if not page_name:
-                normalpath = self.cfg.data_dir
-            else:
-                raise NotImplementedError(
-                    "TODO: handle other values of rootpage (not used yet)")
-        else:
-            normalpath = self.request.rootpage.getPagePath("pages", qpagename,
-                                                           check_create=0, use_underlay=0)
         
-        # TUNING - remember some essential values
+        # Add page info dict to cache, create new empty if its not there
+        if not self.page_name in self.request.pages:
+            self.reset()
+        else:
+            self._info = self.request.pages[self.page_name]     
 
-        # does the page come from normal page storage (0) or from
-        # underlay dir (1) (can be used as index into following lists)
-        self._underlay = None
+    def reset(self):
+        """ Called when page information has been changed
 
-        # path to normal / underlay page dir
-        self._pagepath = [normalpath, underlaypath]
+        After this call, any page state query, such as exists() will
+        cause another disk access and rebuild of the page cache state.
+        """
+        self._info = self.request.pages[self.page_name] = {}
 
-        # path to normal / underlay page file
-        self._pagefile = [normalpath, underlaypath]
+    def storageName(self):
+        """ Return the storage name of the page """
+        if not 'storage_name' in self._info:
+            self._info['storage_name'] = wikiutil.quoteWikinameFS(self.page_name)
+        return self._info['storage_name']
 
-        # *latest* revision of this page XXX needs to be reset to None
-        # when current rev changes
-        self._current_rev = [None, None]
+    def _getInfoFromPage(self, path):
+        """ Get page current information
 
-        # does a page in _pagepath (current revision) exist?  XXX needs
-        # to be reset when rev is created/deleted
-        self._exists = [None, None]
+        Access the disk directly, does not change page state.
         
-    def get_current_from_pagedir(self, pagedir):
-        """ get the current revision number from an arbitrary pagedir.
-            does not modify page object's state, uncached, direct disk access.
-            @param pagedir: the pagedir with the 'current' file to read
-            @return: int currentrev
-        """
-        revfilename = os.path.join(pagedir, 'current')
-        try:
-            revfile = open(revfilename)
-            revstr = revfile.read().strip()
-            revfile.close()
-            rev = int(revstr)
-        except:
-            rev = 99999999 # XXX
-        return rev
-        
-    def get_rev_dir(self, pagedir, rev=0):
-        """ 
-        get a revision of a page from an arbitrary pagedir.
-        does not modify page object's state, uncached, direct disk access.
-        @param pagedir: the path to the pagedir
-        @param rev: int revision to get (default is 0 and means the current
+        @param path: the path to the pagedir
+        @param revision: int revision to get (default is None and means the current
                     revision (in this case, the real revint is returned)
-        @return: (str pagefilename, int realrevint, bool exists)
+        @rtype: dict
+        @return: dictionary with page information
         """
-        if rev == 0:
-            rev = self.get_current_from_pagedir(pagedir)
-        
-        revstr = '%08d' % rev
-        pagefile = os.path.join(pagedir, 'revisions', revstr)
-        exists = os.path.exists(pagefile)
-        return pagefile, rev, exists
-    
-    def get_rev(self, use_underlay=-1, rev=0):
-        """ 
-        get a revision of this page 
-        @param use_underlay: -1 == auto, 0 == normal, 1 == underlay
-        @param rev: int revision to get (default is 0 and means the current
-                    revision (in this case, the real revint is returned)
-        @return: (str pagefilename, int realrevint, bool exists)
+        # Get current revision
+        current = os.path.join(path, 'current')
+        try:
+            f = file(current)
+            filename = f.read().strip()
+            f.close()
+            revision = int(filename)
+        except (IOError, OSError, ValueError):
+            # No current file of broken file
+            # Keeping old code behavior
+            filename = '99999999'
+            revision =  99999999
+
+        # Make information dict
+        textfile = os.path.join(path, 'revisions', filename)
+        info = {
+            'revision': revision,
+            'textfile': textfile,
+            'exists': os.path.exists(textfile),
+            }
+
+        return info
+
+    def _getInfoFromDomain(self, domain, name):
+        """ Get info from one of the wiki domains,
+
+        Access the disk directly, does not change page state.
+
+        @param domain: the wiki domain, 'standard' or 'underlay'
+        @param name: the storage name of the page
+        @rtype: dict
+        @return: dict with page information
+        """        
+        # Setup path
+        options = {
+            'standard': self.cfg.data_dir,
+            'underlay': self.cfg.data_underlay_dir,
+            }
+        path = options[domain]
+        info = {}
+        if path:
+            pagepath = os.path.join(path, 'pages', name)
+            if os.path.exists(pagepath):
+                info['path'] = pagepath
+                info['standard'] = domain == 'standard'
+                info.update(self._getInfoFromPage(pagepath))
+                                    
+        return info
+
+    def getInfo(self):
+        """ Get page current information
+
+        This is the central point to get information about the current
+        state of the page. All accessors calls use this to get page
+        information.
+
+        This method use lower level _get methods to get information from
+        the disk, then return cached values. self._info is actually
+        bound to request.pages[pagename] dict. All instances of same
+        page share same information.
+
+        @rtype: dict
+        @return: page information dict
         """
-        if use_underlay == -1:
-            if self._underlay is not None and self._pagepath[self._underlay] is not None:
-                underlay = self._underlay
-                pagedir = self._pagepath[underlay]
+        if not self._info.get('checked'):
+            # This is the first time, get info from disk
+            if self is self.request.rootpage:
+                # The wiki rootpage
+                self._info['path'] = self.cfg.data_dir
+                self._info['exists'] = True
             else:
-                underlay, pagedir = self.getPageStatus(check_create=0)
-        else:
-            underlay, pagedir = use_underlay, self._pagepath[use_underlay]
+                name = self.storageName()
+                info = self._getInfoFromDomain('standard', name)
+                self._info.update(info)
+                if not info.get('exists'):
+                    # If the page does not exists in the standard domain,
+                    # look for it in underlay.
+                    info = self._getInfoFromDomain('underlay', name)
+                    self._info.update(info)
+                    
+            # Mark as checked - no more disk access
+            self._info['checked'] = True
 
-        if rev == 0:
-            if self._current_rev[underlay] is None:
-                realrev = self.get_current_from_pagedir(pagedir)
-                self._current_rev[underlay] = realrev # XXX XXX
-            else:
-                realrev = self._current_rev[underlay]
-        
-            _exists = self._exists[underlay]
-            _realrev = self._current_rev[underlay]
-            _pagefile = self._pagefile[underlay]
-            if _pagefile is not None and \
-               _realrev is not None and _exists is not None:
-                return _pagefile, _realrev, _exists
-        else:
-            realrev = rev
-        
-        pagefile, realrev, exists = self.get_rev_dir(pagedir, realrev)
-        if rev == 0:
-            self._exists[underlay] = exists # XXX XXX
-            self._current_rev[underlay] = realrev # XXX XXX
-            self._pagefile[underlay] = pagefile # XXX XXX
-            
-        return pagefile, realrev, exists
+        return self._info
 
     def current_rev(self):
-        pagefile, rev, exists = self.get_rev()
-        return rev
+        """ Return the current revision of the page
 
+        @rtype: int
+        @return: current revision
+        """
+        # If page exists, we simply return the real revision from the
+        # disk.
+        if self.exists():
+            return self.getInfo()['revision']
+
+        # For non existing pages, the old behavior is to return
+        # 99999999, which is quite broken, and need to be fixed in the
+        # future.
+        return 99999999            
+        
     def get_real_rev(self):
-        """Returns the real revision number of this page. A rev=0 is
-        translated to the current revision.
+        """ Returns the real revision number of this page.
+
+        A rev=0 is translated to the current revision.
 
         @returns: revision number > 0
         @rtype: int
@@ -197,106 +207,88 @@
         if self.rev == 0:
             return self.current_rev()
         return self.rev
-
-    def getPageBasePath(self, use_underlay):
-        """
+    
+    def getPagePath(self, *args, **kw):
+        """ Get page path to storage area
+        
         Get full path to a page-specific storage area. `args` can
-        contain additional path components that are added to the base path.
+        contain additional path components that are added to the base
+        path.
+
+        This method return real paths for existing pages. For non
+        existing pages, the default path will be at the data_dir, or as
+        specified by use_underlay.
 
-        @param use_underlay: force using a specific pagedir, default '-1'
-                                '-1' = automatically choose page dir
-                                '1' = use underlay page dir
-                                '0' = use standard page dir
+        @param args: additional path components
+        @keyword use_underlay: use the underlay page directory (default -1)
+            -1 will look for the page and return the true page path, or a
+            page path in the standard directory.
+        @keyword check_create: create the path (including all missing
+            directories) if it does not exist. (default True)
+        @keyword isfile: is the last component in args a filename? (default False)
         @rtype: string
         @return: the full path to the storage area
         """
-        standardpath, underlaypath = self._pagepath
-        if underlaypath is None:
-            use_underlay = 0
-        
-        # self is a NORMAL page
-        if not self is self.request.rootpage:
-            if use_underlay == -1: # automatic
-                if self._underlay is None:
-                    underlay, path = 0, standardpath
-                    pagefile, rev, exists = self.get_rev(use_underlay=0)
-                    if not exists:
-                        pagefile, rev, exists = self.get_rev(use_underlay=1)
-                        if exists:                         
-                            underlay, path = 1, underlaypath
-                    self._underlay = underlay # XXX XXX
-                else:
-                    underlay = self._underlay
-                    path = self._pagepath[underlay]
-            else: # normal or underlay
-                underlay, path = use_underlay, self._pagepath[use_underlay]                
+        use_underlay = kw.get('use_underlay', -1)
         
-        # self is rootpage
+        if self is self.request.rootpage and self.page_name == '':
+            # Root page
+            if use_underlay == 1:
+                path = self.cfg.data_underlay_dir
+            else:
+                # use_underlay -1 is ignored, does not make sense here.
+                path = self.cfg.data_dir
         else:
-            # our current rootpage is not a toplevel, but under another page
-            if self.page_name:
-                # this assumes flat storage of pages and sub pages on same level
-                if use_underlay == -1: # automatic
-                    if self._underlay is None:
-                        underlay, path = 0, standardpath
-                        pagefile, rev, exists = self.get_rev(use_underlay=0)
-                        if not exists:
-                            pagefile, rev, exists = self.get_rev(use_underlay=1)
-                            if exists:
-                                underlay, path = 1, underlaypath
-                        self._underlay = underlay # XXX XXX
-                    else:
-                        underlay = self._underlay
-                        path = self._pagepath[underlay]
-                else: # normal or underlay
-                    underlay, path = use_underlay, self._pagepath[use_underlay]                
-            
-            # our current rootpage is THE virtual rootpage, really at top of all
+            # Regular page
+
+            # Default page paths
+            # TODO: cache these instead of recreating for each call?
+            name = self.storageName()
+            standard = os.path.join(self.cfg.data_dir, 'pages', name)
+            if self.cfg.data_underlay_dir is not None:
+                underlay = os.path.join(self.cfg.data_underlay_dir, 'pages',
+                                        name)
             else:
-                # 'auto' doesn't make sense here. maybe not even 'underlay':
-                if use_underlay == 1:
-                    underlay, path = 1, self.cfg.data_underlay_dir
-                # no need to check 'standard' case, we just use path in that case!
+                underlay = None
+
+            if use_underlay == -1:
+                if self.exists():
+                    # For existing pages, return the path to the page
+                    path = self.getInfo()['path']
                 else:
-                    # this is the location of the virtual root page
-                    underlay, path = 0, self.cfg.data_dir
-        
-        return underlay, path
+                    # If the page does not exists, return the path to
+                    # where it will be created - in the standard directory.
+                    path = standard
+            elif use_underlay == 1:
+                # Return path in the underlay directory
+                path = underlay
+            elif use_underlay == 0:
+                # Return path in the standard directory
+                path = standard
+            else:
+                raise ValueError('Invalid value for use_underlay: %r' %
+                                 use_underlay)
 
-    def getPageStatus(self, *args, **kw):
-        """
-        Get full path to a page-specific storage area. `args` can
-        contain additional path components that are added to the base path.
+        # Create full path
+        if path:
+            fullpath = os.path.join(*((path,) + args))
+            
+            # Self repair storage directories
+            # TODO: move this into a separate function?
+            check_create = kw.get('check_create', True)
+            if check_create:
+                isfile = kw.get('isfile', False)
+                if isfile:
+                    dirname, filename = os.path.split(fullpath)
+                else:
+                    dirname = fullpath
+                if not os.path.exists(dirname):
+                    filesys.makeDirs(dirname)
 
-        @param args: additional path components
-        @keyword use_underlay: force using a specific pagedir, default '-1'
-                                -1 = automatically choose page dir
-                                1 = use underlay page dir
-                                0 = use standard page dir
-        @keyword check_create: if true, ensures that the path requested really exists
-                               (if it doesn't, create all directories automatically).
-                               (default true)
-        @keyword isfile: is the last component in args a filename? (default is false)
-        @rtype: string
-        @return: the full path to the storage area
-        """
-        check_create = kw.get('check_create', 1)
-        isfile = kw.get('isfile', 0)
-        use_underlay = kw.get('use_underlay', -1)
-        underlay, path = self.getPageBasePath(use_underlay)
-        fullpath = os.path.join(*((path,) + args))
-        if check_create:
-            if isfile:
-                dirname, filename = os.path.split(fullpath)
-            else:
-                dirname = fullpath
-            if not os.path.exists(dirname):
-                filesys.makeDirs(dirname)
-        return underlay, fullpath
-    
-    def getPagePath(self, *args, **kw):
-        return self.getPageStatus(*args, **kw)[1]
+            return fullpath
 
+        return path
+        
     def split_title(self, request, force=0):
         """
         Return a string with the page name split by spaces, if
@@ -319,30 +311,45 @@
         return splitted
 
     def _text_filename(self, **kw):
-        """
-        The name of the page file, possibly of an older page.
+        """ The name of the page file, possibly of an older page.
         
         @keyword rev: page revision, overriding self.rev
         @rtype: string
         @return: complete filename (including path) to this page
         """
+        # Force filename, ignore page state
         if hasattr(self, '_text_filename_force'):
             return self._text_filename_force
+
+        # rev keyword override the current revision
         rev = kw.get('rev', 0)
         if not rev and self.rev:
             rev = self.rev
-        fname, rev, exists = self.get_rev(-1, rev)
-        return fname
+        
+        if rev == 0 and self.exists():
+            # Return the current real textfile
+            textfile = self.getInfo()['textfile']
+        else:               
+            # For all other caese, return the a default textfile, which
+            # might not exists.
+            textfile = self.getPagePath('revisions', '%08d' % rev, check_create=0)
+
+        return textfile
 
     def _tmp_filename(self):
-        """
-        The name of the temporary file used while saving.
+        """ The name of the temporary file used while saving.
+
+        TODO: can we use tempfile module instead?
         
         @rtype: string
         @return: temporary filename (complete path + filename)
         """
-        rnd = random.randint(0,1000000000)
-        tmpname = os.path.join(self.cfg.data_dir, '#%s.%d#' % (self.page_name_fs, rnd))
+        import time
+        
+        path = self.getPagePath('temp')
+        now = int(time.time())
+        rnd = random.randint(0,100000)
+        tmpname = os.path.join(path, '%d.%d' % (now, rnd))
         return tmpname
 
     # XXX TODO clean up the mess, rewrite _last_edited, last_edit, lastEditInfo for new logs,
@@ -426,88 +433,81 @@
         """
         return os.access(self._text_filename(), os.W_OK) or not self.exists()
 
-    def isUnderlayPage(self, includeDeleted=True):
+    def isUnderlay(self):
         """ Does this page live in the underlay dir?
 
-        Return true even if the data dir has a copy of this page. To
-        check for underlay only page, use ifUnderlayPage() and not
-        isStandardPage()
-
-        @param includeDeleted: include deleted pages
         @rtype: bool
         @return: true if page lives in the underlay dir
         """
-        return self.exists(domain='underlay', includeDeleted=includeDeleted)
+        return not self.isStandard()
 
-    def isStandardPage(self, includeDeleted=True):
+    def isStandard(self):
         """ Does this page live in the data dir?
 
-        Return true even if this is a copy of an underlay page. To check
-        for data only page, use isStandardPage() and not isUnderlayPage().
+        Return true even if this is a copy of an underlay page.
+
+        @rtype: bool
+        @return: true if page lives in the data dir
+        """
+        info = self.getInfo()
+        return info.get('exists') and info.get('standard')
+
+    def isOverlay(self):
+        """ Is this a standard page overlaying an underlay page?
+
+        Access the disk and updage page state.
 
-        @param includeDeleted: include deleted pages
         @rtype: bool
         @return: true if page lives in the data dir
         """
-        return self.exists(domain='standard', includeDeleted=includeDeleted)
+        info = self.getInfo()
+        if not 'overlay' in info:
+            overlay = False
+            if info.get('exists') and info.get('standard'):
+                underlay = self._getInfoFromDomain('underlay',
+                                                  self.storageName())
+                overlay = underlay.get('exists', False)
+            info['overlay'] = overlay
+            
+        return info['overlay']
+
+    def isDeleted(self):
+        """ Is this page deleted?
+
+        Deleted page have a directory but no text file
+        """
+        info = self.getInfo()
+        return info.get('path') and not info['exists']
                 
-    def exists(self, rev=0, domain=None, includeDeleted=False):
+    def exists(self):
         """ Does this page exist?
-
-        This is the lower level method for checking page existence. Use
-        the higher level methods isUnderlayPagea and isStandardPage for
-        cleaner code.
         
         @param rev: revision to look for. Default check current
-        @param domain: where to look for the page. Default look in all,
-            available values: 'underlay', 'standard'
-        @param includeDeleted: ignore page state, just check its pagedir
         @rtype: bool
         @return: true, if page exists
         """
-        # Edge cases
-        if domain == 'underlay' and not self.request.cfg.data_underlay_dir:
-            return False
-                        
-        if includeDeleted:                
-            # Look for page directory, ignore page state
-            if domain is None:
-                checklist = [0, 1]
-            else:
-                checklist = [domain == 'underlay']
-            for use_underlay in checklist:
-                pagedir = self.getPagePath(use_underlay=use_underlay, check_create=0)
-                if os.path.exists(pagedir):
-                    return True
-            return False
-        else:
-            # Look for non-deleted pages only, using get_rev
-            if not rev and self.rev:
-                rev = self.rev
-
-            if domain is None:
-                use_underlay = -1
-            else:
-                use_underlay = domain == 'underlay'
-            d, d, exists = self.get_rev(use_underlay, rev)
-            return exists
-
+        info = self.getInfo()
+        return info.get('exists', False)
+        
     def size(self, rev=0):
         """ Get Page size.
         
         @rtype: int
         @return: page size, 0 for non-existent pages.
         """
-        if rev == self.rev: # same revision as self
+        if rev == self.rev:
+            # Report size of current revision
             if self._raw_body is not None:
                 return len(self._raw_body)
 
+        # Report size for specific revision, that might not exists
         try:
             return os.path.getsize(self._text_filename(rev=rev))
         except EnvironmentError, e:
             import errno
-            if e.errno == errno.ENOENT: return 0
-            raise
+            if e.errno == errno.ENOENT:
+                return 0
+            raise e
              
     def mtime_usecs(self):
         """
@@ -568,8 +568,9 @@
             # WARNING: SLOW
             pages = self.getPageList(user='')
         else:
-            pages = self.request.getPages()
-            if not pages:
+            if self.request.pages_complete:
+                pages = self.request.pages
+            else:
                 pages = self._listPages()
         count = len(pages)
         self.request.clock.stop('getPageCount')
@@ -591,8 +592,13 @@
         if user is None:
             user = self.request.user
 
-        acl = self.getACL(self.request)
-        return acl.may(self.request, user.name, what)
+        # User may read is cached per user name in page info
+        key = u'user.%s.%s' % (user.name, what)
+        if not key in self._info:
+            acl = self.getACL(self.request)
+            self._info[key] = acl.may(self.request, user.name, what)
+
+        return self._info[key]
 
     def getPageList(self, user=None, exists=1, filter=None):
         ''' List user readable pages under current page
@@ -628,10 +634,15 @@
         # Check input
         if user is None:
             user = request.user
-
-        # Get pages cache or create it
-        cache = request.getPages()
-        if not cache:
+        # ACL right cached by user name and right name for quick second
+        # access.
+        if user:
+            userkey = u'user.%s.read' % (user.name)
+
+        # Get pages cache, that may contain already partial pages data
+        cache = request.pages
+        if not request.pages_complete:
+            # Update cache from disk
             for name in self._listPages():
                 # Unquote file system names
                 pagename = wikiutil.unquoteWikiname(name)
@@ -639,15 +650,21 @@
                 # Filter those annoying editor backups
                 if pagename.endswith(u'/MoinEditorBackup'):
                     continue
-                
-                cache[pagename] = 1
+
+                # Add new pages
+                if not pagename in cache:
+                    cache[pagename] = {}
+                                
+            # Set complete flag to True, so next call will use cache
+            request.pages_complete = True
 
         if user or exists or filter:
             # Filter names
             pages = []
             for name in cache:
                 page = None
-
+                info = cache[name]
+                
                 # First, custom filter - exists and acl check are very
                 # expensive!
                 if filter and not filter(name):
@@ -655,18 +672,24 @@
 
                 # Filter deleted pages
                 if exists:
-                    page = Page(request, name)
-                    if not page.exists():
+                    if not 'checked' in info:
+                        page = Page(request, name)
+                        if not page.exists():
+                            continue
+                    elif not info.get('exists'):
                         continue
-
+                    
                 # Filter out page user may not read. Bypass user.may.read
                 # to avoid duplicate page instance and page exists call. 
                 if user:
-                    if not page:
-                        page = Page(request, name)
-                    if not page.userMay('read', user=user):
+                    if not userkey in info:
+                        if not page:
+                            page = Page(request, name)
+                        if not page.userMay('read', user=user):
+                            continue
+                    elif not info[userkey]:
                         continue
-
+                    
                 pages.append(name)
         else:
             pages = cache.keys()
@@ -711,7 +734,7 @@
             path = self.getPagePath('pages', use_underlay=1)
             underlay = self._listPageInPath(path)
             pages.update(underlay)
-                                
+                                                    
         return pages
 
     def _listPageInPath(self, path):
@@ -735,7 +758,7 @@
             if name.startswith('.') or name.startswith('#') or name == 'CVS':
                 continue
             
-            pages[name] = 1
+            pages[name] = None
             
         return pages
 
@@ -773,7 +796,7 @@
                 file.close()
 
         return self._raw_body
-
+    
     def set_raw_body(self, body, modified=0):
         """ Set the raw body text (prevents loading from disk).
 
@@ -1430,7 +1453,8 @@
         # Lazy compile regex on first use. All instances share the
         # same regex, compiled once when the first call in an instance is done.
         if isinstance(self.__class__.header_re, (str, unicode)):
-            self.__class__.header_re = re.compile(self.__class__.header_re, re.MULTILINE | re.UNICODE)
+            self.__class__.header_re = re.compile(self.__class__.header_re,
+                                                  re.MULTILINE | re.UNICODE)
 				    
         body = self.get_raw_body() or ''
         header = self.header_re.search(body)
@@ -1453,7 +1477,8 @@
         # Lazy compile regex on first use. All instances share the
         # same regex, compiled once when the first call in an instance is done.
         if isinstance(self.__class__.header_re, (str, unicode)):
-            self.__class__.header_re = re.compile(self.__class__.header_re, re.MULTILINE | re.UNICODE)
+            self.__class__.header_re = re.compile(self.__class__.header_re,
+                                                  re.MULTILINE | re.UNICODE)
 
         body = self.get_raw_body() or ''
         header = self.header_re.search(body)
@@ -1545,7 +1570,9 @@
             return wikiacl.AccessControlList(request)
 
         # Get page state
-        pagefile, revision, exists = self.get_rev()
+        info = self.getInfo()
+        revision = info.get('revision')
+        exists = info.get('exists')
         if not exists:
             # Get previous revision
             revisions = self.getRevList()
@@ -1553,7 +1580,7 @@
                 revision = revisions[1]
             
         # Try to get value from cache
-        key = (request.cfg.siteid, self.page_name)
+        key = (self.cfg.siteid, self.page_name)
         aclRevision, acl = _acl_cache.get(key, (None, None))
         
         if aclRevision != revision or acl is None:


--- orig/MoinMoin/PageEditor.py
+++ mod/MoinMoin/PageEditor.py
@@ -759,14 +759,23 @@
 
         self.copypage()
 
-        pagedir = self.getPagePath(check_create=0)
+        # Write always on the standard directory, never change the
+        # underlay directory copy!
+        pagedir = self.getPagePath(use_underlay=0, check_create=0)
+
         revdir = os.path.join(pagedir, 'revisions')
         cfn = os.path.join(pagedir,'current')
         clfn = os.path.join(pagedir,'current-locked')
-
+        
         # !!! these log objects MUST be created outside the locked area !!!
+
+        # The local log should be the standard edit log, not the
+        # underlay copy log!
+        pagelog = self.getPagePath('edit-log', use_underlay=0, isfile=1)
+        llog = editlog.EditLog(self.request, filename=pagelog,
+                               uid_override=self.uid_override)
+        # Open the global log
         glog = editlog.EditLog(self.request, uid_override=self.uid_override)
-        llog = editlog.EditLog(self.request, rootpagename=self.page_name, uid_override=self.uid_override)
         
         if not os.path.exists(pagedir): # new page, create and init pagedir
             os.mkdir(pagedir)


--- orig/MoinMoin/action/RenamePage.py
+++ mod/MoinMoin/action/RenamePage.py
@@ -97,17 +97,17 @@
 
         # Valid new name
         newpage = PageEditor(self.request, newpagename)
-
-        # Check whether a page with the new name already exists
-        if newpage.exists(includeDeleted=1):
-            return self.pageExistsError(newpagename)
-        
+    
         # Get old page text
         savetext = self.page.get_raw_body()
 
         oldpath = self.page.getPagePath(check_create=0)
         newpath = newpage.getPagePath(check_create=0)
 
+        # Check whether a page with the new name already exists
+        if os.path.exists(newpath):
+            return self.pageExistsError(newpagename)
+
         # Rename page
 
         # NOTE: might fail if another process created newpagename just
@@ -125,7 +125,7 @@
         except OSError, err:
             # Try to understand what happened. Maybe its better to check
             # the error code, but I just reused the available code above...
-            if newpage.exists(includeDeleted=1):
+            if os.path.exists(newpath):
                 return self.pageExistsError(newpagename)
             else:
                 self.error = _('Could not rename page because of file system'



--- orig/MoinMoin/macro/EditedSystemPages.py
+++ mod/MoinMoin/macro/EditedSystemPages.py
@@ -21,12 +21,11 @@
         from MoinMoin.Page import Page
 
         # Get page list for current user (use this as admin), filter
-        # pages that are both underlay and standard pages.
+        # standard pages that overlay underlay pages.
         def filter(name):
             page = Page(self.request, name)
-            return (page.isStandardPage(includeDeleted=0) and
-                    page.isUnderlayPage(includeDeleted=0))
-
+            return page.isOverlay()
+                
         # Get page filtered page list. We don't need to filter by
         # exists, because our filter check this already.
         pages = self.request.rootpage.getPageList(filter=filter, exists=0)


--- orig/MoinMoin/request.py
+++ mod/MoinMoin/request.py
@@ -67,7 +67,9 @@
         self._known_actions = None
 
         # Pages meta data that we collect in one request
-        self._pages = {}
+        self.pages = {}
+        # Does pages contains all pages in the wiki?
+        self.pages_complete = False
                     
         self.sent_headers = 0
         self.user_headers = []
@@ -355,7 +357,7 @@
             actions.extend(extension_actions)           
            
             # TODO: Use set when we require Python 2.3
-            actions = dict(zip(actions, [''] * len(actions)))            
+            actions = dict(zip(actions, [None] * len(actions)))            
             self.known_actions = actions
 
         # Return a copy, so clients will not change the dict.
@@ -372,10 +374,9 @@
         @return: dict of avaiable actions
         """
         if self._available_actions is None:
-            # Add actions for existing pages only!, incliding deleted pages.
+            # Add actions for existing pages only!, including deleted pages.
             # Fix *OnNonExistingPage bugs.
-            if not (page.exists(includeDeleted=1) and
-                    self.user.may.read(page.page_name)):
+            if not (page.exists() or page.isDeleted()) and page.userMay('read'):
                 return []
 
             # Filter non ui actions (starts with lower case letter)
@@ -391,11 +392,10 @@
 
             # Filter actions by page type, acl and user state
             excluded = []
-            if ((page.isUnderlayPage() and not page.isStandardPage()) or
-                not self.user.may.write(page.page_name)):
-                # Prevent modification of underlay only pages, or pages
-                # the user can't write to
-                excluded = [u'RenamePage', u'DeletePage',] # AttachFile must NOT be here!
+            if page.isUnderlay() or not page.userMay('write'):
+                # Protect underlay pages or pages the user may not write
+                # AttachFile must NOT be here!
+                excluded = [u'RenamePage', u'DeletePage',]
             elif not self.user.valid:
                 # Prevent rename and delete for non registered users
                 excluded = [u'RenamePage', u'DeletePage']
@@ -407,16 +407,6 @@
 
         # Return a copy, so clients will not change the dict.
         return self._available_actions.copy()
-
-    def getPages(self):
-        """ Return a page meta data dict
-
-        _pages dict contain meta data about pages that we collect in one
-        request. This save the need to access the disk every time we
-        need information about the same page. This is a very common case
-        according to the profiles.
-        """
-        return self._pages
     
     def redirect(self, file=None):
         if file: # redirect output to "file"


--- orig/MoinMoin/theme/__init__.py
+++ mod/MoinMoin/theme/__init__.py
@@ -502,7 +502,7 @@
         @rtype: bool
         @return: true if should show page info
         """
-        if page.exists() and self.request.user.may.read(page.page_name):
+        if page.exists() and page.userMay('read'):
             # These  actions show the  page content.
             # TODO: on new action, page info will not show. A better
             # solution will be if the action itself answer the question:
@@ -769,17 +769,21 @@
         @rtype: bool
         @return: true if editbar should show
         """
-        # Show editbar only for existing pages, including deleted pages,
-        # that the user may read. If you may not read, you can't edit,
-        # so you don't need editbar.
-        if (page.exists(includeDeleted=1) and
-            self.request.user.may.read(page.page_name)):
-            form = self.request.form
-            action = form.get('action', [''])[0]
-            # Do not show editbar on edit or edit preview
-            return not (action == 'edit' or form.has_key('button_preview'))
-        
-        return False
+        key = 'shouldShowEditbar'
+        should = False
+        if not key in self._cache:
+            # Show editbar only for existing pages, including deleted pages,
+            # that the user may read. If you may not read, you can't edit,
+            # so you don't need editbar.
+            if (page.exists() or page.isDeleted()) and page.userMay('read'):
+                form = self.request.form
+                action = form.get('action', [''])[0]
+                # Do not show editbar on edit or edit preview
+                should = not (action == 'edit' or
+                              form.has_key('button_preview'))
+            self._cache[key] = should
+
+        return self._cache[key]
 
     def subscribeLink(self, page):
         """ Return subscribe/unsubscribe link to valid users
@@ -952,10 +956,12 @@
            add(parent.link_to(request, _("Show Parent", formatted=False))) 
         
         # Page actions
-        if page.isWritable() and request.user.may.write(page.page_name):
+        if page.isWritable() and page.userMay('write'):
             add(link(request, quotedname + '?action=edit', _('Edit')))
         else:
             add(_('Immutable Page', formatted=False))              
+
+        # Refresh cache is disabled because it was misused by user as 'reload'
         if 0: # page.canUseCache():
             query = '%(quotedname)s?action=refresh&amp;arena=Page.py&amp;key=%(key)s'
             query = query % {
@@ -963,6 +969,8 @@
                 'key': page.getFormatterName(),
                 }
             add(link(request, query, _('Refresh', formatted=False)))
+
+        # Other stuff
         add(link(request, quotedname + '?action=diff', _('Show Changes', formatted=False)))
         add(link(request, quotedname + '?action=info', _('Get Info', formatted=False)))
         add(self.subscribeLink(page))


--- orig/MoinMoin/util/filesys.py
+++ mod/MoinMoin/util/filesys.py
@@ -105,7 +105,7 @@
         except (IOError, os.error), why:
             errors.append((srcname, dstname, why))
     if errors:
-        raise Error, errors
+        raise RuntimeError, errors
 
 # Code could come from http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/65203
 


