diff sat/plugins/plugin_xep_0050.py @ 2624:56f94936df1e

code style reformatting using black
author Goffi <goffi@goffi.org>
date Wed, 27 Jun 2018 20:14:46 +0200
parents 0112c1f7dcf0
children 8dd9db785ac8
line wrap: on
line diff
--- a/sat/plugins/plugin_xep_0050.py	Wed Jun 27 07:51:29 2018 +0200
+++ b/sat/plugins/plugin_xep_0050.py	Wed Jun 27 20:14:46 2018 +0200
@@ -20,6 +20,7 @@
 from sat.core.i18n import _, D_
 from sat.core.constants import Const as C
 from sat.core.log import getLogger
+
 log = getLogger(__name__)
 from twisted.words.protocols.jabber import jid
 from twisted.words.protocols import jabber
@@ -41,7 +42,7 @@
 from collections import namedtuple
 
 try:
-    from collections import OrderedDict # only available from python 2.7
+    from collections import OrderedDict  # only available from python 2.7
 except ImportError:
     from ordereddict import OrderedDict
 
@@ -51,12 +52,16 @@
 ID_CMD_NODE = disco.DiscoIdentity("automation", "command-node")
 CMD_REQUEST = IQ_SET + '/command[@xmlns="' + NS_COMMANDS + '"]'
 
-SHOWS = OrderedDict([('default', _('Online')),
-                     ('away', _('Away')),
-                     ('chat', _('Free for chat')),
-                     ('dnd', _('Do not disturb')),
-                     ('xa', _('Left')),
-                     ('disconnect', _('Disconnect'))])
+SHOWS = OrderedDict(
+    [
+        ("default", _("Online")),
+        ("away", _("Away")),
+        ("chat", _("Free for chat")),
+        ("dnd", _("Do not disturb")),
+        ("xa", _("Left")),
+        ("disconnect", _("Disconnect")),
+    ]
+)
 
 PLUGIN_INFO = {
     C.PI_NAME: "Ad-Hoc Commands",
@@ -65,12 +70,11 @@
     C.PI_PROTOCOLS: ["XEP-0050"],
     C.PI_MAIN: "XEP_0050",
     C.PI_HANDLER: "yes",
-    C.PI_DESCRIPTION: _("""Implementation of Ad-Hoc Commands""")
+    C.PI_DESCRIPTION: _("""Implementation of Ad-Hoc Commands"""),
 }
 
 
 class AdHocError(Exception):
-
     def __init__(self, error_const):
         """ Error to be used from callback
         @param error_const: one of XEP_0050.ERROR
@@ -78,16 +82,37 @@
         assert error_const in XEP_0050.ERROR
         self.callback_error = error_const
 
+
 class AdHocCommand(XMPPHandler):
     implements(iwokkel.IDisco)
 
-    def  __init__(self, parent, callback, label, node, features, timeout, allowed_jids, allowed_groups, allowed_magics, forbidden_jids, forbidden_groups, client):
+    def __init__(
+        self,
+        parent,
+        callback,
+        label,
+        node,
+        features,
+        timeout,
+        allowed_jids,
+        allowed_groups,
+        allowed_magics,
+        forbidden_jids,
+        forbidden_groups,
+        client,
+    ):
         self.parent = parent
         self.callback = callback
         self.label = label
         self.node = node
         self.features = [disco.DiscoFeature(feature) for feature in features]
-        self.allowed_jids, self.allowed_groups, self.allowed_magics, self.forbidden_jids, self.forbidden_groups = allowed_jids, allowed_groups, allowed_magics, forbidden_jids, forbidden_groups
+        self.allowed_jids, self.allowed_groups, self.allowed_magics, self.forbidden_jids, self.forbidden_groups = (
+            allowed_jids,
+            allowed_groups,
+            allowed_magics,
+            forbidden_jids,
+            forbidden_groups,
+        )
         self.client = client
         self.sessions = Sessions(timeout=timeout)
 
@@ -95,7 +120,7 @@
         return self.label
 
     def isAuthorised(self, requestor):
-        if '@ALL@' in self.allowed_magics:
+        if "@ALL@" in self.allowed_magics:
             return True
         forbidden = set(self.forbidden_jids)
         for group in self.forbidden_groups:
@@ -107,18 +132,25 @@
             try:
                 allowed.update(self.client.roster.getJidsFromGroup(group))
             except exceptions.UnknownGroupError:
-                log.warning(_(u"The groups [%(group)s] is unknown for profile [%(profile)s])" % {'group':group, 'profile':self.client.profile}))
+                log.warning(
+                    _(
+                        u"The groups [%(group)s] is unknown for profile [%(profile)s])"
+                        % {"group": group, "profile": self.client.profile}
+                    )
+                )
         if requestor.userhostJID() in allowed:
             return True
         return False
 
-    def getDiscoInfo(self, requestor, target, nodeIdentifier=''):
-        if nodeIdentifier != NS_COMMANDS: # FIXME: we should manage other disco nodes here
+    def getDiscoInfo(self, requestor, target, nodeIdentifier=""):
+        if (
+            nodeIdentifier != NS_COMMANDS
+        ):  # FIXME: we should manage other disco nodes here
             return []
         # identities = [ID_CMD_LIST if self.node == NS_COMMANDS else ID_CMD_NODE] # FIXME
         return [disco.DiscoFeature(NS_COMMANDS)] + self.features
 
-    def getDiscoItems(self, requestor, target, nodeIdentifier=''):
+    def getDiscoItems(self, requestor, target, nodeIdentifier=""):
         return []
 
     def _sendAnswer(self, callback_data, session_id, request):
@@ -134,30 +166,30 @@
         @return: deferred
         """
         payload, status, actions, note = callback_data
