plugin xep-0050 (ad-hoc commands): requesting part. first draft

This commit is contained in:
Goffi 2013-12-24 15:43:52 +01:00
commit edb1d7c76b

View file

@ -22,10 +22,11 @@ from twisted.words.protocols.jabber import jid
from twisted.words.protocols.jabber import error as xmpp_error
from twisted.words.xish import domish
from twisted.internet import defer, reactor
from wokkel import disco, iwokkel, data_form
from wokkel import disco, iwokkel, data_form, compat
from sat.core import exceptions
from sat.memory.memory import Sessions
from uuid import uuid4
from sat.tools import xml_tools
from zope.interface import implements
@ -204,8 +205,13 @@ class XEP_0050(object):
def __init__(self, host):
info(_("plugin XEP-0050 initialization"))
self.host = host
self.requesting = {}
self.requesting = Sessions()
self.answering = {}
host.bridge.addMethod("requestCommand", ".plugin", in_sign='ss', out_sign='s',
method=self._requestCommandsList,
async=True)
self.__requesting_id = host.registerCallback(self._requestingEntity, with_data=True)
host.importMenu("Service", "commands", self._commandsMenu, help_string=_("Execute ad-hoc commands"))
def getHandler(self, profile):
return XEP_0050_handler(self)
@ -219,12 +225,121 @@ class XEP_0050(object):
except KeyError:
pass
def _items2XMLUI(self, items):
""" Convert discovery items to XMLUI dialog """
# TODO: manage items on different jids
form_ui = xml_tools.XMLUI("form", submit_id=self.__requesting_id)
form_ui.addText(_("Please select a command"), 'instructions')
options = [(item.nodeIdentifier, item.name) for item in items]
form_ui.addList(options, "node")
return form_ui
def _commandsAnswer2XMLUI(self, iq_elt, session_id, session_data):
"""
Convert command answer to an ui for frontend
@param iq_elt: command result
@param session_id: id of the session used with the frontend
@param profile_key: %(doc_profile_key)s
"""
command_elt = iq_elt.elements(NS_COMMANDS, "command").next()
status = command_elt.getAttribute('status', XEP_0050.STATUS.EXECUTING)
if status in [XEP_0050.STATUS.COMPLETED, XEP_0050.STATUS.CANCELED]:
# the command session is finished, we purge our session
del self.requesting[session_id]
return None
remote_session_id = command_elt.getAttribute('sessionid')
if remote_session_id:
session_data['remote_id'] = remote_session_id
data_elt = command_elt.elements(data_form.NS_X_DATA, 'x').next()
form = data_form.Form.fromElement(data_elt)
return xml_tools.dataForm2XMLUI(form, self.__requesting_id, session_id=session_id)
def _requestingEntity(self, data, profile):
"""
request and entity and create XMLUI accordingly
@param data: data returned by previous XMLUI (first one must come from self._commandsMenu)
@param profile: %(doc_profile)s
@return: callback dict result (with "xmlui" corresponding to the answering dialog, or empty if it's finished without error)
"""
# TODO: cancel, prev and next are not managed
# TODO: managed answerer errors
# TODO: manage nodes with a non data form payload
if "session_id" not in data:
# we just had the jid, we now request it for the available commands
session_id, session_data = self.requesting.newSession(profile=profile)
entity = jid.JID(data[xml_tools.SAT_FORM_PREFIX+'jid'])
session_data['jid'] = entity
d = self.requestCommandsList(entity, profile)
def sendItems(xmlui):
xmlui.setSessionId(session_id) # we need to keep track of the session
return {'xmlui': xmlui.toXml()}
d.addCallback(sendItems)
else:
# we have started a several forms sessions
try:
session_data = self.requesting.profileGet(data["session_id"], profile)
except KeyError:
warning ("session id doesn't exist, session has probably expired")
# TODO: send error dialog
return defer.succeed({})
session_id = data["session_id"]
entity = session_data['jid']
try:
node = session_data['node']
# node has already been received
except KeyError:
# it's the first time we know the node, we save it in session data
node = session_data['node'] = data[xml_tools.SAT_FORM_PREFIX+'node']
client = self.host.getClient(profile)
if not client:
raise exceptions.ProfileUnknownError
# we request execute node's command
iq_elt = compat.IQ(client.xmlstream, 'set')
iq_elt['to'] = entity.full()
command_elt = iq_elt.addElement("command", NS_COMMANDS)
command_elt['node'] = session_data['node']
command_elt['action'] = XEP_0050.ACTION.EXECUTE
try:
# remote_id is the XEP_0050 sessionid used by answering command
# while session_id is our own session id used with the frontend
command_elt['sessionid'] = session_data['remote_id']
except KeyError:
pass
command_elt.addChild(xml_tools.XMLUIResultToElt(data)) # We add the XMLUI result to the command payload
d = iq_elt.send()
d.addCallback(self._commandsAnswer2XMLUI, session_id, session_data)
d.addCallback(lambda xmlui: {'xmlui': xmlui.toXml()} if xmlui is not None else {})
return d
def _commandsMenu(self, profile):
""" First XMLUI activated by menu: ask for target jid
@param profile: %(doc_profile)s
"""
form_ui = xml_tools.XMLUI("form", submit_id=self.__requesting_id)
form_ui.addText(_("Please enter target jid"), 'instructions')
form_ui.changeLayout("pairs")
form_ui.addLabel("jid")
form_ui.addString("jid")
return form_ui.toXml()
def _statusCallback(self, command_elt, session_data, action, node, profile):
""" Ad-hoc command used to change the "show" part of status """
actions = session_data.setdefault('actions',[])
actions.append(action)
if len(actions) == 1:
# it's our first request, we ask the desired new status
status = XEP_0050.STATUS.EXECUTING
form = data_form.Form('form', title=_('status selection'))
show_options = [data_form.Option(name, label) for name, label in SHOWS.items()]
@ -234,7 +349,8 @@ class XEP_0050(object):
payload = form.toElement()
note = None
elif len(actions) == 2: # we should have the answer here
elif len(actions) == 2:
# we should have the answer here
try:
x_elt = command_elt.elements(data_form.NS_X_DATA,'x').next()
answer_form = data_form.Form.fromElement(x_elt)
@ -259,6 +375,22 @@ class XEP_0050(object):
return (payload, status, None, note)
def _requestCommandsList(self, to_jid_s, profile_key):
d = self.requestCommandsList(jid.JID(to_jid_s), profile_key)
d.addCallback(lambda xmlui: xmlui.toXml())
return d
def requestCommandsList(self, to_jid, profile_key):
""" Request available commands
@param to_jid: the entity answering the commands
@param profile_key: %(doc_profile)s
"""
client = self.host.getClient(profile_key)
d = client.disco.requestItems(to_jid, NS_COMMANDS)
d.addCallback(self._items2XMLUI)
return d
def addAdHocCommand(self, callback, label, node="", features = None, timeout = 600, allowed_jids = None, allowed_groups = None,
allowed_magics = None, forbidden_jids = None, forbidden_groups = None, profile_key="@NONE@"):
"""