incubator-bloodhound-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From and...@apache.org
Subject svn commit: r1455199 - in /incubator/bloodhound/trunk/trac/trac: attachment.py resource.py tests/attachment.py tests/resource.py ticket/api.py ticket/model.py ticket/tests/model.py wiki/model.py wiki/tests/model.py
Date Mon, 11 Mar 2013 15:54:59 GMT
Author: andrej
Date: Mon Mar 11 15:54:57 2013
New Revision: 1455199

URL: http://svn.apache.org/r1455199
Log:
patching trac with IResourceChangeListener support - towards #411

Modified:
    incubator/bloodhound/trunk/trac/trac/attachment.py
    incubator/bloodhound/trunk/trac/trac/resource.py
    incubator/bloodhound/trunk/trac/trac/tests/attachment.py
    incubator/bloodhound/trunk/trac/trac/tests/resource.py
    incubator/bloodhound/trunk/trac/trac/ticket/api.py
    incubator/bloodhound/trunk/trac/trac/ticket/model.py
    incubator/bloodhound/trunk/trac/trac/ticket/tests/model.py
    incubator/bloodhound/trunk/trac/trac/wiki/model.py
    incubator/bloodhound/trunk/trac/trac/wiki/tests/model.py

Modified: incubator/bloodhound/trunk/trac/trac/attachment.py
URL: http://svn.apache.org/viewvc/incubator/bloodhound/trunk/trac/trac/attachment.py?rev=1455199&r1=1455198&r2=1455199&view=diff
==============================================================================
--- incubator/bloodhound/trunk/trac/trac/attachment.py (original)
+++ incubator/bloodhound/trunk/trac/trac/attachment.py Mon Mar 11 15:54:57 2013
@@ -221,7 +221,7 @@ class Attachment(object):
         with self.env.db_transaction as db:
             db("""
                 DELETE FROM attachment WHERE type=%s AND id=%s AND filename=%s
-                """, (self.parent_realm, self.parent_id, self.filename))
+                    """, (self.parent_realm, self.parent_id, self.filename))
             path = self.path
             if os.path.isfile(path):
                 try:
@@ -235,8 +235,7 @@ class Attachment(object):
 
         self.env.log.info("Attachment removed: %s" % self.title)
 
-        for listener in AttachmentModule(self.env).change_listeners:
-            listener.attachment_deleted(self)
+        ResourceSystem(self.env).resource_deleted(self)
 
     def reparent(self, new_realm, new_id):
         assert self.filename, "Cannot reparent non-existent attachment"
@@ -284,9 +283,8 @@ class Attachment(object):
 
         self.env.log.info("Attachment reparented: %s" % self.title)
 
-        for listener in AttachmentModule(self.env).change_listeners:
-            if hasattr(listener, 'attachment_reparented'):
-                listener.attachment_reparented(self, old_realm, old_id)
+        old_values = dict(parent_realm=old_realm, parent_id=old_id)
+        ResourceSystem(self.env).resource_changed(self, old_values=old_values)
 
     def insert(self, filename, fileobj, size, t=None, db=None):
         """Create a new Attachment record and save the file content.
@@ -330,8 +328,7 @@ class Attachment(object):
                 self.env.log.info("New attachment: %s by %s", self.title,
                                   self.author)
 
-        for listener in AttachmentModule(self.env).change_listeners:
-            listener.attachment_added(self)
+        ResourceSystem(self.env).resource_created(self)
 
 
     @classmethod
@@ -1101,3 +1098,30 @@ class AttachmentAdmin(Component):
                 if destination is not None:
                     output.close()
 
+class ResourceToAttachmentChangeListenerAdapter(Component):
+    """
+    The class provides backward compatibility for components implementing
+    IAttachmentChangeListener interface.
+    """
+    implements(IResourceChangeListener)
+    def match_resource(self, resource):
+        return isinstance(resource, Attachment)
+
+    def resource_created(self, resource, context = None):
+        for listener in AttachmentModule(self.env).change_listeners:
+            listener.attachment_added(resource)
+
+    def resource_changed(self, resource, old_values, context = None):
+        for listener in AttachmentModule(self.env).change_listeners:
+            if hasattr(listener, 'attachment_reparented'):
+                listener.attachment_reparented(
+                    resource,
+                    old_values.get("parent_realm"),
+                    old_values.get("parent_id"))
+
+    def resource_deleted(self, resource, context = None):
+        for listener in AttachmentModule(self.env).change_listeners:
+            listener.attachment_deleted(resource)
+
+    def resource_version_deleted(self, resource, context):
+        pass

Modified: incubator/bloodhound/trunk/trac/trac/resource.py
URL: http://svn.apache.org/viewvc/incubator/bloodhound/trunk/trac/trac/resource.py?rev=1455199&r1=1455198&r2=1455199&view=diff
==============================================================================
--- incubator/bloodhound/trunk/trac/trac/resource.py (original)
+++ incubator/bloodhound/trunk/trac/trac/resource.py Mon Mar 11 15:54:57 2013
@@ -217,6 +217,38 @@ class Resource(object):
         """
         return Resource(realm, id, version, self)
 
+class IResourceChangeListener(Interface):
+    """Extension point interface for components that require notification
+    when resources are created, modified, or deleted.
+
+    'resource' parameters is instance of the a resource e.g. ticket, milestone
+    etc.
+    'context' is an action context, may contain author, comment etc. Context
+    content depends on a resource type.
+    """
+
+    def match_request(resource):
+        """Return whether the listener wants to process the given resource."""
+
+    def resource_created(resource, context=None):
+        """
+        Called when a resource is created.
+        """
+
+    def resource_changed(resource, old_values, context):
+        """Called when a resource is modified.
+
+        `old_values` is a dictionary containing the previous values of the
+        resource properties that changed. Properties are specific for resource
+        type.
+        """
+
+    def resource_deleted(resource, context):
+        """Called when a resource is deleted."""
+
+    def resource_version_deleted(resource, context):
+        """Called when a version of a resource has been deleted."""
+
 
 class ResourceSystem(Component):
     """Resource identification and description manager.
