changeset 311:0aa6ca6cdbdd

plugin group blog: group blog now use PEP to take profit of autosubscribe
author Goffi <goffi@goffi.org>
date Tue, 12 Apr 2011 20:40:47 +0200
parents 53adec87d1d7
children f56108eb2880
files src/plugins/plugin_misc_groupblog.py src/plugins/plugin_xep_0163.py src/plugins/plugin_xep_0277.py
diffstat 3 files changed, 76 insertions(+), 45 deletions(-) [+]
line wrap: on
line diff
--- a/src/plugins/plugin_misc_groupblog.py	Mon Apr 11 12:47:35 2011 +0200
+++ b/src/plugins/plugin_misc_groupblog.py	Tue Apr 12 20:40:47 2011 +0200
@@ -32,7 +32,7 @@
 import uuid
 from time import time
 
-MBLOG_COLLECTION = 'MBLOGCOLLECTION'
+NS_MICROBLOG = 'urn:xmpp:microblog:%02d'
 CONFIG_NODE = 'CONFIG'
 OPT_ACCESS_MODEL = 'pubsub#access_model'
 OPT_PERSIST_ITEMS = 'pubsub#persist_items'
@@ -66,6 +66,8 @@
         info(_("Group blog plugin initialization"))
         self.host = host
         self._blog_nodes={}
