Edgewall Software

Ticket #1106: rename_wikipages_base_functionality.diff

File rename_wikipages_base_functionality.diff, 7.9 KB (added by shookie@…, 3 years ago)

first patch including base functionality and unit tests for it (wiki-rename r7334 and r7335 adapted)

  • trac/attachment.py

     
    299299                                     filename=self.filename)) 
    300300        return fd 
    301301 
     302    @classmethod 
     303    def reparent_all(cls, env, old_realm, old_id, new_realm, new_id, db=None): 
     304        """Reparent all attachments of a given resource to another resource. 
     305         
     306        :param old_realm: old parent's realm 
     307        :param old_id: old parent's id 
     308        :param new_realm: new parent's realm 
     309        :param new_id: new parent's id 
     310        :param env: the environent 
     311        :param db: the database connection 
     312        """ 
     313        old_dir = os.path.join(os.path.normpath(env.path), 'attachments', old_realm, old_id) 
     314        if not os.path.exists(old_dir): 
     315            return 
     316         
     317        new_dir = os.path.join(os.path.normpath(env.path), 'attachments', new_realm, new_id) 
    302318 
     319        if not db: 
     320            db = env.get_db_cnx() 
     321            handle_ta = True 
     322        else: 
     323            handle_ta = False 
     324         
     325        cursor = db.cursor() 
     326        cursor.execute("SELECT filename FROM attachment WHERE type=%s AND id=%s", 
     327                       (old_realm, old_id)) 
     328        renames = [] 
     329        for filename, in cursor: 
     330            old_filename = os.path.join(old_dir, filename) 
     331            new_filename = os.path.join(new_dir, filename) 
     332            if os.path.exists(new_filename): 
     333                raise TracError(_("Can't reparent attachment '%(att)s' as " 
     334                                  "it already exists in %(realm)s:%(id)s",  
     335                                  att=new_filename, realm=new_realm, id=new_id)) 
     336            renames.append((old_filename, new_filename)) 
     337         
     338        try: 
     339            if not os.path.exists(new_dir): 
     340                os.makedirs(new_dir) 
     341        except OSError, e: 
     342            env.log.error("Can't create attachment directory '%s'", new_dir, exc_info=True) 
     343            raise TracError(_("Can't create attachment folder for %(realm)s:%(id)s: %(err)", 
     344                              realm=new_realm, id=new_id, err=e)) 
     345         
     346        for old_filename, new_filename in renames: 
     347            try: 
     348                os.rename(old_filename, new_filename) 
     349            except OSError, e: 
     350                env.log.error("Can't move attachment from '%s' to '%s'", 
     351                              old_filename, new_filename, exc_info=True) 
     352                raise TracError(_("Can't move attachment '%(att)s'", 
     353                                  att=os.path.basename(new_filename))) 
     354 
     355        #should the following be checked for exceptions, and the file move reverted in case? 
     356        #probably not, since then the preceding rename had to be reversed as well 
     357        #if soemthing really goes wrong here, it's all handywork to clean it up 
     358        cursor.execute("UPDATE attachment SET type=%s, id=%s WHERE type=%s AND id=%s",   
     359                       (new_realm, new_id, old_realm, old_id)) 
     360        handle_ta and db.commit() 
     361        env.log.info('Attachments reparented to: %s:%s' % (new_realm, new_id))  
     362 
     363 
    303364class AttachmentModule(Component): 
    304365 
    305366    implements(IEnvironmentSetupParticipant, IRequestHandler, 
  • trac/wiki/api.py

     
    4242    def wiki_page_changed(page, version, t, comment, author, ipnr): 
    4343        """Called when a page has been modified.""" 
    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_deleted(page): 
    4649        """Called when a page has been deleted.""" 
    4750 
  • trac/wiki/tests/model.py

     
    11from datetime import datetime 
    22import unittest 
     3import tempfile 
    34 
    45from trac.core import * 
    56from trac.test import EnvironmentStub 
    67from trac.util.datefmt import utc, to_timestamp 
    78from trac.wiki import WikiPage, IWikiChangeListener 
     9from trac.attachment import Attachment 
    810 
    911 
    1012class 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) 
     
    2629 
    2730    def wiki_page_version_deleted(self, page): 
    2831        self.deleted_version.append(page) 
     32     
     33    def wiki_page_renamed(self, page, old_page_name): 
     34        self.renamed[old_page_name] = page 
    2935 
    30  
    3136class WikiPageTestCase(unittest.TestCase): 
    3237 
    3338    def setUp(self): 
     
    167172        self.assertEqual(page, listener.deleted[0]) 
    168173 
    169174 
     175 
     176    def test_rename_page(self): 
     177        cursor = self.db.cursor() 
     178        data = (1, 42, 'joe', '::1', 'Bla bla', 'Testing', 0) 
     179        cursor.execute("INSERT INTO wiki VALUES(%s,%s,%s,%s,%s,%s,%s,%s)", 
     180                       ('TestPage',) + data) 
     181         
     182        page = WikiPage(self.env, 'TestPage') 
     183 
     184        attachment = Attachment(self.env, 'wiki', 'TestPage') 
     185        attachment.insert('foo.txt', tempfile.TemporaryFile(), 0, 1) 
     186 
     187        page.rename('PageRenamed') 
     188         
     189        cursor.execute("SELECT version,time,author,ipnr,text,comment," 
     190                       "readonly FROM wiki WHERE name=%s", ('PageRenamed',)) 
     191        self.assertEqual(data, cursor.fetchone()) 
     192        self.assertEqual(None, cursor.fetchone()) 
     193         
     194        attachments = Attachment.select(self.env, 'wiki', 'PageRenamed') 
     195        self.assertEqual('foo.txt', attachments.next().filename) 
     196        self.assertRaises(StopIteration, attachments.next) 
     197        Attachment.delete_all(self.env, 'wiki', 'PageRenamed', self.db) 
     198 
     199 
     200        old_page = WikiPage(self.env, 'TestPage') 
     201         
     202        cursor.execute("SELECT version,time,author,ipnr,text,comment," 
     203                       "readonly FROM wiki WHERE name=%s", ('TestPage',)) 
     204        self.assertEqual(None, cursor.fetchone()) 
     205         
     206        listener = TestWikiChangeListener(self.env) 
     207        self.assertEqual({'TestPage': page}, listener.renamed) 
     208 
    170209def suite(): 
    171210    return unittest.makeSuite(WikiPageTestCase, 'test') 
    172211 
  • trac/wiki/model.py

     
    175175        for version,ts,author,comment,ipnr in cursor: 
    176176            time = datetime.fromtimestamp(ts, utc) 
    177177            yield version,time,author,comment,ipnr 
     178 
     179    def rename(self, new_name, db=None): 
     180        """Rename wiki page in-place, keeping the history intact. 
     181        Renaming a page this way will eventually leave dangling references 
     182        to the old page - which litterally doesn't exist anymore. 
     183        """ 
     184        assert self.exists, 'Cannot rename non-existent page' 
     185        if not db: 
     186            db = self.env.get_db_cnx() 
     187            handle_ta = True 
     188        else: 
     189            handle_ta = False 
     190        new_page = WikiPage(self.env, new_name, version=None, db=db) 
     191        if new_page.exists: 
     192            raise TracError(_("Can't rename to existing %(new_name)s page.", 
     193                              new_name=new_name)) 
     194 
     195        old_name = self.name 
     196        cursor = db.cursor() 
     197        cursor.execute("UPDATE wiki SET name=%s WHERE name=%s", (new_name, old_name)) 
     198 
     199        handle_ta and db.commit() 
     200 
     201        self.env.log.info('Renamed page %s in-place to %s' % (old_name, new_name)) 
     202 
     203        self.name = new_name 
     204        self._fetch(self.name, None, db) 
     205 
     206        from trac.attachment import Attachment 
     207        # Also rename attachments 
     208        Attachment.reparent_all(self.env, 'wiki', old_name, 'wiki', self.name, db) 
     209 
     210        for listener in WikiSystem(self.env).change_listeners: 
     211            if hasattr(listener, 'wiki_page_renamed'): 
     212                listener.wiki_page_renamed(self, old_name) 
     213