Mercurial > libervia-backend
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 |