diff --git a/trac/admin/tests/console-tests.txt b/trac/admin/tests/console-tests.txt
index e05f6f5..a3dcc00 100644
--- a/trac/admin/tests/console-tests.txt
+++ b/trac/admin/tests/console-tests.txt
@@ -115,7 +115,7 @@ Available actions:
  TICKET_EDIT_CC, TICKET_EDIT_COMMENT, TICKET_EDIT_DESCRIPTION,
  TICKET_MODIFY, TICKET_VIEW, TIMELINE_VIEW, TRAC_ADMIN,
  VERSIONCONTROL_ADMIN, WIKI_ADMIN, WIKI_CREATE, WIKI_DELETE, WIKI_MODIFY,
- WIKI_VIEW
+ WIKI_RENAME, WIKI_VIEW
 
 ===== test_permission_add_one_action_ok =====
 
@@ -150,7 +150,7 @@ Available actions:
  TICKET_EDIT_CC, TICKET_EDIT_COMMENT, TICKET_EDIT_DESCRIPTION,
  TICKET_MODIFY, TICKET_VIEW, TIMELINE_VIEW, TRAC_ADMIN,
  VERSIONCONTROL_ADMIN, WIKI_ADMIN, WIKI_CREATE, WIKI_DELETE, WIKI_MODIFY,
- WIKI_VIEW
+ WIKI_RENAME, WIKI_VIEW
 
 ===== test_permission_add_multiple_actions_ok =====
 
@@ -186,7 +186,7 @@ Available actions:
  TICKET_EDIT_CC, TICKET_EDIT_COMMENT, TICKET_EDIT_DESCRIPTION,
  TICKET_MODIFY, TICKET_VIEW, TIMELINE_VIEW, TRAC_ADMIN,
  VERSIONCONTROL_ADMIN, WIKI_ADMIN, WIKI_CREATE, WIKI_DELETE, WIKI_MODIFY,
- WIKI_VIEW
+ WIKI_RENAME, WIKI_VIEW
 
 ===== test_permission_remove_one_action_ok =====
 
@@ -220,7 +220,7 @@ Available actions:
  TICKET_EDIT_CC, TICKET_EDIT_COMMENT, TICKET_EDIT_DESCRIPTION,
  TICKET_MODIFY, TICKET_VIEW, TIMELINE_VIEW, TRAC_ADMIN,
  VERSIONCONTROL_ADMIN, WIKI_ADMIN, WIKI_CREATE, WIKI_DELETE, WIKI_MODIFY,
- WIKI_VIEW
+ WIKI_RENAME, WIKI_VIEW
 
 ===== test_permission_remove_multiple_actions_ok =====
 
@@ -254,7 +254,7 @@ Available actions:
  TICKET_EDIT_CC, TICKET_EDIT_COMMENT, TICKET_EDIT_DESCRIPTION,
  TICKET_MODIFY, TICKET_VIEW, TIMELINE_VIEW, TRAC_ADMIN,
  VERSIONCONTROL_ADMIN, WIKI_ADMIN, WIKI_CREATE, WIKI_DELETE, WIKI_MODIFY,
- WIKI_VIEW
+ WIKI_RENAME, WIKI_VIEW
 
 ===== test_component_list_ok =====
 
diff --git a/trac/attachment.py b/trac/attachment.py
index 77bd09f..3de5389 100644
--- a/trac/attachment.py
+++ b/trac/attachment.py
@@ -274,6 +274,72 @@ class Attachment(object):
             except OSError, e:
                 env.log.error("Can't delete attachment directory %s: %s",
                     attachment_dir, exception_to_unicode(e, traceback=True))
