Ticket #1106: 1106-wiki-rename-r9352.patch
| File 1106-wiki-rename-r9352.patch, 27.1 KB (added by rblank, 2 years ago) |
|---|
-
trac/admin/tests/console-tests.txt
diff --git a/trac/admin/tests/console-tests.txt b/trac/admin/tests/console-tests.txt
a b 120 120 TICKET_EDIT_CC, TICKET_EDIT_COMMENT, TICKET_EDIT_DESCRIPTION, 121 121 TICKET_MODIFY, TICKET_VIEW, TIMELINE_VIEW, TRAC_ADMIN, 122 122 VERSIONCONTROL_ADMIN, WIKI_ADMIN, WIKI_CREATE, WIKI_DELETE, WIKI_MODIFY, 123 WIKI_ VIEW123 WIKI_RENAME, WIKI_VIEW 124 124 125 125 ===== test_permission_add_one_action_ok ===== 126 126 … … 155 155 TICKET_EDIT_CC, TICKET_EDIT_COMMENT, TICKET_EDIT_DESCRIPTION, 156 156 TICKET_MODIFY, TICKET_VIEW, TIMELINE_VIEW, TRAC_ADMIN, 157 157 VERSIONCONTROL_ADMIN, WIKI_ADMIN, WIKI_CREATE, WIKI_DELETE, WIKI_MODIFY, 158 WIKI_ VIEW158 WIKI_RENAME, WIKI_VIEW 159 159 160 160 ===== test_permission_add_multiple_actions_ok ===== 161 161 … … 191 191 TICKET_EDIT_CC, TICKET_EDIT_COMMENT, TICKET_EDIT_DESCRIPTION, 192 192 TICKET_MODIFY, TICKET_VIEW, TIMELINE_VIEW, TRAC_ADMIN, 193 193 VERSIONCONTROL_ADMIN, WIKI_ADMIN, WIKI_CREATE, WIKI_DELETE, WIKI_MODIFY, 194 WIKI_ VIEW194 WIKI_RENAME, WIKI_VIEW 195 195 196 196 ===== test_permission_remove_one_action_ok ===== 197 197 … … 225 225 TICKET_EDIT_CC, TICKET_EDIT_COMMENT, TICKET_EDIT_DESCRIPTION, 226 226 TICKET_MODIFY, TICKET_VIEW, TIMELINE_VIEW, TRAC_ADMIN, 227 227 VERSIONCONTROL_ADMIN, WIKI_ADMIN, WIKI_CREATE, WIKI_DELETE, WIKI_MODIFY, 228 WIKI_ VIEW228 WIKI_RENAME, WIKI_VIEW 229 229 230 230 ===== test_permission_remove_multiple_actions_ok ===== 231 231 … … 259 259 TICKET_EDIT_CC, TICKET_EDIT_COMMENT, TICKET_EDIT_DESCRIPTION, 260 260 TICKET_MODIFY, TICKET_VIEW, TIMELINE_VIEW, TRAC_ADMIN, 261 261 VERSIONCONTROL_ADMIN, WIKI_ADMIN, WIKI_CREATE, WIKI_DELETE, WIKI_MODIFY, 262 WIKI_ VIEW262 WIKI_RENAME, WIKI_VIEW 263 263 264 264 ===== test_component_list_ok ===== 265 265 -
trac/attachment.py
diff --git a/trac/attachment.py b/trac/attachment.py
a b 63 63 def attachment_deleted(attachment): 64 64 """Called when an attachment is deleted.""" 65 65 66 def attachment_reparented(attachment, old_parent_realm, old_parent_id): 67 """Called when an attachment is reparented.""" 68 66 69 67 70 class IAttachmentManipulator(Interface): 68 71 """Extension point interface for components that need to manipulate … … 155 158 self.author = row[4] 156 159 self.ipnr = row[5] 157 160 158 def _get_path(self ):159 path = os.path.join(self.env.path, 'attachments', self.parent_realm,160 unicode_quote( self.parent_id))161 if self.filename:162 path = os.path.join(path, unicode_quote( self.filename))161 def _get_path(self, parent_realm, parent_id, filename): 162 path = os.path.join(self.env.path, 'attachments', parent_realm, 163 unicode_quote(parent_id)) 164 if filename: 165 path = os.path.join(path, unicode_quote(filename)) 163 166 return os.path.normpath(path) 164 path = property(_get_path) 167 168 @property 169 def path(self): 170 return self._get_path(self.parent_realm, self.parent_id, self.filename) 165 171 166 def _get_title(self): 167 return '%s:%s: %s' % (self.parent_realm, 168 self.parent_id, self.filename) 169 title = property(_get_title) 172 @property 173 def title(self): 174 return '%s:%s: %s' % (self.parent_realm, self.parent_id, self.filename) 170 175 171 176 def delete(self, db=None): 172 177 assert self.filename, 'Cannot delete non-existent attachment' … … 192 197 for listener in AttachmentModule(self.env).change_listeners: 193 198 listener.attachment_deleted(self) 194 199 200 def reparent(self, new_realm, new_id, db=None): 201 assert self.filename, 'Cannot reparent non-existent attachment' 202 new_id = unicode(new_id) 203 204 @with_transaction(self.env, db) 205 def do_reparent(db): 206 cursor = db.cursor() 207 new_path = self._get_path(new_realm, new_id, self.filename) 208 if os.path.exists(new_path): 209 raise TracError(_('Cannot reparent attachment "%(att)s" as ' 210 'it already exists in %(realm)s:%(id)s', 211 att=self.filename, realm=new_realm, 212 id=new_id)) 213 cursor.execute(""" 214 UPDATE attachment SET type=%s, id=%s 215 WHERE type=%s AND id=%s AND filename=%s 216 """, (new_realm, new_id, self.parent_realm, self.parent_id, 217 self.filename)) 218 dirname = os.path.dirname(new_path) 219 if not os.path.exists(dirname): 220 os.makedirs(dirname) 221 if os.path.isfile(self.path): 222 try: 223 os.rename(self.path, new_path) 224 except OSError, e: 225 self.env.log.error('Failed to move attachment file %s: %s', 226 self.path, 227 exception_to_unicode(e, traceback=True)) 228 raise TracError(_('Could not reparent attachment %(name)s', 229 name=self.filename)) 230 231 old_realm, old_id = self.parent_realm, self.parent_id 232 self.parent_realm, self.parent_id = new_realm, new_id 233 self.resource = Resource(new_realm, new_id).child('attachment', 234 self.filename) 235 236 self.env.log.info('Attachment reparented: %s' % self.title) 237 238 for listener in AttachmentModule(self.env).change_listeners: 239 if hasattr(listener, 'attachment_reparented'): 240 listener.attachment_deleted(self, old_realm, old_id) 195 241 196 242 def insert(self, filename, fileobj, size, t=None, db=None): 197 243 self.size = size and int(size) or 0 … … 274 320 except OSError, e: 275 321 env.log.error("Can't delete attachment directory %s: %s", 276 322 attachment_dir, exception_to_unicode(e, traceback=True)) 323 324 @classmethod 325 def reparent_all(cls, env, parent_realm, parent_id, new_realm, new_id, 326 db=None): 327 """Reparent all attachments of a given resource to another resource. 328 """ 329 attachment_dir = None 330 for attachment in list(cls.select(env, parent_realm, parent_id, db)): 331 attachment_dir = os.path.dirname(attachment.path) 332 attachment.reparent(new_realm, new_id, db) 333 if attachment_dir: 334 try: 335 os.rmdir(attachment_dir) 336 except OSError, e: 337 env.log.error("Can't delete attachment directory %s: %s", 338 attachment_dir, exception_to_unicode(e, traceback=True)) 277 339 278 340 def open(self): 279 341 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
a b 42 42 #changeinfo br { clear: left } 43 43 #changeinfo .options { padding: 0 0 1em 1em } 44 44 #changeinfo .options, #changeinfo .buttons { clear: left } 45 #delete, # save { margin-left: 6em }45 #delete, #rename, #save { margin-left: 3em } 46 46 #preview { 47 47 background: #f4f4f4 url(../draft.png); 48 48 margin: 1em 0 2em; -
trac/tests/attachment.py
diff --git a/trac/tests/attachment.py b/trac/tests/attachment.py
a b 1 1 # -*- coding: utf-8 -*- 2 2 3 import os 3 import os.path 4 4 import shutil 5 from StringIO import StringIO 5 6 import tempfile 6 7 import unittest 7 8 … … 70 71 71 72 def test_insert(self): 72 73 attachment = Attachment(self.env, 'ticket', 42) 73 attachment.insert('foo.txt', tempfile.TemporaryFile(), 0, 1)74 attachment.insert('foo.txt', StringIO(''), 0, 1) 74 75 attachment = Attachment(self.env, 'ticket', 42) 75 attachment.insert('bar.jpg', tempfile.TemporaryFile(), 0, 2)76 attachment.insert('bar.jpg', StringIO(''), 0, 2) 76 77 77 78 attachments = Attachment.select(self.env, 'ticket', 42) 78 79 self.assertEqual('foo.txt', attachments.next().filename) … … 81 82 82 83 def test_insert_unique(self): 83 84 attachment = Attachment(self.env, 'ticket', 42) 84 attachment.insert('foo.txt', tempfile.TemporaryFile(), 0)85 attachment.insert('foo.txt', StringIO(''), 0) 85 86 self.assertEqual('foo.txt', attachment.filename) 86 87 attachment = Attachment(self.env, 'ticket', 42) 87 attachment.insert('foo.txt', tempfile.TemporaryFile(), 0)88 attachment.insert('foo.txt', StringIO(''), 0) 88 89 self.assertEqual('foo.2.txt', attachment.filename) 89 90 90 91 def test_insert_outside_attachments_dir(self): 91 92 attachment = Attachment(self.env, '../../../../../sth/private', 42) 92 93 self.assertRaises(AssertionError, attachment.insert, 'foo.txt', 93 tempfile.TemporaryFile(), 0)94 StringIO(''), 0) 94 95 95 96 def test_delete(self): 96 97 attachment1 = Attachment(self.env, 'wiki', 'SomePage') 97 attachment1.insert('foo.txt', tempfile.TemporaryFile(), 0)98 attachment1.insert('foo.txt', StringIO(''), 0) 98 99 attachment2 = Attachment(self.env, 'wiki', 'SomePage') 99 attachment2.insert('bar.jpg', tempfile.TemporaryFile(), 0)100 attachment2.insert('bar.jpg', StringIO(''), 0) 100 101 101 102 attachments = Attachment.select(self.env, 'wiki', 'SomePage') 102 103 self.assertEqual(2, len(list(attachments))) … … 116 117 doesn't exist for some reason. 117 118 """ 118 119 attachment = Attachment(self.env, 'wiki', 'SomePage') 119 attachment.insert('foo.txt', tempfile.TemporaryFile(), 0)120 attachment.insert('foo.txt', StringIO(''), 0) 120 121 os.unlink(attachment.path) 121 122 122 123 attachment.delete() 123 124 125 def test_reparent(self): 126 attachment1 = Attachment(self.env, 'wiki', 'SomePage') 127 attachment1.insert('foo.txt', StringIO(''), 0) 128 path1 = attachment1.path 129 attachment2 = Attachment(self.env, 'wiki', 'SomePage') 130 attachment2.insert('bar.jpg', StringIO(''), 0) 131 132 attachments = Attachment.select(self.env, 'wiki', 'SomePage') 133 self.assertEqual(2, len(list(attachments))) 134 attachments = Attachment.select(self.env, 'ticket', 123) 135 self.assertEqual(0, len(list(attachments))) 136 assert os.path.exists(path1) and os.path.exists(attachment2.path) 137 138 attachment1.reparent('ticket', 123) 139 self.assertEqual('ticket', attachment1.parent_realm) 140 self.assertEqual('ticket', attachment1.resource.parent.realm) 141 self.assertEqual('123', attachment1.parent_id) 142 self.assertEqual('123', attachment1.resource.parent.id) 143 144 attachments = Attachment.select(self.env, 'wiki', 'SomePage') 145 self.assertEqual(1, len(list(attachments))) 146 attachments = Attachment.select(self.env, 'ticket', 123) 147 self.assertEqual(1, len(list(attachments))) 148 assert not os.path.exists(path1) and os.path.exists(attachment1.path) 149 assert os.path.exists(attachment2.path) 150 124 151 def test_legacy_permission_on_parent(self): 125 152 """Ensure that legacy action tests are done on parent. As 126 153 `ATTACHMENT_VIEW` maps to `TICKET_VIEW`, the `TICKET_VIEW` is tested -
trac/tests/functional/tester.py
diff --git a/trac/tests/functional/tester.py b/trac/tests/functional/tester.py
a b 176 176 tc.formvalue('attachment', 'replace', True) 177 177 tc.submit() 178 178 tc.url(self.url + '/attachment/ticket/%s/$' % ticketid) 179 return tempfilename 179 180 180 181 def clone_ticket(self, ticketid): 181 182 """Create a clone of the given ticket id using the clone button.""" … … 232 233 tc.formvalue('attachment', 'description', random_sentence()) 233 234 tc.submit() 234 235 tc.url(self.url + '/attachment/wiki/%s/$' % name) 236 return tempfilename 235 237 236 238 def create_milestone(self, name=None, due=None): 237 239 """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
a b 45 45 def wiki_page_version_deleted(page): 46 46 """Called when a version of a page has been deleted.""" 47 47 48 def wiki_page_renamed(page, old_name): 49 """Called when a page has been renamed.""" 50 48 51 49 52 class IWikiPageManipulator(Interface): 50 53 """Extension point interface for components that need to do specific -
trac/wiki/model.py
diff --git a/trac/wiki/model.py b/trac/wiki/model.py
a b 158 158 self.old_readonly = self.readonly 159 159 self.old_text = self.text 160 160 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, db=db) 174 if new_page.exists: 175 raise TracError(_("Can't rename to existing %(name)s page.", 176 name=new_name)) 177 178 cursor.execute("UPDATE wiki SET name=%s WHERE name=%s", 179 (new_name, old_name)) 180 WikiSystem(self.env).pages.invalidate(db) 181 from trac.attachment import Attachment 182 Attachment.reparent_all(self.env, 'wiki', old_name, 183 'wiki', new_name, db) 184 185 self.name = new_name 186 self.env.log.info('Renamed page %s to %s', old_name, new_name) 187 188 for listener in WikiSystem(self.env).change_listeners: 189 if hasattr(listener, 'wiki_page_renamed'): 190 listener.wiki_page_renamed(self, old_name) 191 161 192 def get_history(self, db=None): 162 193 if not db: 163 194 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
- + 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" py:with="current_href = href.wiki(page.name)"> 14 <h1>Rename <a href="$current_href">$page.name</a></h1> 15 <form id="rename" action="$current_href" method="post"> 16 <p> 17 <input type="hidden" name="action" value="rename" /> 18 <strong>Renaming the page will rename all existing versions of the page in place.</strong><br /> 19 The complete history of the page will be moved to the new location. 20 </p> 21 <div class="field"> 22 <label>New name: <input type="text" id="new_name" name="new_name" size="40" value="$page.name" /></label> 23 </div> 24 <div class="field"> 25 <label><input type="checkbox" id="redirect" name="redirect"/> 26 Leave a redirection page at the old location</label> 27 </div> 28 <div class="buttons"> 29 <input type="submit" name="cancel" value="${_('Cancel')}" /> 30 <input type="submit" name="submit" value="${_('Rename')}" /> 31 </div> 32 </form> 33 </div> 34 </body> 35 </html> -
trac/wiki/templates/wiki_view.html
diff --git a/trac/wiki/templates/wiki_view.html b/trac/wiki/templates/wiki_view.html
a b 71 71 72 72 <py:with vars="modify_perm = 'WIKI_MODIFY' in perm(page.resource); 73 73 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)"> 75 76 <py:if test="admin_perm or (not page.readonly and (modify_perm or delete_perm))"> 76 77 <div class="buttons"> 77 78 <py:if test="modify_perm"> … … 101 102 <xi:include href="attach_file_form.html" py:with="alist = attachments"/> 102 103 </py:if> 103 104 </py:if> 105 <form method="get" action="${href.wiki(page.name)}" id="rename" py:if="page.exists and rename_perm"> 106 <div> 107 <input type="hidden" name="action" value="rename" /> 108 <input type="submit" value="${_('Rename page')}" /> 109 </div> 110 </form> 104 111 <py:if test="page.exists and delete_perm"> 105 112 <form method="get" action="${href.wiki(page.name)}"> 106 113 <div id="delete"> -
trac/wiki/tests/functional.py
diff --git a/trac/wiki/tests/functional.py b/trac/wiki/tests/functional.py
a b 12 12 self._tester.attach_file_to_wiki(pagename) 13 13 14 14 15 class 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 attachment = self._tester.attach_file_to_wiki(pagename) 21 base_url = self._tester.url 22 page_url = base_url + "/wiki/" + pagename 23 24 def click_rename(): 25 tc.formvalue('rename', 'action', 'rename') 26 tc.submit() 27 tc.url(page_url + r'\?action=rename') 28 tc.find("New name:") 29 30 tc.go(page_url) 31 tc.find("Rename page") 32 click_rename() 33 # attempt to rename the page to the current page name 34 tc.formvalue('rename', 'new_name', pagename) 35 tc.submit('submit') 36 tc.url(page_url) 37 tc.find("New name must be different from old name") 38 # attempt to rename the page to an existing page name 39 tc.formvalue('rename', 'new_name', 'WikiStart') 40 tc.submit('submit') 41 tc.url(page_url) 42 tc.find("Trac Error") 43 tc.find("Can't rename to existing WikiStart page") 44 # correct rename to new page name (old page replaced by a redirection) 45 tc.go(page_url) 46 click_rename() 47 newpagename = pagename + 'Renamed' 48 tc.formvalue('rename', 'new_name', newpagename) 49 tc.formvalue('rename', 'redirect', True) 50 tc.submit('submit') 51 # check redirection page 52 tc.url(page_url) 53 tc.find("See.*/wiki/" + newpagename) 54 # check whether attachment exists on the new page but not on old page 55 tc.go(base_url + '/attachment/wiki/' + newpagename + '/' + attachment) 56 tc.notfind("Error: Invalid Attachment") 57 tc.go(base_url + '/attachment/wiki/' + pagename + '/' + attachment) 58 tc.find("Error: Invalid Attachment") 59 # rename again to another new page name (this time, no redirection) 60 tc.go(page_url) 61 click_rename() 62 newpagename = pagename + 'RenamedAgain' 63 tc.formvalue('rename', 'new_name', newpagename) 64 tc.formvalue('rename', 'redirect', False) 65 tc.submit('submit') 66 tc.url(base_url + "/wiki/" + newpagename) 67 # this time, the original page is gone 68 tc.go(page_url) 69 tc.url(page_url) 70 tc.find("The page %s does not exist" % pagename) 71 72 15 73 class RegressionTestTicket4812(FunctionalTwillTestCaseSetup): 16 74 def runTest(self): 17 75 """Test for regression of http://trac.edgewall.org/ticket/4812""" … … 65 123 import trac.tests.functional.testcases 66 124 suite = trac.tests.functional.testcases.functionalSuite() 67 125 suite.addTest(TestWiki()) 126 suite.addTest(TestWikiRename()) 68 127 suite.addTest(RegressionTestTicket4812()) 69 128 if has_docutils: 70 129 import docutils -
trac/wiki/tests/model.py
diff --git a/trac/wiki/tests/model.py b/trac/wiki/tests/model.py
a b 1 # -*- coding: utf-8 -*- 2 1 3 from datetime import datetime 4 import os.path 5 import shutil 6 from StringIO import StringIO 7 import tempfile 2 8 import unittest 3 9 10 from trac.attachment import Attachment 4 11 from trac.core import * 5 12 from trac.test import EnvironmentStub 6 13 from trac.util.datefmt import utc, to_utimestamp … … 14 21 self.changed = [] 15 22 self.deleted = [] 16 23 self.deleted_version = [] 24 self.renamed = [] 17 25 18 26 def wiki_page_added(self, page): 19 27 self.added.append(page) … … 27 35 def wiki_page_version_deleted(self, page): 28 36 self.deleted_version.append(page) 29 37 38 def wiki_page_renamed(self, page, old_name): 39 self.renamed.append((page, old_name)) 40 30 41 31 42 class WikiPageTestCase(unittest.TestCase): 32 43 33 44 def setUp(self): 34 45 self.env = EnvironmentStub() 46 self.env.path = os.path.join(tempfile.gettempdir(), 'trac-tempenv') 47 os.mkdir(self.env.path) 35 48 self.db = self.env.get_db_cnx() 36 49 37 50 def tearDown(self): 51 shutil.rmtree(self.env.path) 38 52 self.env.reset_db() 39 53 40 54 def test_new_page(self): … … 194 208 listener = TestWikiChangeListener(self.env) 195 209 self.assertEqual(page, listener.deleted[0]) 196 210 211 def test_rename_page(self): 212 cursor = self.db.cursor() 213 data = (1, 42, 'joe', '::1', 'Bla bla', 'Testing', 0) 214 cursor.execute("INSERT INTO wiki VALUES(%s,%s,%s,%s,%s,%s,%s,%s)", 215 ('TestPage',) + data) 216 attachment = Attachment(self.env, 'wiki', 'TestPage') 217 attachment.insert('foo.txt', StringIO(), 0, 1) 218 219 page = WikiPage(self.env, 'TestPage') 220 page.rename('PageRenamed') 221 self.assertEqual('PageRenamed', page.name) 222 223 cursor.execute("SELECT version,time,author,ipnr,text,comment," 224 "readonly FROM wiki WHERE name=%s", ('PageRenamed',)) 225 self.assertEqual(data, cursor.fetchone()) 226 self.assertEqual(None, cursor.fetchone()) 227 228 attachments = Attachment.select(self.env, 'wiki', 'PageRenamed') 229 self.assertEqual('foo.txt', attachments.next().filename) 230 self.assertRaises(StopIteration, attachments.next) 231 Attachment.delete_all(self.env, 'wiki', 'PageRenamed', self.db) 232 233 old_page = WikiPage(self.env, 'TestPage') 234 self.assertEqual(False, old_page.exists) 235 236 cursor.execute("SELECT version,time,author,ipnr,text,comment," 237 "readonly FROM wiki WHERE name=%s", ('TestPage',)) 238 self.assertEqual(None, cursor.fetchone()) 239 240 listener = TestWikiChangeListener(self.env) 241 self.assertEqual((page, 'TestPage'), listener.renamed[0]) 242 197 243 198 244 def suite(): 199 245 return unittest.makeSuite(WikiPageTestCase, 'test') -
trac/wiki/web_ui.py
diff --git a/trac/wiki/web_ui.py b/trac/wiki/web_ui.py
a b 93 93 # IPermissionRequestor methods 94 94 95 95 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', 97 'WIKI_VIEW'] 97 98 return actions + [('WIKI_ADMIN', actions)] 98 99 99 100 # IRequestHandler methods … … 145 146 return self._render_editor(req, page, action, has_collision) 146 147 elif action == 'delete': 147 148 self._do_delete(req, versioned_page) 149 elif action == 'rename': 150 return self._do_rename(req, page) 148 151 elif action == 'diff': 149 152 style, options, diff_data = get_diff_options(req) 150 153 contextall = diff_data['options']['contextall'] … … 153 156 version=version, 154 157 contextall=contextall or None)) 155 158 elif action == 'delete': 156 return self._render_confirm(req, versioned_page) 159 return self._render_confirm_delete(req, versioned_page) 160 elif action == 'rename': 161 return self._render_confirm_rename(req, page) 157 162 elif action == 'edit': 158 163 return self._render_editor(req, versioned_page) 159 164 elif action == 'diff': … … 248 253 old_version = int(req.args.get('old_version', 0)) or version 249 254 250 255 @with_transaction(self.env) 251 def do_ transaction(db):256 def do_delete(db): 252 257 if version and old_version and version > old_version: 253 258 # delete from `old_version` exclusive to `version` inclusive: 254 259 for v in range(old_version, version): … … 272 277 version=version, name=page.name)) 273 278 req.redirect(req.href.wiki(page.name)) 274 279 280 def _do_rename(self, req, page): 281 if page.readonly: 282 req.perm(page.resource).require('WIKI_ADMIN') 283 else: 284 req.perm(page.resource).require('WIKI_RENAME') 285 286 if 'cancel' in req.args: 287 req.redirect(get_resource_url(self.env, page.resource, req.href)) 288 289 old_name, old_version = page.name, page.version 290 new_name = req.args.get('new_name', '').rstrip('/') 291 redirect = req.args.get('redirect') 292 293 # verify input parameters 294 warn = None 295 if not new_name: 296 warn = _('New name is mandatory for a rename.') 297 elif new_name == old_name: 298 warn = _('New name must be different from old name.') 299 if warn: 300 add_warning(req, warn) 301 return self._render_confirm_rename(req, page) 302 303 @with_transaction(self.env) 304 def do_rename(db): 305 page.rename(new_name, db) 306 if redirect: 307 redirection = WikiPage(self.env, old_name) 308 redirection.text = 'See [wiki:"%s"].' % new_name 309 author = get_reporter_id(req) 310 comment = '[wiki:"%s@%d" %s] was renamed to [wiki:"%s"].' % ( 311 new_name, old_version, old_name, new_name) 312 redirection.save(author, comment, req.remote_addr, db=db) 313 314 req.redirect(req.href.wiki(redirect and old_name or new_name)) 315 275 316 def _do_save(self, req, page): 276 317 if page.readonly: 277 318 req.perm(page.resource).require('WIKI_ADMIN') … … 296 337 add_warning(req, _("Page not modified, showing latest version.")) 297 338 return self._render_view(req, page) 298 339 299 def _render_confirm (self, req, page):340 def _render_confirm_delete(self, req, page): 300 341 if page.readonly: 301 342 req.perm(page.resource).require('WIKI_ADMIN') 302 343 else: … … 321 362 self._wiki_ctxtnav(req, page) 322 363 return 'wiki_delete.html', data, None 323 364 365 def _render_confirm_rename(self, req, page): 366 if page.readonly: 367 req.perm(page.resource).require('WIKI_ADMIN') 368 else: 369 req.perm(page.resource).require('WIKI_RENAME') 370 371 data = self._page_data(req, page, 'rename') 372 self._wiki_ctxtnav(req, page) 373 return 'wiki_rename.html', data, None 374 324 375 def _render_diff(self, req, page): 325 376 if not page.exists: 326 377 raise TracError(_('Version %(num)s of page "%(name)s" does not '
