comparison 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
comparison
equal deleted inserted replaced
2362:3acbaf5c29f5 2363:41c7717b52cd
18 # along with this program. If not, see <http://www.gnu.org/licenses/>. 18 # along with this program. If not, see <http://www.gnu.org/licenses/>.
19 19
20 from sat.core.i18n import _ 20 from sat.core.i18n import _
21 from sat.core import exceptions 21 from sat.core import exceptions
22 from sat.core.constants import Const as C 22 from sat.core.constants import Const as C
23 from sat.tools import xml_tools
23 from twisted.words.protocols.jabber import jid 24 from twisted.words.protocols.jabber import jid
24 from twisted.words.protocols.jabber.xmlstream import XMPPHandler 25 from twisted.words.protocols.jabber.xmlstream import XMPPHandler
26 from twisted.internet import defer
25 from sat.core.log import getLogger 27 from sat.core.log import getLogger
26 log = getLogger(__name__) 28 log = getLogger(__name__)
27 from wokkel import disco, iwokkel 29 from wokkel import disco, iwokkel
28 from wokkel import data_form 30 from wokkel import data_form
29 from wokkel import generic 31 from wokkel import generic
47 class PubsubSchema(object): 49 class PubsubSchema(object):
48 50
49 def __init__(self, host): 51 def __init__(self, host):
50 log.info(_(u"PubSub Schema initialization")) 52 log.info(_(u"PubSub Schema initialization"))
51 self.host = host 53 self.host = host
54 self._p = self.host.plugins["XEP-0060"]
52 host.bridge.addMethod("psSchemaGet", ".plugin", 55 host.bridge.addMethod("psSchemaGet", ".plugin",
53 in_sign='sss', out_sign='s', 56 in_sign='sss', out_sign='s',
54 method=self._getSchema, 57 method=self._getSchema,
55 async=True 58 async=True
56 ) 59 )
57 host.bridge.addMethod("psSchemaSet", ".plugin", 60 host.bridge.addMethod("psSchemaSet", ".plugin",
58 in_sign='ssss', out_sign='', 61 in_sign='ssss', out_sign='',
59 method=self._setSchema, 62 method=self._setSchema,
60 async=True 63 async=True
61 ) 64 )
65 host.bridge.addMethod("psSchemaUIGet", ".plugin",
66 in_sign='sss', out_sign='s',
67 method=self._getUISchema,
68 async=True
69 )
70 host.bridge.addMethod("psItemsFormGet", ".plugin",
71 in_sign='ssssiassa{ss}s', out_sign='(asa{ss})',
72 method=self._getDataFormItems,
73 async=True)
74 host.bridge.addMethod("psItemFormSend", ".plugin",
75 in_sign='ssa{sas}ssa{ss}s', out_sign='s',
76 method=self._sendDataFormItem,
77 async=True)
62 78
63 def getHandler(self, client): 79 def getHandler(self, client):
64 return SchemaHandler() 80 return SchemaHandler()
65 81
66 def _getSchemaBridgeCb(self, schema_elt): 82 def _getSchemaBridgeCb(self, schema_elt):
106 schema_elt['node'] = nodeIdentifier 122 schema_elt['node'] = nodeIdentifier
107 d = iq_elt.send() 123 d = iq_elt.send()
108 d.addCallback(self._getSchemaCb) 124 d.addCallback(self._getSchemaCb)
109 return d 125 return d
110 126
127 @defer.inlineCallbacks
128 def getSchemaForm(self, client, service, nodeIdentifier, schema=None, form_type='form'):
129 """get data form from node's schema
130
131 @param service(None, jid.JID): PubSub service
132 @param nodeIdentifier(unicode): node
133 @param schema(domish.Element, None): node schema
134 if None, it will be retrieved from node (imply one additional XMPP request)
135 @return(data_form.Form): data form
136 """
137 if schema is None:
138 log.debug(_(u"unspecified schema, we need to request it"))
139 schema = yield self.getSchema(client, service, nodeIdentifier)
140 if schema is None:
141 raise exceptions.DataError(_(u"no schema specified, and this node has no schema either, we can't construct the data form"))
142
143 try:
144 form = data_form.Form.fromElement(schema)
145 except data_form.Error as e:
146 raise exceptions.DataError(_(u"Invalid Schema: {msg}").format(
147 msg = e))
148 form.formType = form_type
149 defer.returnValue(form)
150
151 def schema2XMLUI(self, schema_elt):
152 form = data_form.Form.fromElement(schema_elt)
153 xmlui = xml_tools.dataForm2XMLUI(form, '')
154 return xmlui
155
156 def _getUISchema(self, service, nodeIdentifier, profile_key=C.PROF_KEY_NONE):
157 client = self.host.getClient(profile_key)
158 service = None if not service else jid.JID(service)
159 d = self.getUISchema(client, service, nodeIdentifier)
160 d.addCallback(lambda xmlui: xmlui.toXml())
161 return d
162
163 def getUISchema(self, client, service, nodeIdentifier):
164 d = self.getSchema(client, service, nodeIdentifier)
165 d.addCallback(self.schema2XMLUI)
166 return d
167
111 def _setSchema(self, service, nodeIdentifier, schema, profile_key=C.PROF_KEY_NONE): 168 def _setSchema(self, service, nodeIdentifier, schema, profile_key=C.PROF_KEY_NONE):
112 client = self.host.getClient(profile_key) 169 client = self.host.getClient(profile_key)
113 service = None if not service else jid.JID(service) 170 service = None if not service else jid.JID(service)
114 schema = generic.parseXml(schema.encode('utf-8')) 171 schema = generic.parseXml(schema.encode('utf-8'))
115 return self.setSchema(client, service, nodeIdentifier, schema) 172 return self.setSchema(client, service, nodeIdentifier, schema)
128 schema_elt['node'] = nodeIdentifier 185 schema_elt['node'] = nodeIdentifier
129 if schema is not None: 186 if schema is not None:
130 schema_elt.addChild(schema) 187 schema_elt.addChild(schema)
131 return iq_elt.send() 188 return iq_elt.send()
132 189
190 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):
191 client = self.host.getClient(profile_key)
192 service = jid.JID(service) if service else None
193 if schema:
194 schema = generic.parseXml(schema.encode('utf-8'))
195 else:
196 schema = None
197 max_items = None if max_items == C.NO_LIMIT else max_items
198 extra = self._p.parseExtra(extra_dict)
199 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)
200 d.addCallback(self._p.serItemsData)
201 return d
202
203 @defer.inlineCallbacks
204 def getDataFormItems(self, client, form_ns, service, nodeIdentifier, schema=None, max_items=None, item_ids=None, sub_id=None, rsm_request=None, extra=None):
205 """Get items known as being data forms, and convert them to XMLUI
206
207 @param form_ns (unicode, None): namespace of the form
208 None to accept everything, even if form has no namespace
209 @param schema(domish.Element, None): schema of the node if known
210 if None, it will be retrieved from node
211 other parameters as the same as for [getItems]
212 @return (list[unicode]): XMLUI of the forms
213 if an item is invalid (not corresponding to form_ns or not a data_form)
214 it will be skipped
215 """
216 # we need the initial form to get options of fields when suitable
217 schema_form = yield self.getSchemaForm(client, service, nodeIdentifier, schema, form_type='result')
218 items_data = yield self._p.getItems(client, service, nodeIdentifier, max_items, item_ids, sub_id, rsm_request, extra)
219 items, metadata = items_data
220 items_xmlui = []
221 for item_elt in items:
222 for x_elt in item_elt.elements((data_form.NS_X_DATA, u'x')):
223 form = data_form.Form.fromElement(x_elt)
224 if form_ns and form.formNamespace != form_ns:
225 continue
226 xmlui = xml_tools.dataFormResult2XMLUI(form, schema_form)
227 xmlui.addLabel('id')
228 xmlui.addText(item_elt['id'], name='_id')
229 items_xmlui.append(xmlui)
230 break
231 defer.returnValue((items_xmlui, metadata))
232
233
234 def _sendDataFormItem(self, service, nodeIdentifier, values, schema=None, item_id=None, extra=None, profile_key=C.PROF_KEY_NONE):
235 client = self.host.getClient(profile_key)
236 service = None if not service else jid.JID(service)
237 if schema:
238 schema = generic.parseXml(schema.encode('utf-8'))
239 else:
240 schema = None
241 d = self.sendDataFormItem(client, service, nodeIdentifier, values, schema, item_id or None, extra)
242 d.addCallback(lambda ret: ret or u'')
243 return d
244
245 @defer.inlineCallbacks
246 def sendDataFormItem(self, client, service, nodeIdentifier, values, schema=None, item_id=None, extra=None):
247 """Publish an item as a dataform when we know that there is a schema
248
249 @param values(dict[unicode, list[unicode]]): values set for the form
250 @param schema(unicode, None): data schema
251 None to retrieve data schema from node (need to do a additional XMPP call)
252 Schema is need to construct data form to publish
253 other parameters as the same as for [self._p.sendItem]
254 """
255 form = yield self.getSchemaForm(client, service, nodeIdentifier, schema, form_type='submit')
256
257 for name, values_list in values.iteritems():
258 try:
259 field = form.fields[name]
260 except KeyError:
261 log.warning(_(u"field {name} doesn't exist, ignoring it").format(name=name))
262 continue
263 if field.fieldType == 'boolean':
264 values_list = [C.bool(v) for v in values_list]
265 elif 'jid' in field.fieldType:
266 values_list = [jid.JID(v) for v in values_list]
267 field.values = values_list
268
269 yield self._p.sendItem(client, service, nodeIdentifier, form.toElement(), item_id, extra)
270
133 271
134 class SchemaHandler(XMPPHandler): 272 class SchemaHandler(XMPPHandler):
135 implements(iwokkel.IDisco) 273 implements(iwokkel.IDisco)
136 274
137 def getDiscoInfo(self, requestor, service, nodeIdentifier=''): 275 def getDiscoInfo(self, requestor, service, nodeIdentifier=''):