@@ -226,6 +258,8 @@ class ResourceSystem(Component):
     """
 
     resource_managers = ExtensionPoint(IResourceManager)
+    change_listeners = ExtensionPoint(IResourceChangeListener)
+
 
     def __init__(self):
         self._resource_managers_map = None
@@ -255,6 +289,25 @@ class ResourceSystem(Component):
                 realms.append(realm)
         return realms
 
+    def resource_created(self, resource, context=None):
+        for listener in self.change_listeners:
+            if listener.match_resource(resource):
+                listener.resource_created(resource, context)
+
+    def resource_changed(self, resource, old_values, context=None):
+        for listener in self.change_listeners:
+            if listener.match_resource(resource):
+                listener.resource_changed(resource, old_values, context)
+
+    def resource_deleted(self, resource, context=None):
+        for listener in self.change_listeners:
+            if listener.match_resource(resource):
+                listener.resource_deleted(resource, context)
+
+    def resource_version_deleted(self, resource, context=None):
+        for listener in self.change_listeners:
+            if listener.match_resource(resource):
+                listener.resource_version_deleted(resource, context)
 
 # -- Utilities for manipulating resources in a generic way
 

Modified: incubator/bloodhound/trunk/trac/trac/tests/attachment.py
URL: http://svn.apache.org/viewvc/incubator/bloodhound/trunk/trac/trac/tests/attachment.py?rev=1455199&r1=1455198&r2=1455199&view=diff
==============================================================================
--- incubator/bloodhound/trunk/trac/trac/tests/attachment.py (original)
+++ incubator/bloodhound/trunk/trac/trac/tests/attachment.py Mon Mar 11 15:54:57 2013
@@ -6,11 +6,13 @@ from StringIO import StringIO
 import tempfile
 import unittest
 
-from trac.attachment import Attachment, AttachmentModule
+from trac.attachment import (Attachment, AttachmentModule,
+                             IAttachmentChangeListener)
 from trac.core import Component, implements, TracError
 from trac.perm import IPermissionPolicy, PermissionCache
 from trac.resource import Resource, resource_exists
 from trac.test import EnvironmentStub
+from trac.tests.resource import TestResourceChangeListener
 
 
 hashes = {
@@ -28,6 +30,24 @@ hashes = {
     u'ÜberSicht': 'a16c6837f6d3d2cc3addd68976db1c55deb694c8',
 }
 
+class TestAttachmentChangeListener(Component):
+    implements(IAttachmentChangeListener)
+
+    def attachment_added(self, attachment):
+        self.action = "added"
+        self.attachment = attachment
+
+    def attachment_deleted(self, attachment):
+        self.action = "deleted"
+        self.attachment = attachment
+
+    def attachment_reparented(
+            self, attachment, old_parent_realm, old_parent_id):
+        self.action = "reparented"
+        self.attachment = attachment
+        self.old_parent_realm = old_parent_realm
+        self.old_parent_id = old_parent_id
+
 
 class TicketOnlyViewsTicket(Component):
     implements(IPermissionPolicy)
@@ -221,10 +241,94 @@ class AttachmentTestCase(unittest.TestCa
         att.insert('file.txt', StringIO(''), 1)
         self.assertTrue(resource_exists(self.env, att.resource))
 
+    def test_change_listener_created(self):
+        listener = TestAttachmentChangeListener(self.env)
+        attachment = self._create_attachment()
+        self.assertEqual('added', listener.action)
+        self.assertEqual(attachment, listener.attachment)
+
+    def test_change_listener_deleted(self):
+        listener = TestAttachmentChangeListener(self.env)
+        attachment = self._create_attachment()
+        attachment.delete()
+        self.assertEqual('deleted', listener.action)
+        self.assertEqual(attachment, listener.attachment)
+
+    def test_change_listener_deleted(self):
+        listener = TestAttachmentChangeListener(self.env)
+        attachment = self._create_attachment()
+        old_parent_realm = attachment.parent_realm
+        old_parent_id = attachment.parent_id
+        attachment.reparent("wiki", "SomePage")
+        self.assertEqual('reparented', listener.action)
+        self.assertEqual(attachment, listener.attachment)
+        self.assertEqual(old_parent_realm, listener.old_parent_realm)
+        self.assertEqual(old_parent_id, listener.old_parent_id)
+
+    def _create_attachment(self):
+        attachment = Attachment(self.env, 'wiki', 'WikiStart')
+        attachment.insert('file.txt', StringIO(''), 1)
+        return attachment
+
+
+class AttachmentResourceChangeListenerTestCase(unittest.TestCase):
+    DUMMY_PARENT_REALM = "wiki"
+    DUMMY_PARENT_ID = "WikiStart"
+
+    def setUp(self):
+        self.env = EnvironmentStub(default_data=True)
+        self.listener = TestResourceChangeListener(self.env)
+        self.listener.resource_type = Attachment
+        self.listener.callback = self.listener_callback
+
+    def tearDown(self):
+        self.env.reset_db()
+
+    def test_change_listener_created(self):
+        attachment = self._create_attachment()
+        self.assertEqual('created', self.listener.action)
+        self.assertIsInstance(self.listener.resource, Attachment)
+        self.assertEqual(attachment.filename, self.filename)
+        self.assertEqual(attachment.parent_realm, self.parent_realm)
+        self.assertEqual(attachment.parent_id, self.parent_id)
+
+    def test_change_listener_reparent(self):
+        attachment = self._create_attachment()
+        attachment.reparent(self.DUMMY_PARENT_REALM, "SomePage")
+
+        self.assertEqual('changed', self.listener.action)
+        self.assertIsInstance(self.listener.resource, Attachment)
+        self.assertEqual(attachment.filename, self.filename)
+        self.assertEqual(attachment.parent_realm, self.parent_realm)
+        self.assertEqual("SomePage", self.parent_id)
+        self.assertEqual(
+            self.DUMMY_PARENT_REALM, self.listener.old_values["parent_realm"])
+        self.assertEqual(
+            self.DUMMY_PARENT_ID, self.listener.old_values["parent_id"])
+
+    def test_change_listener_deleted(self):
+        attachment = self._create_attachment()
+        attachment.delete()
+        self.assertEqual('deleted', self.listener.action)
+        self.assertIsInstance(self.listener.resource, Attachment)
+        self.assertEqual(attachment.filename, self.filename)
+
+    def _create_attachment(self):
+        attachment = Attachment(
+            self.env, self.DUMMY_PARENT_REALM, self.DUMMY_PARENT_ID)
+        attachment.insert('file.txt', StringIO(''), 1)
+        return attachment
+
+    def listener_callback(self, action, resource, context, old_values = None):
+        self.parent_realm = resource.parent_realm
+        self.parent_id = resource.parent_id
+        self.filename = resource.filename
 
 def suite():
     suite = unittest.TestSuite()
     suite.addTest(unittest.makeSuite(AttachmentTestCase, 'test'))
+    suite.addTest(unittest.makeSuite(
+        AttachmentResourceChangeListenerTestCase, 'test'))
     return suite
 
 if __name__ == '__main__':

Modified: incubator/bloodhound/trunk/trac/trac/tests/resource.py
URL: http://svn.apache.org/viewvc/incubator/bloodhound/trunk/trac/trac/tests/resource.py?rev=1455199&r1=1455198&r2=1455199&view=diff
==============================================================================
--- incubator/bloodhound/trunk/trac/trac/tests/resource.py (original)
+++ incubator/bloodhound/trunk/trac/trac/tests/resource.py Mon Mar 11 15:54:57 2013
@@ -15,6 +15,8 @@ import doctest
 import unittest
 
 from trac import resource
+from trac.resource import IResourceChangeListener
+from trac.core import implements, Component
 
 
 class ResourceTestCase(unittest.TestCase):
@@ -42,6 +44,46 @@ class ResourceTestCase(unittest.TestCase
         r2.parent = r2.parent(version=42)
         self.assertNotEqual(r1, r2)
 
+class TestResourceChangeListener(Component):
+    implements(IResourceChangeListener)
+
+    def __init__(self):
+        self.resource_type = None
+
+    def callback(self, action, resource, context, old_values = None):
+        pass
+
+    def match_resource(self, resource):
+        if self.resource_type is None:
+            return False
+        return isinstance(resource, self.resource_type)
+
+    def resource_created(self, resource, context = None):
+        self.action = "created"
+        self.resource = resource
+        self.context = context
+        self.callback(self.action, resource, context)
+
+    def resource_changed(self, resource, old_values, context = None):
+        self.action = "changed"
+        self.resource = resource
+        self.old_values = old_values
+        self.context = context
+        self.callback(
+            self.action, resource, context, old_values=self.old_values)
+
+    def resource_deleted(self, resource, context = None):
+        self.action = "deleted"
+        self.resource = resource
+        self.context = context
+        self.callback(self.action, resource, context)
+
+    def resource_version_deleted(self, resource, context):
+        self.action = "version_deleted"
+        self.resource = resource
+        self.context = context
+        self.callback(self.action, resource, context)
+
 def suite():
     suite = unittest.TestSuite()
     suite.addTest(doctest.DocTestSuite(resource))

Modified: incubator/bloodhound/trunk/trac/trac/ticket/api.py
URL: http://svn.apache.org/viewvc/incubator/bloodhound/trunk/trac/trac/ticket/api.py?rev=1455199&r1=1455198&r2=1455199&view=diff
==============================================================================
--- incubator/bloodhound/trunk/trac/trac/ticket/api.py (original)
+++ incubator/bloodhound/trunk/trac/trac/ticket/api.py Mon Mar 11 15:54:57 2013
@@ -156,31 +156,6 @@ class IMilestoneChangeListener(Interface
     def milestone_deleted(milestone):
         """Called when a milestone is deleted."""
 
-class IResourceChangeListener(Interface):
-    """Extension point interface for components that require notification
-    when resources are created, modified, or deleted.
-
-    'resource' instance of the a resource e.g. ticket, milestone etc.
-    'context' action context, may contain author, comment etc. Context
-    content depends on a resource type.
-    """
-
-    def resource_created(resource, context):
-        """
-        Called when a resource is created.
-        """
-
-    def resource_changed(resource, old_values, context):
-        """Called when a resource is modified.
-
-        `old_values` is a dictionary containing the previous values of the
-        resource properties that changed. Properties are specific for resource
-        type.
-        """
-
-    def resource_deleted(resource, context):
-        """Called when a resource is deleted."""
-
 class ITicketFieldProvider(Interface):
     """Extension point interface for components that provide fields for the
     ticket system."""
@@ -218,7 +193,6 @@ class TicketSystem(Component):
     ticket_field_providers = ExtensionPoint(ITicketFieldProvider)
     change_listeners = ExtensionPoint(ITicketChangeListener)
     milestone_change_listeners = ExtensionPoint(IMilestoneChangeListener)
-    resource_change_listeners = ExtensionPoint(IResourceChangeListener)
 
     ticket_custom_section = ConfigSection('ticket-custom',
         """In this section, you can define additional fields for tickets. See

