Mercurial > libervia-backend
comparison src/plugins/plugin_xep_0050.py @ 2406:1e5b2c35964e
plugin XEP-0050: renamed "requestCommandsList" method to "list" + added a run command + fixes:
- XMLUI cancellation is fixed
- when called from bridge, "list" (adHocList) command doesn't prepend instructions
author | Goffi <goffi@goffi.org> |
---|---|
date | Tue, 31 Oct 2017 23:11:13 +0100 |
parents | 918e38622a48 |
children | 8b37a62336c3 |
comparison
equal
deleted
inserted
replaced
2405:f4b6176eb65f | 2406:1e5b2c35964e |
---|---|
213 def __init__(self, host): | 213 def __init__(self, host): |
214 log.info(_("plugin XEP-0050 initialization")) | 214 log.info(_("plugin XEP-0050 initialization")) |
215 self.host = host | 215 self.host = host |
216 self.requesting = Sessions() | 216 self.requesting = Sessions() |
217 self.answering = {} | 217 self.answering = {} |
218 host.bridge.addMethod("requestCommand", ".plugin", in_sign='ss', out_sign='s', | 218 host.bridge.addMethod("adHocRun", ".plugin", in_sign='sss', out_sign='s', |
219 method=self._requestCommandsList, | 219 method=self._run, |
220 async=True) | |
221 host.bridge.addMethod("adHocList", ".plugin", in_sign='ss', out_sign='s', | |
222 method=self._list, | |
220 async=True) | 223 async=True) |
221 self.__requesting_id = host.registerCallback(self._requestingEntity, with_data=True) | 224 self.__requesting_id = host.registerCallback(self._requestingEntity, with_data=True) |
222 host.importMenu((D_("Service"), D_("Commands")), self._commandsMenu, security_limit=2, help_string=D_("Execute ad-hoc commands")) | 225 host.importMenu((D_("Service"), D_("Commands")), self._commandsMenu, security_limit=2, help_string=D_("Execute ad-hoc commands")) |
223 | 226 |
224 def getHandler(self, client): | 227 def getHandler(self, client): |
231 try: | 234 try: |
232 del self.answering[client.profile] | 235 del self.answering[client.profile] |
233 except KeyError: | 236 except KeyError: |
234 pass | 237 pass |
235 | 238 |
236 def _items2XMLUI(self, items): | 239 def _items2XMLUI(self, items, no_instructions): |
237 """ Convert discovery items to XMLUI dialog """ | 240 """ Convert discovery items to XMLUI dialog """ |
238 # TODO: manage items on different jids | 241 # TODO: manage items on different jids |
239 form_ui = xml_tools.XMLUI("form", submit_id=self.__requesting_id) | 242 form_ui = xml_tools.XMLUI("form", submit_id=self.__requesting_id) |
240 | 243 |
241 form_ui.addText(_("Please select a command"), 'instructions') | 244 if not no_instructions: |
245 form_ui.addText(_("Please select a command"), 'instructions') | |
242 | 246 |
243 options = [(item.nodeIdentifier, item.name) for item in items] | 247 options = [(item.nodeIdentifier, item.name) for item in items] |
244 form_ui.addList("node", options) | 248 form_ui.addList("node", options) |
245 return form_ui | 249 return form_ui |
246 | 250 |
329 # we add any present note to the instructions | 333 # we add any present note to the instructions |
330 form.instructions.extend(self._mergeNotes(notes)) | 334 form.instructions.extend(self._mergeNotes(notes)) |
331 return xml_tools.dataForm2XMLUI(form, self.__requesting_id, session_id=session_id) | 335 return xml_tools.dataForm2XMLUI(form, self.__requesting_id, session_id=session_id) |
332 | 336 |
333 def _requestingEntity(self, data, profile): | 337 def _requestingEntity(self, data, profile): |
338 def serialise(ret_data): | |
339 if 'xmlui' in ret_data: | |
340 ret_data['xmlui'] = ret_data['xmlui'].toXml() | |
341 return ret_data | |
342 | |
343 d = self.requestingEntity(data, profile) | |
344 d.addCallback(serialise) | |
345 return d | |
346 | |
347 def requestingEntity(self, data, profile): | |
334 """ | 348 """ |
335 request and entity and create XMLUI accordingly | 349 request and entity and create XMLUI accordingly |
336 @param data: data returned by previous XMLUI (first one must come from self._commandsMenu) | 350 @param data: data returned by previous XMLUI (first one must come from self._commandsMenu) |
337 @param profile: %(doc_profile)s | 351 @param profile: %(doc_profile)s |
338 @return: callback dict result (with "xmlui" corresponding to the answering dialog, or empty if it's finished without error) | 352 @return: callback dict result (with "xmlui" corresponding to the answering dialog, or empty if it's finished without error) |
339 | 353 |
340 """ | 354 """ |
355 if C.bool(data.get('cancelled', C.BOOL_FALSE)): | |
356 return defer.succeed({}) | |
357 client = self.host.getClient(profile) | |
341 # TODO: cancel, prev and next are not managed | 358 # TODO: cancel, prev and next are not managed |
342 # TODO: managed answerer errors | 359 # TODO: managed answerer errors |
343 # TODO: manage nodes with a non data form payload | 360 # TODO: manage nodes with a non data form payload |
344 if "session_id" not in data: | 361 if "session_id" not in data: |
345 # we just had the jid, we now request it for the available commands | 362 # we just had the jid, we now request it for the available commands |
346 session_id, session_data = self.requesting.newSession(profile=profile) | 363 session_id, session_data = self.requesting.newSession(profile=client.profile) |
347 entity = jid.JID(data[xml_tools.SAT_FORM_PREFIX+'jid']) | 364 entity = jid.JID(data[xml_tools.SAT_FORM_PREFIX+'jid']) |
348 session_data['jid'] = entity | 365 session_data['jid'] = entity |
349 d = self.requestCommandsList(entity, profile) | 366 d = self.list(client, entity) |
350 | 367 |
351 def sendItems(xmlui): | 368 def sendItems(xmlui): |
352 xmlui.session_id = session_id # we need to keep track of the session | 369 xmlui.session_id = session_id # we need to keep track of the session |
353 return {'xmlui': xmlui.toXml()} | 370 return {'xmlui': xmlui} |
354 | 371 |
355 d.addCallback(sendItems) | 372 d.addCallback(sendItems) |
356 else: | 373 else: |
357 # we have started a several forms sessions | 374 # we have started a several forms sessions |
358 try: | 375 try: |
359 session_data = self.requesting.profileGet(data["session_id"], profile) | 376 session_data = self.requesting.profileGet(data["session_id"], client.profile) |
360 except KeyError: | 377 except KeyError: |
361 log.warning ("session id doesn't exist, session has probably expired") | 378 log.warning ("session id doesn't exist, session has probably expired") |
362 # TODO: send error dialog | 379 # TODO: send error dialog |
363 return defer.succeed({}) | 380 return defer.succeed({}) |
364 session_id = data["session_id"] | 381 session_id = data["session_id"] |
368 # node has already been received | 385 # node has already been received |
369 except KeyError: | 386 except KeyError: |
370 # it's the first time we know the node, we save it in session data | 387 # it's the first time we know the node, we save it in session data |
371 session_data['node'] = data[xml_tools.SAT_FORM_PREFIX+'node'] | 388 session_data['node'] = data[xml_tools.SAT_FORM_PREFIX+'node'] |
372 | 389 |
373 client = self.host.getClient(profile) | |
374 | |
375 # we request execute node's command | 390 # we request execute node's command |
376 iq_elt = compat.IQ(client.xmlstream, 'set') | 391 iq_elt = compat.IQ(client.xmlstream, 'set') |
377 iq_elt['to'] = entity.full() | 392 iq_elt['to'] = entity.full() |
378 command_elt = iq_elt.addElement("command", NS_COMMANDS) | 393 command_elt = iq_elt.addElement("command", NS_COMMANDS) |
379 command_elt['node'] = session_data['node'] | 394 command_elt['node'] = session_data['node'] |
386 pass | 401 pass |
387 | 402 |
388 command_elt.addChild(xml_tools.XMLUIResultToElt(data)) # We add the XMLUI result to the command payload | 403 command_elt.addChild(xml_tools.XMLUIResultToElt(data)) # We add the XMLUI result to the command payload |
389 d = iq_elt.send() | 404 d = iq_elt.send() |
390 d.addCallback(self._commandsAnswer2XMLUI, session_id, session_data) | 405 d.addCallback(self._commandsAnswer2XMLUI, session_id, session_data) |
391 d.addCallback(lambda xmlui: {'xmlui': xmlui.toXml()} if xmlui is not None else {}) | 406 d.addCallback(lambda xmlui: {'xmlui': xmlui} if xmlui is not None else {}) |
392 | 407 |
393 return d | 408 return d |
394 | 409 |
395 def _commandsMenu(self, menu_data, profile): | 410 def _commandsMenu(self, menu_data, profile): |
396 """ First XMLUI activated by menu: ask for target jid | 411 """ First XMLUI activated by menu: ask for target jid |
444 else: | 459 else: |
445 raise AdHocError(XEP_0050.ERROR.INTERNAL) | 460 raise AdHocError(XEP_0050.ERROR.INTERNAL) |
446 | 461 |
447 return (payload, status, None, note) | 462 return (payload, status, None, note) |
448 | 463 |
449 def _requestCommandsList(self, to_jid_s, profile_key): | 464 def _run(self, service_jid_s='', node='', profile_key=C.PROF_KEY_NONE): |
450 d = self.requestCommandsList(jid.JID(to_jid_s), profile_key) | 465 client = self.host.getClient(profile_key) |
466 service_jid = jid.JID(service_jid_s) if service_jid_s else None | |
467 d = self.run(client, service_jid, node or None) | |
451 d.addCallback(lambda xmlui: xmlui.toXml()) | 468 d.addCallback(lambda xmlui: xmlui.toXml()) |
452 return d | 469 return d |
453 | 470 |
454 def requestCommandsList(self, to_jid, profile_key): | 471 @defer.inlineCallbacks |
455 """ Request available commands | 472 def run(self, client, service_jid=None, node=None): |
456 @param to_jid: the entity answering the commands | 473 """run an ad-hoc command |
457 @param profile_key: %(doc_profile)s | 474 |
458 | 475 @param service_jid(jid.JID, None): jid of the ad-hoc service |
459 """ | 476 None to use profile's server |
477 @param node(unicode, None): node of the ad-hoc commnad | |
478 None to get initial list | |
479 @return(unicode): command page XMLUI | |
480 """ | |
481 if service_jid is None: | |
482 service_jid = jid.JID(client.jid.host) | |
483 session_id, session_data = self.requesting.newSession(profile=client.profile) | |
484 session_data['jid'] = service_jid | |
485 if node is None: | |
486 xmlui = yield self.list(client, service_jid) | |
487 else: | |
488 session_data['node'] = node | |
489 cb_data = yield self.requestingEntity({'session_id': session_id}, client.profile) | |
490 xmlui = cb_data['xmlui'] | |
491 | |
492 xmlui.session_id = session_id | |
493 defer.returnValue(xmlui) | |
494 | |
495 def _list(self, to_jid_s, profile_key): | |
460 client = self.host.getClient(profile_key) | 496 client = self.host.getClient(profile_key) |
461 d = client.disco.requestItems(to_jid, NS_COMMANDS) | 497 to_jid = jid.JID(to_jid_s) if to_jid_s else None |
462 d.addCallback(self._items2XMLUI) | 498 d = self.list(client, to_jid, no_instructions=True) |
499 d.addCallback(lambda xmlui: xmlui.toXml()) | |
463 return d | 500 return d |
464 | 501 |
465 def addAdHocCommand(self, callback, label, node=None, features = None, timeout = 600, allowed_jids = None, allowed_groups = None, | 502 def list(self, client, to_jid, no_instructions=False): |
466 allowed_magics = None, forbidden_jids = None, forbidden_groups = None, profile_key=C.PROF_KEY_NONE): | 503 """Request available commands |
504 | |
505 @param to_jid(jid.JID, None): the entity answering the commands | |
506 None to use profile's server | |
507 @param no_instructions(bool): if True, don't add instructions widget | |
508 """ | |
509 d = self.host.getDiscoItems(client, to_jid, NS_COMMANDS) | |
510 d.addCallback(self._items2XMLUI, no_instructions) | |
511 return d | |
512 | |
513 def addAdHocCommand(self, callback, label, node=None, features=None, timeout=600, allowed_jids=None, allowed_groups=None, | |
514 allowed_magics=None, forbidden_jids=None, forbidden_groups=None, profile_key=C.PROF_KEY_NONE): | |
467 """Add an ad-hoc command for the current profile | 515 """Add an ad-hoc command for the current profile |
468 | 516 |
469 @param callback: method associated with this ad-hoc command which return the payload data (see AdHocCommand._sendAnswer), can return a deferred | 517 @param callback: method associated with this ad-hoc command which return the payload data (see AdHocCommand._sendAnswer), can return a deferred |
470 @param label: label associated with this command on the main menu | 518 @param label: label associated with this command on the main menu |
471 @param node: disco item node associated with this command. None to use autogenerated node | 519 @param node: disco item node associated with this command. None to use autogenerated node |