changeset 90:59378610b16e

Implement node purging and node deletion.
author Ralph Meijer <ralphm@ik.nu>
date Wed, 17 Nov 2004 20:43:13 +0000
parents ccf699adae5e
children 47ee5369c410
files idavoll/backend.py idavoll/pgsql_backend.py idavoll/pubsub.py
diffstat 3 files changed, 188 insertions(+), 1 deletions(-) [+]
line wrap: on
line diff
--- a/idavoll/backend.py	Tue Nov 16 12:16:04 2004 +0000
+++ b/idavoll/backend.py	Wed Nov 17 20:43:13 2004 +0000
@@ -54,6 +54,40 @@
         @return: a deferred that fires when the node has been created.
         """
 
+class INodeDeletionService(components.Interface):
+    """ A service for deleting nodes. """
+
+    def register_pre_delete(self, pre_delete_fn):
+        """ Register a callback that is called just before a node deletion.
+        
+        The function C{pre_deleted_fn} is added to a list of functions
+        to be called just before deletion of a node. The callback
+        C{pre_delete_fn} is called with the C{node_id} that is about to be
+        deleted and should return a deferred that returns a list of deferreds
+        that are to be fired after deletion. The backend collects the lists
+        from all these callbacks before actually deleting the node in question.
+        After deletion all collected deferreds are fired to do post-processing.
+
+        The idea is that you want to be able to collect data from the
+        node before deleting it, for example to get a list of subscribers
+        that have to be notified after the node has been deleted. To do this,
+        C{pre_delete_fn} fetches the subscriber list and passes this
+        list to a callback attached to a deferred that it sets up. This
+        deferred is returned in the list of deferreds.
+        """
+
+    def get_subscribers(self, node_id):
+        """ Get node subscriber list.
+        
+        @return: a deferred that fires with the list of subscribers.
+        """
+
+    def delete_node(self, node_id, requestor):
+        """ Delete a node.
+        
+        @return: a deferred that fires when the node has been deleted.
+        """
+
 class IPublishService(components.Interface):
     """ A service for publishing items to a node. """
 
@@ -379,4 +413,75 @@
                              '//event/pubsub/retract')
                                                                                 
     def purge_node(self, node_id, requestor):
-        pass
+        d1 = self.parent.storage.get_node_configuration(node_id)
+        d2 = self.parent.storage.get_affiliation(node_id, requestor)
+        d = defer.DeferredList([d1, d2], fireOnOneErrback=1)
+        d.addErrback(lambda x: x.value[0])
+        d.addCallback(self._do_purge, node_id)
+        return d
+    
+    def _do_purge(self, result, node_id):
+        configuration = result[0][1]
+        persist_items = configuration["persist_items"]
+        affiliation = result[1][1]
+                                                                                
+        if affiliation != 'owner':
+            raise NotAuthorized
+                                                                                
+        if not persist_items:
+            raise NodeNotPersistent
+                                                                                
+        d = self.parent.storage.purge_node(node_id)
+        d.addCallback(self._do_notify_purge, node_id)
+        return d
+    
+    def _do_notify_purge(self, result, node_id):
+        self.parent.dispatch(node_id, '//event/pubsub/purge')
+
+class NodeDeletionService(service.Service):
+
+    __implements__ = INodeDeletionService,
+
+    def __init__(self):
+        self._callback_list = []
+
+    def register_pre_delete(self, pre_delete_fn):
+        self._callback_list.append(pre_delete_fn)
+
+    def get_subscribers(self, node_id):
+        return self.parent.storage.get_subscribers(node_id)
+
+    def delete_node(self, node_id, requestor):
+        d1 = self.parent.storage.get_node_configuration(node_id)
+        d2 = self.parent.storage.get_affiliation(node_id, requestor)
+        d = defer.DeferredList([d1, d2], fireOnOneErrback=1)
+        d.addErrback(lambda x: x.value[0])
+        d.addCallback(self._do_pre_delete, node_id)
+        return d
+    
+    def _do_pre_delete(self, result, node_id):
+        configuration = result[0][1]
+        persist_items = configuration["persist_items"]
+        affiliation = result[1][1]
+                                                                                
+        if affiliation != 'owner':
+            raise NotAuthorized
+
+        d = defer.DeferredList([cb(node_id) for cb in self._callback_list],
+                               consumeErrors=1)
+        d.addCallback(self._do_delete, node_id)
+
+    def _do_delete(self, result, node_id):
+        dl = []
+        for succeeded, r in result:
+            if succeeded and r:
+                dl.extend(r)
+
+        d = self.parent.storage.delete_node(node_id)
+        d.addCallback(self._do_notify_delete, dl)
+
+        return d
+    
+    def _do_notify_delete(self, result, dl):
+        for d in dl:
+            d.callback(None)
--- a/idavoll/pgsql_backend.py	Tue Nov 16 12:16:04 2004 +0000
+++ b/idavoll/pgsql_backend.py	Wed Nov 17 20:43:13 2004 +0000
@@ -293,6 +293,8 @@
         return self.dbpool.runInteraction(self._remove_items, node_id, item_ids)
 
     def _remove_items(self, cursor, node_id, item_ids):
+        self._check_node_exists(cursor, node_id)
+        
         deleted = []
 
         for item_id in item_ids:
@@ -307,6 +309,25 @@
 
         return deleted
 
+    def purge_node(self, node_id):
+        return self.dbpool.runInteraction(self._purge_node, node_id)
+
+    def _purge_node(self, cursor, node_id):
+        self._check_node_exists(cursor, node_id)
+
+        cursor.execute("""DELETE FROM items WHERE
+                          node_id=(SELECT id FROM nodes WHERE node=%s)""",
+                       (node_id.encode('utf-8'),))
+
+    def delete_node(self, node_id):
+        return self.dbpool.runInteraction(self._delete_node, node_id)
+
+    def _delete_node(self, cursor, node_id):
+        self._check_node_exists(cursor, node_id)
+
+        cursor.execute("""DELETE FROM nodes WHERE node=%s""",
+                       (node_id.encode('utf-8'),))
+
 class BackendService(backend.BackendService):
     """ PostgreSQL backend Service for a JEP-0060 pubsub service """
 
@@ -330,3 +351,6 @@
 
 class RetractionService(backend.RetractionService):
     pass
+
+class NodeDeletionService(backend.NodeDeletionService):
+    pass
--- a/idavoll/pubsub.py	Tue Nov 16 12:16:04 2004 +0000
+++ b/idavoll/pubsub.py	Wed Nov 17 20:43:13 2004 +0000
@@ -28,6 +28,8 @@
 PUBSUB_AFFILIATIONS = PUBSUB_GET + '/affiliations'
 PUBSUB_ITEMS = PUBSUB_GET + '/items'
 PUBSUB_RETRACT = PUBSUB_SET + '/retract'
+PUBSUB_PURGE = PUBSUB_SET + '/purge'
+PUBSUB_DELETE = PUBSUB_SET + '/delete'
 
 class Error(Exception):
     pubsub_error = None
@@ -413,6 +415,7 @@
 
     def componentConnected(self, xmlstream):
         xmlstream.addObserver(PUBSUB_RETRACT, self.onRetract)
+        xmlstream.addObserver(PUBSUB_PURGE, self.onPurge)
 
     def onRetract(self, iq):
         self.handler_wrapper(self._onRetract, iq)
@@ -436,4 +439,59 @@
         return self.backend.retract_item(node, item_ids,
                                     jid.JID(iq["from"]).userhostJID())
 
+    def onPurge(self, iq):
+        self.handler_wrapper(self._onPurge, iq)
+
+    def _onPurge(self, iq):
+        try:
+            node = iq.pubsub.purge["node"]
+        except KeyError:
+            raise BadRequest
+
+        return self.backend.purge_node(node, jid.JID(iq["from"]).userhostJID())
+
 components.registerAdapter(ComponentServiceFromRetractionService, backend.IRetractionService, component.IService)
+
+class ComponentServiceFromNodeDeletionService(Service):
+
+    def __init__(self, backend):
+        Service.__init__(self, backend)
+        self.subscribers = []
+
+    def componentConnected(self, xmlstream):
+        self.backend.register_pre_delete(self._pre_delete)
+        xmlstream.addObserver(PUBSUB_DELETE, self.onDelete)
+
+    def _pre_delete(self, node_id):
+        d = self.backend.get_subscribers(node_id)
+        d.addCallback(self._return_deferreds, node_id)
+        return d
+
+    def _return_deferreds(self, subscribers, node_id):
+        d = defer.Deferred()
+        d.addCallback(self._notify, subscribers, node_id)
+        return [d]
+
+    def _notify(self, result, subscribers, node_id):
+        message = domish.Element((NS_COMPONENT, "message"))
+        message["from"] = self.parent.jabberId
+        event = message.addElement((NS_PUBSUB_EVENT, "event"))
+        event.addElement("delete")["node"] = node_id
+
+        for subscriber in subscribers:
+            message["to"] = subscriber
+            print message.toXml()
+            self.send(message)
+
+    def onDelete(self, iq):
+        self.handler_wrapper(self._onDelete, iq)
+
+    def _onDelete(self, iq):
+        try:
+            node = iq.pubsub.delete["node"]
+        except KeyError:
+            raise BadRequest
+
+        return self.backend.delete_node(node, jid.JID(iq["from"]).userhostJID())
+
+components.registerAdapter(ComponentServiceFromNodeDeletionService, backend.INodeDeletionService, component.IService)