Modified: incubator/bloodhound/trunk/trac/trac/ticket/model.py
URL: http://svn.apache.org/viewvc/incubator/bloodhound/trunk/trac/trac/ticket/model.py?rev=1455199&r1=1455198&r2=1455199&view=diff
==============================================================================
--- incubator/bloodhound/trunk/trac/trac/ticket/model.py (original)
+++ incubator/bloodhound/trunk/trac/trac/ticket/model.py Mon Mar 11 15:54:57 2013
@@ -25,8 +25,9 @@ from datetime import datetime
 from trac.attachment import Attachment
 from trac import core
 from trac.cache import cached
-from trac.core import TracError
-from trac.resource import Resource, ResourceNotFound
+from trac.core import TracError, implements
+from trac.resource import (Resource, ResourceNotFound, IResourceChangeListener,
+                           ResourceSystem)
 from trac.ticket.api import TicketSystem
 from trac.util import embedded_numbers, partition
 from trac.util.text import empty
@@ -252,8 +253,7 @@ class Ticket(object):
         self.resource = self.resource(id=tkt_id)
         self._old = {}
 
-        for listener in TicketSystem(self.env).change_listeners:
-            listener.ticket_created(self)
+        ResourceSystem(self.env).resource_created(self)
 
         return self.id
 