-        assert(isinstance(payload, domish.Element) or payload is None)
-        assert(status in XEP_0050.STATUS)
+        assert isinstance(payload, domish.Element) or payload is None
+        assert status in XEP_0050.STATUS
         if not actions:
             actions = [XEP_0050.ACTION.EXECUTE]
-        result = domish.Element((None, 'iq'))
-        result['type'] = 'result'
-        result['id'] = request['id']
-        result['to'] = request['from']
-        command_elt = result.addElement('command', NS_COMMANDS)
-        command_elt['sessionid'] = session_id
-        command_elt['node'] = self.node
-        command_elt['status'] = status
+        result = domish.Element((None, "iq"))
+        result["type"] = "result"
+        result["id"] = request["id"]
+        result["to"] = request["from"]
+        command_elt = result.addElement("command", NS_COMMANDS)
+        command_elt["sessionid"] = session_id
+        command_elt["node"] = self.node
+        command_elt["status"] = status
 
         if status != XEP_0050.STATUS.CANCELED:
             if status != XEP_0050.STATUS.COMPLETED:
-                actions_elt = command_elt.addElement('actions')
-                actions_elt['execute'] = actions[0]
+                actions_elt = command_elt.addElement("actions")
+                actions_elt["execute"] = actions[0]
                 for action in actions:
                     actions_elt.addElement(action)
 
             if note is not None:
                 note_type, note_mess = note
-                note_elt = command_elt.addElement('note', content=note_mess)
-                note_elt['type'] = note_type
+                note_elt = command_elt.addElement("note", content=note_mess)
+                note_elt["type"] = note_type
 
             if payload is not None:
                 command_elt.addChild(payload)
@@ -181,54 +213,116 @@
 
     def onRequest(self, command_elt, requestor, action, session_id):
         if not self.isAuthorised(requestor):
-            return self._sendError(XEP_0050.ERROR.FORBIDDEN, session_id, command_elt.parent)
+            return self._sendError(
+                XEP_0050.ERROR.FORBIDDEN, session_id, command_elt.parent
+            )
         if session_id:
             try:
                 session_data = self.sessions[session_id]
             except KeyError:
-                return self._sendError(XEP_0050.ERROR.SESSION_EXPIRED, session_id, command_elt.parent)
-            if session_data['requestor'] != requestor:
-                return self._sendError(XEP_0050.ERROR.FORBIDDEN, session_id, command_elt.parent)
+                return self._sendError(
+                    XEP_0050.ERROR.SESSION_EXPIRED, session_id, command_elt.parent
+                )
+            if session_data["requestor"] != requestor:
+                return self._sendError(
+                    XEP_0050.ERROR.FORBIDDEN, session_id, command_elt.parent
+                )
         else:
             session_id, session_data = self.sessions.newSession()
-            session_data['requestor'] = requestor
+            session_data["requestor"] = requestor
         if action == XEP_0050.ACTION.CANCEL:
             d = defer.succeed((None, XEP_0050.STATUS.CANCELED, None, None))
         else:
