Edgewall Software

Ticket #1106: rename_base_functionality.2.diff

File rename_base_functionality.2.diff, 8.0 KB (added by shookie@…, 2 years ago)

fixed the strange import Attachment error in wiki/model.py, that doesn't show up in unit tests for some reason, but only when starting web server

  • trac/attachment.py

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

    diff --git a/trac/wiki/api.py b/trac/wiki/api.py
    index e95c8c1..e79e98f 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 d8183df..ad9805e 100644
    a b class WikiPage(object): 
    157157        self.old_readonly = self.readonly 
    158158        self.old_text = self.text 
    159159 
     160    def rename(self, new_name, db=None): 
     161        """Rename wiki page in-place, keeping the history intact. 
     162        Renaming a page this way will eventually leave dangling references 
     163        to the old page - which litterally doesn't exist anymore. 
     164        """ 
     165        assert self.exists, 'Cannot rename non-existent page' 
     166 
     167        old_name = self.name 
     168         
     169        @with_transaction(self.env, db) 
     170        def do_rename(db): 
     171            cursor = db.cursor() 
     172            new_page = WikiPage(self.env, new_name, version=None, db=db) 
     173            if new_page.exists: 
     174                raise TracError(_("Can't rename to existing %(nn)s page.", 
     175                                  nn=new_name)) 
     176 
     177            cursor.execute("UPDATE wiki SET name=%s WHERE name=%s", 
     178                           (new_name, old_name)) 
     179            self.name = new_name 
     180            self._fetch(self.name, None, db) 
     181 
     182            from trac.attachment import Attachment 
     183            Attachment.reparent_all(self.env, 'wiki', old_name, 
     184                                    'wiki', self.name, db) 
     185 
     186        self.env.log.info('Renamed page %s in-place 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 
     192 
    160193    def get_history(self, db=None): 
    161194        if not db: 
    162195            db = self.env.get_db_cnx() 
  • trac/wiki/tests/model.py

    diff --git a/trac/wiki/tests/model.py b/trac/wiki/tests/model.py
    index 3099e2f..cbbc093 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_timestamp 
    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')