+
+    @classmethod
+    def reparent_all(cls, env, old_realm, old_id, new_realm, new_id, db=None):
+        """Reparent all attachments of a given resource to another resource.
+        
+        :param old_realm: old parent's realm
+        :param old_id: old parent's id
+        :param new_realm: new parent's realm
+        :param new_id: new parent's id
+        :param env: the environent
+        :param db: the database connection
+        """
+        old_dir = os.path.join(os.path.normpath(env.path), 'attachments',
+                               old_realm, old_id)
+        if not os.path.exists(old_dir):
+            return
+
+        new_dir = os.path.join(os.path.normpath(env.path), 'attachments',
+                               new_realm, new_id)
+        
+        @with_transaction(env, db)
+        def do_reparent(db):
+            cursor = db.cursor()
+            cursor.execute("""
+                SELECT filename FROM attachment
+                WHERE type=%s AND id=%s
+                """, (old_realm, old_id))
+            
+            renames = []
+            for filename, in cursor:
+                old_filename = os.path.join(old_dir, filename)
+                new_filename = os.path.join(new_dir, filename)
+                if os.path.exists(new_filename):
+                    raise TracError(_("Can't reparent attachment '%(att)s' as "
+                                      "it already exists in %(realm)s:%(id)s", 
+                                      att=new_filename,
+                                      realm=new_realm, id=new_id))
+                renames.append((old_filename, new_filename))
+            
+            try:
+                if not os.path.exists(new_dir):
+                    os.makedirs(new_dir)
+            except OSError, e:
+                env.log.error("Can't create attachment directory '%s'",
+                              new_dir, exc_info=True)
+                raise TracError(_("Can't create attachment folder for "
+                                  "%(realm)s:%(id)s: %(err)",
+                                  realm=new_realm, id=new_id, err=e))
+            
+            for old_filename, new_filename in renames:
+                try:
+                    os.rename(old_filename, new_filename)
+                except OSError, e:
+                    env.log.error("Can't move attachment from '%s' to '%s'",
+                                  old_filename, new_filename, exc_info=True)
+                    raise TracError(_("Can't move attachment '%(att)s'",
+                                      att=os.path.basename(new_filename)))
+            cursor.execute("""
+                UPDATE attachment SET type=%s, id=%s
+                WHERE type=%s AND id=%s
+                """, (new_realm, new_id, old_realm, old_id))
+            
+        env.log.info('Attachments reparented to: %s:%s', new_realm, new_id)
+
+        
+        return
             
     def open(self):
         self.env.log.debug('Trying to open attachment at %s', self.path)
diff --git a/trac/db_default.py b/trac/db_default.py
index 2c9e1a6..7d57acb 100644
--- a/trac/db_default.py
+++ b/trac/db_default.py
@@ -38,6 +38,9 @@ schema = [
     Table('system', key='name')[
         Column('name'),
         Column('value')],
+    Table('urlmap', key='key')[
+        Column('key'),
+        Column('value')],
     Table('permission', key=('username', 'action'))[
         Column('username'),
         Column('action')],
@@ -394,6 +397,13 @@ def get_data(db):
               ('name', 'value'),
                 (('database_version', str(db_version)),
                  ('initial_database_version', str(db_version)))),
+            ('urlmap',
+              ('key', 'value'),
+                (('WikiStart', 'WikiStart'),
+                 ('TitleIndex', 'TitleIndex'),
+                 ('TracGuide', 'TracGuide'),
+                 ('TracInstall', 'TracInstall'),
+                 ('InterMapTxt', 'InterMapTxt'))),
             ('report',
               ('author', 'title', 'query', 'description'),
                 __mkreports(get_reports(db))))
diff --git a/trac/env.py b/trac/env.py
index 50babb3..0ec5b47 100644
--- a/trac/env.py
+++ b/trac/env.py
@@ -384,6 +384,24 @@ class Environment(Component, ComponentManager):
         row = cursor.fetchone()
         return row and int(row[0])
 