-            d = defer.maybeDeferred(self.callback, command_elt, session_data, action, self.node, self.client.profile)
+            d = defer.maybeDeferred(
+                self.callback,
+                command_elt,
+                session_data,
+                action,
+                self.node,
+                self.client.profile,
+            )
         d.addCallback(self._sendAnswer, session_id, command_elt.parent)
-        d.addErrback(lambda failure, request: self._sendError(failure.value.callback_error, session_id, request), command_elt.parent)
+        d.addErrback(
+            lambda failure, request: self._sendError(
+                failure.value.callback_error, session_id, request
+            ),
+            command_elt.parent,
+        )
 
 
 class XEP_0050(object):
-    STATUS = namedtuple('Status', ('EXECUTING', 'COMPLETED', 'CANCELED'))('executing', 'completed', 'canceled')
-    ACTION = namedtuple('Action', ('EXECUTE', 'CANCEL', 'NEXT', 'PREV'))('execute', 'cancel', 'next', 'prev')
-    NOTE = namedtuple('Note', ('INFO','WARN','ERROR'))('info','warn','error')
-    ERROR = namedtuple('Error', ('MALFORMED_ACTION', 'BAD_ACTION', 'BAD_LOCALE', 'BAD_PAYLOAD', 'BAD_SESSIONID', 'SESSION_EXPIRED',
-                                 'FORBIDDEN', 'ITEM_NOT_FOUND', 'FEATURE_NOT_IMPLEMENTED', 'INTERNAL'))(('bad-request', 'malformed-action'),
-                                 ('bad-request', 'bad-action'), ('bad-request', 'bad-locale'), ('bad-request','bad-payload'),
-                                 ('bad-request','bad-sessionid'), ('not-allowed','session-expired'), ('forbidden', None),
-                                 ('item-not-found', None), ('feature-not-implemented', None), ('internal-server-error', None)) # XEP-0050 §4.4 Table 5
+    STATUS = namedtuple("Status", ("EXECUTING", "COMPLETED", "CANCELED"))(
+        "executing", "completed", "canceled"
+    )
+    ACTION = namedtuple("Action", ("EXECUTE", "CANCEL", "NEXT", "PREV"))(
+        "execute", "cancel", "next", "prev"
+    )
+    NOTE = namedtuple("Note", ("INFO", "WARN", "ERROR"))("info", "warn", "error")
+    ERROR = namedtuple(
+        "Error",
+        (
+            "MALFORMED_ACTION",
+            "BAD_ACTION",
+            "BAD_LOCALE",
+            "BAD_PAYLOAD",
+            "BAD_SESSIONID",
+            "SESSION_EXPIRED",
+            "FORBIDDEN",
+            "ITEM_NOT_FOUND",
+            "FEATURE_NOT_IMPLEMENTED",
+            "INTERNAL",
+        ),
+    )(
+        ("bad-request", "malformed-action"),
+        ("bad-request", "bad-action"),
+        ("bad-request", "bad-locale"),
+        ("bad-request", "bad-payload"),
+        ("bad-request", "bad-sessionid"),
+        ("not-allowed", "session-expired"),
+        ("forbidden", None),
+        ("item-not-found", None),
+        ("feature-not-implemented", None),
+        ("internal-server-error", None),
+    )  # XEP-0050 §4.4 Table 5
 
     def __init__(self, host):
         log.info(_("plugin XEP-0050 initialization"))
         self.host = host
         self.requesting = Sessions()
         self.answering = {}
-        host.bridge.addMethod("adHocRun", ".plugin", in_sign='sss', out_sign='s',
-                              method=self._run,
-                              async=True)
-        host.bridge.addMethod("adHocList", ".plugin", in_sign='ss', out_sign='s',
-                              method=self._list,
-                              async=True)
-        self.__requesting_id = host.registerCallback(self._requestingEntity, with_data=True)
-        host.importMenu((D_("Service"), D_("Commands")), self._commandsMenu, security_limit=2, help_string=D_("Execute ad-hoc commands"))
+        host.bridge.addMethod(
+            "adHocRun",
+            ".plugin",
+            in_sign="sss",
+            out_sign="s",
+            method=self._run,
+            async=True,
+        )
+        host.bridge.addMethod(
+            "adHocList",
+            ".plugin",
+            in_sign="ss",
+            out_sign="s",
+            method=self._list,
+            async=True,
+        )
+        self.__requesting_id = host.registerCallback(
+            self._requestingEntity, with_data=True
+        )
+        host.importMenu(
+            (D_("Service"), D_("Commands")),
+            self._commandsMenu,
+            security_limit=2,
+            help_string=D_("Execute ad-hoc commands"),
+        )
 
     def getHandler(self, client):
         return XEP_0050_handler(self)
 
     def profileConnected(self, client):
