comparison libervia/backend/plugins/plugin_xep_0346.py @ 4270:0d7bb4df2343

Reformatted code base using black.
author Goffi <goffi@goffi.org>
date Wed, 19 Jun 2024 18:44:57 +0200
parents 4b842c1fb686
children
comparison
equal deleted inserted replaced
4269:64a85ce8be70 4270:0d7bb4df2343
81 "ps_schema_ui_get", 81 "ps_schema_ui_get",
82 ".plugin", 82 ".plugin",
83 in_sign="sss", 83 in_sign="sss",
84 out_sign="s", 84 out_sign="s",
85 method=lambda service, nodeIdentifier, profile_key: self._get_ui_schema( 85 method=lambda service, nodeIdentifier, profile_key: self._get_ui_schema(
86 service, nodeIdentifier, default_node=None, profile_key=profile_key), 86 service, nodeIdentifier, default_node=None, profile_key=profile_key
87 ),
87 async_=True, 88 async_=True,
88 ) 89 )
89 host.bridge.add_method( 90 host.bridge.add_method(
90 "ps_schema_dict_get", 91 "ps_schema_dict_get",
91 ".plugin", 92 ".plugin",
136 return SchemaHandler() 137 return SchemaHandler()
137 138
138 def get_application_ns(self, namespace): 139 def get_application_ns(self, namespace):
139 """Retrieve application namespace, i.e. namespace without FDP prefix""" 140 """Retrieve application namespace, i.e. namespace without FDP prefix"""
140 if namespace.startswith(SUBMITTED_PREFIX): 141 if namespace.startswith(SUBMITTED_PREFIX):
141 namespace = namespace[len(SUBMITTED_PREFIX):] 142 namespace = namespace[len(SUBMITTED_PREFIX) :]
142 elif namespace.startswith(TEMPLATE_PREFIX): 143 elif namespace.startswith(TEMPLATE_PREFIX):
143 namespace = namespace[len(TEMPLATE_PREFIX):] 144 namespace = namespace[len(TEMPLATE_PREFIX) :]
144 return namespace 145 return namespace
145 146
146 def get_template_ns(self, namespace: str) -> str: 147 def get_template_ns(self, namespace: str) -> str:
147 """Returns node used for data template (i.e. schema)""" 148 """Returns node used for data template (i.e. schema)"""
148 app_ns = self.get_application_ns(namespace) 149 app_ns = self.get_application_ns(namespace)
175 """ 176 """
176 app_ns = self.get_application_ns(nodeIdentifier) 177 app_ns = self.get_application_ns(nodeIdentifier)
177 node_id = f"{TEMPLATE_PREFIX}{app_ns}" 178 node_id = f"{TEMPLATE_PREFIX}{app_ns}"
178 items_data = await self._p.get_items(client, service, node_id, max_items=1) 179 items_data = await self._p.get_items(client, service, node_id, max_items=1)
179 try: 180 try:
180 schema = next(items_data[0][0].elements(data_form.NS_X_DATA, 'x')) 181 schema = next(items_data[0][0].elements(data_form.NS_X_DATA, "x"))
181 except IndexError: 182 except IndexError:
182 schema = None 183 schema = None
183 except StopIteration: 184 except StopIteration:
184 log.warning( 185 log.warning(
185 f"No schema found in item of {service!r} at node {nodeIdentifier!r}: " 186 f"No schema found in item of {service!r} at node {nodeIdentifier!r}: "
186 f"\n{items_data[0][0].toXml()}" 187 f"\n{items_data[0][0].toXml()}"
187 ) 188 )
188 schema = None 189 schema = None
189 return schema 190 return schema
190 191
191 async def get_schema_form(self, client, service, nodeIdentifier, schema=None, 192 async def get_schema_form(
192 form_type="form", copy_form=True): 193 self,
194 client,
195 service,
196 nodeIdentifier,
197 schema=None,
198 form_type="form",
199 copy_form=True,
200 ):
193 """Get data form from node's schema 201 """Get data form from node's schema
194 202
195 @param service(None, jid.JID): PubSub service 203 @param service(None, jid.JID): PubSub service
196 @param nodeIdentifier(unicode): node 204 @param nodeIdentifier(unicode): node
197 @param schema(domish.Element, data_form.Form, None): node schema 205 @param schema(domish.Element, data_form.Form, None): node schema
217 if copy_form: 225 if copy_form:
218 # XXX: we don't use deepcopy as it will do an infinite loop if a 226 # XXX: we don't use deepcopy as it will do an infinite loop if a
219 # domish.Element is present in the form fields (happens for 227 # domish.Element is present in the form fields (happens for
220 # XEP-0315 data forms XML Element) 228 # XEP-0315 data forms XML Element)
221 schema = data_form.Form( 229 schema = data_form.Form(
222 formType = schema.formType, 230 formType=schema.formType,
223 title = schema.title, 231 title=schema.title,
224 instructions = schema.instructions[:], 232 instructions=schema.instructions[:],
225 formNamespace = schema.formNamespace, 233 formNamespace=schema.formNamespace,
226 fields = schema.fieldList, 234 fields=schema.fieldList,
227 ) 235 )
228 return schema 236 return schema
229 237
230 try: 238 try:
231 form = data_form.Form.fromElement(schema) 239 form = data_form.Form.fromElement(schema)
237 def schema_2_xmlui(self, schema_elt): 245 def schema_2_xmlui(self, schema_elt):
238 form = data_form.Form.fromElement(schema_elt) 246 form = data_form.Form.fromElement(schema_elt)
239 xmlui = xml_tools.data_form_2_xmlui(form, "") 247 xmlui = xml_tools.data_form_2_xmlui(form, "")
240 return xmlui 248 return xmlui
241 249
242 def _get_ui_schema(self, service, nodeIdentifier, default_node=None, 250 def _get_ui_schema(
243 profile_key=C.PROF_KEY_NONE): 251 self, service, nodeIdentifier, default_node=None, profile_key=C.PROF_KEY_NONE
252 ):
244 if not nodeIdentifier: 253 if not nodeIdentifier:
245 if not default_node: 254 if not default_node:
246 raise ValueError(_("nodeIndentifier needs to be set")) 255 raise ValueError(_("nodeIndentifier needs to be set"))
247 nodeIdentifier = default_node 256 nodeIdentifier = default_node
248 client = self.host.get_client(profile_key) 257 client = self.host.get_client(profile_key)
288 d = defer.ensureDeferred(self.get_schema_dict(client, service, nodeIdentifier)) 297 d = defer.ensureDeferred(self.get_schema_dict(client, service, nodeIdentifier))
289 d.addCallback(data_format.serialise) 298 d.addCallback(data_format.serialise)
290 return d 299 return d
291 300
292 async def get_schema_dict( 301 async def get_schema_dict(
293 self, 302 self, client: SatXMPPEntity, service: Optional[jid.JID], nodeIdentifier: str
294 client: SatXMPPEntity, 303 ) -> dict:
295 service: Optional[jid.JID],
296 nodeIdentifier: str) -> dict:
297 """Retrieve a node schema and format it a simple dictionary 304 """Retrieve a node schema and format it a simple dictionary
298 305
299 The dictionary is made so it can be easily serialisable 306 The dictionary is made so it can be easily serialisable
300 """ 307 """
301 schema_form = await self.get_schema_form(client, service, nodeIdentifier) 308 schema_form = await self.get_schema_form(client, service, nodeIdentifier)
302 return xml_tools.data_form_2_data_dict(schema_form) 309 return xml_tools.data_form_2_data_dict(schema_form)
303 310
304 def _get_data_form_items(self, form_ns="", service="", node="", schema="", max_items=10, 311 def _get_data_form_items(
305 item_ids=None, sub_id=None, extra="", 312 self,
306 profile_key=C.PROF_KEY_NONE): 313 form_ns="",
314 service="",
315 node="",
316 schema="",
317 max_items=10,
318 item_ids=None,
319 sub_id=None,
320 extra="",
321 profile_key=C.PROF_KEY_NONE,
322 ):
307 client = self.host.get_client(profile_key) 323 client = self.host.get_client(profile_key)
308 service = jid.JID(service) if service else None 324 service = jid.JID(service) if service else None
309 if not node: 325 if not node:
310 raise exceptions.DataError(_("empty node is not allowed")) 326 raise exceptions.DataError(_("empty node is not allowed"))
311 if schema: 327 if schema:
329 ) 345 )
330 ) 346 )
331 d.addCallback(self._p.trans_items_data) 347 d.addCallback(self._p.trans_items_data)
332 return d 348 return d
333 349
334 async def get_data_form_items(self, client, service, nodeIdentifier, schema=None, 350 async def get_data_form_items(
335 max_items=None, item_ids=None, sub_id=None, rsm_request=None, 351 self,
336 extra=None, default_node=None, form_ns=None, filters=None): 352 client,
353 service,
354 nodeIdentifier,
355 schema=None,
356 max_items=None,
357 item_ids=None,
358 sub_id=None,
359 rsm_request=None,
360 extra=None,
361 default_node=None,
362 form_ns=None,
363 filters=None,
364 ):
337 """Get items known as being data forms, and convert them to XMLUI 365 """Get items known as being data forms, and convert them to XMLUI
338 366
339 @param schema(domish.Element, data_form.Form, None): schema of the node if known 367 @param schema(domish.Element, data_form.Form, None): schema of the node if known
340 if None, it will be retrieved from node 368 if None, it will be retrieved from node
341 @param default_node(unicode): node to use if nodeIdentifier is None or empty 369 @param default_node(unicode): node to use if nodeIdentifier is None or empty
384 ("label", "id"), 412 ("label", "id"),
385 ("text", item_elt["id"], "id"), 413 ("text", item_elt["id"], "id"),
386 ("label", "publisher"), 414 ("label", "publisher"),
387 ] 415 ]
388 try: 416 try:
389 publisher = jid.JID(item_elt['publisher']) 417 publisher = jid.JID(item_elt["publisher"])
390 except (KeyError, jid.InvalidFormat): 418 except (KeyError, jid.InvalidFormat):
391 pass 419 pass
392 else: 420 else:
393 prepend.append(("jid", publisher, "publisher")) 421 prepend.append(("jid", publisher, "publisher"))
394 xmlui = xml_tools.data_form_result_2_xmlui( 422 xmlui = xml_tools.data_form_result_2_xmlui(
402 ) 430 )
403 items_xmlui.append(xmlui) 431 items_xmlui.append(xmlui)
404 break 432 break
405 return (items_xmlui, metadata) 433 return (items_xmlui, metadata)
406 434
407 def _send_data_form_item(self, service, nodeIdentifier, values, schema=None, 435 def _send_data_form_item(
408 item_id=None, extra=None, profile_key=C.PROF_KEY_NONE): 436 self,
437 service,
438 nodeIdentifier,
439 values,
440 schema=None,
441 item_id=None,
442 extra=None,
443 profile_key=C.PROF_KEY_NONE,
444 ):
409 client = self.host.get_client(profile_key) 445 client = self.host.get_client(profile_key)
410 service = None if not service else jid.JID(service) 446 service = None if not service else jid.JID(service)
411 if schema: 447 if schema:
412 schema = generic.parseXml(schema.encode("utf-8")) 448 schema = generic.parseXml(schema.encode("utf-8"))
413 else: 449 else:
426 ) 462 )
427 d.addCallback(lambda ret: ret or "") 463 d.addCallback(lambda ret: ret or "")
428 return d 464 return d
429 465
430 async def send_data_form_item( 466 async def send_data_form_item(
431 self, client, service, nodeIdentifier, values, schema=None, item_id=None, 467 self,
432 extra=None, deserialise=False): 468 client,
469 service,
470 nodeIdentifier,
471 values,
472 schema=None,
473 item_id=None,
474 extra=None,
475 deserialise=False,
476 ):
433 """Publish an item as a dataform when we know that there is a schema 477 """Publish an item as a dataform when we know that there is a schema
434 478
435 @param values(dict[key(unicode), [iterable[object], object]]): values set for the 479 @param values(dict[key(unicode), [iterable[object], object]]): values set for the
436 form. If not iterable, will be put in a list. 480 form. If not iterable, will be put in a list.
437 @param schema(domish.Element, data_form.Form, None): data schema 481 @param schema(domish.Element, data_form.Form, None): data schema
455 except KeyError: 499 except KeyError:
456 log.warning( 500 log.warning(
457 _("field {name} doesn't exist, ignoring it").format(name=name) 501 _("field {name} doesn't exist, ignoring it").format(name=name)
458 ) 502 )
459 continue 503 continue
460 if isinstance(values_list, str) or not isinstance( 504 if isinstance(values_list, str) or not isinstance(values_list, Iterable):
461 values_list, Iterable
462 ):
463 values_list = [values_list] 505 values_list = [values_list]
464 if deserialise: 506 if deserialise:
465 if field.fieldType == "boolean": 507 if field.fieldType == "boolean":
466 values_list = [C.bool(v) for v in values_list] 508 values_list = [C.bool(v) for v in values_list]
467 elif field.fieldType == "text-multi": 509 elif field.fieldType == "text-multi":
468 # for text-multi, lines must be put on separate values 510 # for text-multi, lines must be put on separate values
469 values_list = list( 511 values_list = list(
470 itertools.chain(*[v.splitlines() for v in values_list]) 512 itertools.chain(*[v.splitlines() for v in values_list])
471 ) 513 )
472 elif xml_tools.is_xhtml_field(field): 514 elif xml_tools.is_xhtml_field(field):
473 values_list = [generic.parseXml(v.encode("utf-8")) 515 values_list = [
474 for v in values_list] 516 generic.parseXml(v.encode("utf-8")) for v in values_list
517 ]
475 elif "jid" in (field.fieldType or ""): 518 elif "jid" in (field.fieldType or ""):
476 values_list = [jid.JID(v) for v in values_list] 519 values_list = [jid.JID(v) for v in values_list]
477 if "list" in (field.fieldType or ""): 520 if "list" in (field.fieldType or ""):
478 # for lists, we check that given values are allowed in form 521 # for lists, we check that given values are allowed in form
479 allowed_values = [o.value for o in field.options] 522 allowed_values = [o.value for o in field.options]
480 values_list = [v for v in values_list if v in allowed_values] 523 values_list = [v for v in values_list if v in allowed_values]
481 if not values_list: 524 if not values_list:
482 # if values don't map to allowed values, we use default ones 525 # if values don't map to allowed values, we use default ones
483 values_list = field.values 526 values_list = field.values
484 elif field.ext_type == 'xml': 527 elif field.ext_type == "xml":
485 # FIXME: XML elements are not handled correctly, we need to know if we 528 # FIXME: XML elements are not handled correctly, we need to know if we
486 # have actual XML/XHTML, or text to escape 529 # have actual XML/XHTML, or text to escape
487 for idx, value in enumerate(values_list[:]): 530 for idx, value in enumerate(values_list[:]):
488 if isinstance(value, domish.Element): 531 if isinstance(value, domish.Element):
489 if (field.value and (value.name != field.value.name 532 if field.value and (
490 or value.uri != field.value.uri)): 533 value.name != field.value.name or value.uri != field.value.uri
534 ):
491 # the element is not the one expected in form, so we create the right element 535 # the element is not the one expected in form, so we create the right element
492 # to wrap the current value 536 # to wrap the current value
493 wrapper_elt = domish.Element((field.value.uri, field.value.name)) 537 wrapper_elt = domish.Element(
538 (field.value.uri, field.value.name)
539 )
494 wrapper_elt.addChild(value) 540 wrapper_elt.addChild(value)
495 values_list[idx] = wrapper_elt 541 values_list[idx] = wrapper_elt
496 else: 542 else:
497 # we have to convert the value to a domish.Element 543 # we have to convert the value to a domish.Element
498 if field.value and field.value.uri == C.NS_XHTML: 544 if field.value and field.value.uri == C.NS_XHTML:
499 div_elt = domish.Element((C.NS_XHTML, 'div')) 545 div_elt = domish.Element((C.NS_XHTML, "div"))
500 div_elt.addContent(str(value)) 546 div_elt.addContent(str(value))
501 values_list[idx] = div_elt 547 values_list[idx] = div_elt
502 else: 548 else:
503 # only XHTML fields are handled for now 549 # only XHTML fields are handled for now
504 raise NotImplementedError 550 raise NotImplementedError
568 sub_id = None 614 sub_id = None
569 extra = self._p.parse_extra(extra) 615 extra = self._p.parse_extra(extra)
570 616
571 return client, service, node, max_items, extra, sub_id 617 return client, service, node, max_items, extra, sub_id
572 618
573 def _get(self, service="", node="", max_items=10, item_ids=None, sub_id=None, 619 def _get(
574 extra="", default_node=None, form_ns=None, filters=None, 620 self,
575 profile_key=C.PROF_KEY_NONE): 621 service="",
622 node="",
623 max_items=10,
624 item_ids=None,
625 sub_id=None,
626 extra="",
627 default_node=None,
628 form_ns=None,
629 filters=None,
630 profile_key=C.PROF_KEY_NONE,
631 ):
576 """bridge method to retrieve data from node with schema 632 """bridge method to retrieve data from node with schema
577 633
578 this method is a helper so dependant plugins can use it directly 634 this method is a helper so dependant plugins can use it directly
579 when adding *Get methods 635 when adding *Get methods
580 extra can have the key "labels_as_list" which is a hack to convert 636 extra can have the key "labels_as_list" which is a hack to convert
645 client, service, node, item_ids=[item_id] 701 client, service, node, item_ids=[item_id]
646 ) 702 )
647 item_elt = items_data[0][0] 703 item_elt = items_data[0][0]
648 except Exception as e: 704 except Exception as e:
649 log.warning( 705 log.warning(
650 _("Can't get previous item, update ignored: {reason}").format( 706 _("Can't get previous item, update ignored: {reason}").format(reason=e)
651 reason=e
652 )
653 ) 707 )
654 else: 708 else:
655 # and parse it 709 # and parse it
656 form = data_form.findForm(item_elt, form_ns) 710 form = data_form.findForm(item_elt, form_ns)
657 if form is None: 711 if form is None:
661 else: 715 else:
662 for name, field in form.fields.items(): 716 for name, field in form.fields.items():
663 if name not in values: 717 if name not in values:
664 values[name] = "\n".join(str(v) for v in field.values) 718 values[name] = "\n".join(str(v) for v in field.values)
665 719
666 def _set(self, service, node, values, schema=None, item_id=None, extra=None, 720 def _set(
667 default_node=None, form_ns=None, fill_author=True, 721 self,
668 profile_key=C.PROF_KEY_NONE): 722 service,
723 node,
724 values,
725 schema=None,
726 item_id=None,
727 extra=None,
728 default_node=None,
729 form_ns=None,
730 fill_author=True,
731 profile_key=C.PROF_KEY_NONE,
732 ):
669 """bridge method to set item in node with schema 733 """bridge method to set item in node with schema
670 734
671 this method is a helper so dependant plugins can use it directly 735 this method is a helper so dependant plugins can use it directly
672 when adding *Set methods 736 when adding *Set methods
673 """ 737 """
674 client, service, node, schema, item_id, extra = self.prepare_bridge_set( 738 client, service, node, schema, item_id, extra = self.prepare_bridge_set(
675 service, node, schema, item_id, extra 739 service, node, schema, item_id, extra
676 ) 740 )
677 d = defer.ensureDeferred(self.set( 741 d = defer.ensureDeferred(
678 client, 742 self.set(
679 service, 743 client,
680 node, 744 service,
681 values, 745 node,
682 schema, 746 values,
683 item_id, 747 schema,
684 extra, 748 item_id,
685 deserialise=True, 749 extra,
686 form_ns=form_ns, 750 deserialise=True,
687 default_node=default_node, 751 form_ns=form_ns,
688 fill_author=fill_author, 752 default_node=default_node,
689 )) 753 fill_author=fill_author,
754 )
755 )
690 d.addCallback(lambda ret: ret or "") 756 d.addCallback(lambda ret: ret or "")
691 return d 757 return d
692 758
693 async def set( 759 async def set(
694 self, client, service, node, values, schema, item_id, extra, deserialise, 760 self,
695 form_ns, default_node=None, fill_author=True): 761 client,
762 service,
763 node,
764 values,
765 schema,
766 item_id,
767 extra,
768 deserialise,
769 form_ns,
770 default_node=None,
771 fill_author=True,
772 ):
696 """Set an item in a node with a schema 773 """Set an item in a node with a schema
697 774
698 This method can be used directly by *Set methods added by dependant plugin 775 This method can be used directly by *Set methods added by dependant plugin
699 @param values(dict[key(unicode), [iterable[object]|object]]): values of the items 776 @param values(dict[key(unicode), [iterable[object]|object]]): values of the items
700 if value is not iterable, it will be put in a list 777 if value is not iterable, it will be put in a list
723 elif extra.get("update", False): 800 elif extra.get("update", False):
724 if item_id is None: 801 if item_id is None:
725 raise exceptions.DataError( 802 raise exceptions.DataError(
726 _('if extra["update"] is set, item_id must be set too') 803 _('if extra["update"] is set, item_id must be set too')
727 ) 804 )
728 await self.copy_missing_values(client, service, node, item_id, form_ns, values) 805 await self.copy_missing_values(
806 client, service, node, item_id, form_ns, values
807 )
729 808
730 values["updated"] = now 809 values["updated"] = now
731 if fill_author: 810 if fill_author:
732 if not values.get("author"): 811 if not values.get("author"):
733 id_data = await self._i.get_identity(client, None, ["nicknames"]) 812 id_data = await self._i.get_identity(client, None, ["nicknames"])
734 values["author"] = id_data['nicknames'][0] 813 values["author"] = id_data["nicknames"][0]
735 if not values.get("author_jid"): 814 if not values.get("author_jid"):
736 values["author_jid"] = client.jid.full() 815 values["author_jid"] = client.jid.full()
737 item_id = await self.send_data_form_item( 816 item_id = await self.send_data_form_item(
738 client, service, node, values, schema, item_id, extra, deserialise 817 client, service, node, values, schema, item_id, extra, deserialise
739 ) 818 )