+    def get_urlmap(self, key, db=None, reverse=False, keycheck=False):
+        """Return the real path of some fixed pages trac expects,
+        but cannot rely on existing because
+        renaming and deleting pages is possible.
+        """
+        if not db:
+            db = self.get_db_cnx()
+        cursor = db.cursor()
+        k, v = ('key', 'value')
+        if keycheck:
+            k, v = k, k
+        if reverse:
+            k, v = v, k
+        q = "SELECT %s FROM urlmap WHERE %s=" % (v, k)
+        cursor.execute(q + "%s", (key,))
+        row = cursor.fetchone()
+        return row and row[0]
+
     def setup_config(self, load_defaults=False):
         """Load the configuration file."""
         self.config = Configuration(os.path.join(self.path, 'conf',
diff --git a/trac/htdocs/css/wiki.css b/trac/htdocs/css/wiki.css
index 091f2f6..f109783 100644
--- a/trac/htdocs/css/wiki.css
+++ b/trac/htdocs/css/wiki.css
@@ -42,7 +42,7 @@
 #changeinfo br { clear: left }
 #changeinfo .options { padding: 0 0 1em 1em }
 #changeinfo .options, #changeinfo .buttons { clear: left }
-#delete, #save { margin-left: 6em }
+#delete, #rename, #save { margin-left: 6em }
 #preview {
  background: #f4f4f4 url(../draft.png);
  margin: 1em 0 2em;
diff --git a/trac/tests/functional/tester.py b/trac/tests/functional/tester.py
index b983e1b..24a06e5 100755
--- a/trac/tests/functional/tester.py
+++ b/trac/tests/functional/tester.py
@@ -176,6 +176,7 @@ class FunctionalTester(object):
             tc.formvalue('attachment', 'replace', True)
         tc.submit()
         tc.url(self.url + '/attachment/ticket/%s/$' % ticketid)
+        return tempfilename
 
     def clone_ticket(self, ticketid):
         """Create a clone of the given ticket id using the clone button."""
@@ -232,6 +233,7 @@ class FunctionalTester(object):
         tc.formvalue('attachment', 'description', random_sentence())
         tc.submit()
         tc.url(self.url + '/attachment/wiki/%s/$' % name)
+        return tempfilename
 
     def create_milestone(self, name=None, due=None):
         """Creates the specified milestone, with a random name if none is
diff --git a/trac/wiki/api.py b/trac/wiki/api.py
index 69dbed9..eaba52d 100644
--- a/trac/wiki/api.py
+++ b/trac/wiki/api.py
@@ -42,6 +42,9 @@ class IWikiChangeListener(Interface):
     def wiki_page_deleted(page):
         """Called when a page has been deleted."""
 
+    def wiki_page_renamed(page,old_page_name): 
+        """Called when a page has been renamed in-place.""" 
+
     def wiki_page_version_deleted(page):
         """Called when a version of a page has been deleted."""
 
@@ -290,7 +293,7 @@ class WikiSystem(Component):
             pagename, version = pagename.split('@', 1)
         if version and query:
             query = '&' + query[1:]
-        pagename = pagename.rstrip('/') or 'WikiStart'
+        pagename = pagename.rstrip('/') or self.env.get_urlmap('WikiStart')
         referrer = ''
         if formatter.resource and formatter.resource.realm == 'wiki':
             referrer = formatter.resource.id
diff --git a/trac/wiki/model.py b/trac/wiki/model.py
index 6dc95b5..d302dc8 100644
--- a/trac/wiki/model.py
+++ b/trac/wiki/model.py
@@ -91,6 +91,11 @@ class WikiPage(object):
             if version is None:
                 # Delete a wiki page completely
                 cursor.execute("DELETE FROM wiki WHERE name=%s", (self.name,))
+                mapped = self.env.get_urlmap(self.name, db=db, reverse=True)
+                if mapped:
+                    cursor.execute("""UPDATE urlmap SET value=NULL
+                                   WHERE key=%s""", (mapped,))
+                
                 self.env.log.info('Deleted page %s' % self.name)
             else:
                 # Delete only a specific page version
@@ -135,6 +140,10 @@ class WikiPage(object):
                     """, (self.name, self.version + 1, to_utimestamp(t),
                           author, remote_addr, self.text, comment,
                           self.readonly))
+                mappable = self.env.get_urlmap(self.name, db=db, keycheck=True)
+                if mappable:
+                    cursor.execute("""UPDATE urlmap SET value=%s
+                                   WHERE key=%s""", (self.name, self.name))
                 self.version += 1
                 self.resource = self.resource(version=self.version)
             else:
@@ -158,6 +167,45 @@ class WikiPage(object):
         self.old_readonly = self.readonly
         self.old_text = self.text
 
+    def rename(self, new_name, db=None):
+        """Rename wiki page in-place, keeping the history intact.
+        Renaming a page this way will eventually leave dangling references
+        to the old page - which litterally doesn't exist anymore.
+        """
+        assert self.exists, 'Cannot rename non-existent page'
+
+        old_name = self.name
+        
+        @with_transaction(self.env, db)
+        def do_rename(db):
+            cursor = db.cursor()
+            new_page = WikiPage(self.env, new_name, version=None, db=db)
+            if new_page.exists:
+                raise TracError(_("Can't rename to existing %(nn)s page.",
+                                  nn=new_name))
+
+            cursor.execute("UPDATE wiki SET name=%s WHERE name=%s",
+                           (new_name, old_name))
+            mapped = self.env.get_urlmap(old_name, db=db, reverse=True)
+            mappable = self.env.get_urlmap(new_name, db=db, keycheck=True)
+            
+            if mapped or mappable:
+                cursor.execute("""UPDATE urlmap SET value=%s
+                               WHERE key=%s""", (new_name, mapped or mappable))
+            self.name = new_name
+            self._fetch(self.name, None, db)
+
+            from trac.attachment import Attachment
+            Attachment.reparent_all(self.env, 'wiki', old_name,
+                                    'wiki', self.name, db)
+
+        self.env.log.info('Renamed page %s in-place to %s', old_name, new_name)
+        
+        for listener in WikiSystem(self.env).change_listeners:
+            if hasattr(listener, 'wiki_page_renamed'):
+                listener.wiki_page_renamed(self, old_name)
+
+
     def get_history(self, db=None):
         if not db:
             db = self.env.get_db_cnx()
diff --git a/trac/wiki/templates/wiki_rename.html b/trac/wiki/templates/wiki_rename.html
new file mode 100644
index 0000000..f00b807
--- /dev/null
+++ b/trac/wiki/templates/wiki_rename.html
@@ -0,0 +1,48 @@
+<!DOCTYPE html
+    PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"
+      xmlns:py="http://genshi.edgewall.org/"
+      xmlns:xi="http://www.w3.org/2001/XInclude">
+  <xi:include href="layout.html" />
+  <head>
+    <title>$title</title>
+  </head>
+
+  <body>
+    <div id="content" class="wiki"
+      py:with="
+      current_href = href.wiki(page.name);
+     ">
+      <h1>${_('Rename')} <a href="$current_href">$page.name</a></h1>
+      <form action="$current_href" method="post">
+        <p>
+          <input type="hidden" name="action" value="rename" />
+          <strong>Renaming the page will rename all existing versions
+          of the page in place. <br />
+          The history of the renamed page will be exactly the same as the one
+          of the original page.</strong>
+          <br />
+          <p>
+            <label for="new_name">New name:</label>
+            <input type="text" id="new_name" name="new_name" size="40" value="$page.name" />
+          </p>
+          <br />
+          <fieldset id="rename_options" class="group">
+            <legend>${_('Rename')} ${_('Options')}</legend>
+            <input type="checkbox" id="leave_redirection" name="leave_redirection"/>
+            <label for="leave_redirection">Leave a redirection to the new page.</label>
+            <br />
+            If you choose to leave a redirection, this will 
+            re-create the $page.name page with a link pointing to the new
+            page.
+          </fieldset>
+       </p>
+        <div class="buttons">
+          <input type="submit" name="cancel" value="${_('Cancel')}" />
+          <input type="submit" value="${_('Rename')} $page.name" />
+        </div>
+      </form>
+    </div>
+  </body>
+</html>
diff --git a/trac/wiki/templates/wiki_view.html b/trac/wiki/templates/wiki_view.html
index 0715fbe..17880bd 100644
--- a/trac/wiki/templates/wiki_view.html
+++ b/trac/wiki/templates/wiki_view.html
@@ -71,7 +71,8 @@
 
       <py:with vars="modify_perm = 'WIKI_MODIFY' in perm(page.resource);
                      delete_perm = 'WIKI_DELETE' in perm(page.resource);
-                     admin_perm = 'WIKI_ADMIN' in perm(page.resource)">
+                     admin_perm = 'WIKI_ADMIN' in perm(page.resource);
+	                   rename_perm = 'WIKI_RENAME' in perm(page.resource)">
         <py:if test="admin_perm or (not page.readonly and (modify_perm or delete_perm))">
           <div class="buttons">
             <py:if test="modify_perm">
@@ -101,6 +102,14 @@
                 ${attach_file_form(attachments)}
               </py:if>
             </py:if>
+            <py:if test="page.exists and rename_perm"> 
+	             <form method="get" action="${href.wiki(page.name)}" id="rename"> 
+	                <div> 
+	                  <input type="hidden" name="action" value="rename" /> 
+	                  <input type="submit" value="${_('Rename page')}" /> 
+	                </div> 
+	             </form> 
+	          </py:if> 
             <py:if test="page.exists and delete_perm">
               <form method="get" action="${href.wiki(page.name)}">
                 <div id="delete">
diff --git a/trac/wiki/tests/functional.py b/trac/wiki/tests/functional.py
index bd85ec8..07a290e 100755
--- a/trac/wiki/tests/functional.py
+++ b/trac/wiki/tests/functional.py
@@ -12,6 +12,66 @@ class TestWiki(FunctionalTwillTestCaseSetup):
         self._tester.attach_file_to_wiki(pagename)
 
 
+class TestWikiRename(FunctionalTwillTestCaseSetup):
+    def runTest(self):
+        """Test for simple wiki rename"""
+        pagename = random_unique_camel()
+        self._tester.create_wiki_page(pagename)
+        self._tester.rename_wiki_page(pagename)
+        attachment = self._tester.attach_file_to_wiki(pagename)
+        base_url = self._tester.url
+        page_url = base_url + "/wiki/" + pagename
+        
+        def click_rename():
+            tc.formvalue('rename', 'action', 'rename')
+            tc.submit()
+            tc.url(page_url+r'\?action=rename')
+            tc.find("New name:")
+        
+        tc.go(page_url)
+        tc.find("Rename page")
+        click_rename()
+        # attempt to rename the page to the current page name ...        
+        tc.formvalue(page_url, 'new_name', 'rename')
+        tc.submit('rename')
+        tc.url(page_url)
+        tc.find("New name must be different from old name.")
+        # attempt to rename the page to an existing page name ...
+        tc.formvalue(page_url, 'new_name', 'WikiStart')
+        tc.submit('rename')
+        tc.url(page_url)
+        tc.find("Trac Error")
+        tc.find("Can't rename to existing WikiStart page")
+        # correct rename to new page name (old page replaced by a redirection)
+        tc.go(page_url)
+        click_rename()
+        newpagename = pagename+'Renamed'
+        tc.formvalue(page_url, 'new_name', newpagename)
+        tc.formvalue(page_url, 'leave_redirection', True) # the default
+        tc.submit('rename')
+        # check redirection page
+        tc.url(page_url)
+        tc.find("See.*/wiki/"+newpagename)
+        # check whether attachment exists on the new page but not on old page
+        tc.go(base_url+'/attachment/wiki/'+newpagename+'/'+attachment)
+        tc.notfind("Error: Invalid Attachment")
+        tc.go(base_url+'/attachment/wiki/'+pagename+'/'+attachment)
+        tc.find("Error: Invalid Attachment")
+        # rename again to another new page name (this time, no redirection)
+        tc.go(page_url)
+        click_rename()
+        newpagename = pagename+'RenamedAgain'
+        tc.formvalue(page_url, 'new_name', newpagename)
+        tc.formvalue(page_url, 'leave_redirection', False)
+        tc.submit('rename')
+        tc.url(base_url + "/wiki/" + newpagename)
+        # this time, the original page is gone
+        tc.go(page_url)
+        tc.url(page_url)
+        tc.find("Describe %s here" % pagename)
+        
+
+
 class RegressionTestTicket4812(FunctionalTwillTestCaseSetup):
     def runTest(self):
         """Test for regression of http://trac.edgewall.org/ticket/4812"""
diff --git a/trac/wiki/tests/model.py b/trac/wiki/tests/model.py
index e4fde47..13f84b7 100644
--- a/trac/wiki/tests/model.py
+++ b/trac/wiki/tests/model.py
@@ -1,6 +1,8 @@
 from datetime import datetime
+import tempfile
 import unittest
 
+from trac.attachment import Attachment
 from trac.core import *
 from trac.test import EnvironmentStub
 from trac.util.datefmt import utc, to_utimestamp
@@ -14,6 +16,7 @@ class TestWikiChangeListener(Component):
         self.changed = []
         self.deleted = []
         self.deleted_version = []
+        self.renamed = {}
 
     def wiki_page_added(self, page):
         self.added.append(page)
@@ -27,6 +30,9 @@ class TestWikiChangeListener(Component):
     def wiki_page_version_deleted(self, page):
         self.deleted_version.append(page)
 
+    def wiki_page_renamed(self, page, old_page_name):
+        self.renamed[old_page_name] = page
+
 
 class WikiPageTestCase(unittest.TestCase):
 
@@ -194,6 +200,60 @@ class WikiPageTestCase(unittest.TestCase):
         listener = TestWikiChangeListener(self.env)
         self.assertEqual(page, listener.deleted[0])
 
+    def test_rename_page(self):
+        cursor = self.db.cursor()
+        data = (1, 42, 'joe', '::1', 'Bla bla', 'Testing', 0)
+        cursor.execute("INSERT INTO wiki VALUES(%s,%s,%s,%s,%s,%s,%s,%s)",
+                       ('TestPage',) + data)
+        
+        page = WikiPage(self.env, 'TestPage')
+
+        attachment = Attachment(self.env, 'wiki', 'TestPage')
+        attachment.insert('foo.txt', tempfile.TemporaryFile(), 0, 1)
+
+        page.rename('PageRenamed')
+        
+        cursor.execute("SELECT version,time,author,ipnr,text,comment,"
+                       "readonly FROM wiki WHERE name=%s", ('PageRenamed',))
+        self.assertEqual(data, cursor.fetchone())
+        self.assertEqual(None, cursor.fetchone())
+        
+        attachments = Attachment.select(self.env, 'wiki', 'PageRenamed')
+        self.assertEqual('foo.txt', attachments.next().filename)
+        self.assertRaises(StopIteration, attachments.next)
+        Attachment.delete_all(self.env, 'wiki', 'PageRenamed', self.db)
+
+
+        old_page = WikiPage(self.env, 'TestPage')
+        
+        cursor.execute("SELECT version,time,author,ipnr,text,comment,"
+                       "readonly FROM wiki WHERE name=%s", ('TestPage',))
+        self.assertEqual(None, cursor.fetchone())
+        
+        listener = TestWikiChangeListener(self.env)
+        self.assertEqual({'TestPage': page}, listener.renamed)
+
+    def test_sys_pages(self):
+        cursor = self.db.cursor()
+        cursor.execute("INSERT INTO urlmap VALUES(%s, NULL)",
+                       ('WikiStart',))
+
+        page = WikiPage(self.env)
+        page.name = 'WikiStart'
+        page.text = 'Bla bla'
+        t = datetime(2001, 1, 1, 1, 1, 1, 0, utc)
+        page.save('joe', 'Testing', '::1', t)
+        
+        cursor.execute("SELECT key, value FROM urlmap WHERE key=%s", ('WikiStart',))
+        self.assertEqual(('WikiStart', 'WikiStart'), cursor.fetchone())
+        
+        page.rename('Home')
+        cursor.execute("SELECT key, value FROM urlmap WHERE key=%s", ('WikiStart',))
+        self.assertEqual(('WikiStart', 'Home'), cursor.fetchone())
+        
+        page.delete()
+        cursor.execute("SELECT key, value FROM urlmap WHERE key=%s", ('WikiStart',))
+        self.assertEqual(('WikiStart', None), cursor.fetchone())
 
 def suite():
     return unittest.makeSuite(WikiPageTestCase, 'test')
diff --git a/trac/wiki/web_ui.py b/trac/wiki/web_ui.py
index 6e3bcde..7bcda70 100644
--- a/trac/wiki/web_ui.py
+++ b/trac/wiki/web_ui.py
@@ -93,7 +93,7 @@ class WikiModule(Component):
     # IPermissionRequestor methods
 
     def get_permission_actions(self):
-        actions = ['WIKI_CREATE', 'WIKI_DELETE', 'WIKI_MODIFY', 'WIKI_VIEW']
+        actions = ['WIKI_CREATE', 'WIKI_DELETE', 'WIKI_MODIFY', 'WIKI_RENAME', 'WIKI_VIEW']
         return actions + [('WIKI_ADMIN', actions)]
 
     # IRequestHandler methods
@@ -107,7 +107,8 @@ class WikiModule(Component):
 
     def process_request(self, req):
         action = req.args.get('action', 'view')
-        pagename = req.args.get('page', 'WikiStart')
+        pagename = req.args.get('page', self.env.get_urlmap('WikiStart'))
+        pagename = pagename or 'WikiStart'
         version = req.args.get('version')
         old_version = req.args.get('old_version')
 
@@ -145,12 +146,16 @@ class WikiModule(Component):
                     return self._render_editor(req, page, action, has_collision)
             elif action == 'delete':
                 self._do_delete(req, versioned_page)
+            elif action == 'rename':
+                return self._do_rename(req, page)
             elif action == 'diff':
                 get_diff_options(req)
                 req.redirect(req.href.wiki(versioned_page.name, action='diff',
                                            old_version=old_version))
         elif action == 'delete':
-            return self._render_confirm(req, versioned_page)
+            return self._render_confirm_delete(req, versioned_page)
+        elif action == 'rename':
+            return self._render_confirm_rename(req, page)
         elif action == 'edit':
             return self._render_editor(req, versioned_page)
         elif action == 'diff':
@@ -245,7 +250,7 @@ class WikiModule(Component):
         old_version = int(req.args.get('old_version', 0)) or version
 
         @with_transaction(self.env)
-        def do_transaction(db):
+        def do_delete(db):
             if version and old_version and version > old_version:
                 # delete from `old_version` exclusive to `version` inclusive:
                 for v in range(old_version, version):
@@ -269,6 +274,49 @@ class WikiModule(Component):
                                   version=version, name=page.name))
             req.redirect(req.href.wiki(page.name))
 