@@ -361,8 +361,9 @@ class Ticket(object):
         self._old = {}
         self.values['changetime'] = when
 
-        for listener in TicketSystem(self.env).change_listeners:
-            listener.ticket_changed(self, comment, author, old_values)
+        context = dict(comment=comment, author=author)
+        ResourceSystem(self.env).resource_changed(self, old_values, context)
+
         return int(cnum.rsplit('.', 1)[-1])
 
     def get_changelog(self, when=None, db=None):
@@ -425,8 +426,7 @@ class Ticket(object):
             db("DELETE FROM ticket_change WHERE ticket=%s", (self.id,))
             db("DELETE FROM ticket_custom WHERE ticket=%s", (self.id,))
 
-        for listener in TicketSystem(self.env).change_listeners:
-            listener.ticket_deleted(self)
+        ResourceSystem(self.env).resource_deleted(self)
 
     def get_change(self, cnum=None, cdate=None, db=None):
         """Return a ticket change by its number or date.
@@ -712,6 +712,8 @@ class AbstractEnum(object):
                 except ValueError:
                     pass # Ignore cast error for this non-essential operation
             TicketSystem(self.env).reset_ticket_fields()
+
+        ResourceSystem(self.env).resource_deleted(self)
         self.value = self._old_value = None
         self.name = self._old_name = None
 
@@ -739,6 +741,7 @@ class AbstractEnum(object):
 
         self._old_name = self.name
         self._old_value = self.value
+        ResourceSystem(self.env).resource_created(self)
 
     def update(self, db=None):
         """Update the enum value.
@@ -762,8 +765,10 @@ class AbstractEnum(object):
                    (self.name, self._old_name))
             TicketSystem(self.env).reset_ticket_fields()
 
+        old_values = dict(name=self._old_name, value=self._old_value)
         self._old_name = self.name
         self._old_value = self.value
+        ResourceSystem(self.env).resource_changed(self, old_values)
 
     @classmethod
     def select(cls, env, db=None):
@@ -846,8 +851,7 @@ class Component(object):
             db("DELETE FROM component WHERE name=%s", (self.name,))
             TicketSystem(self.env).reset_ticket_fields()
 
-        for listener in TicketSystem(self.env).resource_change_listeners:
-            listener.resource_deleted(self)
+        ResourceSystem(self.env).resource_deleted(self)
         self.name = self._old_name = None
 
     def insert(self, db=None):
@@ -869,8 +873,7 @@ class Component(object):
             self._old_name = self.name
             TicketSystem(self.env).reset_ticket_fields()
 
-        for listener in TicketSystem(self.env).resource_change_listeners:
-            listener.resource_created(self)
+        ResourceSystem(self.env).resource_created(self)
 
     def update(self, db=None):
         """Update the component.
@@ -898,8 +901,7 @@ class Component(object):
             TicketSystem(self.env).reset_ticket_fields()
 
         old_values = dict(name=old_name)
-        for listener in TicketSystem(self.env).resource_change_listeners:
-            listener.resource_changed(self, old_values)
+        ResourceSystem(self.env).resource_changed(self, old_values)
 
     @classmethod
     def select(cls, env, db=None):
@@ -1035,8 +1037,7 @@ class Milestone(object):
             del self.cache.milestones
             TicketSystem(self.env).reset_ticket_fields()
 
-        for listener in TicketSystem(self.env).milestone_change_listeners:
-            listener.milestone_deleted(self)
+        ResourceSystem(self.env).resource_deleted(self)
 
     def insert(self, db=None):
         """Insert a new milestone.
@@ -1057,8 +1058,7 @@ class Milestone(object):
             self.checkin()
             TicketSystem(self.env).reset_ticket_fields()
 