-        self.addAdHocCommand(self._statusCallback, _("Status"), profile_key=client.profile)
+        self.addAdHocCommand(
+            self._statusCallback, _("Status"), profile_key=client.profile
+        )
 
     def profileDisconnected(self, client):
         try:
@@ -242,7 +336,7 @@
         form_ui = xml_tools.XMLUI("form", submit_id=self.__requesting_id)
 
         if not no_instructions:
-            form_ui.addText(_("Please select a command"), 'instructions')
+            form_ui.addText(_("Please select a command"), "instructions")
 
         options = [(item.nodeIdentifier, item.name) for item in items]
         form_ui.addList("node", options)
@@ -254,12 +348,12 @@
         @param type_: note type (see XEP-0050 §4.3)
         @return: a C.XMLUI_DATA_LVL_* constant
         """
-        if type_ == 'error':
+        if type_ == "error":
             return C.XMLUI_DATA_LVL_ERROR
-        elif type_ == 'warn':
+        elif type_ == "warn":
             return C.XMLUI_DATA_LVL_WARNING
         else:
-            if type_ != 'info':
+            if type_ != "info":
                 log.warning(_(u"Invalid note type [%s], using info") % type_)
             return C.XMLUI_DATA_LVL_INFO
 
@@ -269,10 +363,11 @@
         @param notes (list): list of tuple (level, message)
         @return: list of messages
         """
-        lvl_map = {C.XMLUI_DATA_LVL_INFO: '',
-                   C.XMLUI_DATA_LVL_WARNING: "%s: " % _("WARNING"),
-                   C.XMLUI_DATA_LVL_ERROR: "%s: " % _("ERROR")
-                  }
+        lvl_map = {
+            C.XMLUI_DATA_LVL_INFO: "",
+            C.XMLUI_DATA_LVL_WARNING: "%s: " % _("WARNING"),
+            C.XMLUI_DATA_LVL_ERROR: "%s: " % _("ERROR"),
+        }
         return [u"%s%s" % (lvl_map[lvl], msg) for lvl, msg in notes]
 
     def _commandsAnswer2XMLUI(self, iq_elt, session_id, session_data):
