# HG changeset patch # User Ralph Meijer # Date 1100724193 0 # Node ID 59378610b16e19f43cd162aa1de85703c966a195 # Parent ccf699adae5e0ae6925ae95382d926fd3c09e000 Implement node purging and node deletion. diff -r ccf699adae5e -r 59378610b16e idavoll/backend.py --- 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) diff -r ccf699adae5e -r 59378610b16e idavoll/pgsql_backend.py --- 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 diff -r ccf699adae5e -r 59378610b16e idavoll/pubsub.py --- 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)