comparison src/plugins/plugin_misc_groupblog.py @ 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
comparison
equal deleted inserted replaced
310:53adec87d1d7 311:0aa6ca6cdbdd
30 from wokkel import disco,pubsub 30 from wokkel import disco,pubsub
31 from feed.atom import Entry, Author 31 from feed.atom import Entry, Author
32 import uuid 32 import uuid
33 from time import time 33 from time import time
34 34
35 MBLOG_COLLECTION = 'MBLOGCOLLECTION' 35 NS_MICROBLOG = 'urn:xmpp:microblog:%02d'
36 CONFIG_NODE = 'CONFIG' 36 CONFIG_NODE = 'CONFIG'
37 OPT_ACCESS_MODEL = 'pubsub#access_model' 37 OPT_ACCESS_MODEL = 'pubsub#access_model'
38 OPT_PERSIST_ITEMS = 'pubsub#persist_items' 38 OPT_PERSIST_ITEMS = 'pubsub#persist_items'
39 OPT_MAX_ITEMS = 'pubsub#max_items' 39 OPT_MAX_ITEMS = 'pubsub#max_items'
40 OPT_NODE_TYPE = 'pubsub#node_type' 40 OPT_NODE_TYPE = 'pubsub#node_type'
64 64
65 def __init__(self, host): 65 def __init__(self, host):
66 info(_("Group blog plugin initialization")) 66 info(_("Group blog plugin initialization"))
67 self.host = host 67 self.host = host
68 self._blog_nodes={} 68 self._blog_nodes={}
69 for i in range(1,21):
70 self.host.plugins["XEP-0163"].addPEPEvent("MICROBLOG_%02d" % i, NS_MICROBLOG % i, self.host.plugins["XEP-0277"].microblogCB, None)
69 71
70 host.bridge.addMethod("cleanBlogCollection", ".communication", in_sign='s', out_sign='', 72 host.bridge.addMethod("cleanBlogCollection", ".communication", in_sign='s', out_sign='',
71 method=self.cleanBlogCollection, 73 method=self.cleanBlogCollection,
72 doc = { 74 doc = {
73 }) 75 })
95 'param_1':'%(doc_profile)s' 97 'param_1':'%(doc_profile)s'
96 }) 98 })
97 99
98 def _getRootNode(self, entity): 100 def _getRootNode(self, entity):
99 return "%(entity)s_%(root_suff)s" % {'entity':entity.userhost(), 'root_suff':MBLOG_COLLECTION} 101 return "%(entity)s_%(root_suff)s" % {'entity':entity.userhost(), 'root_suff':MBLOG_COLLECTION}
102
103 def _getNodeName(self, number):
104 """Return the node name
105 @param number: int number of the node
106 @param entity: jid of the owner"""
107 return NS_MICROBLOG % number
100 108
101 def _getConfigNode(self, entity): 109 def _getConfigNode(self, entity):
102 return "%(entity)s_%(root_suff)s" % {'entity':entity.userhost(), 'root_suff':CONFIG_NODE} 110 return "%(entity)s_%(root_suff)s" % {'entity':entity.userhost(), 'root_suff':CONFIG_NODE}
103 111
104 def _configNodeCb(self, result, callback, profile): 112 def _configNodeCb(self, result, callback, profile):
143 if not client: 151 if not client:
144 error(_('No client for this profile key: %s') % profile_key) 152 error(_('No client for this profile key: %s') % profile_key)
145 return 153 return
146 client.client_initialized.addCallback(after_init) 154 client.client_initialized.addCallback(after_init)
147 155
148 def _publishMblog(self, name, message, pubsub_ent, profile): 156 def _publishMblog(self, name, message, client):
149 """Actually publish the message on the group blog 157 """Actually publish the message on the group blog
150 @param name: name of the node where we publish 158 @param name: name of the node where we publish
151 @param message: message to publish 159 @param message: message to publish
152 @param pubsub_ent: entity of the publish-subscribe service 160 @param client: SatXMPPClient of the published"""
153 @param profile: profile of the owner of the group""" 161 mblog_item = self.host.plugins["XEP-0277"].data2entry({'content':message}, client.profile)
154 mblog_item = self.host.plugins["XEP-0277"].data2entry({'content':message}, profile) 162 defer_blog = self.host.plugins["XEP-0060"].publish(client.jid.userhostJID(), name, items=[mblog_item], profile_key=client.profile)
155 defer_blog = self.host.plugins["XEP-0060"].publish(pubsub_ent, name, items=[mblog_item], profile_key=profile)
156 defer_blog.addErrback(self._mblogPublicationFailed) 163 defer_blog.addErrback(self._mblogPublicationFailed)
157 164
158 def _groupNodeCreated(self, ignore, groups, name, message, user_jid, pubsub_ent, profile): 165 def _groupNodeCreated(self, ignore, groups, name, message, client):
159 """A group node as been created, we need to add it to the configure node, and send the message to it 166 """A group node as been created, we need to add it to the configure node, and send the message to it
160 @param groups: list of groups authorized to subscribe to the node 167 @param groups: list of groups authorized to subscribe to the node
161 @param name: unique name of the group 168 @param name: unique name of the node
162 @param message: message to publish to the group 169 @param message: message to publish to the group
163 @param user_jid: jid of the owner of the node 170 @param client: SatXMPPClient"""
164 @param pubsub_ent: entity of the publish-subscribe service 171 def configNodeUpdated(result):
165 @param profile: profile of the owner of the group""" 172 self._blog_nodes[client.profile][name] = groups
166 config_node = self._getConfigNode(user_jid) 173 debug(_("Configuration node updated"))
174
175 config_node = self._getConfigNode(client.jid)
167 _payload = domish.Element(('','node_association')) 176 _payload = domish.Element(('','node_association'))
168 _payload['node'] = name 177 _payload['node'] = name
169 for group in groups: 178 for group in groups:
170 _payload.addElement('group',content=group) 179 _payload.addElement('group',content=group)
171 config_item = pubsub.Item(payload=_payload) 180 config_item = pubsub.Item(payload=_payload)
172 defer_config = self.host.plugins["XEP-0060"].publish(pubsub_ent, config_node, items=[config_item], profile_key=profile) 181 pubsub_ent = self.host.memory.getServerServiceEntity("pubsub", "service", client.profile)
173 defer_config.addCallback(lambda x: debug(_("Configuration node updated"))) 182 defer_config = self.host.plugins["XEP-0060"].publish(pubsub_ent, config_node, items=[config_item], profile_key=client.profile)
183 defer_config.addCallback(configNodeUpdated)
174 defer_config.addErrback(self._configUpdateFailed) 184 defer_config.addErrback(self._configUpdateFailed)
175 185
176 #Finally, we publish the message 186 #Finally, we publish the message
177 self._publishMblog(name, message, pubsub_ent, profile) 187 self._publishMblog(name, message, client)
178 188
179 189
180 def _mblogPublicationFailed(self, failure): 190 def _mblogPublicationFailed(self, failure):
181 #TODO 191 #TODO
182 import pdb 192 import pdb
185 def _configUpdateFailed(self, failure): 195 def _configUpdateFailed(self, failure):
186 #TODO 196 #TODO
187 import pdb 197 import pdb
188 pdb.set_trace() 198 pdb.set_trace()
189 199
190 def _nodeCreationFailed(self, failure, name, user_jid, groups, pubsub_ent, message, profile): 200 def _nodeCreationFailed(self, failure, name, groups, message, client):
191 #TODO 201 #FIXME: temporary behaviour is to delete the node,
192 if failure.value.condition == "item-not-found": 202 #user input should be required in the future
193 #The root node doesn't exists 203 def unmanagedError(failure):
194 def err_creating_root_node(failure): 204 msg = _("Can't create node")
195 msg = _("Can't create Root node") 205 error(msg)
196 error(msg) 206 raise NodeCreationError(msg)
197 raise NodeCreationError(msg) 207
198 208 if failure.value.condition == 'conflict':
199 _options = {OPT_NODE_TYPE:TYPE_COLLECTION} 209 pubsub_elts = filter(lambda elt: elt.name == 'pubsub', failure.value.children)
200 d = self.host.plugins["XEP-0060"].createNode(pubsub_ent, self._getRootNode(user_jid), _options, profile_key=profile) 210 if pubsub_elts:
201 d.addCallback(self._createNode, name, user_jid, groups, pubsub_ent, message, profile) 211 create_elts = filter(lambda elt: elt.name == 'create', pubsub_elts[0].children)
202 d.addErrback(err_creating_root_node) 212 if create_elts:
213 _from = jid.JID(failure.value.stanza['from'])
214 _node = create_elts[0]['node']
215 d = self.host.plugins["XEP-0060"].deleteNode(_from, _node, client.profile)
216 d.addCallback(self._createNode, name, groups, message, client)
217 d.addErrback(unmanagedError)
203 else: 218 else:
204 import pdb 219 unmanagedError(None)
205 pdb.set_trace() 220 msg = _("Can't create node")
206 221 error(msg)
207 def _createNode(self, ignore, name, user_jid, groups, pubsub_ent, message, profile): 222 raise NodeCreationError(msg)
223
224 def _createNode(self, ignore, name, groups, message, client):
208 """create a group microblog node 225 """create a group microblog node
209 @param ignore: ignored param, necessary to be added as a deferred callback 226 @param ignore: ignored param, necessary to be added as a deferred callback
210 @param name: name of the node 227 @param name: name of the node
211 @param user_jid: jid of the user creating the node
212 @param groups: list of group than can subscribe to the node 228 @param groups: list of group than can subscribe to the node
213 @param pubsub_ent: publish/subscribe service's entity
214 @param message: message to publish 229 @param message: message to publish
215 @param profile: profile of the user creating the node""" 230 @param client: SatXMPPClient"""
216 _options = {OPT_ACCESS_MODEL:"roster", OPT_PERSIST_ITEMS:1, OPT_MAX_ITEMS:-1, 231 _options = {OPT_ACCESS_MODEL:"roster", OPT_PERSIST_ITEMS:1, OPT_MAX_ITEMS:-1,
217 'pubsub#node_type':'leaf', 'pubsub#collection':self._getRootNode(user_jid),
218 'pubsub#roster_groups_allowed':groups} 232 'pubsub#roster_groups_allowed':groups}
219 d = self.host.plugins["XEP-0060"].createNode(pubsub_ent, name, _options, profile_key=profile) 233 d = self.host.plugins["XEP-0060"].createNode(client.jid.userhostJID(), name, _options, client.profile)
220 d.addCallback(self._groupNodeCreated, groups, name, message, user_jid, pubsub_ent, profile) 234 d.addCallback(self._groupNodeCreated, groups, name, message, client)
221 d.addErrback(self._nodeCreationFailed, name, user_jid, groups, pubsub_ent, message, profile) 235 d.addErrback(self._nodeCreationFailed, name, groups, message, client)
222 236
223 def _getNodeForGroups(self, groups, profile): 237 def _getNodeForGroups(self, groups, profile):
224 """Return node associated with the given list of groups 238 """Return node associated with the given list of groups
225 @param groups: list of groups 239 @param groups: list of groups
226 @param profile: profile of publisher""" 240 @param profile: profile of publisher"""
228 node_groups = self._blog_nodes[profile][node] 242 node_groups = self._blog_nodes[profile][node]
229 if set(node_groups) == set(groups): 243 if set(node_groups) == set(groups):
230 return node 244 return node
231 return None 245 return None
232 246
247 def _getFreeNode(self, entity, profile):
248 """Return a free group number,
249 raise an exception if we have reach limit"""
250 _all = set([self._getNodeName(idx) for idx in range(1,21)])
251 _used = set(self._blog_nodes[profile].keys())
252 _free = _all.difference(_used)
253 if not _free:
254 msg = _("Can't create group node: no more free node available")
255 warning(msg)
256 raise NodeCreationError(msg)
257 else:
258 return _free.pop()
259
233 def sendGroupBlog(self, groups, message, profile_key='@DEFAULT@'): 260 def sendGroupBlog(self, groups, message, profile_key='@DEFAULT@'):
234 """Publish a microblog to the node associated to the groups 261 """Publish a microblog to the node associated to the groups
235 If the node doesn't exist, it is created, then the message is posted 262 If the node doesn't exist, it is created, then the message is posted
236 @param groups: list of groups allowed to retrieve the microblog 263 @param groups: list of groups allowed to retrieve the microblog
237 @param message: microblog 264 @param message: microblog
244 271
245 def after_init(ignore): 272 def after_init(ignore):
246 _groups = list(set(groups).intersection(client.roster.getGroups())) #We only keep group which actually exist 273 _groups = list(set(groups).intersection(client.roster.getGroups())) #We only keep group which actually exist
247 #TODO: send an error signal if user want to post to non existant groups 274 #TODO: send an error signal if user want to post to non existant groups
248 _groups.sort() 275 _groups.sort()
249 pubsub_ent = self.host.memory.getServerServiceEntity("pubsub", "service", profile)
250 for group in _groups: 276 for group in _groups:
251 _node = self._getNodeForGroups([group], profile) 277 _node = self._getNodeForGroups([group], profile)
252 if not _node: 278 if not _node:
253 _node_name = unicode(uuid.uuid4()) 279 _node_name = self._getFreeNode(client.jid, profile)
254 self._createNode(None, _node_name, client.jid, [group], pubsub_ent, message, profile) 280 self._createNode(None, _node_name, [group], message, client)
255 else: 281 else:
256 self._publishMblog(_node, message, pubsub_ent, profile) 282 self._publishMblog(_node, message, client)
257 283
258 client = self.host.getClient(profile) 284 client = self.host.getClient(profile)
259 if not client: 285 if not client:
260 error(_('No client for this profile key: %s') % profile_key) 286 error(_('No client for this profile key: %s') % profile_key)
261 return 287 return
320 client = self.host.getClient(profile) 346 client = self.host.getClient(profile)
321 if not client: 347 if not client:
322 error(_('No client for this profile key: %s') % profile_key) 348 error(_('No client for this profile key: %s') % profile_key)
323 return 349 return
324 client.client_initialized.addCallback(after_init) 350 client.client_initialized.addCallback(after_init)
351