diff src/plugins/plugin_exp_pubsub_schema.py @ 2363:41c7717b52cd

plugin PubSub Schema: schema helper methods: 3 new methods are introduced by this patch: - psSchemaUIGet to get XMLUI from node schema - psItemsFormGet to get items as XMLUI when we know they are data forms - psItemFormSend to set item from values when we know it's a node with schema. For this method, schema can be specified, and if it is not it will be retrieved from node (which imply an additional XMPP request)
author Goffi <goffi@goffi.org>
date Sun, 01 Oct 2017 12:21:21 +0200
parents 388226e9c3ff
children 2268df8c99bf
line wrap: on
line diff
--- a/src/plugins/plugin_exp_pubsub_schema.py	Sun Sep 24 16:39:36 2017 +0200
+++ b/src/plugins/plugin_exp_pubsub_schema.py	Sun Oct 01 12:21:21 2017 +0200
@@ -20,8 +20,10 @@
 from sat.core.i18n import _
 from sat.core import exceptions
 from sat.core.constants import Const as C
+from sat.tools import xml_tools
 from twisted.words.protocols.jabber import jid
 from twisted.words.protocols.jabber.xmlstream import XMPPHandler
+from twisted.internet import defer
 from sat.core.log import getLogger
 log = getLogger(__name__)
 from wokkel import disco, iwokkel
@@ -49,6 +51,7 @@
     def __init__(self, host):
         log.info(_(u"PubSub Schema initialization"))
         self.host = host
+        self._p = self.host.plugins["XEP-0060"]
         host.bridge.addMethod("psSchemaGet", ".plugin",
                               in_sign='sss', out_sign='s',
                               method=self._getSchema,
@@ -59,6 +62,19 @@
                               method=self._setSchema,
                               async=True
                               )
+        host.bridge.addMethod("psSchemaUIGet", ".plugin",
+                              in_sign='sss', out_sign='s',
+                              method=self._getUISchema,
+                              async=True
+                              )
+        host.bridge.addMethod("psItemsFormGet", ".plugin",
+                              in_sign='ssssiassa{ss}s', out_sign='(asa{ss})',
+                              method=self._getDataFormItems,
+                              async=True)
+        host.bridge.addMethod("psItemFormSend", ".plugin",
+                              in_sign='ssa{sas}ssa{ss}s', out_sign='s',
+                              method=self._sendDataFormItem,
+                              async=True)
 
     def getHandler(self, client):
         return SchemaHandler()
@@ -108,6 +124,47 @@
         d.addCallback(self._getSchemaCb)
         return d
 
+    @defer.inlineCallbacks
+    def getSchemaForm(self, client, service, nodeIdentifier, schema=None, form_type='form'):
+        """get data form from node's schema
+
+        @param service(None, jid.JID): PubSub service
+        @param nodeIdentifier(unicode): node
+        @param schema(domish.Element, None): node schema
+            if None, it will be retrieved from node (imply one additional XMPP request)
+        @return(data_form.Form): data form
+        """
+        if schema is None:
+            log.debug(_(u"unspecified schema, we need to request it"))
+            schema = yield self.getSchema(client, service, nodeIdentifier)
+            if schema is None:
+                raise exceptions.DataError(_(u"no schema specified, and this node has no schema either, we can't construct the data form"))
+
+        try:
+            form = data_form.Form.fromElement(schema)
+        except data_form.Error as e:
+            raise exceptions.DataError(_(u"Invalid Schema: {msg}").format(
+                msg = e))
+        form.formType = form_type
+        defer.returnValue(form)
+
+    def schema2XMLUI(self, schema_elt):
+        form = data_form.Form.fromElement(schema_elt)
+        xmlui = xml_tools.dataForm2XMLUI(form, '')
+        return xmlui
+
+    def _getUISchema(self, service, nodeIdentifier, profile_key=C.PROF_KEY_NONE):
+        client = self.host.getClient(profile_key)
+        service = None if not service else jid.JID(service)
+        d = self.getUISchema(client, service, nodeIdentifier)
+        d.addCallback(lambda xmlui: xmlui.toXml())
+        return d
+
+    def getUISchema(self, client, service, nodeIdentifier):
+        d = self.getSchema(client, service, nodeIdentifier)
+        d.addCallback(self.schema2XMLUI)
+        return d
+
     def _setSchema(self, service, nodeIdentifier, schema, profile_key=C.PROF_KEY_NONE):
         client = self.host.getClient(profile_key)
         service = None if not service else jid.JID(service)