+        for i in range(1,21):
+            self.host.plugins["XEP-0163"].addPEPEvent("MICROBLOG_%02d" % i, NS_MICROBLOG % i, self.host.plugins["XEP-0277"].microblogCB, None)
 
         host.bridge.addMethod("cleanBlogCollection", ".communication", in_sign='s', out_sign='',
                                method=self.cleanBlogCollection,
@@ -98,6 +100,12 @@
     def _getRootNode(self, entity):
         return "%(entity)s_%(root_suff)s" % {'entity':entity.userhost(), 'root_suff':MBLOG_COLLECTION}
 
+    def _getNodeName(self, number):
+        """Return the node name
+         @param number: int number of the node
+         @param entity: jid of the owner"""
+        return NS_MICROBLOG % number 
+
     def _getConfigNode(self, entity):
         return "%(entity)s_%(root_suff)s" % {'entity':entity.userhost(), 'root_suff':CONFIG_NODE}
 
@@ -145,36 +153,38 @@
             return
         client.client_initialized.addCallback(after_init)
 
-    def _publishMblog(self, name, message, pubsub_ent, profile):
+    def _publishMblog(self, name, message, client):
         """Actually publish the message on the group blog
         @param name: name of the node where we publish
         @param message: message to publish
-        @param pubsub_ent: entity of the publish-subscribe service
-        @param profile: profile of the owner of the group"""
-        mblog_item = self.host.plugins["XEP-0277"].data2entry({'content':message}, profile)
-        defer_blog = self.host.plugins["XEP-0060"].publish(pubsub_ent, name, items=[mblog_item], profile_key=profile)
+        @param client: SatXMPPClient of the published"""
+        mblog_item = self.host.plugins["XEP-0277"].data2entry({'content':message}, client.profile)
+        defer_blog = self.host.plugins["XEP-0060"].publish(client.jid.userhostJID(), name, items=[mblog_item], profile_key=client.profile)
         defer_blog.addErrback(self._mblogPublicationFailed)
 
-    def _groupNodeCreated(self, ignore, groups, name, message, user_jid, pubsub_ent, profile):
+    def _groupNodeCreated(self, ignore, groups, name, message, client):
         """A group node as been created, we need to add it to the configure node, and send the message to it
         @param groups: list of groups authorized to subscribe to the node
-        @param name: unique name of the group
+        @param name: unique name of the node
         @param message: message to publish to the group
-        @param user_jid: jid of the owner of the node 
-        @param pubsub_ent: entity of the publish-subscribe service
-        @param profile: profile of the owner of the group"""
-        config_node = self._getConfigNode(user_jid)
+        @param client: SatXMPPClient"""
+        def configNodeUpdated(result):
+            self._blog_nodes[client.profile][name] = groups
+            debug(_("Configuration node updated"))
+
+        config_node = self._getConfigNode(client.jid)
         _payload = domish.Element(('','node_association'))
         _payload['node'] = name
         for group in groups:
             _payload.addElement('group',content=group)
         config_item = pubsub.Item(payload=_payload)
-        defer_config = self.host.plugins["XEP-0060"].publish(pubsub_ent, config_node, items=[config_item], profile_key=profile)
-        defer_config.addCallback(lambda x: debug(_("Configuration node updated")))
+        pubsub_ent = self.host.memory.getServerServiceEntity("pubsub", "service", client.profile)
+        defer_config = self.host.plugins["XEP-0060"].publish(pubsub_ent, config_node, items=[config_item], profile_key=client.profile)
+        defer_config.addCallback(configNodeUpdated)
         defer_config.addErrback(self._configUpdateFailed)
 
         #Finally, we publish the message
-        self._publishMblog(name, message, pubsub_ent, profile)
+        self._publishMblog(name, message, client)
 
 
     def _mblogPublicationFailed(self, failure):
@@ -187,38 +197,42 @@
         import pdb
         pdb.set_trace()
 
-    def _nodeCreationFailed(self, failure, name, user_jid, groups, pubsub_ent, message, profile):
-        #TODO
-        if failure.value.condition == "item-not-found":
-            #The root node doesn't exists
-            def err_creating_root_node(failure):
-                msg = _("Can't create Root node")
-                error(msg)
-                raise NodeCreationError(msg)
+    def _nodeCreationFailed(self, failure, name, groups, message, client):
+        #FIXME: temporary behaviour is to delete the node,
+        #user input should be required in the future
+        def unmanagedError(failure):
+            msg = _("Can't create node")
+            error(msg)
+            raise NodeCreationError(msg)
+        
+        if failure.value.condition == 'conflict':
+            pubsub_elts = filter(lambda elt: elt.name == 'pubsub', failure.value.children)
+            if pubsub_elts:
+                create_elts = filter(lambda elt: elt.name == 'create', pubsub_elts[0].children)
+                if create_elts:
+                    _from = jid.JID(failure.value.stanza['from'])
+                    _node = create_elts[0]['node']
+                    d = self.host.plugins["XEP-0060"].deleteNode(_from, _node, client.profile)
+                    d.addCallback(self._createNode, name, groups, message, client)
+                    d.addErrback(unmanagedError)
+        else:
+            unmanagedError(None)
+            msg = _("Can't create node")
+            error(msg)
+            raise NodeCreationError(msg)
 
-            _options = {OPT_NODE_TYPE:TYPE_COLLECTION}
-            d = self.host.plugins["XEP-0060"].createNode(pubsub_ent, self._getRootNode(user_jid), _options, profile_key=profile)
-            d.addCallback(self._createNode, name, user_jid, groups, pubsub_ent, message, profile)
-            d.addErrback(err_creating_root_node) 
-        else:
-            import pdb
-            pdb.set_trace()
-
-    def _createNode(self, ignore, name, user_jid, groups, pubsub_ent, message, profile):
+    def _createNode(self, ignore, name, groups, message, client):
         """create a group microblog node
         @param ignore: ignored param, necessary to be added as a deferred callback
         @param name: name of the node
-        @param user_jid: jid of the user creating the node
         @param groups: list of group than can subscribe to the node
-        @param pubsub_ent: publish/subscribe service's entity
         @param message: message to publish
-        @param profile: profile of the user creating the node"""
+        @param client: SatXMPPClient"""
         _options = {OPT_ACCESS_MODEL:"roster", OPT_PERSIST_ITEMS:1, OPT_MAX_ITEMS:-1,
-                    'pubsub#node_type':'leaf', 'pubsub#collection':self._getRootNode(user_jid),
                     'pubsub#roster_groups_allowed':groups}
-        d = self.host.plugins["XEP-0060"].createNode(pubsub_ent, name, _options, profile_key=profile)
-        d.addCallback(self._groupNodeCreated, groups, name, message, user_jid, pubsub_ent, profile)
-        d.addErrback(self._nodeCreationFailed, name, user_jid, groups, pubsub_ent, message, profile)
+        d = self.host.plugins["XEP-0060"].createNode(client.jid.userhostJID(), name, _options, client.profile)
+        d.addCallback(self._groupNodeCreated, groups, name, message, client)
+        d.addErrback(self._nodeCreationFailed, name, groups, message, client)
     
     def _getNodeForGroups(self, groups, profile):
         """Return node associated with the given list of groups
@@ -230,6 +244,19 @@
                 return node
         return None
 
+    def _getFreeNode(self, entity, profile):
+        """Return a free group number,
+        raise an exception if we have reach limit"""
+        _all = set([self._getNodeName(idx) for idx in range(1,21)])
+        _used = set(self._blog_nodes[profile].keys())
+        _free = _all.difference(_used)
+        if not _free:
+            msg = _("Can't create group node: no more free node available")
+            warning(msg)
+            raise NodeCreationError(msg)
+        else:
+            return _free.pop()
+
     def sendGroupBlog(self, groups, message, profile_key='@DEFAULT@'):
         """Publish a microblog to the node associated to the groups
         If the node doesn't exist, it is created, then the message is posted
@@ -246,14 +273,13 @@
             _groups = list(set(groups).intersection(client.roster.getGroups())) #We only keep group which actually exist
             #TODO: send an error signal if user want to post to non existant groups
             _groups.sort()
-            pubsub_ent = self.host.memory.getServerServiceEntity("pubsub", "service", profile)
             for group in _groups:
                 _node = self._getNodeForGroups([group], profile)
                 if not _node:
-                    _node_name = unicode(uuid.uuid4())
-                    self._createNode(None, _node_name, client.jid, [group], pubsub_ent, message, profile)
+                    _node_name = self._getFreeNode(client.jid, profile)
+                    self._createNode(None, _node_name, [group], message, client)
                 else:
-                    self._publishMblog(_node, message, pubsub_ent, profile)
+                    self._publishMblog(_node, message, client)
         
         client = self.host.getClient(profile)
         if not client:
@@ -322,3 +348,4 @@
             error(_('No client for this profile key: %s') % profile_key)
             return
         client.client_initialized.addCallback(after_init)
+
--- a/src/plugins/plugin_xep_0163.py	Mon Apr 11 12:47:35 2011 +0200
+++ b/src/plugins/plugin_xep_0163.py	Tue Apr 12 20:40:47 2011 +0200
@@ -62,14 +62,15 @@
         disco_info.extend(map(disco.DiscoFeature, self.pep_events))
         return True
 
-    def addPEPEvent(self, event_type, name, in_callback, out_callback, notify = True):
+    def addPEPEvent(self, event_type, name, in_callback, out_callback = None, notify = True):
         """Add a Personal Eventing Protocol event manager
         @param event_type: type of the event (always uppercase), can be MOOD, TUNE, etc
         @param name: namespace of the node (e.g. http://jabber.org/protocol/mood for User Mood)
         @param in_callback: method to call when this event occur
         @param out_callback: method to call when we want to publish this event
         @param notify: add autosubscribe (+notify) if True"""
-        self.pep_out_cb[event_type]=out_callback
+        if out_callback:
+            self.pep_out_cb[event_type] = out_callback
         self.pep_events.add(name)
         if notify:
             self.pep_events.add(name+"+notify")
--- a/src/plugins/plugin_xep_0277.py	Mon Apr 11 12:47:35 2011 +0200
+++ b/src/plugins/plugin_xep_0277.py	Tue Apr 12 20:40:47 2011 +0200
@@ -159,6 +159,9 @@
         @param profile_key: profile key"""
 
         _jid, xmlstream = self.host.getJidNStream(profile_key)
+        if not _jid:
+            error(_("Can't find profile's jid"))
+            return
         _options = {OPT_ACCESS_MODEL:access, OPT_PERSIST_ITEMS:1, OPT_MAX_ITEMS:-1}
         def cb(result):
             #Node is created with right permission