comparison sat/tools/xml_tools.py @ 2624:56f94936df1e

code style reformatting using black
author Goffi <goffi@goffi.org>
date Wed, 27 Jun 2018 20:14:46 +0200
parents 26edcf3a30eb
children a55a14c3cbf4
comparison
equal deleted inserted replaced
2623:49533de4540b 2624:56f94936df1e
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.constants import Const as C 21 from sat.core.constants import Const as C
22 from sat.core.log import getLogger 22 from sat.core.log import getLogger
23
23 log = getLogger(__name__) 24 log = getLogger(__name__)
24 25
25 from xml.dom import minidom, NotFoundErr 26 from xml.dom import minidom, NotFoundErr
26 from wokkel import data_form 27 from wokkel import data_form
27 from twisted.words.xish import domish 28 from twisted.words.xish import domish
35 36
36 """This library help manage XML used in SàT (parameters, registration, etc)""" 37 """This library help manage XML used in SàT (parameters, registration, etc)"""
37 38
38 SAT_FORM_PREFIX = "SAT_FORM_" 39 SAT_FORM_PREFIX = "SAT_FORM_"
39 SAT_PARAM_SEPARATOR = "_XMLUI_PARAM_" # used to have unique elements names 40 SAT_PARAM_SEPARATOR = "_XMLUI_PARAM_" # used to have unique elements names
40 html_entity_re = re.compile(r'&([a-zA-Z]+?);') 41 html_entity_re = re.compile(r"&([a-zA-Z]+?);")
41 XML_ENTITIES = ('quot', 'amp', 'apos', 'lt', 'gt') 42 XML_ENTITIES = ("quot", "amp", "apos", "lt", "gt")
42 43
43 # TODO: move XMLUI stuff in a separate module 44 # TODO: move XMLUI stuff in a separate module
44 # TODO: rewrite this with lxml or ElementTree or domish.Element: it's complicated and difficult to maintain with current minidom implementation 45 # TODO: rewrite this with lxml or ElementTree or domish.Element: it's complicated and difficult to maintain with current minidom implementation
45 46
46 # Helper functions 47 # Helper functions
48
47 49
48 def _dataFormField2XMLUIData(field, read_only=False): 50 def _dataFormField2XMLUIData(field, read_only=False):
49 """Get data needed to create an XMLUI's Widget from Wokkel's data_form's Field. 51 """Get data needed to create an XMLUI's Widget from Wokkel's data_form's Field.
50 52
51 The attribute field can be modified (if it's fixed and it has no value). 53 The attribute field can be modified (if it's fixed and it has no value).
53 @param read_only (bool): if True and it makes sense, create a read only input widget 55 @param read_only (bool): if True and it makes sense, create a read only input widget
54 @return: a tuple (widget_type, widget_args, widget_kwargs) 56 @return: a tuple (widget_type, widget_args, widget_kwargs)
55 """ 57 """
56 widget_args = [field.value] 58 widget_args = [field.value]
57 widget_kwargs = {} 59 widget_kwargs = {}
58 if field.fieldType == 'fixed' or field.fieldType is None: 60 if field.fieldType == "fixed" or field.fieldType is None:
59 widget_type = 'text' 61 widget_type = "text"
60 if field.value is None: 62 if field.value is None:
61 if field.label is None: 63 if field.label is None:
62 log.warning(_("Fixed field has neither value nor label, ignoring it")) 64 log.warning(_("Fixed field has neither value nor label, ignoring it"))
63 field.value = "" 65 field.value = ""
64 else: 66 else:
65 field.value = field.label 67 field.value = field.label
66 field.label = None 68 field.label = None
67 widget_args[0] = field.value 69 widget_args[0] = field.value
68 elif field.fieldType == 'text-single': 70 elif field.fieldType == "text-single":
69 widget_type = "string" 71 widget_type = "string"
70 widget_kwargs['read_only'] = read_only 72 widget_kwargs["read_only"] = read_only
71 elif field.fieldType == 'jid-single': 73 elif field.fieldType == "jid-single":
72 widget_type = "jid_input" 74 widget_type = "jid_input"
73 widget_kwargs['read_only'] = read_only 75 widget_kwargs["read_only"] = read_only
74 elif field.fieldType == 'text-multi': 76 elif field.fieldType == "text-multi":
75 widget_type = "textbox" 77 widget_type = "textbox"
76 widget_args[0] = u'\n'.join(field.values) 78 widget_args[0] = u"\n".join(field.values)
77 widget_kwargs['read_only'] = read_only 79 widget_kwargs["read_only"] = read_only
78 elif field.fieldType == 'text-private': 80 elif field.fieldType == "text-private":
79 widget_type = "password" 81 widget_type = "password"
80 widget_kwargs['read_only'] = read_only 82 widget_kwargs["read_only"] = read_only
81 elif field.fieldType == 'boolean': 83 elif field.fieldType == "boolean":
82 widget_type = "bool" 84 widget_type = "bool"
83 if widget_args[0] is None: 85 if widget_args[0] is None:
84 widget_args[0] = 'false' 86 widget_args[0] = "false"
85 widget_kwargs['read_only'] = read_only 87 widget_kwargs["read_only"] = read_only
86 elif field.fieldType == 'integer': 88 elif field.fieldType == "integer":
87 widget_type = "integer" 89 widget_type = "integer"
88 widget_kwargs['read_only'] = read_only 90 widget_kwargs["read_only"] = read_only
89 elif field.fieldType == 'list-single': 91 elif field.fieldType == "list-single":
90 widget_type = "list" 92 widget_type = "list"
91 widget_kwargs["options"] = [(option.value, option.label or option.value) for option in field.options] 93 widget_kwargs["options"] = [
94 (option.value, option.label or option.value) for option in field.options
95 ]
92 widget_kwargs["selected"] = widget_args 96 widget_kwargs["selected"] = widget_args
93 widget_args = [] 97 widget_args = []
94 else: 98 else:
95 log.error(u"FIXME FIXME FIXME: Type [%s] is not managed yet by SàT" % field.fieldType) 99 log.error(
100 u"FIXME FIXME FIXME: Type [%s] is not managed yet by SàT" % field.fieldType
101 )
96 widget_type = "string" 102 widget_type = "string"
97 widget_kwargs['read_only'] = read_only 103 widget_kwargs["read_only"] = read_only
98 104
99 if field.var: 105 if field.var:
100 widget_kwargs["name"] = field.var 106 widget_kwargs["name"] = field.var
101 107
102 return widget_type, widget_args, widget_kwargs 108 return widget_type, widget_args, widget_kwargs
119 @return: the completed XMLUI instance 125 @return: the completed XMLUI instance
120 """ 126 """
121 if filters is None: 127 if filters is None:
122 filters = {} 128 filters = {}
123 if form.instructions: 129 if form.instructions:
124 form_ui.addText('\n'.join(form.instructions), 'instructions') 130 form_ui.addText("\n".join(form.instructions), "instructions")
125 131
126 form_ui.changeContainer("label") 132 form_ui.changeContainer("label")
127 133
128 if prepend is not None: 134 if prepend is not None:
129 for widget_args in prepend: 135 for widget_args in prepend:
130 form_ui.addWidget(*widget_args) 136 form_ui.addWidget(*widget_args)
131 137
132 for field in form.fieldList: 138 for field in form.fieldList:
133 widget_type, widget_args, widget_kwargs = _dataFormField2XMLUIData(field, read_only) 139 widget_type, widget_args, widget_kwargs = _dataFormField2XMLUIData(
140 field, read_only
141 )
134 try: 142 try:
135 widget_filter = filters[widget_kwargs['name']] 143 widget_filter = filters[widget_kwargs["name"]]
136 except KeyError: 144 except KeyError:
137 pass 145 pass
138 else: 146 else:
139 widget_type, widget_args, widget_kwargs = widget_filter(form_ui, widget_type, widget_args, widget_kwargs) 147 widget_type, widget_args, widget_kwargs = widget_filter(
148 form_ui, widget_type, widget_args, widget_kwargs
149 )
140 label = field.label or field.var 150 label = field.label or field.var
141 if label: 151 if label:
142 form_ui.addLabel(label) 152 form_ui.addLabel(label)
143 else: 153 else:
144 form_ui.addEmpty() 154 form_ui.addEmpty()
170 - headers (dict{unicode: unicode}): form headers (field labels and types) 180 - headers (dict{unicode: unicode}): form headers (field labels and types)
171 - xmlui_data (list[tuple]): list of (widget_type, widget_args, widget_kwargs) 181 - xmlui_data (list[tuple]): list of (widget_type, widget_args, widget_kwargs)
172 """ 182 """
173 headers = OrderedDict() 183 headers = OrderedDict()
174 try: 184 try:
175 reported_elt = form_xml.elements('jabber:x:data', 'reported').next() 185 reported_elt = form_xml.elements("jabber:x:data", "reported").next()
176 except StopIteration: 186 except StopIteration:
177 raise exceptions.DataError("Couldn't find expected <reported> tag in %s" % form_xml.toXml()) 187 raise exceptions.DataError(
188 "Couldn't find expected <reported> tag in %s" % form_xml.toXml()
189 )
178 190
179 for elt in reported_elt.elements(): 191 for elt in reported_elt.elements():
180 if elt.name != "field": 192 if elt.name != "field":
181 raise exceptions.DataError("Unexpected tag") 193 raise exceptions.DataError("Unexpected tag")
182 name = elt["var"] 194 name = elt["var"]
183 label = elt.attributes.get('label', '') 195 label = elt.attributes.get("label", "")
184 type_ = elt.attributes.get('type') 196 type_ = elt.attributes.get("type")
185 headers[name] = (label, type_) 197 headers[name] = (label, type_)
186 198
187 if not headers: 199 if not headers:
188 raise exceptions.DataError("No reported fields (see XEP-0004 §3.4)") 200 raise exceptions.DataError("No reported fields (see XEP-0004 §3.4)")
189 201
190 xmlui_data = [] 202 xmlui_data = []
191 item_elts = form_xml.elements('jabber:x:data', 'item') 203 item_elts = form_xml.elements("jabber:x:data", "item")
192 204
193 for item_elt in item_elts: 205 for item_elt in item_elts:
194 for elt in item_elt.elements(): 206 for elt in item_elt.elements():
195 if elt.name != 'field': 207 if elt.name != "field":
196 log.warning(u"Unexpected tag (%s)" % elt.name) 208 log.warning(u"Unexpected tag (%s)" % elt.name)
197 continue 209 continue
198 field = data_form.Field.fromElement(elt) 210 field = data_form.Field.fromElement(elt)
199 211
200 xmlui_data.append(_dataFormField2XMLUIData(field)) 212 xmlui_data.append(_dataFormField2XMLUIData(field))
209 @param xmlui (XMLUI): the XMLUI where the AdvancedList will be added 221 @param xmlui (XMLUI): the XMLUI where the AdvancedList will be added
210 @param headers (dict{unicode: unicode}): form headers (field labels and types) 222 @param headers (dict{unicode: unicode}): form headers (field labels and types)
211 @param xmlui_data (list[tuple]): list of (widget_type, widget_args, widget_kwargs) 223 @param xmlui_data (list[tuple]): list of (widget_type, widget_args, widget_kwargs)
212 @return: the completed XMLUI instance 224 @return: the completed XMLUI instance
213 """ 225 """
214 adv_list = AdvancedListContainer(xmlui, headers=headers, columns=len(headers), parent=xmlui.current_container) 226 adv_list = AdvancedListContainer(
227 xmlui, headers=headers, columns=len(headers), parent=xmlui.current_container
228 )
215 xmlui.changeContainer(adv_list) 229 xmlui.changeContainer(adv_list)
216 230
217 for widget_type, widget_args, widget_kwargs in xmlui_data: 231 for widget_type, widget_args, widget_kwargs in xmlui_data:
218 xmlui.addWidget(widget_type, *widget_args, **widget_kwargs) 232 xmlui.addWidget(widget_type, *widget_args, **widget_kwargs)
219 233
246 except exceptions.DataError: 260 except exceptions.DataError:
247 parsed_form = data_form.Form.fromElement(form_elt) 261 parsed_form = data_form.Form.fromElement(form_elt)
248 dataForm2Widgets(xml_ui, parsed_form, read_only=True) 262 dataForm2Widgets(xml_ui, parsed_form, read_only=True)
249 return xml_ui 263 return xml_ui
250 264
251 def dataFormResult2XMLUI(result_form, base_form, session_id=None, prepend=None, filters=None): 265
266 def dataFormResult2XMLUI(
267 result_form, base_form, session_id=None, prepend=None, filters=None
268 ):
252 """Convert data form result to SàT XMLUI. 269 """Convert data form result to SàT XMLUI.
253 270
254 @param result_form (data_form.Form): result form to convert 271 @param result_form (data_form.Form): result form to convert
255 @param base_form (data_form.Form): initial form (i.e. of form type "form") 272 @param base_form (data_form.Form): initial form (i.e. of form type "form")
256 this one is necessary to reconstruct options when needed (e.g. list elements) 273 this one is necessary to reconstruct options when needed (e.g. list elements)
288 """ Extract form data from a XMLUI return. 305 """ Extract form data from a XMLUI return.
289 306
290 @param xmlui_data (dict): data returned by frontends for XMLUI form 307 @param xmlui_data (dict): data returned by frontends for XMLUI form
291 @return: dict of data usable by Wokkel's data form 308 @return: dict of data usable by Wokkel's data form
292 """ 309 """
293 return {key[len(SAT_FORM_PREFIX):]: _cleanValue(value) for key, value in xmlui_data.iteritems() if key.startswith(SAT_FORM_PREFIX)} 310 return {
311 key[len(SAT_FORM_PREFIX) :]: _cleanValue(value)
312 for key, value in xmlui_data.iteritems()
313 if key.startswith(SAT_FORM_PREFIX)
314 }
294 315
295 316
296 def formEscape(name): 317 def formEscape(name):
297 """Return escaped name for forms. 318 """Return escaped name for forms.
298 319
306 """Construct result domish.Element from XMLUI result. 327 """Construct result domish.Element from XMLUI result.
307 328
308 @param xmlui_data (dict): data returned by frontends for XMLUI form 329 @param xmlui_data (dict): data returned by frontends for XMLUI form
309 @return: domish.Element 330 @return: domish.Element
310 """ 331 """
311 form = data_form.Form('submit') 332 form = data_form.Form("submit")
312 form.makeFields(XMLUIResult2DataFormResult(xmlui_data)) 333 form.makeFields(XMLUIResult2DataFormResult(xmlui_data))
313 return form.toElement() 334 return form.toElement()
314 335
315 336
316 def tupleList2dataForm(values): 337 def tupleList2dataForm(values):
317 """Convert a list of tuples (name, value) to a wokkel submit data form. 338 """Convert a list of tuples (name, value) to a wokkel submit data form.
318 339
319 @param values (list): list of tuples 340 @param values (list): list of tuples
320 @return: data_form.Form 341 @return: data_form.Form
321 """ 342 """
322 form = data_form.Form('submit') 343 form = data_form.Form("submit")
323 for value in values: 344 for value in values:
324 field = data_form.Field(var=value[0], value=value[1]) 345 field = data_form.Field(var=value[0], value=value[1])
325 form.addField(field) 346 form.addField(field)
326 347
327 return form 348 return form
332 353
333 @param xml (unicode) 354 @param xml (unicode)
334 @return: XMLUI 355 @return: XMLUI
335 """ 356 """
336 # TODO: refactor params and use Twisted directly to parse XML 357 # TODO: refactor params and use Twisted directly to parse XML
337 params_doc = minidom.parseString(xml.encode('utf-8')) 358 params_doc = minidom.parseString(xml.encode("utf-8"))
338 top = params_doc.documentElement 359 top = params_doc.documentElement
339 if top.nodeName != 'params': 360 if top.nodeName != "params":
340 raise exceptions.DataError(_('INTERNAL ERROR: parameters xml not valid')) 361 raise exceptions.DataError(_("INTERNAL ERROR: parameters xml not valid"))
341 362
342 param_ui = XMLUI("param", "tabs") 363 param_ui = XMLUI("param", "tabs")
343 tabs_cont = param_ui.current_container 364 tabs_cont = param_ui.current_container
344 365
345 for category in top.getElementsByTagName("category"): 366 for category in top.getElementsByTagName("category"):
346 category_name = category.getAttribute('name') 367 category_name = category.getAttribute("name")
347 label = category.getAttribute('label') 368 label = category.getAttribute("label")
348 if not category_name: 369 if not category_name:
349 raise exceptions.DataError(_('INTERNAL ERROR: params categories must have a name')) 370 raise exceptions.DataError(
371 _("INTERNAL ERROR: params categories must have a name")
372 )
350 tabs_cont.addTab(category_name, label=label, container=LabelContainer) 373 tabs_cont.addTab(category_name, label=label, container=LabelContainer)
351 for param in category.getElementsByTagName("param"): 374 for param in category.getElementsByTagName("param"):
352 widget_kwargs = {} 375 widget_kwargs = {}
353 376
354 param_name = param.getAttribute('name') 377 param_name = param.getAttribute("name")
355 param_label = param.getAttribute('label') 378 param_label = param.getAttribute("label")
356 type_ = param.getAttribute('type') 379 type_ = param.getAttribute("type")
357 if not param_name and type_ != 'text': 380 if not param_name and type_ != "text":
358 raise exceptions.DataError(_('INTERNAL ERROR: params must have a name')) 381 raise exceptions.DataError(_("INTERNAL ERROR: params must have a name"))
359 382
360 value = param.getAttribute('value') or None 383 value = param.getAttribute("value") or None
361 callback_id = param.getAttribute('callback_id') or None 384 callback_id = param.getAttribute("callback_id") or None
362 385
363 if type_ == 'list': 386 if type_ == "list":
364 options, selected = _paramsGetListOptions(param) 387 options, selected = _paramsGetListOptions(param)
365 widget_kwargs['options'] = options 388 widget_kwargs["options"] = options
366 widget_kwargs['selected'] = selected 389 widget_kwargs["selected"] = selected
367 widget_kwargs['styles'] = ['extensible'] 390 widget_kwargs["styles"] = ["extensible"]
368 elif type_ == 'jids_list': 391 elif type_ == "jids_list":
369 widget_kwargs['jids'] = _paramsGetListJids(param) 392 widget_kwargs["jids"] = _paramsGetListJids(param)
370 393
371 if type_ in ("button", "text"): 394 if type_ in ("button", "text"):
372 param_ui.addEmpty() 395 param_ui.addEmpty()
373 value = param_label 396 value = param_label
374 else: 397 else:
376 399
377 if value: 400 if value:
378 widget_kwargs["value"] = value 401 widget_kwargs["value"] = value
379 402
380 if callback_id: 403 if callback_id:
381 widget_kwargs['callback_id'] = callback_id 404 widget_kwargs["callback_id"] = callback_id
382 others = ["%s%s%s" % (category_name, SAT_PARAM_SEPARATOR, other.getAttribute('name')) 405 others = [
383 for other in category.getElementsByTagName('param') 406 "%s%s%s"
384 if other.getAttribute('type') != 'button'] 407 % (category_name, SAT_PARAM_SEPARATOR, other.getAttribute("name"))
385 widget_kwargs['fields_back'] = others 408 for other in category.getElementsByTagName("param")
386 409 if other.getAttribute("type") != "button"
387 widget_kwargs['name'] = "%s%s%s" % (category_name, SAT_PARAM_SEPARATOR, param_name) 410 ]
411 widget_kwargs["fields_back"] = others
412
413 widget_kwargs["name"] = "%s%s%s" % (
414 category_name,
415 SAT_PARAM_SEPARATOR,
416 param_name,
417 )
388 418
389 param_ui.addWidget(type_, **widget_kwargs) 419 param_ui.addWidget(type_, **widget_kwargs)
390 420
391 return param_ui.toXml() 421 return param_ui.toXml()
392 422
397 The <option/> tags must be direct children of <param/>. 427 The <option/> tags must be direct children of <param/>.
398 @param param (domish.Element): element 428 @param param (domish.Element): element
399 @return: a tuple (options, selected_value) 429 @return: a tuple (options, selected_value)
400 """ 430 """
401 if len(param.getElementsByTagName("options")) > 0: 431 if len(param.getElementsByTagName("options")) > 0:
402 raise exceptions.DataError(_("The 'options' tag is not allowed in parameter of type 'list'!")) 432 raise exceptions.DataError(
433 _("The 'options' tag is not allowed in parameter of type 'list'!")
434 )
403 elems = param.getElementsByTagName("option") 435 elems = param.getElementsByTagName("option")
404 if len(elems) == 0: 436 if len(elems) == 0:
405 return [] 437 return []
406 options = [elem.getAttribute("value") for elem in elems] 438 options = [elem.getAttribute("value") for elem in elems]
407 selected = [elem.getAttribute("value") for elem in elems if elem.getAttribute("selected") == 'true'] 439 selected = [
440 elem.getAttribute("value")
441 for elem in elems
442 if elem.getAttribute("selected") == "true"
443 ]
408 return (options, selected) 444 return (options, selected)
445
409 446
410 def _paramsGetListJids(param): 447 def _paramsGetListJids(param):
411 """Retrive jids from a jids_list element. 448 """Retrive jids from a jids_list element.
412 449
413 the <jid/> tags must be direct children of <param/> 450 the <jid/> tags must be direct children of <param/>
414 @param param (domish.Element): element 451 @param param (domish.Element): element
415 @return: a list of jids 452 @return: a list of jids
416 """ 453 """
417 elems = param.getElementsByTagName("jid") 454 elems = param.getElementsByTagName("jid")
418 jids = [elem.firstChild.data for elem in elems 455 jids = [
419 if elem.firstChild is not None 456 elem.firstChild.data
420 and elem.firstChild.nodeType == elem.TEXT_NODE] 457 for elem in elems
458 if elem.firstChild is not None and elem.firstChild.nodeType == elem.TEXT_NODE
459 ]
421 return jids 460 return jids
422 461
423 462
424 ### XMLUI Elements ### 463 ### XMLUI Elements ###
425 464
426 465
427 class Element(object): 466 class Element(object):
428 """ Base XMLUI element """ 467 """ Base XMLUI element """
468
429 type = None 469 type = None
430 470
431 def __init__(self, xmlui, parent=None): 471 def __init__(self, xmlui, parent=None):
432 """Create a container element 472 """Create a container element
433 473
434 @param xmlui: XMLUI instance 474 @param xmlui: XMLUI instance
435 @parent: parent element 475 @parent: parent element
436 """ 476 """
437 assert self.type is not None 477 assert self.type is not None
438 self.children = [] 478 self.children = []
439 if not hasattr(self, 'elem'): 479 if not hasattr(self, "elem"):
440 self.elem = parent.xmlui.doc.createElement(self.type) 480 self.elem = parent.xmlui.doc.createElement(self.type)
441 self.xmlui = xmlui 481 self.xmlui = xmlui
442 if parent is not None: 482 if parent is not None:
443 parent.append(self) 483 parent.append(self)
444 self.parent = parent 484 self.parent = parent
455 return child 495 return child
456 496
457 497
458 class TopElement(Element): 498 class TopElement(Element):
459 """ Main XML Element """ 499 """ Main XML Element """
460 type = 'top' 500
501 type = "top"
461 502
462 def __init__(self, xmlui): 503 def __init__(self, xmlui):
463 self.elem = xmlui.doc.documentElement 504 self.elem = xmlui.doc.documentElement
464 super(TopElement, self).__init__(xmlui) 505 super(TopElement, self).__init__(xmlui)
465 506
466 507
467 class TabElement(Element): 508 class TabElement(Element):
468 """ Used by TabsContainer to give name and label to tabs.""" 509 """ Used by TabsContainer to give name and label to tabs."""
469 type = 'tab' 510
511 type = "tab"
470 512
471 def __init__(self, parent, name, label, selected=False): 513 def __init__(self, parent, name, label, selected=False):
472 """ 514 """
473 515
474 @param parent (TabsContainer): parent container 516 @param parent (TabsContainer): parent container
477 @param selected (bool): set to True to select this tab 519 @param selected (bool): set to True to select this tab
478 """ 520 """
479 if not isinstance(parent, TabsContainer): 521 if not isinstance(parent, TabsContainer):
480 raise exceptions.DataError(_("TabElement must be a child of TabsContainer")) 522 raise exceptions.DataError(_("TabElement must be a child of TabsContainer"))
481 super(TabElement, self).__init__(parent.xmlui, parent) 523 super(TabElement, self).__init__(parent.xmlui, parent)
482 self.elem.setAttribute('name', name) 524 self.elem.setAttribute("name", name)
483 self.elem.setAttribute('label', label) 525 self.elem.setAttribute("label", label)
484 if selected: 526 if selected:
485 self.setSelected(selected) 527 self.setSelected(selected)
486 528
487 def setSelected(self, selected=False): 529 def setSelected(self, selected=False):
488 """Set the tab selected. 530 """Set the tab selected.
489 531
490 @param selected (bool): set to True to select this tab 532 @param selected (bool): set to True to select this tab
491 """ 533 """
492 self.elem.setAttribute('selected', 'true' if selected else 'false') 534 self.elem.setAttribute("selected", "true" if selected else "false")
493 535
494 536
495 class FieldBackElement(Element): 537 class FieldBackElement(Element):
496 """ Used by ButtonWidget to indicate which field have to be sent back """ 538 """ Used by ButtonWidget to indicate which field have to be sent back """
497 type = 'field_back' 539
540 type = "field_back"
498 541
499 def __init__(self, parent, name): 542 def __init__(self, parent, name):
500 assert isinstance(parent, ButtonWidget) 543 assert isinstance(parent, ButtonWidget)
501 super(FieldBackElement, self).__init__(parent.xmlui, parent) 544 super(FieldBackElement, self).__init__(parent.xmlui, parent)
502 self.elem.setAttribute('name', name) 545 self.elem.setAttribute("name", name)
503 546
504 547
505 class InternalFieldElement(Element): 548 class InternalFieldElement(Element):
506 """ Used by internal callbacks to indicate which fields are manipulated """ 549 """ Used by internal callbacks to indicate which fields are manipulated """
507 type = 'internal_field' 550
551 type = "internal_field"
508 552
509 def __init__(self, parent, name): 553 def __init__(self, parent, name):
510 super(InternalFieldElement, self).__init__(parent.xmlui, parent) 554 super(InternalFieldElement, self).__init__(parent.xmlui, parent)
511 self.elem.setAttribute('name', name) 555 self.elem.setAttribute("name", name)
512 556
513 557
514 class InternalDataElement(Element): 558 class InternalDataElement(Element):
515 """ Used by internal callbacks to retrieve extra data """ 559 """ Used by internal callbacks to retrieve extra data """
516 type = 'internal_data' 560
561 type = "internal_data"
517 562
518 def __init__(self, parent, children): 563 def __init__(self, parent, children):
519 super(InternalDataElement, self).__init__(parent.xmlui, parent) 564 super(InternalDataElement, self).__init__(parent.xmlui, parent)
520 assert isinstance(children, list) 565 assert isinstance(children, list)
521 for child in children: 566 for child in children:
522 self.elem.childNodes.append(child) 567 self.elem.childNodes.append(child)
523 568
524 569
525 class OptionElement(Element): 570 class OptionElement(Element):
526 """" Used by ListWidget to specify options """ 571 """" Used by ListWidget to specify options """
527 type = 'option' 572
573 type = "option"
528 574
529 def __init__(self, parent, option, selected=False): 575 def __init__(self, parent, option, selected=False):
530 """ 576 """
531 577
532 @param parent 578 @param parent
539 value, label = option, option 585 value, label = option, option
540 elif isinstance(option, tuple): 586 elif isinstance(option, tuple):
541 value, label = option 587 value, label = option
542 else: 588 else:
543 raise NotImplementedError 589 raise NotImplementedError
544 self.elem.setAttribute('value', value) 590 self.elem.setAttribute("value", value)
545 self.elem.setAttribute('label', label) 591 self.elem.setAttribute("label", label)
546 if selected: 592 if selected:
547 self.elem.setAttribute('selected', 'true') 593 self.elem.setAttribute("selected", "true")
548 594
549 595
550 class JidElement(Element): 596 class JidElement(Element):
551 """" Used by JidsListWidget to specify jids""" 597 """" Used by JidsListWidget to specify jids"""
552 type = 'jid' 598
599 type = "jid"
553 600
554 def __init__(self, parent, jid_): 601 def __init__(self, parent, jid_):
555 """ 602 """
556 @param jid_(jid.JID, unicode): jid to append 603 @param jid_(jid.JID, unicode): jid to append
557 """ 604 """
567 self.elem.appendChild(jid_txt) 614 self.elem.appendChild(jid_txt)
568 615
569 616
570 class RowElement(Element): 617 class RowElement(Element):
571 """" Used by AdvancedListContainer """ 618 """" Used by AdvancedListContainer """
572 type = 'row' 619
620 type = "row"
573 621
574 def __init__(self, parent): 622 def __init__(self, parent):
575 assert isinstance(parent, AdvancedListContainer) 623 assert isinstance(parent, AdvancedListContainer)
576 super(RowElement, self).__init__(parent.xmlui, parent) 624 super(RowElement, self).__init__(parent.xmlui, parent)
577 if parent.next_row_idx is not None: 625 if parent.next_row_idx is not None:
578 if parent.auto_index: 626 if parent.auto_index:
579 raise exceptions.DataError(_("Can't set row index if auto_index is True")) 627 raise exceptions.DataError(_("Can't set row index if auto_index is True"))
580 self.elem.setAttribute('index', parent.next_row_idx) 628 self.elem.setAttribute("index", parent.next_row_idx)
581 parent.next_row_idx = None 629 parent.next_row_idx = None
582 630
583 631
584 class HeaderElement(Element): 632 class HeaderElement(Element):
585 """" Used by AdvancedListContainer """ 633 """" Used by AdvancedListContainer """
586 type = 'header' 634
635 type = "header"
587 636
588 def __init__(self, parent, name=None, label=None, description=None): 637 def __init__(self, parent, name=None, label=None, description=None):
589 """ 638 """
590 @param parent: AdvancedListContainer instance 639 @param parent: AdvancedListContainer instance
591 @param name: name of the container 640 @param name: name of the container
593 @param description: long descriptive text 642 @param description: long descriptive text
594 """ 643 """
595 assert isinstance(parent, AdvancedListContainer) 644 assert isinstance(parent, AdvancedListContainer)
596 super(HeaderElement, self).__init__(parent.xmlui, parent) 645 super(HeaderElement, self).__init__(parent.xmlui, parent)
597 if name: 646 if name:
598 self.elem.setAttribute('name', name) 647 self.elem.setAttribute("name", name)
599 if label: 648 if label:
600 self.elem.setAttribute('label', label) 649 self.elem.setAttribute("label", label)
601 if description: 650 if description:
602 self.elem.setAttribute('description', description) 651 self.elem.setAttribute("description", description)
603 652
604 653
605 ## Containers ## 654 ## Containers ##
606 655
607 656
608 class Container(Element): 657 class Container(Element):
609 """ And Element which contains other ones and has a layout """ 658 """ And Element which contains other ones and has a layout """
659
610 type = None 660 type = None
611 661
612 def __init__(self, xmlui, parent=None): 662 def __init__(self, xmlui, parent=None):
613 """Create a container element 663 """Create a container element
614 664
615 @param xmlui: XMLUI instance 665 @param xmlui: XMLUI instance
616 @parent: parent element or None 666 @parent: parent element or None
617 """ 667 """
618 self.elem = xmlui.doc.createElement('container') 668 self.elem = xmlui.doc.createElement("container")
619 super(Container, self).__init__(xmlui, parent) 669 super(Container, self).__init__(xmlui, parent)
620 self.elem.setAttribute('type', self.type) 670 self.elem.setAttribute("type", self.type)
621 671
622 def getParentContainer(self): 672 def getParentContainer(self):
623 """ Return first parent container 673 """ Return first parent container
624 674
625 @return: parent container or None 675 @return: parent container or None
626 """ 676 """
627 current = self.parent 677 current = self.parent
628 while(not isinstance(current, (Container)) and 678 while not isinstance(current, (Container)) and current is not None:
629 current is not None):
630 current = current.parent 679 current = current.parent
631 return current 680 return current
632 681
633 682
634 class VerticalContainer(Container): 683 class VerticalContainer(Container):
674 self.xmlui.changeContainer(parent_container) 723 self.xmlui.changeContainer(parent_container)
675 724
676 725
677 class AdvancedListContainer(Container): 726 class AdvancedListContainer(Container):
678 """A list which can contain other widgets, headers, etc""" 727 """A list which can contain other widgets, headers, etc"""
728
679 type = "advanced_list" 729 type = "advanced_list"
680 730
681 def __init__(self, xmlui, callback_id=None, name=None, headers=None, items=None, columns=None, selectable='no', auto_index=False, parent=None): 731 def __init__(
732 self,
733 xmlui,
734 callback_id=None,
735 name=None,
736 headers=None,
737 items=None,
738 columns=None,
739 selectable="no",
740 auto_index=False,
741 parent=None,
742 ):
682 """Create an advanced list 743 """Create an advanced list
683 744
684 @param headers: optional headers information 745 @param headers: optional headers information
685 @param callback_id: id of the method to call when selection is done 746 @param callback_id: id of the method to call when selection is done
686 @param items: list of widgets to add (just the first row) 747 @param items: list of widgets to add (just the first row)
689 'no': nothing is done 750 'no': nothing is done
690 'single': one row can be selected 751 'single': one row can be selected
691 @param auto_index: if True, indexes will be generated by frontends, starting from 0 752 @param auto_index: if True, indexes will be generated by frontends, starting from 0
692 @return: created element 753 @return: created element
693 """ 754 """
694 assert selectable in ('no', 'single') 755 assert selectable in ("no", "single")
695 if not items and columns is None: 756 if not items and columns is None:
696 raise exceptions.DataError(_("either items or columns need do be filled")) 757 raise exceptions.DataError(_("either items or columns need do be filled"))
697 if headers is None: 758 if headers is None:
698 headers = [] 759 headers = []
699 if items is None: 760 if items is None:
704 self._columns = columns 765 self._columns = columns
705 self._item_idx = 0 766 self._item_idx = 0
706 self.current_row = None 767 self.current_row = None
707 if headers: 768 if headers:
708 if len(headers) != self._columns: 769 if len(headers) != self._columns:
709 raise exceptions.DataError(_("Headers lenght doesn't correspond to columns")) 770 raise exceptions.DataError(
771 _("Headers lenght doesn't correspond to columns")
772 )
710 self.addHeaders(headers) 773 self.addHeaders(headers)
711 if items: 774 if items:
712 self.addItems(items) 775 self.addItems(items)
713 self.elem.setAttribute('columns', str(self._columns)) 776 self.elem.setAttribute("columns", str(self._columns))
714 if callback_id is not None: 777 if callback_id is not None:
715 self.elem.setAttribute('callback', callback_id) 778 self.elem.setAttribute("callback", callback_id)
716 self.elem.setAttribute('selectable', selectable) 779 self.elem.setAttribute("selectable", selectable)
717 self.auto_index = auto_index 780 self.auto_index = auto_index
718 if auto_index: 781 if auto_index:
719 self.elem.setAttribute('auto_index', 'true') 782 self.elem.setAttribute("auto_index", "true")
720 self.next_row_idx = None 783 self.next_row_idx = None
721 784
722 def addHeaders(self, headers): 785 def addHeaders(self, headers):
723 for header in headers: 786 for header in headers:
724 self.addHeader(header) 787 self.addHeader(header)
768 831
769 @param xmlui: XMLUI instance 832 @param xmlui: XMLUI instance
770 @param name: name of the element or None 833 @param name: name of the element or None
771 @param parent: parent element or None 834 @param parent: parent element or None
772 """ 835 """
773 self.elem = xmlui.doc.createElement('widget') 836 self.elem = xmlui.doc.createElement("widget")
774 super(Widget, self).__init__(xmlui, parent) 837 super(Widget, self).__init__(xmlui, parent)
775 if name: 838 if name:
776 self.elem.setAttribute('name', name) 839 self.elem.setAttribute("name", name)
777 if name in xmlui.named_widgets: 840 if name in xmlui.named_widgets:
778 raise exceptions.ConflictError(_(u'A widget with the name "{name}" already exists.').format(name=name)) 841 raise exceptions.ConflictError(
842 _(u'A widget with the name "{name}" already exists.').format(
843 name=name
844 )
845 )
779 xmlui.named_widgets[name] = self 846 xmlui.named_widgets[name] = self
780 self.elem.setAttribute('type', self.type) 847 self.elem.setAttribute("type", self.type)
781 848
782 def setInternalCallback(self, callback, fields, data_elts=None): 849 def setInternalCallback(self, callback, fields, data_elts=None):
783 """Set an internal UI callback when the widget value is changed. 850 """Set an internal UI callback when the widget value is changed.
784 851
785 The internal callbacks are NO callback ids, they are strings from 852 The internal callbacks are NO callback ids, they are strings from
795 to which the JID selected in A belongs. 862 to which the JID selected in A belongs.
796 - more operation to be added when necessary... 863 - more operation to be added when necessary...
797 @param fields (list): a list of widget names (string) 864 @param fields (list): a list of widget names (string)
798 @param data_elts (list[Element]): extra data elements 865 @param data_elts (list[Element]): extra data elements
799 """ 866 """
800 self.elem.setAttribute('internal_callback', callback) 867 self.elem.setAttribute("internal_callback", callback)
801 if fields: 868 if fields:
802 for field in fields: 869 for field in fields:
803 InternalFieldElement(self, field) 870 InternalFieldElement(self, field)
804 if data_elts: 871 if data_elts:
805 InternalDataElement(self, data_elts) 872 InternalDataElement(self, data_elts)
806 873
807 874
808 class EmptyWidget(Widget): 875 class EmptyWidget(Widget):
809 """Place holder widget""" 876 """Place holder widget"""
810 type = 'empty' 877
878 type = "empty"
811 879
812 880
813 class TextWidget(Widget): 881 class TextWidget(Widget):
814 """Used for blob of text""" 882 """Used for blob of text"""
815 type = 'text' 883
884 type = "text"
816 885
817 def __init__(self, xmlui, value, name=None, parent=None): 886 def __init__(self, xmlui, value, name=None, parent=None):
818 super(TextWidget, self).__init__(xmlui, name, parent) 887 super(TextWidget, self).__init__(xmlui, name, parent)
819 value_elt = self.xmlui.doc.createElement('value') 888 value_elt = self.xmlui.doc.createElement("value")
820 text = self.xmlui.doc.createTextNode(value) 889 text = self.xmlui.doc.createTextNode(value)
821 value_elt.appendChild(text) 890 value_elt.appendChild(text)
822 self.elem.appendChild(value_elt) 891 self.elem.appendChild(value_elt)
823 892
824 @property 893 @property
829 class LabelWidget(Widget): 898 class LabelWidget(Widget):
830 """One line blob of text 899 """One line blob of text
831 900
832 used most of time to display the desciption or name of the next widget 901 used most of time to display the desciption or name of the next widget
833 """ 902 """
834 type = 'label' 903
904 type = "label"
835 905
836 def __init__(self, xmlui, label, name=None, parent=None): 906 def __init__(self, xmlui, label, name=None, parent=None):
837 super(LabelWidget, self).__init__(xmlui, name, parent) 907 super(LabelWidget, self).__init__(xmlui, name, parent)
838 self.elem.setAttribute('value', label) 908 self.elem.setAttribute("value", label)
839 909
840 910
841 class JidWidget(Widget): 911 class JidWidget(Widget):
842 """Used to display a Jabber ID, some specific methods can be added""" 912 """Used to display a Jabber ID, some specific methods can be added"""
843 type = 'jid' 913
914 type = "jid"
844 915
845 def __init__(self, xmlui, jid, name=None, parent=None): 916 def __init__(self, xmlui, jid, name=None, parent=None):
846 super(JidWidget, self).__init__(xmlui, name, parent) 917 super(JidWidget, self).__init__(xmlui, name, parent)
847 try: 918 try:
848 self.elem.setAttribute('value', jid.full()) 919 self.elem.setAttribute("value", jid.full())
849 except AttributeError: 920 except AttributeError:
850 self.elem.setAttribute('value', unicode(jid)) 921 self.elem.setAttribute("value", unicode(jid))
851 922
852 923
853 class DividerWidget(Widget): 924 class DividerWidget(Widget):
854 type = 'divider' 925 type = "divider"
855 926
856 def __init__(self, xmlui, style='line', name=None, parent=None): 927 def __init__(self, xmlui, style="line", name=None, parent=None):
857 """ Create a divider 928 """ Create a divider
858 929
859 @param xmlui: XMLUI instance 930 @param xmlui: XMLUI instance
860 @param style: one of: 931 @param style: one of:
861 - line: a simple line 932 - line: a simple line
866 @param name: name of the widget 937 @param name: name of the widget
867 @param parent: parent container 938 @param parent: parent container
868 939
869 """ 940 """
870 super(DividerWidget, self).__init__(xmlui, name, parent) 941 super(DividerWidget, self).__init__(xmlui, name, parent)
871 self.elem.setAttribute('style', style) 942 self.elem.setAttribute("style", style)
872 943
873 944
874 ### Inputs ### 945 ### Inputs ###
875 946
876 947
877 class InputWidget(Widget): 948 class InputWidget(Widget):
878 """Widget which can accept user inputs 949 """Widget which can accept user inputs
879 950
880 used mainly in forms 951 used mainly in forms
881 """ 952 """
953
882 def __init__(self, xmlui, name=None, parent=None, read_only=False): 954 def __init__(self, xmlui, name=None, parent=None, read_only=False):
883 super(InputWidget, self).__init__(xmlui, name, parent) 955 super(InputWidget, self).__init__(xmlui, name, parent)
884 if read_only: 956 if read_only:
885 self.elem.setAttribute('read_only', 'true') 957 self.elem.setAttribute("read_only", "true")
886 958
887 959
888 class StringWidget(InputWidget): 960 class StringWidget(InputWidget):
889 type = 'string' 961 type = "string"
890 962
891 def __init__(self, xmlui, value=None, name=None, parent=None, read_only=False): 963 def __init__(self, xmlui, value=None, name=None, parent=None, read_only=False):
892 super(StringWidget, self).__init__(xmlui, name, parent, read_only=read_only) 964 super(StringWidget, self).__init__(xmlui, name, parent, read_only=read_only)
893 if value: 965 if value:
894 value_elt = self.xmlui.doc.createElement('value') 966 value_elt = self.xmlui.doc.createElement("value")
895 text = self.xmlui.doc.createTextNode(value) 967 text = self.xmlui.doc.createTextNode(value)
896 value_elt.appendChild(text) 968 value_elt.appendChild(text)
897 self.elem.appendChild(value_elt) 969 self.elem.appendChild(value_elt)
898 970
899 @property 971 @property
900 def value(self): 972 def value(self):
901 return self.elem.firstChild.firstChild.wholeText 973 return self.elem.firstChild.firstChild.wholeText
902 974
903 975
904 class PasswordWidget(StringWidget): 976 class PasswordWidget(StringWidget):
905 type = 'password' 977 type = "password"
906 978
907 979
908 class TextBoxWidget(StringWidget): 980 class TextBoxWidget(StringWidget):
909 type = 'textbox' 981 type = "textbox"
910 982
911 983
912 class JidInputWidget(StringWidget): 984 class JidInputWidget(StringWidget):
913 type = 'jid_input' 985 type = "jid_input"
914 986
915 987
916 # TODO handle min and max values 988 # TODO handle min and max values
917 class IntWidget(StringWidget): 989 class IntWidget(StringWidget):
918 type = 'int' 990 type = "int"
919 991
920 def __init__(self, xmlui, value=0, name=None, parent=None, read_only=False): 992 def __init__(self, xmlui, value=0, name=None, parent=None, read_only=False):
921 try: 993 try:
922 int(value) 994 int(value)
923 except ValueError: 995 except ValueError:
924 raise exceptions.DataError(_("Value must be an integer")) 996 raise exceptions.DataError(_("Value must be an integer"))
925 super(IntWidget, self).__init__(xmlui, value, name, parent, read_only=read_only) 997 super(IntWidget, self).__init__(xmlui, value, name, parent, read_only=read_only)
926 998
927 999
928 class BoolWidget(InputWidget): 1000 class BoolWidget(InputWidget):
929 type = 'bool' 1001 type = "bool"
930 1002
931 def __init__(self, xmlui, value='false', name=None, parent=None, read_only=False): 1003 def __init__(self, xmlui, value="false", name=None, parent=None, read_only=False):
932 if isinstance(value, bool): 1004 if isinstance(value, bool):
933 value = 'true' if value else 'false' 1005 value = "true" if value else "false"
934 elif value == '0': 1006 elif value == "0":
935 value = 'false' 1007 value = "false"
936 elif value == '1': 1008 elif value == "1":
937 value = 'true' 1009 value = "true"
938 if value not in ('true', 'false'): 1010 if value not in ("true", "false"):
939 raise exceptions.DataError(_("Value must be 0, 1, false or true")) 1011 raise exceptions.DataError(_("Value must be 0, 1, false or true"))
940 super(BoolWidget, self).__init__(xmlui, name, parent, read_only=read_only) 1012 super(BoolWidget, self).__init__(xmlui, name, parent, read_only=read_only)
941 self.elem.setAttribute('value', value) 1013 self.elem.setAttribute("value", value)
942 1014
943 1015
944 class ButtonWidget(Widget): 1016 class ButtonWidget(Widget):
945 type = 'button' 1017 type = "button"
946 1018
947 def __init__(self, xmlui, callback_id, value=None, fields_back=None, name=None, parent=None): 1019 def __init__(
1020 self, xmlui, callback_id, value=None, fields_back=None, name=None, parent=None
1021 ):
948 """Add a button 1022 """Add a button
949 1023
950 @param callback_id: callback which will be called if button is pressed 1024 @param callback_id: callback which will be called if button is pressed
951 @param value: label of the button 1025 @param value: label of the button
952 @param fields_back: list of names of field to give back when pushing the button 1026 @param fields_back: list of names of field to give back when pushing the button
954 @param parent: parent container 1028 @param parent: parent container
955 """ 1029 """
956 if fields_back is None: 1030 if fields_back is None:
957 fields_back = [] 1031 fields_back = []
958 super(ButtonWidget, self).__init__(xmlui, name, parent) 1032 super(ButtonWidget, self).__init__(xmlui, name, parent)
959 self.elem.setAttribute('callback', callback_id) 1033 self.elem.setAttribute("callback", callback_id)
960 if value: 1034 if value:
961 self.elem.setAttribute('value', value) 1035 self.elem.setAttribute("value", value)
962 for field in fields_back: 1036 for field in fields_back:
963 FieldBackElement(self, field) 1037 FieldBackElement(self, field)
964 1038
965 1039
966 class ListWidget(InputWidget): 1040 class ListWidget(InputWidget):
967 type = 'list' 1041 type = "list"
968 STYLES = (u'multi', u'noselect', u'extensible', u'reducible', u'inline') 1042 STYLES = (u"multi", u"noselect", u"extensible", u"reducible", u"inline")
969 1043
970 def __init__(self, xmlui, options, selected=None, styles=None, name=None, parent=None): 1044 def __init__(
1045 self, xmlui, options, selected=None, styles=None, name=None, parent=None
1046 ):
971 """ 1047 """
972 1048
973 @param xmlui 1049 @param xmlui
974 @param options (list[option]): each option can be given as: 1050 @param options (list[option]): each option can be given as:
975 - a single string if the label and the value are the same 1051 - a single string if the label and the value are the same
989 styles = set() if styles is None else set(styles) 1065 styles = set() if styles is None else set(styles)
990 if styles is None: 1066 if styles is None:
991 styles = set() 1067 styles = set()
992 else: 1068 else:
993 styles = set(styles) 1069 styles = set(styles)
994 if u'noselect' in styles and (u'multi' in styles or selected): 1070 if u"noselect" in styles and (u"multi" in styles or selected):
995 raise exceptions.DataError(_(u'"multi" flag and "selected" option are not compatible with "noselect" flag')) 1071 raise exceptions.DataError(
1072 _(
1073 u'"multi" flag and "selected" option are not compatible with "noselect" flag'
1074 )
1075 )
996 if not options: 1076 if not options:
997 # we can have no options if we get a submitted data form 1077 # we can have no options if we get a submitted data form
998 # but we can't use submitted values directly, 1078 # but we can't use submitted values directly,
999 # because we would not have the labels 1079 # because we would not have the labels
1000 log.warning(_('empty "options" list')) 1080 log.warning(_('empty "options" list'))
1016 1096
1017 def setStyles(self, styles): 1097 def setStyles(self, styles):
1018 if not styles.issubset(self.STYLES): 1098 if not styles.issubset(self.STYLES):
1019 raise exceptions.DataError(_(u"invalid styles")) 1099 raise exceptions.DataError(_(u"invalid styles"))
1020 for style in styles: 1100 for style in styles:
1021 self.elem.setAttribute(style, 'yes') 1101 self.elem.setAttribute(style, "yes")
1022 # TODO: check flags incompatibily (noselect and multi) like in __init__ 1102 # TODO: check flags incompatibily (noselect and multi) like in __init__
1023 1103
1024 def setStyle(self, style): 1104 def setStyle(self, style):
1025 self.setStyles([style]) 1105 self.setStyles([style])
1026 1106
1027 @property 1107 @property
1028 def value(self): 1108 def value(self):
1029 """Return the value of first selected option""" 1109 """Return the value of first selected option"""
1030 for child in self.elem.childNodes: 1110 for child in self.elem.childNodes:
1031 if child.tagName == u'option' and child.getAttribute(u'selected') == u'true': 1111 if child.tagName == u"option" and child.getAttribute(u"selected") == u"true":
1032 return child.getAttribute(u'value') 1112 return child.getAttribute(u"value")
1033 return u'' 1113 return u""
1114
1034 1115
1035 class JidsListWidget(InputWidget): 1116 class JidsListWidget(InputWidget):
1036 """A list of text or jids where elements can be added/removed or modified""" 1117 """A list of text or jids where elements can be added/removed or modified"""
1037 type = 'jids_list' 1118
1119 type = "jids_list"
1038 1120
1039 def __init__(self, xmlui, jids, styles=None, name=None, parent=None): 1121 def __init__(self, xmlui, jids, styles=None, name=None, parent=None):
1040 """ 1122 """
1041 1123
1042 @param xmlui 1124 @param xmlui
1045 @param name (string) 1127 @param name (string)
1046 @param parent 1128 @param parent
1047 """ 1129 """
1048 super(JidsListWidget, self).__init__(xmlui, name, parent) 1130 super(JidsListWidget, self).__init__(xmlui, name, parent)
1049 styles = set() if styles is None else set(styles) 1131 styles = set() if styles is None else set(styles)
1050 if not styles.issubset([]): # TODO 1132 if not styles.issubset([]): # TODO
1051 raise exceptions.DataError(_("invalid styles")) 1133 raise exceptions.DataError(_("invalid styles"))
1052 for style in styles: 1134 for style in styles:
1053 self.elem.setAttribute(style, 'yes') 1135 self.elem.setAttribute(style, "yes")
1054 if not jids: 1136 if not jids:
1055 log.debug('empty jids list') 1137 log.debug("empty jids list")
1056 else: 1138 else:
1057 self.addJids(jids) 1139 self.addJids(jids)
1058 1140
1059 def addJids(self, jids): 1141 def addJids(self, jids):
1060 for jid_ in jids: 1142 for jid_ in jids:
1064 ## Dialog Elements ## 1146 ## Dialog Elements ##
1065 1147
1066 1148
1067 class DialogElement(Element): 1149 class DialogElement(Element):
1068 """Main dialog element """ 1150 """Main dialog element """
1069 type = 'dialog' 1151
1152 type = "dialog"
1070 1153
1071 def __init__(self, parent, type_, level=None): 1154 def __init__(self, parent, type_, level=None):
1072 if not isinstance(parent, TopElement): 1155 if not isinstance(parent, TopElement):
1073 raise exceptions.DataError(_("DialogElement must be a direct child of TopElement")) 1156 raise exceptions.DataError(
1157 _("DialogElement must be a direct child of TopElement")
1158 )
1074 super(DialogElement, self).__init__(parent.xmlui, parent) 1159 super(DialogElement, self).__init__(parent.xmlui, parent)
1075 self.elem.setAttribute(C.XMLUI_DATA_TYPE, type_) 1160 self.elem.setAttribute(C.XMLUI_DATA_TYPE, type_)
1076 self.elem.setAttribute(C.XMLUI_DATA_LVL, level or C.XMLUI_DATA_LVL_DEFAULT) 1161 self.elem.setAttribute(C.XMLUI_DATA_LVL, level or C.XMLUI_DATA_LVL_DEFAULT)
1077 1162
1078 1163
1079 class MessageElement(Element): 1164 class MessageElement(Element):
1080 """Element with the instruction message""" 1165 """Element with the instruction message"""
1166
1081 type = C.XMLUI_DATA_MESS 1167 type = C.XMLUI_DATA_MESS
1082 1168
1083 def __init__(self, parent, message): 1169 def __init__(self, parent, message):
1084 if not isinstance(parent, DialogElement): 1170 if not isinstance(parent, DialogElement):
1085 raise exceptions.DataError(_("MessageElement must be a direct child of DialogElement")) 1171 raise exceptions.DataError(
1172 _("MessageElement must be a direct child of DialogElement")
1173 )
1086 super(MessageElement, self).__init__(parent.xmlui, parent) 1174 super(MessageElement, self).__init__(parent.xmlui, parent)
1087 message_txt = self.xmlui.doc.createTextNode(message) 1175 message_txt = self.xmlui.doc.createTextNode(message)
1088 self.elem.appendChild(message_txt) 1176 self.elem.appendChild(message_txt)
1089 1177
1090 1178
1091 class ButtonsElement(Element): 1179 class ButtonsElement(Element):
1092 """Buttons element which indicate which set to use""" 1180 """Buttons element which indicate which set to use"""
1093 type = 'buttons' 1181
1182 type = "buttons"
1094 1183
1095 def __init__(self, parent, set_): 1184 def __init__(self, parent, set_):
1096 if not isinstance(parent, DialogElement): 1185 if not isinstance(parent, DialogElement):
1097 raise exceptions.DataError(_("ButtonsElement must be a direct child of DialogElement")) 1186 raise exceptions.DataError(
1187 _("ButtonsElement must be a direct child of DialogElement")
1188 )
1098 super(ButtonsElement, self).__init__(parent.xmlui, parent) 1189 super(ButtonsElement, self).__init__(parent.xmlui, parent)
1099 self.elem.setAttribute('set', set_) 1190 self.elem.setAttribute("set", set_)
1100 1191
1101 1192
1102 class FileElement(Element): 1193 class FileElement(Element):
1103 """File element used for FileDialog""" 1194 """File element used for FileDialog"""
1104 type = 'file' 1195
1196 type = "file"
1105 1197
1106 def __init__(self, parent, type_): 1198 def __init__(self, parent, type_):
1107 if not isinstance(parent, DialogElement): 1199 if not isinstance(parent, DialogElement):
1108 raise exceptions.DataError(_("FileElement must be a direct child of DialogElement")) 1200 raise exceptions.DataError(
1201 _("FileElement must be a direct child of DialogElement")
1202 )
1109 super(FileElement, self).__init__(parent.xmlui, parent) 1203 super(FileElement, self).__init__(parent.xmlui, parent)
1110 self.elem.setAttribute('type', type_) 1204 self.elem.setAttribute("type", type_)
1111 1205
1112 1206
1113 ## XMLUI main class 1207 ## XMLUI main class
1114 1208
1115 1209
1116 class XMLUI(object): 1210 class XMLUI(object):
1117 """This class is used to create a user interface (form/window/parameters/etc) using SàT XML""" 1211 """This class is used to create a user interface (form/window/parameters/etc) using SàT XML"""
1118 1212
1119 def __init__(self, panel_type="window", container="vertical", dialog_opt=None, title=None, submit_id=None, session_id=None): 1213 def __init__(
1214 self,
1215 panel_type="window",
1216 container="vertical",
1217 dialog_opt=None,
1218 title=None,
1219 submit_id=None,
1220 session_id=None,
1221 ):
1120 """Init SàT XML Panel 1222 """Init SàT XML Panel
1121 1223
1122 @param panel_type: one of 1224 @param panel_type: one of
1123 - C.XMLUI_WINDOW (new window) 1225 - C.XMLUI_WINDOW (new window)
1124 - C.XMLUI_POPUP 1226 - C.XMLUI_POPUP
1160 @param title: title or default if None 1262 @param title: title or default if None
1161 @param submit_id: callback id to call for panel_type we can submit (form, param, dialog) 1263 @param submit_id: callback id to call for panel_type we can submit (form, param, dialog)
1162 @param session_id: use to keep a session attached to the dialog, must be returned by frontends 1264 @param session_id: use to keep a session attached to the dialog, must be returned by frontends
1163 @attribute named_widgets(dict): map from name to widget 1265 @attribute named_widgets(dict): map from name to widget
1164 """ 1266 """
1165 self._introspect() # FIXME: why doing that on each XMLUI ? should be done once 1267 self._introspect() # FIXME: why doing that on each XMLUI ? should be done once
1166 if panel_type not in [C.XMLUI_WINDOW, C.XMLUI_FORM, C.XMLUI_PARAM, C.XMLUI_POPUP, C.XMLUI_DIALOG]: 1268 if panel_type not in [
1269 C.XMLUI_WINDOW,
1270 C.XMLUI_FORM,
1271 C.XMLUI_PARAM,
1272 C.XMLUI_POPUP,
1273 C.XMLUI_DIALOG,
1274 ]:
1167 raise exceptions.DataError(_("Unknown panel type [%s]") % panel_type) 1275 raise exceptions.DataError(_("Unknown panel type [%s]") % panel_type)
1168 if panel_type == C.XMLUI_FORM and submit_id is None: 1276 if panel_type == C.XMLUI_FORM and submit_id is None:
1169 raise exceptions.DataError(_("form XMLUI need a submit_id")) 1277 raise exceptions.DataError(_("form XMLUI need a submit_id"))
1170 if not isinstance(container, basestring): 1278 if not isinstance(container, basestring):
1171 raise exceptions.DataError(_("container argument must be a string")) 1279 raise exceptions.DataError(_("container argument must be a string"))
1172 if dialog_opt is not None and panel_type != C.XMLUI_DIALOG: 1280 if dialog_opt is not None and panel_type != C.XMLUI_DIALOG:
1173 raise exceptions.DataError(_("dialog_opt can only be used with dialog panels")) 1281 raise exceptions.DataError(
1282 _("dialog_opt can only be used with dialog panels")
1283 )
1174 self.type = panel_type 1284 self.type = panel_type
1175 impl = minidom.getDOMImplementation() 1285 impl = minidom.getDOMImplementation()
1176 1286
1177 self.doc = impl.createDocument(None, "sat_xmlui", None) 1287 self.doc = impl.createDocument(None, "sat_xmlui", None)
1178 top_element = self.doc.documentElement 1288 top_element = self.doc.documentElement
1195 self._containers = {} 1305 self._containers = {}
1196 self._widgets = {} 1306 self._widgets = {}
1197 for obj in globals().values(): 1307 for obj in globals().values():
1198 try: 1308 try:
1199 if issubclass(obj, Widget): 1309 if issubclass(obj, Widget):
1200 if obj.__name__ == 'Widget': 1310 if obj.__name__ == "Widget":
1201 continue 1311 continue
1202 self._widgets[obj.type] = obj 1312 self._widgets[obj.type] = obj
1203 elif issubclass(obj, Container): 1313 elif issubclass(obj, Container):
1204 if obj.__name__ == 'Container': 1314 if obj.__name__ == "Container":
1205 continue 1315 continue
1206 self._containers[obj.type] = obj 1316 self._containers[obj.type] = obj
1207 except TypeError: 1317 except TypeError:
1208 pass 1318 pass
1209 1319
1210 def __del__(self): 1320 def __del__(self):
1211 self.doc.unlink() 1321 self.doc.unlink()
1212 1322
1213 def __getattr__(self, name): 1323 def __getattr__(self, name):
1214 if name.startswith("add") and name not in ('addWidget',): # addWidgetName(...) create an instance of WidgetName 1324 if name.startswith("add") and name not in (
1325 "addWidget",
1326 ): # addWidgetName(...) create an instance of WidgetName
1215 if self.type == C.XMLUI_DIALOG: 1327 if self.type == C.XMLUI_DIALOG:
1216 raise exceptions.InternalError(_("addXXX can't be used with dialogs")) 1328 raise exceptions.InternalError(_("addXXX can't be used with dialogs"))
1217 class_name = name[3:] + "Widget" 1329 class_name = name[3:] + "Widget"
1218 if class_name in globals(): 1330 if class_name in globals():
1219 cls = globals()[class_name] 1331 cls = globals()[class_name]
1220 if issubclass(cls, Widget): 1332 if issubclass(cls, Widget):
1333
1221 def createWidget(*args, **kwargs): 1334 def createWidget(*args, **kwargs):
1222 if "parent" not in kwargs: 1335 if "parent" not in kwargs:
1223 kwargs["parent"] = self.current_container 1336 kwargs["parent"] = self.current_container
1224 if "name" not in kwargs and issubclass(cls, InputWidget): # name can be given as first argument or in keyword arguments for InputWidgets 1337 if "name" not in kwargs and issubclass(
1338 cls, InputWidget
1339 ): # name can be given as first argument or in keyword arguments for InputWidgets
1225 args = list(args) 1340 args = list(args)
1226 kwargs["name"] = args.pop(0) 1341 kwargs["name"] = args.pop(0)
1227 return cls(self, *args, **kwargs) 1342 return cls(self, *args, **kwargs)
1343
1228 return createWidget 1344 return createWidget
1229 return object.__getattribute__(self, name) 1345 return object.__getattribute__(self, name)
1230 1346
1231 @property 1347 @property
1232 def submit_id(self): 1348 def submit_id(self):
1268 else: 1384 else:
1269 raise exceptions.DataError("session_id can't be empty") 1385 raise exceptions.DataError("session_id can't be empty")
1270 1386
1271 def _createDialog(self, dialog_opt): 1387 def _createDialog(self, dialog_opt):
1272 dialog_type = dialog_opt.setdefault(C.XMLUI_DATA_TYPE, C.XMLUI_DIALOG_MESSAGE) 1388 dialog_type = dialog_opt.setdefault(C.XMLUI_DATA_TYPE, C.XMLUI_DIALOG_MESSAGE)
1273 if dialog_type in [C.XMLUI_DIALOG_CONFIRM, C.XMLUI_DIALOG_FILE] and self.submit_id is None: 1389 if (
1274 raise exceptions.InternalError(_("Submit ID must be filled for this kind of dialog")) 1390 dialog_type in [C.XMLUI_DIALOG_CONFIRM, C.XMLUI_DIALOG_FILE]
1391 and self.submit_id is None
1392 ):
1393 raise exceptions.InternalError(
1394 _("Submit ID must be filled for this kind of dialog")
1395 )
1275 top_element = TopElement(self) 1396 top_element = TopElement(self)
1276 level = dialog_opt.get(C.XMLUI_DATA_LVL) 1397 level = dialog_opt.get(C.XMLUI_DATA_LVL)
1277 dialog_elt = DialogElement(top_element, dialog_type, level) 1398 dialog_elt = DialogElement(top_element, dialog_type, level)
1278 1399
1279 try: 1400 try:
1307 """Change the current container 1428 """Change the current container
1308 1429
1309 @param container: either container type (container it then created), 1430 @param container: either container type (container it then created),
1310 or an Container instance""" 1431 or an Container instance"""
1311 if isinstance(container, basestring): 1432 if isinstance(container, basestring):
1312 self.current_container = self._createContainer(container, self.current_container.getParentContainer() or self.main_container, **kwargs) 1433 self.current_container = self._createContainer(
1434 container,
1435 self.current_container.getParentContainer() or self.main_container,
1436 **kwargs
1437 )
1313 else: 1438 else:
1314 self.current_container = self.main_container if container is None else container 1439 self.current_container = (
1440 self.main_container if container is None else container
1441 )
1315 assert isinstance(self.current_container, Container) 1442 assert isinstance(self.current_container, Container)
1316 return self.current_container 1443 return self.current_container
1317 1444
1318 def addWidget(self, type_, *args, **kwargs): 1445 def addWidget(self, type_, *args, **kwargs):
1319 """Convenience method to add an element""" 1446 """Convenience method to add an element"""
1329 return self.doc.toxml() 1456 return self.doc.toxml()
1330 1457
1331 1458
1332 # Some sugar for XMLUI dialogs 1459 # Some sugar for XMLUI dialogs
1333 1460
1334 def note(message, title='', level=C.XMLUI_DATA_LVL_INFO): 1461
1462 def note(message, title="", level=C.XMLUI_DATA_LVL_INFO):
1335 """sugar to easily create a Note Dialog 1463 """sugar to easily create a Note Dialog
1336 1464
1337 @param message(unicode): body of the note 1465 @param message(unicode): body of the note
1338 @param title(unicode): title of the note 1466 @param title(unicode): title of the note
1339 @param level(unicode): one of C.XMLUI_DATA_LVL_* 1467 @param level(unicode): one of C.XMLUI_DATA_LVL_*
1340 @return(XMLUI): instance of XMLUI 1468 @return(XMLUI): instance of XMLUI
1341 """ 1469 """
1342 note_xmlui = XMLUI(C.XMLUI_DIALOG, dialog_opt={ 1470 note_xmlui = XMLUI(
1343 C.XMLUI_DATA_TYPE: C.XMLUI_DIALOG_NOTE, 1471 C.XMLUI_DIALOG,
1344 C.XMLUI_DATA_MESS: message, 1472 dialog_opt={
1345 C.XMLUI_DATA_LVL: level}, 1473 C.XMLUI_DATA_TYPE: C.XMLUI_DIALOG_NOTE,
1346 title=title 1474 C.XMLUI_DATA_MESS: message,
1347 ) 1475 C.XMLUI_DATA_LVL: level,
1476 },
1477 title=title,
1478 )
1348 return note_xmlui 1479 return note_xmlui
1349 1480
1350 1481
1351 def quickNote(host, client, message, title='', level=C.XMLUI_DATA_LVL_INFO): 1482 def quickNote(host, client, message, title="", level=C.XMLUI_DATA_LVL_INFO):
1352 """more sugar to do the whole note process""" 1483 """more sugar to do the whole note process"""
1353 note_ui = note(message, title, level) 1484 note_ui = note(message, title, level)
1354 host.actionNew({'xmlui': note_ui.toXml()}, profile=client.profile) 1485 host.actionNew({"xmlui": note_ui.toXml()}, profile=client.profile)
1355 1486
1356 1487
1357 def deferredUI(host, xmlui, chained=False): 1488 def deferredUI(host, xmlui, chained=False):
1358 """create a deferred linked to XMLUI 1489 """create a deferred linked to XMLUI
1359 1490
1361 Must be an XMLUI that you can submit, with submit_id set to '' 1492 Must be an XMLUI that you can submit, with submit_id set to ''
1362 @param chained(bool): True if the Deferred result must be returned to the frontend 1493 @param chained(bool): True if the Deferred result must be returned to the frontend
1363 useful when backend is in a series of dialogs with an ui 1494 useful when backend is in a series of dialogs with an ui
1364 @return (D(data)): a deferred which fire the data 1495 @return (D(data)): a deferred which fire the data
1365 """ 1496 """
1366 assert xmlui.submit_id == '' 1497 assert xmlui.submit_id == ""
1367 xmlui_d = defer.Deferred() 1498 xmlui_d = defer.Deferred()
1368 1499
1369 def onSubmit(data, profile): 1500 def onSubmit(data, profile):
1370 xmlui_d.callback(data) 1501 xmlui_d.callback(data)
1371 return xmlui_d if chained else {} 1502 return xmlui_d if chained else {}
1372 1503
1373 xmlui.submit_id = host.registerCallback(onSubmit, with_data=True, one_shot=True) 1504 xmlui.submit_id = host.registerCallback(onSubmit, with_data=True, one_shot=True)
1374 return xmlui_d 1505 return xmlui_d
1375 1506
1376 def deferXMLUI(host, xmlui, action_extra=None, security_limit=C.NO_SECURITY_LIMIT, chained=False, profile=C.PROF_KEY_NONE): 1507
1508 def deferXMLUI(
1509 host,
1510 xmlui,
1511 action_extra=None,
1512 security_limit=C.NO_SECURITY_LIMIT,
1513 chained=False,
1514 profile=C.PROF_KEY_NONE,
1515 ):
1377 """Create a deferred linked to XMLUI 1516 """Create a deferred linked to XMLUI
1378 1517
1379 @param xmlui(XMLUI): instance of the XMLUI 1518 @param xmlui(XMLUI): instance of the XMLUI
1380 Must be an XMLUI that you can submit, with submit_id set to '' 1519 Must be an XMLUI that you can submit, with submit_id set to ''
1381 @param profile: %(doc_profile)s 1520 @param profile: %(doc_profile)s
1385 @param chained(bool): True if the Deferred result must be returned to the frontend 1524 @param chained(bool): True if the Deferred result must be returned to the frontend
1386 useful when backend is in a series of dialogs with an ui 1525 useful when backend is in a series of dialogs with an ui
1387 @return (data): a deferred which fire the data 1526 @return (data): a deferred which fire the data
1388 """ 1527 """
1389 xmlui_d = deferredUI(host, xmlui, chained) 1528 xmlui_d = deferredUI(host, xmlui, chained)
1390 action_data = {'xmlui': xmlui.toXml()} 1529 action_data = {"xmlui": xmlui.toXml()}
1391 if action_extra is not None: 1530 if action_extra is not None:
1392 action_data.update(action_extra) 1531 action_data.update(action_extra)
1393 host.actionNew(action_data, security_limit=security_limit, keep_id=xmlui.submit_id, profile=profile) 1532 host.actionNew(
1533 action_data,
1534 security_limit=security_limit,
1535 keep_id=xmlui.submit_id,
1536 profile=profile,
1537 )
1394 return xmlui_d 1538 return xmlui_d
1395 1539
1396 def deferDialog(host, message, title=u'Please confirm', type_=C.XMLUI_DIALOG_CONFIRM, options=None, 1540
1397 action_extra=None, security_limit=C.NO_SECURITY_LIMIT, chained=False, profile=C.PROF_KEY_NONE): 1541 def deferDialog(
1542 host,
1543 message,
1544 title=u"Please confirm",
1545 type_=C.XMLUI_DIALOG_CONFIRM,
1546 options=None,
1547 action_extra=None,
1548 security_limit=C.NO_SECURITY_LIMIT,
1549 chained=False,
1550 profile=C.PROF_KEY_NONE,
1551 ):
1398 """Create a submitable dialog and manage it with a deferred 1552 """Create a submitable dialog and manage it with a deferred
1399 1553
1400 @param message(unicode): message to display 1554 @param message(unicode): message to display
1401 @param title(unicode): title of the dialog 1555 @param title(unicode): title of the dialog
1402 @param type(unicode): dialog type (C.XMLUI_DIALOG_*) 1556 @param type(unicode): dialog type (C.XMLUI_DIALOG_*)
1408 useful when backend is in a series of dialogs with an ui 1562 useful when backend is in a series of dialogs with an ui
1409 @param profile: %(doc_profile)s 1563 @param profile: %(doc_profile)s
1410 @return (dict): Deferred dict 1564 @return (dict): Deferred dict
1411 """ 1565 """
1412 assert profile is not None 1566 assert profile is not None
1413 dialog_opt = {'type': type_, 'message': message} 1567 dialog_opt = {"type": type_, "message": message}
1414 if options is not None: 1568 if options is not None:
1415 dialog_opt.update(options) 1569 dialog_opt.update(options)
1416 dialog = XMLUI(C.XMLUI_DIALOG, title=title, dialog_opt=dialog_opt, submit_id='') 1570 dialog = XMLUI(C.XMLUI_DIALOG, title=title, dialog_opt=dialog_opt, submit_id="")
1417 return deferXMLUI(host, dialog, action_extra, security_limit, chained, profile) 1571 return deferXMLUI(host, dialog, action_extra, security_limit, chained, profile)
1572
1418 1573
1419 def deferConfirm(*args, **kwargs): 1574 def deferConfirm(*args, **kwargs):
1420 """call deferDialog and return a boolean instead of the whole data dict""" 1575 """call deferDialog and return a boolean instead of the whole data dict"""
1421 d = deferDialog(*args, **kwargs) 1576 d = deferDialog(*args, **kwargs)
1422 d.addCallback(lambda data: C.bool(data['answer'])) 1577 d.addCallback(lambda data: C.bool(data["answer"]))
1423 return d 1578 return d
1424 1579
1580
1425 # Misc other funtions 1581 # Misc other funtions
1582
1426 1583
1427 class ElementParser(object): 1584 class ElementParser(object):
1428 """callable class to parse XML string into Element""" 1585 """callable class to parse XML string into Element"""
1586
1429 # XXX: Found at http://stackoverflow.com/questions/2093400/how-to-create-twisted-words-xish-domish-element-entirely-from-raw-xml/2095942#2095942 1587 # XXX: Found at http://stackoverflow.com/questions/2093400/how-to-create-twisted-words-xish-domish-element-entirely-from-raw-xml/2095942#2095942
1430 1588
1431 def _escapeHTML(self, matchobj): 1589 def _escapeHTML(self, matchobj):
1432 entity = matchobj.group(1) 1590 entity = matchobj.group(1)
1433 if entity in XML_ENTITIES: 1591 if entity in XML_ENTITIES:
1436 else: 1594 else:
1437 try: 1595 try:
1438 return unichr(htmlentitydefs.name2codepoint[entity]) 1596 return unichr(htmlentitydefs.name2codepoint[entity])
1439 except KeyError: 1597 except KeyError:
1440 log.warning(u"removing unknown entity {}".format(entity)) 1598 log.warning(u"removing unknown entity {}".format(entity))
1441 return u'' 1599 return u""
1442 1600
1443 def __call__(self, raw_xml, force_spaces=False, namespace=None): 1601 def __call__(self, raw_xml, force_spaces=False, namespace=None):
1444 """ 1602 """
1445 @param raw_xml(unicode): the raw XML 1603 @param raw_xml(unicode): the raw XML
1446 @param force_spaces (bool): if True, replace occurrences of '\n' and '\t' with ' '. 1604 @param force_spaces (bool): if True, replace occurrences of '\n' and '\t' with ' '.
1471 parser.DocumentStartEvent = onStart 1629 parser.DocumentStartEvent = onStart
1472 parser.ElementEvent = onElement 1630 parser.ElementEvent = onElement
1473 parser.DocumentEndEvent = onEnd 1631 parser.DocumentEndEvent = onEnd
1474 tmp = domish.Element((None, "s")) 1632 tmp = domish.Element((None, "s"))
1475 if force_spaces: 1633 if force_spaces:
1476 raw_xml = raw_xml.replace('\n', ' ').replace('\t', ' ') 1634 raw_xml = raw_xml.replace("\n", " ").replace("\t", " ")
1477 tmp.addRawXml(raw_xml) 1635 tmp.addRawXml(raw_xml)
1478 parser.parse(tmp.toXml().encode('utf-8')) 1636 parser.parse(tmp.toXml().encode("utf-8"))
1479 top_elt = self.result.firstChildElement() 1637 top_elt = self.result.firstChildElement()
1480 # we now can check if there was a unique element on the top 1638 # we now can check if there was a unique element on the top
1481 # and remove our wrapping <div/> is this was the case 1639 # and remove our wrapping <div/> is this was the case
1482 if len(top_elt.children) == 1 and domish.IElement.providedBy(top_elt.children[0]): 1640 if len(top_elt.children) == 1 and domish.IElement.providedBy(top_elt.children[0]):
1483 top_elt = top_elt.firstChildElement() 1641 top_elt = top_elt.firstChildElement()
1495 for child in node.childNodes: 1653 for child in node.childNodes:
1496 if child.nodeType == child.TEXT_NODE: 1654 if child.nodeType == child.TEXT_NODE:
1497 data.append(child.wholeText) 1655 data.append(child.wholeText)
1498 return u"".join(data) 1656 return u"".join(data)
1499 1657
1500 def findAll(elt, namespaces=None, names=None, ): 1658
1659 def findAll(elt, namespaces=None, names=None):
1501 """Find child element at any depth matching criteria 1660 """Find child element at any depth matching criteria
1502 1661
1503 @param elt(domish.Element): top parent of the elements to find 1662 @param elt(domish.Element): top parent of the elements to find
1504 @param names(iterable[unicode], basestring, None): names to match 1663 @param names(iterable[unicode], basestring, None): names to match
1505 None to accept every names 1664 None to accept every names
1506 @param namespace(iterable[unicode], basestring, None): URIs to match 1665 @param namespace(iterable[unicode], basestring, None): URIs to match
1507 None to accept every namespaces 1666 None to accept every namespaces
1508 @return ((G)domish.Element): found elements 1667 @return ((G)domish.Element): found elements
1509 """ 1668 """
1510 if isinstance(namespaces, basestring): 1669 if isinstance(namespaces, basestring):
1511 namespaces=tuple((namespaces,)) 1670 namespaces = tuple((namespaces,))
1512 if isinstance(names, basestring): 1671 if isinstance(names, basestring):
1513 names=tuple((names,)) 1672 names = tuple((names,))
1514 1673
1515 for child in elt.elements(): 1674 for child in elt.elements():
1516 if (domish.IElement.providedBy(child) and 1675 if (
1517 (not names or child.name in names) and 1676 domish.IElement.providedBy(child)
1518 (not namespaces or child.uri in namespaces)): 1677 and (not names or child.name in names)
1678 and (not namespaces or child.uri in namespaces)
1679 ):
1519 yield child 1680 yield child
1520 for found in findAll(child, namespaces, names): 1681 for found in findAll(child, namespaces, names):
1521 yield found 1682 yield found