+    def _do_rename(self, req, page):
+        if page.readonly:
+            req.perm(page.resource).require('WIKI_ADMIN')
+        else:
+            req.perm(page.resource).require('WIKI_RENAME')
+ 	 
+        if 'cancel' in req.args:
+            req.redirect(get_resource_url(self.env, page.resource, req.href))
+ 	 
+        new_name = req.args.get('new_name', '').rstrip('/')
+        old_name = page.name
+        old_version = page.version
+        leave_redirection = req.args.get('leave_redirection')
+ 	 
+        # verify input parameters
+        warn = None
+        if not new_name:
+            warn = _("New name is mandatory for a rename.")
+        elif new_name == old_name:
+            warn = _("New name must be different from old name.")
+        if warn:
+            add_warning(req, warn)
+            return self._render_confirm_rename(req, page)
+
+        @with_transaction(self.env)
+        def do_rename(db):
+            page.rename(new_name, db)
+            if leave_redirection:
+                redirection_page = WikiPage(self.env, old_name)
+                redirection_page.text = _("See [wiki:%(page)s].",
+                                          page=new_name)
+                author = get_reporter_id(req)
+                comment = _("[wiki:'%(new_name)s@%(old_version)d' "
+                            "%(old_name)s] was renamed to %(new_name)s",
+                            old_name=old_name, old_version=old_version,
+                            new_name=new_name)
+                redirection_page.save(author, comment, req.remote_addr,
+                                      None, db)
+        
+        req.redirect(req.href.wiki(leave_redirection and old_name or new_name))
+
+        
+
     def _do_save(self, req, page):
         if page.readonly:
             req.perm(page.resource).require('WIKI_ADMIN')
