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)
This commit is contained in:
Goffi 2017-10-01 12:21:21 +02:00
commit 499f2525cc

View file

@ -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 @@ class PubsubSchema(object):
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 @@ class PubsubSchema(object):
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 @@ class PubsubSchema(object):
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 @@ class PubsubSchema(object):
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)