Edgewall Software

Ticket #1106: rename_simple.diff

File rename_simple.diff, 22.3 KB (added by shookie@…, 2 years ago)

The renaming functionality, with no frills, on top of trunk

  • trac/admin/tests/console-tests.txt

    diff --git a/trac/admin/tests/console-tests.txt b/trac/admin/tests/console-tests.txt
    index e9c5f15..6c9c997 100644
    a b Available actions: 
    120120 TICKET_EDIT_CC, TICKET_EDIT_COMMENT, TICKET_EDIT_DESCRIPTION, 
    121121 TICKET_MODIFY, TICKET_VIEW, TIMELINE_VIEW, TRAC_ADMIN, 
    122122 VERSIONCONTROL_ADMIN, WIKI_ADMIN, WIKI_CREATE, WIKI_DELETE, WIKI_MODIFY, 
    123  WIKI_VIEW 
     123 WIKI_RENAME, WIKI_VIEW 
    124124 
    125125===== test_permission_add_one_action_ok ===== 
    126126 
    Available actions: 
    155155 TICKET_EDIT_CC, TICKET_EDIT_COMMENT, TICKET_EDIT_DESCRIPTION, 
    156156 TICKET_MODIFY, TICKET_VIEW, TIMELINE_VIEW, TRAC_ADMIN, 
    157157 VERSIONCONTROL_ADMIN, WIKI_ADMIN, WIKI_CREATE, WIKI_DELETE, WIKI_MODIFY, 
    158  WIKI_VIEW 
     158 WIKI_RENAME, WIKI_VIEW 
    159159 
    160160===== test_permission_add_multiple_actions_ok ===== 
    161161 
    Available actions: 
    191191 TICKET_EDIT_CC, TICKET_EDIT_COMMENT, TICKET_EDIT_DESCRIPTION, 
    192192 TICKET_MODIFY, TICKET_VIEW, TIMELINE_VIEW, TRAC_ADMIN, 
    193193 VERSIONCONTROL_ADMIN, WIKI_ADMIN, WIKI_CREATE, WIKI_DELETE, WIKI_MODIFY, 
    194  WIKI_VIEW 
     194 WIKI_RENAME, WIKI_VIEW 
    195195 
    196196===== test_permission_remove_one_action_ok ===== 
    197197 
    Available actions: 
    225225 TICKET_EDIT_CC, TICKET_EDIT_COMMENT, TICKET_EDIT_DESCRIPTION, 
    226226 TICKET_MODIFY, TICKET_VIEW, TIMELINE_VIEW, TRAC_ADMIN, 
    227227 VERSIONCONTROL_ADMIN, WIKI_ADMIN, WIKI_CREATE, WIKI_DELETE, WIKI_MODIFY, 
    228  WIKI_VIEW 
     228 WIKI_RENAME, WIKI_VIEW 
    229229 
    230230===== test_permission_remove_multiple_actions_ok ===== 
    231231 
    Available actions: 
    259259 TICKET_EDIT_CC, TICKET_EDIT_COMMENT, TICKET_EDIT_DESCRIPTION, 
    260260 TICKET_MODIFY, TICKET_VIEW, TIMELINE_VIEW, TRAC_ADMIN, 
    261261 VERSIONCONTROL_ADMIN, WIKI_ADMIN, WIKI_CREATE, WIKI_DELETE, WIKI_MODIFY, 
    262  WIKI_VIEW 
     262 WIKI_RENAME, WIKI_VIEW 
    263263 
    264264===== test_component_list_ok ===== 
    265265 
  • trac/attachment.py

    diff --git a/trac/attachment.py b/trac/attachment.py
    index 77bd09f..3de5389 100644
    a b class Attachment(object): 
    274274            except OSError, e: 
    275275                env.log.error("Can't delete attachment directory %s: %s", 
    276276                    attachment_dir, exception_to_unicode(e, traceback=True)) 
     277 
     278    @classmethod 
     279    def reparent_all(cls, env, old_realm, old_id, new_realm, new_id, db=None): 
     280        """Reparent all attachments of a given resource to another resource. 
     281         
     282        :param old_realm: old parent's realm 
     283        :param old_id: old parent's id 
     284        :param new_realm: new parent's realm 
     285        :param new_id: new parent's id 
     286        :param env: the environent 
     287        :param db: the database connection 
     288        """ 
     289        old_dir = os.path.join(os.path.normpath(env.path), 'attachments', 
     290                               old_realm, old_id) 
     291        if not os.path.exists(old_dir): 
     292            return 
     293 
     294        new_dir = os.path.join(os.path.normpath(env.path), 'attachments', 
     295                               new_realm, new_id) 
     296         
     297        @with_transaction(env, db) 
     298        def do_reparent(db): 
     299            cursor = db.cursor() 
     300            cursor.execute(""" 
     301                SELECT filename FROM attachment 
     302                WHERE type=%s AND id=%s 
     303                """, (old_realm, old_id)) 
     304             
     305            renames = [] 
     306            for filename, in cursor: 
     307                old_filename = os.path.join(old_dir, filename) 
     308                new_filename = os.path.join(new_dir, filename) 
     309                if os.path.exists(new_filename): 
     310                    raise TracError(_("Can't reparent attachment '%(att)s' as " 
     311                                      "it already exists in %(realm)s:%(id)s",  
     312                                      att=new_filename, 
     313                                      realm=new_realm, id=new_id)) 
     314                renames.append((old_filename, new_filename)) 
     315             
     316            try: 
     317                if not os.path.exists(new_dir): 
     318                    os.makedirs(new_dir) 
     319            except OSError, e: 
     320                env.log.error("Can't create attachment directory '%s'", 
     321                              new_dir, exc_info=True) 
     322                raise TracError(_("Can't create attachment folder for " 
     323                                  "%(realm)s:%(id)s: %(err)", 
     324                                  realm=new_realm, id=new_id, err=e)) 
     325             
     326            for old_filename, new_filename in renames: 
     327                try: 
     328                    os.rename(old_filename, new_filename) 
     329                except OSError, e: 
     330                    env.log.error("Can't move attachment from '%s' to '%s'", 
     331                                  old_filename, new_filename, exc_info=True) 
     332                    raise TracError(_("Can't move attachment '%(att)s'", 
     333                                      att=os.path.basename(new_filename))) 
     334            cursor.execute(""" 
     335                UPDATE attachment SET type=%s, id=%s 
     336                WHERE type=%s AND id=%s 
     337                """, (new_realm, new_id, old_realm, old_id)) 
     338             
     339        env.log.info('Attachments reparented to: %s:%s', new_realm, new_id) 
     340 
     341         
     342        return 
    277343             
    278344    def open(self): 
    279345        self.env.log.debug('Trying to open attachment at %s', self.path) 
  • trac/htdocs/css/wiki.css

    diff --git a/trac/htdocs/css/wiki.css b/trac/htdocs/css/wiki.css
    index 091f2f6..f109783 100644
    a b  
    4242#changeinfo br { clear: left } 
    4343#changeinfo .options { padding: 0 0 1em 1em } 
    4444#changeinfo .options, #changeinfo .buttons { clear: left } 
    45 #delete, #save { margin-left: 6em } 
     45#delete, #rename, #save { margin-left: 6em } 
    4646#preview { 
    4747 background: #f4f4f4 url(../draft.png); 
    4848 margin: 1em 0 2em; 
  • trac/tests/functional/tester.py

    diff --git a/trac/tests/functional/tester.py b/trac/tests/functional/tester.py
    index b983e1b..24a06e5 100755
    a b class FunctionalTester(object): 
    176176            tc.formvalue('attachment', 'replace', True) 
    177177        tc.submit() 
    178178        tc.url(self.url + '/attachment/ticket/%s/$' % ticketid) 
     179        return tempfilename 
    179180 
    180181    def clone_ticket(self, ticketid): 
    181182        """Create a clone of the given ticket id using the clone button.""" 
    class FunctionalTester(object): 
    232233        tc.formvalue('attachment', 'description', random_sentence()) 
    233234        tc.submit() 
    234235        tc.url(self.url + '/attachment/wiki/%s/$' % name) 
     236        return tempfilename 
    235237 
    236238    def create_milestone(self, name=None, due=None): 
    237239        """Creates the specified milestone, with a random name if none is 
  • trac/wiki/api.py

    diff --git a/trac/wiki/api.py b/trac/wiki/api.py
    index f0ea7e9..c226599 100644
    a b class IWikiChangeListener(Interface): 
    4242    def wiki_page_deleted(page): 
    4343        """Called when a page has been deleted.""" 
    4444 
     45    def wiki_page_renamed(page,old_page_name):  
     46        """Called when a page has been renamed in-place."""  
     47 
    4548    def wiki_page_version_deleted(page): 
    4649        """Called when a version of a page has been deleted.""" 
    4750 
  • trac/wiki/model.py

    diff --git a/trac/wiki/model.py b/trac/wiki/model.py
    index 6dc95b5..74b94f2 100644
    a b class WikiPage(object): 
    158158        self.old_readonly = self.readonly 
    159159        self.old_text = self.text 
    160160 
     161    def rename(self, new_name, db=None): 
     162        """Rename wiki page in-place, keeping the history intact. 
     163        Renaming a page this way will eventually leave dangling references 
     164        to the old page - which litterally doesn't exist anymore. 
     165        """ 
     166        assert self.exists, 'Cannot rename non-existent page' 
     167 
     168        old_name = self.name 
     169         
     170        @with_transaction(self.env, db) 
     171        def do_rename(db): 
     172            cursor = db.cursor() 
     173            new_page = WikiPage(self.env, new_name, version=None, db=db) 
     174            if new_page.exists: 
     175                raise TracError(_("Can't rename to existing %(nn)s page.", 
     176                                  nn=new_name)) 
     177 
     178            cursor.execute("UPDATE wiki SET name=%s WHERE name=%s", 
     179                           (new_name, old_name)) 
     180            self.name = new_name 
     181            self._fetch(self.name, None, db) 
     182 
     183            from trac.attachment import Attachment 
     184            Attachment.reparent_all(self.env, 'wiki', old_name, 
     185                                    'wiki', self.name, db) 
     186 
     187        self.env.log.info('Renamed page %s in-place to %s', old_name, new_name) 
     188         
     189        for listener in WikiSystem(self.env).change_listeners: 
     190            if hasattr(listener, 'wiki_page_renamed'): 
     191                listener.wiki_page_renamed(self, old_name) 
     192 
     193 
    161194    def get_history(self, db=None): 
    162195        if not db: 
    163196            db = self.env.get_db_cnx() 
  • new file trac/wiki/templates/wiki_rename.html

    diff --git a/trac/wiki/templates/wiki_rename.html b/trac/wiki/templates/wiki_rename.html
    new file mode 100644
    index 0000000..f00b807
    - +  
     1<!DOCTYPE html 
     2    PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" 
     3    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> 
     4<html xmlns="http://www.w3.org/1999/xhtml" 
     5      xmlns:py="http://genshi.edgewall.org/" 
     6      xmlns:xi="http://www.w3.org/2001/XInclude"> 
     7  <xi:include href="layout.html" /> 
     8  <head> 
     9    <title>$title</title> 
     10  </head> 
     11 
     12  <body> 
     13    <div id="content" class="wiki" 
     14      py:with=" 
     15      current_href = href.wiki(page.name); 
     16     "> 
     17      <h1>${_('Rename')} <a href="$current_href">$page.name</a></h1> 
     18      <form action="$current_href" method="post"> 
     19        <p> 
     20          <input type="hidden" name="action" value="rename" /> 
     21          <strong>Renaming the page will rename all existing versions 
     22          of the page in place. <br /> 
     23          The history of the renamed page will be exactly the same as the one 
     24          of the original page.</strong> 
     25          <br /> 
     26          <p> 
     27            <label for="new_name">New name:</label> 
     28            <input type="text" id="new_name" name="new_name" size="40" value="$page.name" /> 
     29          </p> 
     30          <br /> 
     31          <fieldset id="rename_options" class="group"> 
     32            <legend>${_('Rename')} ${_('Options')}</legend> 
     33            <input type="checkbox" id="leave_redirection" name="leave_redirection"/> 
     34            <label for="leave_redirection">Leave a redirection to the new page.</label> 
     35            <br /> 
     36            If you choose to leave a redirection, this will  
     37            re-create the $page.name page with a link pointing to the new 
     38            page. 
     39          </fieldset> 
     40       </p> 
     41        <div class="buttons"> 
     42          <input type="submit" name="cancel" value="${_('Cancel')}" /> 
     43          <input type="submit" value="${_('Rename')} $page.name" /> 
     44        </div> 
     45      </form> 
     46    </div> 
     47  </body> 
     48</html> 
  • trac/wiki/templates/wiki_view.html

    diff --git a/trac/wiki/templates/wiki_view.html b/trac/wiki/templates/wiki_view.html
    index 1929bab..508f1f0 100644
    a b  
    7171 
    7272      <py:with vars="modify_perm = 'WIKI_MODIFY' in perm(page.resource); 
    7373                     delete_perm = 'WIKI_DELETE' in perm(page.resource); 
    74                      admin_perm = 'WIKI_ADMIN' in perm(page.resource)"> 
     74                     admin_perm = 'WIKI_ADMIN' in perm(page.resource); 
     75                           rename_perm = 'WIKI_RENAME' in perm(page.resource)"> 
    7576        <py:if test="admin_perm or (not page.readonly and (modify_perm or delete_perm))"> 
    7677          <div class="buttons"> 
    7778            <py:if test="modify_perm"> 
     
    101102                <xi:include href="attach_file_form.html" py:with="alist = attachments"/> 
    102103              </py:if> 
    103104            </py:if> 
     105            <py:if test="page.exists and rename_perm">  
     106                     <form method="get" action="${href.wiki(page.name)}" id="rename">  
     107                        <div>  
     108                          <input type="hidden" name="action" value="rename" />  
     109                          <input type="submit" value="${_('Rename page')}" />  
     110                        </div>  
     111                     </form>  
     112                  </py:if>  
    104113            <py:if test="page.exists and delete_perm"> 
    105114              <form method="get" action="${href.wiki(page.name)}"> 
    106115                <div id="delete"> 
  • trac/wiki/tests/functional.py

    diff --git a/trac/wiki/tests/functional.py b/trac/wiki/tests/functional.py
    index bd85ec8..07a290e 100755
    a b class TestWiki(FunctionalTwillTestCaseSetup): 
    1212        self._tester.attach_file_to_wiki(pagename) 
    1313 
    1414 
     15class TestWikiRename(FunctionalTwillTestCaseSetup): 
     16    def runTest(self): 
     17        """Test for simple wiki rename""" 
     18        pagename = random_unique_camel() 
     19        self._tester.create_wiki_page(pagename) 
     20        self._tester.rename_wiki_page(pagename) 
     21        attachment = self._tester.attach_file_to_wiki(pagename) 
     22        base_url = self._tester.url 
     23        page_url = base_url + "/wiki/" + pagename 
     24         
     25        def click_rename(): 
     26            tc.formvalue('rename', 'action', 'rename') 
     27            tc.submit() 
     28            tc.url(page_url+r'\?action=rename') 
     29            tc.find("New name:") 
     30         
     31        tc.go(page_url) 
     32        tc.find("Rename page") 
     33        click_rename() 
     34        # attempt to rename the page to the current page name ...         
     35        tc.formvalue(page_url, 'new_name', 'rename') 
     36        tc.submit('rename') 
     37        tc.url(page_url) 
     38        tc.find("New name must be different from old name.") 
     39        # attempt to rename the page to an existing page name ... 
     40        tc.formvalue(page_url, 'new_name', 'WikiStart') 
     41        tc.submit('rename') 
     42        tc.url(page_url) 
     43        tc.find("Trac Error") 
     44        tc.find("Can't rename to existing WikiStart page") 
     45        # correct rename to new page name (old page replaced by a redirection) 
     46        tc.go(page_url) 
     47        click_rename() 
     48        newpagename = pagename+'Renamed' 
     49        tc.formvalue(page_url, 'new_name', newpagename) 
     50        tc.formvalue(page_url, 'leave_redirection', True) # the default 
     51        tc.submit('rename') 
     52        # check redirection page 
     53        tc.url(page_url) 
     54        tc.find("See.*/wiki/"+newpagename) 
     55        # check whether attachment exists on the new page but not on old page 
     56        tc.go(base_url+'/attachment/wiki/'+newpagename+'/'+attachment) 
     57        tc.notfind("Error: Invalid Attachment") 
     58        tc.go(base_url+'/attachment/wiki/'+pagename+'/'+attachment) 
     59        tc.find("Error: Invalid Attachment") 
     60        # rename again to another new page name (this time, no redirection) 
     61        tc.go(page_url) 
     62        click_rename() 
     63        newpagename = pagename+'RenamedAgain' 
     64        tc.formvalue(page_url, 'new_name', newpagename) 
     65        tc.formvalue(page_url, 'leave_redirection', False) 
     66        tc.submit('rename') 
     67        tc.url(base_url + "/wiki/" + newpagename) 
     68        # this time, the original page is gone 
     69        tc.go(page_url) 
     70        tc.url(page_url) 
     71        tc.find("Describe %s here" % pagename) 
     72         
     73 
     74 
    1575class RegressionTestTicket4812(FunctionalTwillTestCaseSetup): 
    1676    def runTest(self): 
    1777        """Test for regression of http://trac.edgewall.org/ticket/4812""" 
  • trac/wiki/tests/model.py

    diff --git a/trac/wiki/tests/model.py b/trac/wiki/tests/model.py
    index e4fde47..a5ea1cd 100644
    a b  
    11from datetime import datetime 
     2import tempfile 
    23import unittest 
    34 
     5from trac.attachment import Attachment 
    46from trac.core import * 
    57from trac.test import EnvironmentStub 
    68from trac.util.datefmt import utc, to_utimestamp 
    class TestWikiChangeListener(Component): 
    1416        self.changed = [] 
    1517        self.deleted = [] 
    1618        self.deleted_version = [] 
     19        self.renamed = {} 
    1720 
    1821    def wiki_page_added(self, page): 
    1922        self.added.append(page) 
    class TestWikiChangeListener(Component): 
    2730    def wiki_page_version_deleted(self, page): 
    2831        self.deleted_version.append(page) 
    2932 
     33    def wiki_page_renamed(self, page, old_page_name): 
     34        self.renamed[old_page_name] = page 
     35 
    3036 
    3137class WikiPageTestCase(unittest.TestCase): 
    3238 
    class WikiPageTestCase(unittest.TestCase): 
    194200        listener = TestWikiChangeListener(self.env) 
    195201        self.assertEqual(page, listener.deleted[0]) 
    196202 
     203    def test_rename_page(self): 
     204        cursor = self.db.cursor() 
     205        data = (1, 42, 'joe', '::1', 'Bla bla', 'Testing', 0) 
     206        cursor.execute("INSERT INTO wiki VALUES(%s,%s,%s,%s,%s,%s,%s,%s)", 
     207                       ('TestPage',) + data) 
     208         
     209        page = WikiPage(self.env, 'TestPage') 
     210 
     211        attachment = Attachment(self.env, 'wiki', 'TestPage') 
     212        attachment.insert('foo.txt', tempfile.TemporaryFile(), 0, 1) 
     213 
     214        page.rename('PageRenamed') 
     215         
     216        cursor.execute("SELECT version,time,author,ipnr,text,comment," 
     217                       "readonly FROM wiki WHERE name=%s", ('PageRenamed',)) 
     218        self.assertEqual(data, cursor.fetchone()) 
     219        self.assertEqual(None, cursor.fetchone()) 
     220         
     221        attachments = Attachment.select(self.env, 'wiki', 'PageRenamed') 
     222        self.assertEqual('foo.txt', attachments.next().filename) 
     223        self.assertRaises(StopIteration, attachments.next) 
     224        Attachment.delete_all(self.env, 'wiki', 'PageRenamed', self.db) 
     225 
     226 
     227        old_page = WikiPage(self.env, 'TestPage') 
     228         
     229        cursor.execute("SELECT version,time,author,ipnr,text,comment," 
     230                       "readonly FROM wiki WHERE name=%s", ('TestPage',)) 
     231        self.assertEqual(None, cursor.fetchone()) 
     232         
     233        listener = TestWikiChangeListener(self.env) 
     234        self.assertEqual({'TestPage': page}, listener.renamed) 
     235 
     236 
    197237 
    198238def suite(): 
    199239    return unittest.makeSuite(WikiPageTestCase, 'test') 
  • trac/wiki/web_ui.py

    diff --git a/trac/wiki/web_ui.py b/trac/wiki/web_ui.py
    index 097c34d..92b6788 100644
    a b class WikiModule(Component): 
    9393    # IPermissionRequestor methods 
    9494 
    9595    def get_permission_actions(self): 
    96         actions = ['WIKI_CREATE', 'WIKI_DELETE', 'WIKI_MODIFY', 'WIKI_VIEW'] 
     96        actions = ['WIKI_CREATE', 'WIKI_DELETE', 'WIKI_MODIFY', 'WIKI_RENAME', 'WIKI_VIEW'] 
    9797        return actions + [('WIKI_ADMIN', actions)] 
    9898 
    9999    # IRequestHandler methods 
    class WikiModule(Component): 
    145145                    return self._render_editor(req, page, action, has_collision) 
    146146            elif action == 'delete': 
    147147                self._do_delete(req, versioned_page) 
     148            elif action == 'rename': 
     149                return self._do_rename(req, page) 
    148150            elif action == 'diff': 
    149151                get_diff_options(req) 
    150152                req.redirect(req.href.wiki(versioned_page.name, action='diff', 
    151153                                           old_version=old_version, 
    152154                                           version=version)) 
    153155        elif action == 'delete': 
    154             return self._render_confirm(req, versioned_page) 
     156            return self._render_confirm_delete(req, versioned_page) 
     157        elif action == 'rename': 
     158            return self._render_confirm_rename(req, page) 
    155159        elif action == 'edit': 
    156160            return self._render_editor(req, versioned_page) 
    157161        elif action == 'diff': 
    class WikiModule(Component): 
    246250        old_version = int(req.args.get('old_version', 0)) or version 
    247251 
    248252        @with_transaction(self.env) 
    249         def do_transaction(db): 
     253        def do_delete(db): 
    250254            if version and old_version and version > old_version: 
    251255                # delete from `old_version` exclusive to `version` inclusive: 
    252256                for v in range(old_version, version): 
    class WikiModule(Component): 
    270274                                  version=version, name=page.name)) 
    271275            req.redirect(req.href.wiki(page.name)) 
    272276 
     277    def _do_rename(self, req, page): 
     278        if page.readonly: 
     279            req.perm(page.resource).require('WIKI_ADMIN') 
     280        else: 
     281            req.perm(page.resource).require('WIKI_RENAME') 
     282          
     283        if 'cancel' in req.args: 
     284            req.redirect(get_resource_url(self.env, page.resource, req.href)) 
     285          
     286        new_name = req.args.get('new_name', '').rstrip('/') 
     287        old_name = page.name 
     288        old_version = page.version 
     289        leave_redirection = req.args.get('leave_redirection') 
     290          
     291        # verify input parameters 
     292        warn = None 
     293        if not new_name: 
     294            warn = _("New name is mandatory for a rename.") 
     295        elif new_name == old_name: 
     296            warn = _("New name must be different from old name.") 
     297        if warn: 
     298            add_warning(req, warn) 
     299            return self._render_rename_form(req, page) 
     300         
     301 
     302        @with_transaction(self.env) 
     303        def do_rename(db): 
     304            page.rename(new_name, db) 
     305            if leave_redirection: 
     306                redirection_page = WikiPage(self.env, old_name) 
     307                redirection_page.text = _("See [wiki:%(page)s].", 
     308                                          page=new_name) 
     309                author = get_reporter_id(req) 
     310                comment = _("[wiki:'%(new_name)s@%(old_version)d' " 
     311                            "%(old_name)s] was renamed to %(new_name)s", 
     312                            old_name=old_name, old_version=old_version, 
     313                            new_name=new_name) 
     314                redirection_page.save(author, comment, req.remote_addr, 
     315                                      None, db) 
     316         
     317        req.redirect(req.href.wiki(leave_redirection and old_name or new_name)) 
     318 
     319         
     320 
    273321    def _do_save(self, req, page): 
    274322        if page.readonly: 
    275323            req.perm(page.resource).require('WIKI_ADMIN') 
    class WikiModule(Component): 
    294342            add_warning(req, _("Page not modified, showing latest version.")) 
    295343            return self._render_view(req, page) 
    296344 
    297     def _render_confirm(self, req, page): 
     345    def _render_confirm_delete(self, req, page): 
    298346        if page.readonly: 
    299347            req.perm(page.resource).require('WIKI_ADMIN') 
    300348        else: 
    class WikiModule(Component): 
    319367        self._wiki_ctxtnav(req, page) 
    320368        return 'wiki_delete.html', data, None 
    321369 
     370    def _render_confirm_rename(self, req, page): 
     371        if page.readonly: 
     372            req.perm(page.resource).require('WIKI_ADMIN') 
     373        else: 
     374            req.perm(page.resource).require('WIKI_RENAME') 
     375            
     376            data = self._page_data(req, page, 'rename') 
     377            self._wiki_ctxtnav(req, page) 
     378            return 'wiki_rename.html', data, None 
     379         
     380 
    322381    def _render_diff(self, req, page): 
    323382        if not page.exists: 
    324383            raise TracError(_('Version %(num)s of page "%(name)s" does not '