comparison src/tools/xml_tools.py @ 1476:48706f4ff19c

core (xmlui): added JidsListWidget to manage editable list of jids: - can be used to manage XEP-0004's jid-multi - can be used in the future for search result to display found entities (need a read-only style) - generaly usefull to manage list of entites - implemented in params too
author Goffi <goffi@goffi.org>
date Thu, 20 Aug 2015 18:33:52 +0200
parents 7ac073d2e7e0
children a77217511afd
comparison
equal deleted inserted replaced
1475:7ac073d2e7e0 1476:48706f4ff19c
23 log = getLogger(__name__) 23 log = getLogger(__name__)
24 24
25 from xml.dom import minidom, NotFoundErr 25 from xml.dom import minidom, NotFoundErr
26 from wokkel import data_form 26 from wokkel import data_form
27 from twisted.words.xish import domish 27 from twisted.words.xish import domish
28 from twisted.words.protocols.jabber import jid
28 from sat.core import exceptions 29 from sat.core import exceptions
29 30
30 31
31 """This library help manage XML used in SàT (parameters, registration, etc)""" 32 """This library help manage XML used in SàT (parameters, registration, etc)"""
32 33
252 """Convert the XML for parameter to a SàT XML User Interface. 253 """Convert the XML for parameter to a SàT XML User Interface.
253 254
254 @param xml (unicode) 255 @param xml (unicode)
255 @return: XMLUI 256 @return: XMLUI
256 """ 257 """
258 # TODO: refactor params and use Twisted directly to parse XML
257 params_doc = minidom.parseString(xml.encode('utf-8')) 259 params_doc = minidom.parseString(xml.encode('utf-8'))
258 top = params_doc.documentElement 260 top = params_doc.documentElement
259 if top.nodeName != 'params': 261 if top.nodeName != 'params':
260 raise exceptions.DataError(_('INTERNAL ERROR: parameters xml not valid')) 262 raise exceptions.DataError(_('INTERNAL ERROR: parameters xml not valid'))
261 263
279 281
280 value = param.getAttribute('value') or None 282 value = param.getAttribute('value') or None
281 callback_id = param.getAttribute('callback_id') or None 283 callback_id = param.getAttribute('callback_id') or None
282 284
283 if type_ == 'list': 285 if type_ == 'list':
284 options, selected = _getParamListOptions(param) 286 options, selected = _paramsGetListOptions(param)
285 widget_kwargs['options'] = options 287 widget_kwargs['options'] = options
286 widget_kwargs['selected'] = selected 288 widget_kwargs['selected'] = selected
289 elif type_ == 'jids_list':
290 widget_kwargs['jids'] = _paramsGetListJids(param)
287 291
288 if type_ in ("button", "text"): 292 if type_ in ("button", "text"):
289 param_ui.addEmpty() 293 param_ui.addEmpty()
290 value = param_label 294 value = param_label
291 else: 295 else:
306 param_ui.addWidget(type_, **widget_kwargs) 310 param_ui.addWidget(type_, **widget_kwargs)
307 311
308 return param_ui.toXml() 312 return param_ui.toXml()
309 313
310 314
311 def _getParamListOptions(param): 315 def _paramsGetListOptions(param):
312 """Retrieve the options for list element. 316 """Retrieve the options for list element.
313 317
314 The <option/> tags must be direct children of <param/>. 318 The <option/> tags must be direct children of <param/>.
315 @param param (domish.Element): element 319 @param param (domish.Element): element
316 @return: a tuple (options, selected_value) 320 @return: a tuple (options, selected_value)
322 return [] 326 return []
323 options = [elem.getAttribute("value") for elem in elems] 327 options = [elem.getAttribute("value") for elem in elems]
324 selected = [elem.getAttribute("value") for elem in elems if elem.getAttribute("selected") == 'true'] 328 selected = [elem.getAttribute("value") for elem in elems if elem.getAttribute("selected") == 'true']
325 return (options, selected) 329 return (options, selected)
326 330
327 331 def _paramsGetListJids(param):
328 ## XMLUI Elements 332 """Retrive jids from a jids_list element.
333
334 the <jid/> tags must be direct children of <param/>
335 @param param (domish.Element): element
336 @return: a list of jids
337 """
338 elems = param.getElementsByTagName("jid")
339 jids = [elem.firstChild.data for elem in elems
340 if elem.firstChild is not None
341 and elem.firstChild.nodeType == elem.TEXT_NODE]
342 return jids
343
344
345 ### XMLUI Elements ###
329 346
330 347
331 class Element(object): 348 class Element(object):
332 """ Base XMLUI element """ 349 """ Base XMLUI element """
333 type = None 350 type = None
425 self.elem.setAttribute('label', label) 442 self.elem.setAttribute('label', label)
426 if selected: 443 if selected:
427 self.elem.setAttribute('selected', 'true') 444 self.elem.setAttribute('selected', 'true')
428 445
429 446
447 class JidElement(Element):
448 """" Used by JidsListWidget to specify jids"""
449 type = 'jid'
450
451 def __init__(self, parent, jid_):
452 """
453 @param jid_(jid.JID, unicode): jid to append
454 """
455 assert isinstance(parent, JidsListWidget)
456 super(JidElement, self).__init__(parent.xmlui, parent)
457 if isinstance(jid_, jid.JID):
458 value = jid.full
459 elif isinstance(jid_, basestring):
460 value = unicode(jid_)
461 else:
462 raise NotImplementedError
463 jid_txt = self.xmlui.doc.createTextNode(value)
464 self.elem.appendChild(jid_txt)
465
466
430 class RowElement(Element): 467 class RowElement(Element):
431 """" Used by AdvancedListContainer """ 468 """" Used by AdvancedListContainer """
432 type = 'row' 469 type = 'row'
433 470
434 def __init__(self, parent): 471 def __init__(self, parent):
458 self.elem.setAttribute('name', name) 495 self.elem.setAttribute('name', name)
459 if label: 496 if label:
460 self.elem.setAttribute('label', label) 497 self.elem.setAttribute('label', label)
461 if description: 498 if description:
462 self.elem.setAttribute('description', description) 499 self.elem.setAttribute('description', description)
500
501
502 ## Containers ##
463 503
464 504
465 class Container(Element): 505 class Container(Element):
466 """ And Element which contains other ones and has a layout """ 506 """ And Element which contains other ones and has a layout """
467 type = None 507 type = None
601 raise exceptions.DataError(_("Incorrect number of items in list")) 641 raise exceptions.DataError(_("Incorrect number of items in list"))
602 parent_container = self.getParentContainer() 642 parent_container = self.getParentContainer()
603 self.xmlui.changeContainer(parent_container) 643 self.xmlui.changeContainer(parent_container)
604 644
605 645
646 ## Widgets ##
647
648
606 class Widget(Element): 649 class Widget(Element):
607 type = None 650 type = None
608 651
609 def __init__(self, xmlui, name=None, parent=None): 652 def __init__(self, xmlui, name=None, parent=None):
610 """Create an element 653 """Create an element
796 839
797 840
798 class ListWidget(InputWidget): 841 class ListWidget(InputWidget):
799 type = 'list' 842 type = 'list'
800 843
801 def __init__(self, xmlui, options, selected=None, style=None, name=None, parent=None): 844 def __init__(self, xmlui, options, selected=None, styles=None, name=None, parent=None):
802 """ 845 """
803 846
804 @param xmlui 847 @param xmlui
805 @param options (list[option]): each option can be given as: 848 @param options (list[option]): each option can be given as:
806 - a single string if the label and the value are the same 849 - a single string if the label and the value are the same
807 - a tuple with a couple of string (value,label) if the label and the value differ 850 - a tuple with a couple of string (value,label) if the label and the value differ
808 @param selected (list[string]): list of the selected values 851 @param selected (list[string]): list of the selected values
809 @param style (string) 852 @param styles (iterable[string]): flags to set the behaviour of the list
810 @param name (string) 853 @param name (string)
811 @param parent 854 @param parent
812 """ 855 """
813 if style is None: 856 styles = set() if styles is None else set(styles)
814 style = set() 857 if styles is None:
815 styles = set(style) 858 styles = set()
859 else:
860 styles = set(styles)
816 if not options: 861 if not options:
817 log.warning(_('empty "options" list')) 862 log.warning(_('empty "options" list'))
818 if not styles.issubset(['multi']): 863 if not styles.issubset(['multi']):
819 raise exceptions.DataError(_("invalid styles")) 864 raise exceptions.DataError(_("invalid styles"))
820 super(ListWidget, self).__init__(xmlui, name, parent) 865 super(ListWidget, self).__init__(xmlui, name, parent)
831 selected = [] 876 selected = []
832 for option in options: 877 for option in options:
833 assert isinstance(option, basestring) or isinstance(option, tuple) 878 assert isinstance(option, basestring) or isinstance(option, tuple)
834 value = option if isinstance(option, basestring) else option[0] 879 value = option if isinstance(option, basestring) else option[0]
835 OptionElement(self, option, value in selected) 880 OptionElement(self, option, value in selected)
881
882 class JidsListWidget(InputWidget):
883 """A list of text or jids where elements can be added/removed or modified"""
884 type = 'jids_list'
885
886 def __init__(self, xmlui, jids, styles=None, name=None, parent=None):
887 """
888
889 @param xmlui
890 @param jids (list[jid.JID]): base jids
891 @param styles (iterable[string]): flags to set the behaviour of the list
892 @param name (string)
893 @param parent
894 """
895 super(JidsListWidget, self).__init__(xmlui, name, parent)
896 styles = set() if styles is None else set(styles)
897 if not styles.issubset([]): # TODO
898 raise exceptions.DataError(_("invalid styles"))
899 for style in styles:
900 self.elem.setAttribute(style, 'yes')
901 if not jids:
902 log.debug('empty jids list')
903 else:
904 self.addJids(jids)
905
906 def addJids(self, jids):
907 for jid_ in jids:
908 JidElement(self, jid_)
836 909
837 910
838 ## Dialog Elements ## 911 ## Dialog Elements ##
839 912
840 913