@@ -284,7 +379,7 @@
 
         """
         command_elt = iq_elt.elements(NS_COMMANDS, "command").next()
-        status = command_elt.getAttribute('status', XEP_0050.STATUS.EXECUTING)
+        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]
@@ -292,27 +387,35 @@
                 session_id = None
             else:
                 return None
-        remote_session_id = command_elt.getAttribute('sessionid')
+        remote_session_id = command_elt.getAttribute("sessionid")
         if remote_session_id:
-            session_data['remote_id'] = remote_session_id
+            session_data["remote_id"] = remote_session_id
         notes = []
-        for note_elt in command_elt.elements(NS_COMMANDS, 'note'):
-            notes.append((self._getDataLvl(note_elt.getAttribute('type', 'info')),
-                          unicode(note_elt)))
-        for data_elt in command_elt.elements(data_form.NS_X_DATA, 'x'):
-            if data_elt['type'] in ('form', 'result'):
+        for note_elt in command_elt.elements(NS_COMMANDS, "note"):
+            notes.append(
+                (
+                    self._getDataLvl(note_elt.getAttribute("type", "info")),
+                    unicode(note_elt),
+                )
+            )
+        for data_elt in command_elt.elements(data_form.NS_X_DATA, "x"):
+            if data_elt["type"] in ("form", "result"):
                 break
         else:
             # no matching data element found
             if status != XEP_0050.STATUS.COMPLETED:
-                log.warning(_("No known payload found in ad-hoc command result, aborting"))
+                log.warning(
+                    _("No known payload found in ad-hoc command result, aborting")
+                )
                 del self.requesting[session_id]
-                return xml_tools.XMLUI(C.XMLUI_DIALOG,
-                                       dialog_opt = {C.XMLUI_DATA_TYPE: C.XMLUI_DIALOG_NOTE,
-                                                     C.XMLUI_DATA_MESS: _("No payload found"),
-                                                     C.XMLUI_DATA_LVL: C.XMLUI_DATA_LVL_ERROR,
-                                                    }
-                                      )
+                return xml_tools.XMLUI(
+                    C.XMLUI_DIALOG,
+                    dialog_opt={
+                        C.XMLUI_DATA_TYPE: C.XMLUI_DIALOG_NOTE,
+                        C.XMLUI_DATA_MESS: _("No payload found"),
+                        C.XMLUI_DATA_LVL: C.XMLUI_DATA_LVL_ERROR,
+                    },
+                )
             if not notes:
                 # the status is completed, and we have no note to show
                 return None
@@ -321,13 +424,14 @@
             # if we have more, we show a dialog with "info" level, and all notes merged
             dlg_level = notes[0][0] if len(notes) == 1 else C.XMLUI_DATA_LVL_INFO
             return xml_tools.XMLUI(
-                                   C.XMLUI_DIALOG,
-                                   dialog_opt = {C.XMLUI_DATA_TYPE: C.XMLUI_DIALOG_NOTE,
-                                                 C.XMLUI_DATA_MESS: u'\n'.join(self._mergeNotes(notes)),
-                                                 C.XMLUI_DATA_LVL: dlg_level,
-                                                },
-                                   session_id = session_id
-                                  )
+                C.XMLUI_DIALOG,
+                dialog_opt={
+                    C.XMLUI_DATA_TYPE: C.XMLUI_DIALOG_NOTE,
+                    C.XMLUI_DATA_MESS: u"\n".join(self._mergeNotes(notes)),
+                    C.XMLUI_DATA_LVL: dlg_level,
+                },
+                session_id=session_id,
+            )
 
         if session_id is None:
             return xml_tools.dataFormEltResult2XMLUI(data_elt)
@@ -338,8 +442,8 @@
 
     def _requestingEntity(self, data, profile):
         def serialise(ret_data):
-            if 'xmlui' in ret_data:
-                ret_data['xmlui'] = ret_data['xmlui'].toXml()
+            if "xmlui" in ret_data:
+                ret_data["xmlui"] = ret_data["xmlui"].toXml()
             return ret_data
 
         d = self.requestingEntity(data, profile)
@@ -354,7 +458,7 @@
         @return: callback dict result (with "xmlui" corresponding to the answering dialog, or empty if it's finished without error)
 
         """
-        if C.bool(data.get('cancelled', C.BOOL_FALSE)):
+        if C.bool(data.get("cancelled", C.BOOL_FALSE)):
             return defer.succeed({})
         client = self.host.getClient(profile)
         # TODO: cancel, prev and next are not managed
@@ -363,49 +467,53 @@
         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=client.profile)
-            entity = jid.JID(data[xml_tools.SAT_FORM_PREFIX+'jid'])
-            session_data['jid'] = entity
+            entity = jid.JID(data[xml_tools.SAT_FORM_PREFIX + "jid"])
+            session_data["jid"] = entity
             d = self.list(client, entity)
 
             def sendItems(xmlui):
-                xmlui.session_id = session_id # we need to keep track of the session
-                return {'xmlui': xmlui}
+                xmlui.session_id = session_id  # we need to keep track of the session
+                return {"xmlui": xmlui}
 
             d.addCallback(sendItems)
         else:
             # we have started a several forms sessions
             try:
-                session_data = self.requesting.profileGet(data["session_id"], client.profile)
+                session_data = self.requesting.profileGet(
+                    data["session_id"], client.profile
+                )
             except KeyError:
-                log.warning ("session id doesn't exist, session has probably expired")
+                log.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']
+            entity = session_data["jid"]
             try:
-                session_data['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
-                session_data['node'] = data[xml_tools.SAT_FORM_PREFIX+'node']
+                session_data["node"] = data[xml_tools.SAT_FORM_PREFIX + "node"]
 
             # we request execute node's command
-            iq_elt = compat.IQ(client.xmlstream, 'set')
-            iq_elt['to'] = entity.full()
+            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
+            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']
+                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
+            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} if xmlui is not None else {})
+            d.addCallback(lambda xmlui: {"xmlui": xmlui} if xmlui is not None else {})
 
         return d
 