-        for listener in TicketSystem(self.env).milestone_change_listeners:
-            listener.milestone_created(self)
+        ResourceSystem(self.env).resource_created(self)
 
     def update(self, db=None):
         """Update the milestone.
@@ -1096,8 +1096,7 @@ class Milestone(object):
 
         old_values = dict((k, v) for k, v in old.iteritems()
                           if getattr(self, k) != v)
-        for listener in TicketSystem(self.env).milestone_change_listeners:
-            listener.milestone_changed(self, old_values)
+        ResourceSystem(self.env).resource_changed(self, old_values)
 
     @classmethod
     def select(cls, env, include_completed=True, db=None):
@@ -1161,9 +1160,11 @@ class Version(object):
         with self.env.db_transaction as db:
             self.env.log.info("Deleting version %s", self.name)
             db("DELETE FROM version WHERE name=%s", (self.name,))
-            self.name = self._old_name = None
             TicketSystem(self.env).reset_ticket_fields()
 
+        ResourceSystem(self.env).resource_deleted(self)
+        self.name = self._old_name = None
+
     def insert(self, db=None):
         """Insert a new version.
 
@@ -1182,6 +1183,8 @@ class Version(object):
             self._old_name = self.name
             TicketSystem(self.env).reset_ticket_fields()
 
+        ResourceSystem(self.env).resource_created(self)
+
     def update(self, db=None):
         """Update the version.
 
@@ -1193,6 +1196,7 @@ class Version(object):
         if not self.name:
             raise TracError(_("Invalid version name."))
 
+        old_name=self._old_name
         with self.env.db_transaction as db:
             self.env.log.info("Updating version '%s'", self.name)
             db("""UPDATE version
@@ -1206,6 +1210,9 @@ class Version(object):
                 self._old_name = self.name
             TicketSystem(self.env).reset_ticket_fields()
 