@@ -293,7 +341,7 @@ class WikiModule(Component):
             add_warning(req, _("Page not modified, showing latest version."))
             return self._render_view(req, page)
 
-    def _render_confirm(self, req, page):
+    def _render_confirm_delete(self, req, page):
         if page.readonly:
             req.perm(page.resource).require('WIKI_ADMIN')
         else:
@@ -315,9 +363,28 @@ class WikiModule(Component):
                     break
             data.update({'new_version': version, 'old_version': old_version,
                          'num_versions': num_versions})
+        mapped = self.env.get_urlmap(page.name, reverse=True)
+        if mapped:
+            add_warning(req, _("""'%s' is the '%s' trac system page.
+                               Really delete?""") % (page.name, mapped))
         self._wiki_ctxtnav(req, page)
         return 'wiki_delete.html', data, None
 
+    def _render_confirm_rename(self, req, page):
+        if page.readonly:
+            req.perm(page.resource).require('WIKI_ADMIN')
+        else:
+            req.perm(page.resource).require('WIKI_RENAME')
+           
+            data = self._page_data(req, page, 'rename')
+            mapped = self.env.get_urlmap(page.name, reverse=True)
+            if mapped:
+                add_warning(req, _("""'%s' is the '%s' trac system page.
+                                   Really rename?""") % (page.name, mapped))
+            self._wiki_ctxtnav(req, page)
+            return 'wiki_rename.html', data, None
+        
+
     def _render_diff(self, req, page):
         if not page.exists:
             raise TracError(_('Version %(num)s of page "%(name)s" does not '
@@ -512,7 +579,7 @@ class WikiModule(Component):
                          conversion[3])
 
         data = self._page_data(req, page)
-        if page.name == 'WikiStart':
+        if page.name == self.env.get_urlmap('WikiStart'):
             data['title'] = ''
 
         ws = WikiSystem(self.env)
@@ -606,8 +673,12 @@ class WikiModule(Component):
     
     def _wiki_ctxtnav(self, req, page):
         """Add the normal wiki ctxtnav entries."""
-        add_ctxtnav(req, _('Start Page'), req.href.wiki('WikiStart'))
-        add_ctxtnav(req, _('Index'), req.href.wiki('TitleIndex'))
+        wikistart = self.env.get_urlmap('WikiStart')
+        titleindex = self.env.get_urlmap('TitleIndex')        
+        if wikistart:
+            add_ctxtnav(req, _('Start Page'), req.href.wiki(wikistart))
+        if titleindex:
+            add_ctxtnav(req, _('Index'), req.href.wiki(titleindex))
         if page.exists:
             add_ctxtnav(req, _('History'), req.href.wiki(page.name, 
                                                          action='history'))