@@ -415,23 +523,27 @@
 
         """
         form_ui = xml_tools.XMLUI("form", submit_id=self.__requesting_id)
-        form_ui.addText(_("Please enter target jid"), 'instructions')
+        form_ui.addText(_("Please enter target jid"), "instructions")
         form_ui.changeContainer("pairs")
         form_ui.addLabel("jid")
         form_ui.addString("jid", value=self.host.getClient(profile).jid.host)
-        return {'xmlui': form_ui.toXml()}
+        return {"xmlui": 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 = 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()]
-            field = data_form.Field('list-single', 'show', options=show_options, required=True)
+            form = data_form.Form("form", title=_("status selection"))
+            show_options = [
+                data_form.Option(name, label) for name, label in SHOWS.items()
+            ]
+            field = data_form.Field(
+                "list-single", "show", options=show_options, required=True
+            )
             form.addField(field)
 
             payload = form.toElement()
@@ -440,9 +552,9 @@
         elif len(actions) == 2:
             # we should have the answer here
             try:
-                x_elt = command_elt.elements(data_form.NS_X_DATA,'x').next()
+                x_elt = command_elt.elements(data_form.NS_X_DATA, "x").next()
                 answer_form = data_form.Form.fromElement(x_elt)
-                show = answer_form['show']
+                show = answer_form["show"]
             except (KeyError, StopIteration):
                 raise AdHocError(XEP_0050.ERROR.BAD_PAYLOAD)
             if show not in SHOWS:
@@ -453,8 +565,8 @@
                 self.host.setPresence(show=show, profile_key=profile)
 
             # job done, we can end the session
-            form = data_form.Form('form', title=_(u'Updated'))
-            form.addField(data_form.Field('fixed', u'Status updated'))
+            form = data_form.Form("form", title=_(u"Updated"))
+            form.addField(data_form.Field("fixed", u"Status updated"))
             status = XEP_0050.STATUS.COMPLETED
             payload = None
             note = (self.NOTE.INFO, _(u"Status updated"))
@@ -463,7 +575,7 @@
 
         return (payload, status, None, note)
 
-    def _run(self, service_jid_s='', node='', profile_key=C.PROF_KEY_NONE):
+    def _run(self, service_jid_s="", node="", profile_key=C.PROF_KEY_NONE):
         client = self.host.getClient(profile_key)
         service_jid = jid.JID(service_jid_s) if service_jid_s else None
         d = self.run(client, service_jid, node or None)
@@ -483,13 +595,15 @@
         if service_jid is None:
             service_jid = jid.JID(client.jid.host)
         session_id, session_data = self.requesting.newSession(profile=client.profile)
-        session_data['jid'] = service_jid
+        session_data["jid"] = service_jid
         if node is None:
             xmlui = yield self.list(client, service_jid)
         else:
-            session_data['node'] = node
-            cb_data = yield self.requestingEntity({'session_id': session_id}, client.profile)
-            xmlui = cb_data['xmlui']
+            session_data["node"] = node
+            cb_data = yield self.requestingEntity(
+                {"session_id": session_id}, client.profile
+            )
+            xmlui = cb_data["xmlui"]
 
         xmlui.session_id = session_id
         defer.returnValue(xmlui)
@@ -512,8 +626,20 @@
         d.addCallback(self._items2XMLUI, no_instructions)
         return d
 
-    def addAdHocCommand(self, callback, label, node=None, features=None, timeout=600, allowed_jids=None, allowed_groups=None,
-                        allowed_magics=None, forbidden_jids=None, forbidden_groups=None, profile_key=C.PROF_KEY_NONE):
+    def addAdHocCommand(
+        self,
+        callback,
+        label,
+        node=None,
+        features=None,
+        timeout=600,
+        allowed_jids=None,
+        allowed_groups=None,
+        allowed_magics=None,
+        forbidden_jids=None,
+        forbidden_groups=None,
+        profile_key=C.PROF_KEY_NONE,
+    ):
         """Add an ad-hoc command for the current profile
 
         @param callback: method associated with this ad-hoc command which return the payload data (see AdHocCommand._sendAnswer), can return a deferred
@@ -534,7 +660,7 @@
         # FIXME: "@ALL@" for profile_key seems useless and dangerous
 
         if node is None:
-            node = "%s_%s" % ('COMMANDS', uuid4())
+            node = "%s_%s" % ("COMMANDS", uuid4())
 
         if features is None:
             features = [data_form.NS_X_DATA]
@@ -544,30 +670,46 @@
         if allowed_groups is None:
             allowed_groups = []
         if allowed_magics is None:
-            allowed_magics = ['@PROFILE_BAREJID@']
+            allowed_magics = ["@PROFILE_BAREJID@"]
         if forbidden_jids is None:
             forbidden_jids = []
         if forbidden_groups is None:
             forbidden_groups = []
 
         for client in self.host.getClients(profile_key):
-            #TODO: manage newly created/removed profiles
-            _allowed_jids = (allowed_jids + [client.jid.userhostJID()]) if '@PROFILE_BAREJID@' in allowed_magics else allowed_jids
-            ad_hoc_command = AdHocCommand(self, callback, label, node, features, timeout, _allowed_jids,
-                                          allowed_groups, allowed_magics, forbidden_jids, forbidden_groups, client)
+            # TODO: manage newly created/removed profiles
+            _allowed_jids = (
+                (allowed_jids + [client.jid.userhostJID()])
+                if "@PROFILE_BAREJID@" in allowed_magics
+                else allowed_jids
+            )
+            ad_hoc_command = AdHocCommand(
+                self,
+                callback,
+                label,
+                node,
+                features,
+                timeout,
+                _allowed_jids,
+                allowed_groups,
+                allowed_magics,
+                forbidden_jids,
+                forbidden_groups,
+                client,
+            )
             ad_hoc_command.setHandlerParent(client)
             profile_commands = self.answering.setdefault(client.profile, {})
             profile_commands[node] = ad_hoc_command
 
     def onCmdRequest(self, request, profile):
         request.handled = True
-        requestor = jid.JID(request['from'])
-        command_elt = request.elements(NS_COMMANDS, 'command').next()
-        action = command_elt.getAttribute('action', self.ACTION.EXECUTE)
-        node = command_elt.getAttribute('node')
+        requestor = jid.JID(request["from"])
+        command_elt = request.elements(NS_COMMANDS, "command").next()
+        action = command_elt.getAttribute("action", self.ACTION.EXECUTE)
+        node = command_elt.getAttribute("node")
         if not node:
             raise exceptions.DataError
-        sessionid = command_elt.getAttribute('sessionid')
+        sessionid = command_elt.getAttribute("sessionid")
         try:
             command = self.answering[profile][node]
         except KeyError:
@@ -582,18 +724,26 @@
         self.plugin_parent = plugin_parent
 
     def connectionInitialized(self):
-        self.xmlstream.addObserver(CMD_REQUEST, self.plugin_parent.onCmdRequest, profile=self.parent.profile)
+        self.xmlstream.addObserver(
+            CMD_REQUEST, self.plugin_parent.onCmdRequest, profile=self.parent.profile
+        )
 
-    def getDiscoInfo(self, requestor, target, nodeIdentifier=''):
+    def getDiscoInfo(self, requestor, target, nodeIdentifier=""):
         identities = []
-        if nodeIdentifier == NS_COMMANDS and self.plugin_parent.answering.get(self.parent.profile): # we only add the identity if we have registred commands
+        if nodeIdentifier == NS_COMMANDS and self.plugin_parent.answering.get(
+            self.parent.profile
+        ):  # we only add the identity if we have registred commands
             identities.append(ID_CMD_LIST)
         return [disco.DiscoFeature(NS_COMMANDS)] + identities
 
-    def getDiscoItems(self, requestor, target, nodeIdentifier=''):
+    def getDiscoItems(self, requestor, target, nodeIdentifier=""):
         ret = []
         if nodeIdentifier == NS_COMMANDS:
-            for command in self.plugin_parent.answering.get(self.parent.profile,{}).values():
+            for command in self.plugin_parent.answering.get(
+                self.parent.profile, {}
+            ).values():
                 if command.isAuthorised(requestor):
-                    ret.append(disco.DiscoItem(self.parent.jid, command.node, command.getName())) #TODO: manage name language
+                    ret.append(
+                        disco.DiscoItem(self.parent.jid, command.node, command.getName())
+                    )  # TODO: manage name language
         return ret