+        old_values = dict(name=old_name)
+        ResourceSystem(self.env).resource_changed(self, old_values)
+
     @classmethod
     def select(cls, env, db=None):
         """
@@ -1223,3 +1230,59 @@ class Version(object):
         def version_order(v):
             return (v.time or utcmax, embedded_numbers(v.name))
         return sorted(versions, key=version_order, reverse=True)
+
+class ResourceToMilestoneChangeListenerAdapter(core.Component):
+    """
+    The class provides backward compatibility for components implementing
+    IMilestoneChangeListener interface.
+    """
+    implements(IResourceChangeListener)
+
+    def match_resource(self, resource):
+        return isinstance(resource, Milestone)
+
+    def resource_created(self, resource, context = None):
+        for listener in TicketSystem(self.env).milestone_change_listeners:
+            listener.milestone_created(resource)
+
+    def resource_changed(self, resource, old_values, context = None):
+        for listener in TicketSystem(self.env).milestone_change_listeners:
+            listener.milestone_changed(resource, old_values)
+
+    def resource_deleted(self, resource, context = None):
+        for listener in TicketSystem(self.env).milestone_change_listeners:
+            listener.milestone_deleted(resource)
+
+    def resource_version_deleted(self, resource, context):
+        pass
+
+class ResourceToTicketChangeListenerAdapter(core.Component):
+    """
+    The class provides backward compatibility for components implementing
+    ITicketChangeListener interface.
+    """
+    implements(IResourceChangeListener)
+    def match_resource(self, resource):
+        return isinstance(resource, Ticket)
+
+    def resource_created(self, resource, context = None):
+        for listener in TicketSystem(self.env).change_listeners:
+            listener.ticket_created(resource)
+
+    def resource_changed(self, resource, old_values, context = None):
+        author = None
+        comment = None
+        if context:
+            comment = context.get("comment")
+            author = context.get("author")
+
+        for listener in TicketSystem(self.env).change_listeners:
+            listener.ticket_changed(resource, comment, author, old_values)
+
+    def resource_deleted(self, resource, context = None):
+        for listener in TicketSystem(self.env).change_listeners:
+            listener.ticket_deleted(resource)
+
+    def resource_version_deleted(self, resource, context):
+        pass
+

Modified: incubator/bloodhound/trunk/trac/trac/ticket/tests/model.py
URL: http://svn.apache.org/viewvc/incubator/bloodhound/trunk/trac/trac/ticket/tests/model.py?rev=1455199&r1=1455198&r2=1455199&view=diff
==============================================================================
--- incubator/bloodhound/trunk/trac/trac/ticket/tests/model.py (original)
+++ incubator/bloodhound/trunk/trac/trac/ticket/tests/model.py Mon Mar 11 15:54:57 2013
@@ -16,12 +16,11 @@ from trac.ticket.model import (
 )
 from trac.ticket.api import (
     IMilestoneChangeListener, ITicketChangeListener, TicketSystem,
-    IResourceChangeListener,
 )
 from trac.test import EnvironmentStub
+from trac.tests.resource import TestResourceChangeListener
 from trac.util.datefmt import from_utimestamp, to_utimestamp, utc
 
-
 class TestTicketChangeListener(core.Component):
     implements(ITicketChangeListener)
 
@@ -1006,28 +1005,6 @@ class MilestoneTestCase(unittest.TestCas
         self.assertEqual('deleted', listener.action)
         self.assertEqual(milestone, listener.milestone)
 
-class TestResourceChangeListener(core.Component):
-    implements(IResourceChangeListener)
-
-    def callback(self, action, resource, old_values = None):
-        pass
-
-    def resource_created(self, resource):
-        self.action = "created"
-        self.resource = resource
-        self.callback(self.action, self.resource)
-
-    def resource_changed(self, resource, old_values):
-        self.action = "changed"
-        self.resource = resource
-        self.old_values = old_values
-        self.callback(self.action, self.resource, old_values=self.old_values)
-
-    def resource_deleted(self, resource):
-        self.action = "deleted"
-        self.resource = resource
-        self.callback(self.action, self.resource)
-
 class ComponentTestCase(unittest.TestCase):
 
     def setUp(self):
@@ -1063,49 +1040,6 @@ class ComponentTestCase(unittest.TestCas
         self.assertEqual([('Test', 'joe', None)], self.env.db_query(
             "SELECT name, owner, description FROM component WHERE name='Test'"))
 
-    def test_change_listener_created(self):
-        listener = TestResourceChangeListener(self.env)
-        self._create_component(name='Component 1')
-        self.assertEqual('created', listener.action)
-        self.assertIsInstance(listener.resource, Component)
-        self.assertEqual('Component 1', listener.resource.name)
-
-    def test_change_listener_changed(self):
-        listener = TestResourceChangeListener(self.env)
-        component = self._create_component(name='Component 1')
-        component.name = 'Component 2'
-        component.update()
-        self.assertEqual('changed', listener.action)
-        self.assertIsInstance(listener.resource, Component)
-        self.assertEqual('Component 2', listener.resource.name)
-        self.assertEqual("Component 1" ,listener.old_values["name"])
-
-    def test_change_listener_deleted(self):
-        listener = TestResourceChangeListener(self.env)
-
-        #component.name property is set to None during delete operation
-        #We need mechanism to remember component name
-        listener.callback = self.listener_callback
-
-        component = self._create_component(name='Component 1')
-        component.delete()
-        self.assertEqual('deleted', listener.action)
-        self.assertIsInstance(listener.resource, Component)
-        self.assertEqual('Component 1', self.resource_name)
-
-    def listener_callback(self, action, resource, old_values = None):
-        self.resource_name = resource.name
-
-    def _create_component(self, name, description = None, owner=None):
-        component = Component(self.env)
-        component.name = name
-        if description is None:
-            component.description = description
-        if owner is None:
-            component.owner = owner
-        component.insert()
-        return component
-
 class VersionTestCase(unittest.TestCase):
 
     def setUp(self):
@@ -1140,6 +1074,107 @@ class VersionTestCase(unittest.TestCase)
         self.assertEqual([('Test', 0, 'Some text')], self.env.db_query(
             "SELECT name, time, description FROM version WHERE name='Test'"))
 
+class BaseResourceChangeListenerTestCase(unittest.TestCase):
+    DUMMY_RESOURCE_NAME = "Resource 1"
+    resource_type = None
+    name_field = "name"
+
+    def setUp(self):
+        self.env = EnvironmentStub(default_data=True)
+        self.listener = TestResourceChangeListener(self.env)
+        self.listener.resource_type = self.resource_type
+        self.listener.callback = self.listener_callback
+
+    def tearDown(self):
+        self.env.reset_db()
+
+    def test_change_listener_created(self):
+        self._create_resource(self.DUMMY_RESOURCE_NAME)
+        self.assertEqual('created', self.listener.action)
+        self.assertIsInstance(self.listener.resource, self.resource_type)
+        self.assertEqual(
+            self.DUMMY_RESOURCE_NAME,
+            self.resource_name)
+
+    def test_change_listener_changed(self):
+        resource = self._create_resource(self.DUMMY_RESOURCE_NAME)
+        self._rename_resource(resource, "UpdatedName")
+        self.assertEqual('changed', self.listener.action)
+        self.assertIsInstance(self.listener.resource, self.resource_type)
+        self.assertEqual("UpdatedName", self.resource_name)
+        self.assertEqual(
+            self.DUMMY_RESOURCE_NAME,
+            self.listener.old_values[self.name_field])
+
+    def test_change_listener_deleted(self):
+        resource = self._create_resource(self.DUMMY_RESOURCE_NAME)
+        resource.delete()
+        self.assertEqual('deleted', self.listener.action)
+        self.assertIsInstance(self.listener.resource, self.resource_type)
+        self.assertEqual(self.DUMMY_RESOURCE_NAME, self.resource_name)
+
+    def _create_resource(self, name):
+        resource = self.resource_type(self.env)
+        resource.name = name
+        resource.insert()
+        return resource
+
+    def _rename_resource(self, resource, new_name):
+        resource.name = new_name
+        resource.update()
+        return resource
+
+    def _get_resource_name(self, resource):
+        return resource.name
+
+    def listener_callback(self, action, resource, context, old_values = None):
+        self.resource_name = self._get_resource_name(resource)
+
+class ComponentResourceChangeListenerTestCase(
+    BaseResourceChangeListenerTestCase):
+    resource_type = Component
+
+class VersionResourceChangeListenerTestCase(
+    BaseResourceChangeListenerTestCase):
+    resource_type = Version
+
+class PriorityResourceChangeListenerTestCase(
+    BaseResourceChangeListenerTestCase):
+    resource_type = Priority
+
+class MilestoneResourceChangeListenerTestCase(
+    BaseResourceChangeListenerTestCase):
+    resource_type = Milestone
+
+class TicketResourceChangeListenerTestCase(
+    BaseResourceChangeListenerTestCase):
+    resource_type = Ticket
+    name_field = "summary"
+    dummy_author = "anAuthor"
+    dummy_comment = "some comment"
+
+    def test_change_listener_changed(self):
+        super(
+            TicketResourceChangeListenerTestCase,
+            self).test_change_listener_changed()
+
+        self.assertEqual(self.dummy_author, self.listener.context["author"])
+        self.assertEqual(self.dummy_comment, self.listener.context["comment"])
+
+
+    def _create_resource(self, name):
+        ticket = Ticket(self.env)
+        ticket["summary"] = name
+        ticket.insert()
+        return ticket
+
+    def _rename_resource(self, resource, new_name):
+        resource["summary"] = new_name
+        resource.save_changes(self.dummy_author, self.dummy_comment)
+        return resource
+
+    def _get_resource_name(self, resource):
+        return resource["summary"]
 
 def suite():
     suite = unittest.TestSuite()
@@ -1150,6 +1185,16 @@ def suite():
     suite.addTest(unittest.makeSuite(MilestoneTestCase, 'test'))
     suite.addTest(unittest.makeSuite(ComponentTestCase, 'test'))
     suite.addTest(unittest.makeSuite(VersionTestCase, 'test'))
+    suite.addTest(unittest.makeSuite(
+        ComponentResourceChangeListenerTestCase, 'test'))
+    suite.addTest(unittest.makeSuite(
+        VersionResourceChangeListenerTestCase, 'test'))
+    suite.addTest(unittest.makeSuite(
+        PriorityResourceChangeListenerTestCase, 'test'))
+    suite.addTest(unittest.makeSuite(
+        MilestoneResourceChangeListenerTestCase, 'test'))
+    suite.addTest(unittest.makeSuite(
+        TicketResourceChangeListenerTestCase, 'test'))
     return suite
 
 if __name__ == '__main__':

Modified: incubator/bloodhound/trunk/trac/trac/wiki/model.py
URL: http://svn.apache.org/viewvc/incubator/bloodhound/trunk/trac/trac/wiki/model.py?rev=1455199&r1=1455198&r2=1455199&view=diff
==============================================================================
--- incubator/bloodhound/trunk/trac/trac/wiki/model.py (original)
+++ incubator/bloodhound/trunk/trac/trac/wiki/model.py Mon Mar 11 15:54:57 2013
@@ -21,7 +21,7 @@ from __future__ import with_statement
 from datetime import datetime
 
 from trac.core import *
-from trac.resource import Resource
+from trac.resource import Resource, ResourceSystem, IResourceChangeListener
 from trac.util.datefmt import from_utimestamp, to_utimestamp, utc
 from trac.util.translation import _
 from trac.wiki.api import WikiSystem, validate_page_name
@@ -110,12 +110,9 @@ class WikiPage(object):
 
         # Let change listeners know about the deletion
         if not self.exists:
-            for listener in WikiSystem(self.env).change_listeners:
-                listener.wiki_page_deleted(self)
+            ResourceSystem(self.env).resource_deleted(self)
         else:
-            for listener in WikiSystem(self.env).change_listeners:
-                if hasattr(listener, 'wiki_page_version_deleted'):
-                    listener.wiki_page_version_deleted(self)
+            ResourceSystem(self.env).resource_version_deleted(self)
 
     def save(self, author, comment, remote_addr, t=None, db=None):
         """Save a new version of a page.