@@ -130,6 +187,87 @@
             schema_elt.addChild(schema)
         return iq_elt.send()
 
+    def _getDataFormItems(self, form_ns='', service='', node='', schema='', max_items=10, item_ids=None, sub_id=None, extra_dict=None, profile_key=C.PROF_KEY_NONE):
+        client = self.host.getClient(profile_key)
+        service = jid.JID(service) if service else None
+        if schema:
+            schema = generic.parseXml(schema.encode('utf-8'))
+        else:
+            schema = None
+        max_items = None if max_items == C.NO_LIMIT else max_items
+        extra = self._p.parseExtra(extra_dict)
+        d = self.getDataFormItems(client, form_ns or None, service, node or None, schema, max_items or None, item_ids, sub_id or None, extra.rsm_request, extra.extra)
+        d.addCallback(self._p.serItemsData)
+        return d
+
+    @defer.inlineCallbacks
+    def getDataFormItems(self, client, form_ns, service, nodeIdentifier, schema=None, max_items=None, item_ids=None, sub_id=None, rsm_request=None, extra=None):
+        """Get items known as being data forms, and convert them to XMLUI
+
+        @param form_ns (unicode, None): namespace of the form
+            None to accept everything, even if form has no namespace
+        @param schema(domish.Element, None): schema of the node if known
+            if None, it will be retrieved from node
+        other parameters as the same as for [getItems]
+        @return (list[unicode]): XMLUI of the forms
+            if an item is invalid (not corresponding to form_ns or not a data_form)
+            it will be skipped
+        """
+        # we need the initial form to get options of fields when suitable
+        schema_form = yield self.getSchemaForm(client, service, nodeIdentifier, schema, form_type='result')
+        items_data = yield self._p.getItems(client, service, nodeIdentifier, max_items, item_ids, sub_id, rsm_request, extra)
+        items, metadata = items_data
+        items_xmlui = []
+        for item_elt in items:
+            for x_elt in item_elt.elements((data_form.NS_X_DATA, u'x')):
+                form = data_form.Form.fromElement(x_elt)
+                if form_ns and form.formNamespace != form_ns:
+                    continue
+                xmlui = xml_tools.dataFormResult2XMLUI(form, schema_form)
+                xmlui.addLabel('id')
+                xmlui.addText(item_elt['id'], name='_id')
+                items_xmlui.append(xmlui)
+                break
+        defer.returnValue((items_xmlui, metadata))
+
+
+    def _sendDataFormItem(self, service, nodeIdentifier, values, schema=None, item_id=None, extra=None, profile_key=C.PROF_KEY_NONE):
+        client = self.host.getClient(profile_key)
+        service = None if not service else jid.JID(service)
+        if schema:
+            schema = generic.parseXml(schema.encode('utf-8'))
+        else:
+            schema = None
+        d = self.sendDataFormItem(client, service, nodeIdentifier, values, schema, item_id or None, extra)
+        d.addCallback(lambda ret: ret or u'')
+        return d
+
+    @defer.inlineCallbacks
+    def sendDataFormItem(self, client, service, nodeIdentifier, values, schema=None, item_id=None, extra=None):
+        """Publish an item as a dataform when we know that there is a schema
+
+        @param values(dict[unicode, list[unicode]]): values set for the form
+        @param schema(unicode, None): data schema
+            None to retrieve data schema from node (need to do a additional XMPP call)
+            Schema is need to construct data form to publish
+        other parameters as the same as for [self._p.sendItem]
+        """
+        form = yield self.getSchemaForm(client, service, nodeIdentifier, schema, form_type='submit')
+
+        for name, values_list in values.iteritems():
+            try:
+                field = form.fields[name]
+            except KeyError:
+                log.warning(_(u"field {name} doesn't exist, ignoring it").format(name=name))
+                continue
+            if field.fieldType == 'boolean':
+                values_list = [C.bool(v) for v in values_list]
+            elif 'jid' in field.fieldType:
+                values_list = [jid.JID(v) for v in values_list]
+            field.values = values_list
+
+        yield self._p.sendItem(client, service, nodeIdentifier, form.toElement(), item_id, extra)
+
 
 class SchemaHandler(XMPPHandler):
     implements(iwokkel.IDisco)