@@ -153,12 +150,23 @@ class WikiPage(object):
         self.comment = comment
         self.time = t
 
-        for listener in WikiSystem(self.env).change_listeners:
-            if self.version == 1:
-                listener.wiki_page_added(self)
-            else:
-                listener.wiki_page_changed(self, self.version, t, comment,
-                                           author, remote_addr)
+        context=dict(
+            version=self.version,
+            time=t,
+            comment=comment,
+            author=author,
+            remote_addr=remote_addr,
+            source_action="save")
+        if self.version == 1:
+            ResourceSystem(self.env).resource_created(self, context)
+        else:
+            ResourceSystem(self.env).resource_changed(
+                self,
+                old_values=dict(
+                    name=self.name,
+                    readonly = self.old_readonly,
+                    text = self.old_text),
+                context = context)
 
         self.old_readonly = self.readonly
         self.old_text = self.text
@@ -192,9 +200,15 @@ class WikiPage(object):
         self.name = new_name
         self.env.log.info('Renamed page %s 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)
+
+        ResourceSystem(self.env).resource_changed(
+            self,
+            old_values=dict(
+                name=old_name,
+                readonly = self.readonly,
+                text = self.text),
+            context=dict(source_action="rename")
+        )
 
     def get_history(self, db=None):
         """Retrieve the edit history of a wiki page.
@@ -207,3 +221,42 @@ class WikiPage(object):
                 WHERE name=%s AND version<=%s ORDER BY version DESC
                 """, (self.name, self.version)):
             yield version, from_utimestamp(ts), author, comment, ipnr
+
+class ResourceToWikiChangeListenerAdapter(Component):
+    """
+    The class provides backward compatibility for components implementing
+    IWikiChangeListener interface.
+    """
+    implements(IResourceChangeListener)
+    def match_resource(self, resource):
+        return isinstance(resource, WikiPage)
+
+    def resource_created(self, resource, context = None):
+        for listener in WikiSystem(self.env).change_listeners:
+            listener.wiki_page_added(resource)
+
+    def resource_changed(self, resource, old_values, context = None):
+        if context is not None and context.get("source_action") == "rename":
+            for listener in WikiSystem(self.env).change_listeners:
+                 if hasattr(listener, 'wiki_page_renamed'):
+                     listener.wiki_page_renamed(
+                         resource, old_values.get("name"))
+        else:
+            for listener in WikiSystem(self.env).change_listeners:
+                    listener.wiki_page_changed(
+                        resource,
+                        context.get("version"),
+                        context.get("time"),
+                        context.get("comment"),
+                        context.get("author"),
+                        context.get("remote_addr"),
+                    )
+
+    def resource_deleted(self, resource, context = None):
+        for listener in WikiSystem(self.env).change_listeners:
+            listener.wiki_page_deleted(resource)
+
+    def resource_version_deleted(self, resource, context):
+        for listener in WikiSystem(self.env).change_listeners:
+            if hasattr(listener, 'wiki_page_version_deleted'):
+                listener.wiki_page_version_deleted(resource)

Modified: incubator/bloodhound/trunk/trac/trac/wiki/tests/model.py
URL: http://svn.apache.org/viewvc/incubator/bloodhound/trunk/trac/trac/wiki/tests/model.py?rev=1455199&r1=1455198&r2=1455199&view=diff
==============================================================================
--- incubator/bloodhound/trunk/trac/trac/wiki/tests/model.py (original)
+++ incubator/bloodhound/trunk/trac/trac/wiki/tests/model.py Mon Mar 11 15:54:57 2013
@@ -12,6 +12,7 @@ import unittest
 from trac.attachment import Attachment
 from trac.core import *
 from trac.test import EnvironmentStub
+from trac.tests.resource import TestResourceChangeListener
 from trac.util.datefmt import utc, to_utimestamp
 from trac.wiki import WikiPage, IWikiChangeListener
 
@@ -266,9 +267,83 @@ class WikiPageTestCase(unittest.TestCase
             page = WikiPage(self.env, 'TestPage')
             self.assertRaises(TracError, page.rename, name)
 
+class WikiResourceChangeListenerTestCase(unittest.TestCase):
+    INITIAL_NAME = "Wiki page 1"
+    INITIAL_TEXT = "some text"
+    INITIAL_AUTHOR = "anAuthor"
+    INITIAL_COMMENT = "some comment"
+    INITIAL_REMOTE_ADDRESS = "::1"
+
+    def setUp(self):
+        self.env = EnvironmentStub(default_data=True)
+        self.listener = TestResourceChangeListener(self.env)
+        self.listener.resource_type = WikiPage
+        self.listener.callback = self.listener_callback
+
+    def tearDown(self):
+        self.env.reset_db()
+
+    def test_change_listener_created(self):
+        self._create_wiki_page(self.INITIAL_NAME)
+        self.assertEqual('created', self.listener.action)
+        self.assertIsInstance(self.listener.resource, WikiPage)
+        self.assertEqual(self.INITIAL_NAME, self.wiki_name)
+        self.assertEqual(self.INITIAL_TEXT, self.wiki_text)
+
+    def test_change_listener_text_changed(self):
+        wiki_page = self._create_wiki_page(self.INITIAL_NAME)
+        CHANGED_TEXT = "some other text"
+        wiki_page.text = CHANGED_TEXT
+        wiki_page.save("author1", "renamed_comment", "::2")
+        self.assertEqual('changed', self.listener.action)
+        self.assertIsInstance(self.listener.resource, WikiPage)
+        self.assertEqual(self.INITIAL_NAME, self.wiki_name)
+        self.assertEqual(CHANGED_TEXT, self.wiki_text)
+        self.assertEqual(
+            dict(text=self.INITIAL_TEXT, readonly=0, name=self.INITIAL_NAME),
+            self.listener.old_values)
+        self.assertEqual("save", self.listener.context["source_action"])
+
+    def test_change_listener_renamed(self):
+        wiki_page = self._create_wiki_page(self.INITIAL_NAME)
+        CHANGED_NAME = "NewWikiName"
+        wiki_page.rename(CHANGED_NAME)
+        self.assertEqual('changed', self.listener.action)
+        self.assertIsInstance(self.listener.resource, WikiPage)
+        self.assertEqual(CHANGED_NAME, self.wiki_name)
+        self.assertEqual(self.INITIAL_TEXT, self.wiki_text)
+        self.assertEqual(
+            dict(text=self.INITIAL_TEXT, readonly=0, name=self.INITIAL_NAME),
+            self.listener.old_values)
+        self.assertEqual("rename", self.listener.context["source_action"])
+
+    def test_change_listener_deleted(self):
+        wiki_page = self._create_wiki_page(self.INITIAL_NAME)
+        wiki_page.delete()
+        self.assertEqual('deleted', self.listener.action)
+        self.assertIsInstance(self.listener.resource, WikiPage)
+        self.assertEqual(self.INITIAL_NAME, self.wiki_name)
+
+    def _create_wiki_page(self, name=None):
+        name = name or self.INITIAL_NAME
+        wiki_page = WikiPage(self.env, name)
+        wiki_page.text = self.INITIAL_TEXT
+        wiki_page.save(
+            self.INITIAL_AUTHOR,
+            self.INITIAL_COMMENT,
+            self.INITIAL_REMOTE_ADDRESS)
+        return wiki_page
+
+    def listener_callback(self, action, resource, context, old_values = None):
+        self.wiki_name = resource.name
+        self.wiki_text = resource.text
 
 def suite():
-    return unittest.makeSuite(WikiPageTestCase, 'test')
+    suite = unittest.TestSuite()
+    suite.addTest(unittest.makeSuite(WikiPageTestCase, 'test'))
+    suite.addTest(unittest.makeSuite(
+        WikiResourceChangeListenerTestCase, 'test'))
+    return suite
 
 if __name__ == '__main__':
     unittest.main(defaultTest='suite')



Mime
View raw message