Mercurial > libervia-backend
comparison sat/tools/xml_tools.py @ 3028:ab2696e34d29
Python 3 port:
/!\ this is a huge commit
/!\ starting from this commit, SàT is needs Python 3.6+
/!\ SàT maybe be instable or some feature may not work anymore, this will improve with time
This patch port backend, bridge and frontends to Python 3.
Roughly this has been done this way:
- 2to3 tools has been applied (with python 3.7)
- all references to python2 have been replaced with python3 (notably shebangs)
- fixed files not handled by 2to3 (notably the shell script)
- several manual fixes
- fixed issues reported by Python 3 that where not handled in Python 2
- replaced "async" with "async_" when needed (it's a reserved word from Python 3.7)
- replaced zope's "implements" with @implementer decorator
- temporary hack to handle data pickled in database, as str or bytes may be returned,
to be checked later
- fixed hash comparison for password
- removed some code which is not needed anymore with Python 3
- deactivated some code which needs to be checked (notably certificate validation)
- tested with jp, fixed reported issues until some basic commands worked
- ported Primitivus (after porting dependencies like urwid satext)
- more manual fixes
author | Goffi <goffi@goffi.org> |
---|---|
date | Tue, 13 Aug 2019 19:08:41 +0200 |
parents | e2cb04b381bb |
children | 9d0df638c8b4 |
comparison
equal
deleted
inserted
replaced
3027:ff5bcb12ae60 | 3028:ab2696e34d29 |
---|---|
1 #!/usr/bin/env python2 | 1 #!/usr/bin/env python3 |
2 # -*- coding: utf-8 -*- | 2 # -*- coding: utf-8 -*- |
3 | 3 |
4 # SAT: a jabber client | 4 # SAT: a jabber client |
5 # Copyright (C) 2009-2019 Jérôme Poisson (goffi@goffi.org) | 5 # Copyright (C) 2009-2019 Jérôme Poisson (goffi@goffi.org) |
6 | 6 |
28 from twisted.words.xish import domish | 28 from twisted.words.xish import domish |
29 from twisted.words.protocols.jabber import jid | 29 from twisted.words.protocols.jabber import jid |
30 from twisted.internet import defer | 30 from twisted.internet import defer |
31 from sat.core import exceptions | 31 from sat.core import exceptions |
32 from collections import OrderedDict | 32 from collections import OrderedDict |
33 from copy import deepcopy | 33 import html.entities |
34 import htmlentitydefs | |
35 import re | 34 import re |
36 | 35 |
37 """This library help manage XML used in SàT (parameters, registration, etc)""" | 36 """This library help manage XML used in SàT (parameters, registration, etc)""" |
38 | 37 |
39 SAT_FORM_PREFIX = "SAT_FORM_" | 38 SAT_FORM_PREFIX = "SAT_FORM_" |
62 """ | 61 """ |
63 widget_args = [field.value] | 62 widget_args = [field.value] |
64 widget_kwargs = {} | 63 widget_kwargs = {} |
65 if field.fieldType is None and field.ext_type is not None: | 64 if field.fieldType is None and field.ext_type is not None: |
66 # we have an extended field | 65 # we have an extended field |
67 if field.ext_type == u"xml": | 66 if field.ext_type == "xml": |
68 element = field.value | 67 element = field.value |
69 if element.uri == C.NS_XHTML: | 68 if element.uri == C.NS_XHTML: |
70 widget_type = "xhtmlbox" | 69 widget_type = "xhtmlbox" |
71 widget_args[0] = element.toXml() | 70 widget_args[0] = element.toXml() |
72 widget_kwargs["read_only"] = read_only | 71 widget_kwargs["read_only"] = read_only |
73 else: | 72 else: |
74 log.warning(u"unknown XML element, falling back to textbox") | 73 log.warning("unknown XML element, falling back to textbox") |
75 widget_type = "textbox" | 74 widget_type = "textbox" |
76 widget_args[0] = element.toXml() | 75 widget_args[0] = element.toXml() |
77 widget_kwargs["read_only"] = read_only | 76 widget_kwargs["read_only"] = read_only |
78 else: | 77 else: |
79 raise exceptions.DataError(u"unknown extended type {ext_type}".format( | 78 raise exceptions.DataError("unknown extended type {ext_type}".format( |
80 ext_type = field.ext_type)) | 79 ext_type = field.ext_type)) |
81 | 80 |
82 elif field.fieldType == "fixed" or field.fieldType is None: | 81 elif field.fieldType == "fixed" or field.fieldType is None: |
83 widget_type = "text" | 82 widget_type = "text" |
84 if field.value is None: | 83 if field.value is None: |
85 if field.label is None: | 84 if field.label is None: |
86 log.warning(_(u"Fixed field has neither value nor label, ignoring it")) | 85 log.warning(_("Fixed field has neither value nor label, ignoring it")) |
87 field.value = "" | 86 field.value = "" |
88 else: | 87 else: |
89 field.value = field.label | 88 field.value = field.label |
90 field.label = None | 89 field.label = None |
91 widget_args[0] = field.value | 90 widget_args[0] = field.value |
95 elif field.fieldType == "jid-single": | 94 elif field.fieldType == "jid-single": |
96 widget_type = "jid_input" | 95 widget_type = "jid_input" |
97 widget_kwargs["read_only"] = read_only | 96 widget_kwargs["read_only"] = read_only |
98 elif field.fieldType == "text-multi": | 97 elif field.fieldType == "text-multi": |
99 widget_type = "textbox" | 98 widget_type = "textbox" |
100 widget_args[0] = u"\n".join(field.values) | 99 widget_args[0] = "\n".join(field.values) |
101 widget_kwargs["read_only"] = read_only | 100 widget_kwargs["read_only"] = read_only |
102 elif field.fieldType == "hidden": | 101 elif field.fieldType == "hidden": |
103 widget_type = "hidden" | 102 widget_type = "hidden" |
104 elif field.fieldType == "text-private": | 103 elif field.fieldType == "text-private": |
105 widget_type = "password" | 104 widget_type = "password" |
119 ] | 118 ] |
120 widget_kwargs["selected"] = widget_args | 119 widget_kwargs["selected"] = widget_args |
121 widget_args = [] | 120 widget_args = [] |
122 else: | 121 else: |
123 log.error( | 122 log.error( |
124 u"FIXME FIXME FIXME: Type [%s] is not managed yet by SàT" % field.fieldType | 123 "FIXME FIXME FIXME: Type [%s] is not managed yet by SàT" % field.fieldType |
125 ) | 124 ) |
126 widget_type = "string" | 125 widget_type = "string" |
127 widget_kwargs["read_only"] = read_only | 126 widget_kwargs["read_only"] = read_only |
128 | 127 |
129 if field.var: | 128 if field.var: |
204 - headers (dict{unicode: unicode}): form headers (field labels and types) | 203 - headers (dict{unicode: unicode}): form headers (field labels and types) |
205 - xmlui_data (list[tuple]): list of (widget_type, widget_args, widget_kwargs) | 204 - xmlui_data (list[tuple]): list of (widget_type, widget_args, widget_kwargs) |
206 """ | 205 """ |
207 headers = OrderedDict() | 206 headers = OrderedDict() |
208 try: | 207 try: |
209 reported_elt = form_xml.elements("jabber:x:data", "reported").next() | 208 reported_elt = next(form_xml.elements("jabber:x:data", "reported")) |
210 except StopIteration: | 209 except StopIteration: |
211 raise exceptions.DataError( | 210 raise exceptions.DataError( |
212 "Couldn't find expected <reported> tag in %s" % form_xml.toXml() | 211 "Couldn't find expected <reported> tag in %s" % form_xml.toXml() |
213 ) | 212 ) |
214 | 213 |
227 item_elts = form_xml.elements("jabber:x:data", "item") | 226 item_elts = form_xml.elements("jabber:x:data", "item") |
228 | 227 |
229 for item_elt in item_elts: | 228 for item_elt in item_elts: |
230 for elt in item_elt.elements(): | 229 for elt in item_elt.elements(): |
231 if elt.name != "field": | 230 if elt.name != "field": |
232 log.warning(u"Unexpected tag (%s)" % elt.name) | 231 log.warning("Unexpected tag (%s)" % elt.name) |
233 continue | 232 continue |
234 field = data_form.Field.fromElement(elt) | 233 field = data_form.Field.fromElement(elt) |
235 | 234 |
236 xmlui_data.append(_dataFormField2XMLUIData(field)) | 235 xmlui_data.append(_dataFormField2XMLUIData(field)) |
237 | 236 |
301 @return: XMLUI instance | 300 @return: XMLUI instance |
302 """ | 301 """ |
303 # we deepcopy the form because _dataFormField2XMLUIData can modify the value | 302 # we deepcopy the form because _dataFormField2XMLUIData can modify the value |
304 # FIXME: check if it's really important, the only modified value seems to be | 303 # FIXME: check if it's really important, the only modified value seems to be |
305 # the replacement of None by "" on fixed fields | 304 # the replacement of None by "" on fixed fields |
306 form = deepcopy(result_form) | 305 # form = deepcopy(result_form) |
307 form = result_form | 306 form = result_form |
308 for name, field in form.fields.iteritems(): | 307 for name, field in form.fields.items(): |
309 try: | 308 try: |
310 base_field = base_form.fields[name] | 309 base_field = base_form.fields[name] |
311 except KeyError: | 310 except KeyError: |
312 continue | 311 continue |
313 field.options = base_field.options[:] | 312 field.options = base_field.options[:] |
322 @param value: value to clean | 321 @param value: value to clean |
323 @return: value in a non DBus type (only clean string yet) | 322 @return: value in a non DBus type (only clean string yet) |
324 """ | 323 """ |
325 # XXX: must be removed when DBus types will no cause problems anymore | 324 # XXX: must be removed when DBus types will no cause problems anymore |
326 # FIXME: should be cleaned inside D-Bus bridge itself | 325 # FIXME: should be cleaned inside D-Bus bridge itself |
327 if isinstance(value, basestring): | 326 if isinstance(value, str): |
328 return unicode(value) | 327 return str(value) |
329 return value | 328 return value |
330 | 329 |
331 | 330 |
332 def XMLUIResult2DataFormResult(xmlui_data): | 331 def XMLUIResult2DataFormResult(xmlui_data): |
333 """ Extract form data from a XMLUI return. | 332 """ Extract form data from a XMLUI return. |
334 | 333 |
335 @param xmlui_data (dict): data returned by frontends for XMLUI form | 334 @param xmlui_data (dict): data returned by frontends for XMLUI form |
336 @return: dict of data usable by Wokkel's data form | 335 @return: dict of data usable by Wokkel's data form |
337 """ | 336 """ |
338 ret = {} | 337 ret = {} |
339 for key, value in xmlui_data.iteritems(): | 338 for key, value in xmlui_data.items(): |
340 if not key.startswith(SAT_FORM_PREFIX): | 339 if not key.startswith(SAT_FORM_PREFIX): |
341 continue | 340 continue |
342 if isinstance(value, basestring) and u'\n' in value: | 341 if isinstance(value, str) and '\n' in value: |
343 # data form expects multi-lines text to be in separated values | 342 # data form expects multi-lines text to be in separated values |
344 value = value.split(u'\n') | 343 value = value.split('\n') |
345 ret[key[len(SAT_FORM_PREFIX) :]] = _cleanValue(value) | 344 ret[key[len(SAT_FORM_PREFIX) :]] = _cleanValue(value) |
346 return ret | 345 return ret |
347 | 346 |
348 | 347 |
349 def formEscape(name): | 348 def formEscape(name): |
350 """Return escaped name for forms. | 349 """Return escaped name for forms. |
351 | 350 |
352 @param name (unicode): form name | 351 @param name (unicode): form name |
353 @return: unicode | 352 @return: unicode |
354 """ | 353 """ |
355 return u"%s%s" % (SAT_FORM_PREFIX, name) | 354 return "%s%s" % (SAT_FORM_PREFIX, name) |
356 | 355 |
357 | 356 |
358 def isXMLUICancelled(raw_xmlui): | 357 def isXMLUICancelled(raw_xmlui): |
359 """Tell if an XMLUI has been cancelled by checking raw XML""" | 358 """Tell if an XMLUI has been cancelled by checking raw XML""" |
360 return C.bool(raw_xmlui.get(u'cancelled', C.BOOL_FALSE)) | 359 return C.bool(raw_xmlui.get('cancelled', C.BOOL_FALSE)) |
361 | 360 |
362 | 361 |
363 def XMLUIResultToElt(xmlui_data): | 362 def XMLUIResultToElt(xmlui_data): |
364 """Construct result domish.Element from XMLUI result. | 363 """Construct result domish.Element from XMLUI result. |
365 | 364 |
616 @param option (string, tuple) | 615 @param option (string, tuple) |
617 @param selected (boolean) | 616 @param selected (boolean) |
618 """ | 617 """ |
619 assert isinstance(parent, ListWidget) | 618 assert isinstance(parent, ListWidget) |
620 super(OptionElement, self).__init__(parent.xmlui, parent) | 619 super(OptionElement, self).__init__(parent.xmlui, parent) |
621 if isinstance(option, basestring): | 620 if isinstance(option, str): |
622 value, label = option, option | 621 value, label = option, option |
623 elif isinstance(option, tuple): | 622 elif isinstance(option, tuple): |
624 value, label = option | 623 value, label = option |
625 else: | 624 else: |
626 raise NotImplementedError | 625 raise NotImplementedError |
641 """ | 640 """ |
642 assert isinstance(parent, JidsListWidget) | 641 assert isinstance(parent, JidsListWidget) |
643 super(JidElement, self).__init__(parent.xmlui, parent) | 642 super(JidElement, self).__init__(parent.xmlui, parent) |
644 if isinstance(jid_, jid.JID): | 643 if isinstance(jid_, jid.JID): |
645 value = jid_.full() | 644 value = jid_.full() |
646 elif isinstance(jid_, basestring): | 645 elif isinstance(jid_, str): |
647 value = unicode(jid_) | 646 value = str(jid_) |
648 else: | 647 else: |
649 raise NotImplementedError | 648 raise NotImplementedError |
650 jid_txt = self.xmlui.doc.createTextNode(value) | 649 jid_txt = self.xmlui.doc.createTextNode(value) |
651 self.elem.appendChild(jid_txt) | 650 self.elem.appendChild(jid_txt) |
652 | 651 |
877 super(Widget, self).__init__(xmlui, parent) | 876 super(Widget, self).__init__(xmlui, parent) |
878 if name: | 877 if name: |
879 self.elem.setAttribute("name", name) | 878 self.elem.setAttribute("name", name) |
880 if name in xmlui.named_widgets: | 879 if name in xmlui.named_widgets: |
881 raise exceptions.ConflictError( | 880 raise exceptions.ConflictError( |
882 _(u'A widget with the name "{name}" already exists.').format( | 881 _('A widget with the name "{name}" already exists.').format( |
883 name=name | 882 name=name |
884 ) | 883 ) |
885 ) | 884 ) |
886 xmlui.named_widgets[name] = self | 885 xmlui.named_widgets[name] = self |
887 self.elem.setAttribute("type", self.type) | 886 self.elem.setAttribute("type", self.type) |
971 def __init__(self, xmlui, jid, name=None, parent=None): | 970 def __init__(self, xmlui, jid, name=None, parent=None): |
972 super(JidWidget, self).__init__(xmlui, name, parent) | 971 super(JidWidget, self).__init__(xmlui, name, parent) |
973 try: | 972 try: |
974 self.elem.setAttribute("value", jid.full()) | 973 self.elem.setAttribute("value", jid.full()) |
975 except AttributeError: | 974 except AttributeError: |
976 self.elem.setAttribute("value", unicode(jid)) | 975 self.elem.setAttribute("value", str(jid)) |
977 | 976 |
978 | 977 |
979 class DividerWidget(Widget): | 978 class DividerWidget(Widget): |
980 type = "divider" | 979 type = "divider" |
981 | 980 |
1047 word, set to False only if you made the XHTML yourself) | 1046 word, set to False only if you made the XHTML yourself) |
1048 """ | 1047 """ |
1049 if clean: | 1048 if clean: |
1050 if cleanXHTML is None: | 1049 if cleanXHTML is None: |
1051 raise exceptions.NotFound( | 1050 raise exceptions.NotFound( |
1052 u"No cleaning method set, can't clean the XHTML") | 1051 "No cleaning method set, can't clean the XHTML") |
1053 value = cleanXHTML(value) | 1052 value = cleanXHTML(value) |
1054 | 1053 |
1055 super(XHTMLBoxWidget, self).__init__( | 1054 super(XHTMLBoxWidget, self).__init__( |
1056 xmlui, value=value, name=name, parent=parent, read_only=read_only) | 1055 xmlui, value=value, name=name, parent=parent, read_only=read_only) |
1057 | 1056 |
1112 FieldBackElement(self, field) | 1111 FieldBackElement(self, field) |
1113 | 1112 |
1114 | 1113 |
1115 class ListWidget(InputWidget): | 1114 class ListWidget(InputWidget): |
1116 type = "list" | 1115 type = "list" |
1117 STYLES = (u"multi", u"noselect", u"extensible", u"reducible", u"inline") | 1116 STYLES = ("multi", "noselect", "extensible", "reducible", "inline") |
1118 | 1117 |
1119 def __init__( | 1118 def __init__( |
1120 self, xmlui, options, selected=None, styles=None, name=None, parent=None | 1119 self, xmlui, options, selected=None, styles=None, name=None, parent=None |
1121 ): | 1120 ): |
1122 """ | 1121 """ |
1142 styles = set() if styles is None else set(styles) | 1141 styles = set() if styles is None else set(styles) |
1143 if styles is None: | 1142 if styles is None: |
1144 styles = set() | 1143 styles = set() |
1145 else: | 1144 else: |
1146 styles = set(styles) | 1145 styles = set(styles) |
1147 if u"noselect" in styles and (u"multi" in styles or selected): | 1146 if "noselect" in styles and ("multi" in styles or selected): |
1148 raise exceptions.DataError( | 1147 raise exceptions.DataError( |
1149 _( | 1148 _( |
1150 u'"multi" flag and "selected" option are not compatible with ' | 1149 '"multi" flag and "selected" option are not compatible with ' |
1151 u'"noselect" flag' | 1150 '"noselect" flag' |
1152 ) | 1151 ) |
1153 ) | 1152 ) |
1154 if not options: | 1153 if not options: |
1155 # we can have no options if we get a submitted data form | 1154 # we can have no options if we get a submitted data form |
1156 # but we can't use submitted values directly, | 1155 # but we can't use submitted values directly, |
1161 self.setStyles(styles) | 1160 self.setStyles(styles) |
1162 | 1161 |
1163 def addOptions(self, options, selected=None): | 1162 def addOptions(self, options, selected=None): |
1164 """Add options to a multi-values element (e.g. list) """ | 1163 """Add options to a multi-values element (e.g. list) """ |
1165 if selected: | 1164 if selected: |
1166 if isinstance(selected, basestring): | 1165 if isinstance(selected, str): |
1167 selected = [selected] | 1166 selected = [selected] |
1168 else: | 1167 else: |
1169 selected = [] | 1168 selected = [] |
1170 for option in options: | 1169 for option in options: |
1171 assert isinstance(option, basestring) or isinstance(option, tuple) | 1170 assert isinstance(option, str) or isinstance(option, tuple) |
1172 value = option if isinstance(option, basestring) else option[0] | 1171 value = option if isinstance(option, str) else option[0] |
1173 OptionElement(self, option, value in selected) | 1172 OptionElement(self, option, value in selected) |
1174 | 1173 |
1175 def setStyles(self, styles): | 1174 def setStyles(self, styles): |
1176 if not styles.issubset(self.STYLES): | 1175 if not styles.issubset(self.STYLES): |
1177 raise exceptions.DataError(_(u"invalid styles")) | 1176 raise exceptions.DataError(_("invalid styles")) |
1178 for style in styles: | 1177 for style in styles: |
1179 self.elem.setAttribute(style, "yes") | 1178 self.elem.setAttribute(style, "yes") |
1180 # TODO: check flags incompatibily (noselect and multi) like in __init__ | 1179 # TODO: check flags incompatibily (noselect and multi) like in __init__ |
1181 | 1180 |
1182 def setStyle(self, style): | 1181 def setStyle(self, style): |
1184 | 1183 |
1185 @property | 1184 @property |
1186 def value(self): | 1185 def value(self): |
1187 """Return the value of first selected option""" | 1186 """Return the value of first selected option""" |
1188 for child in self.elem.childNodes: | 1187 for child in self.elem.childNodes: |
1189 if child.tagName == u"option" and child.getAttribute(u"selected") == u"true": | 1188 if child.tagName == "option" and child.getAttribute("selected") == "true": |
1190 return child.getAttribute(u"value") | 1189 return child.getAttribute("value") |
1191 return u"" | 1190 return "" |
1192 | 1191 |
1193 | 1192 |
1194 class JidsListWidget(InputWidget): | 1193 class JidsListWidget(InputWidget): |
1195 """A list of text or jids where elements can be added/removed or modified""" | 1194 """A list of text or jids where elements can be added/removed or modified""" |
1196 | 1195 |
1356 C.XMLUI_DIALOG, | 1355 C.XMLUI_DIALOG, |
1357 ]: | 1356 ]: |
1358 raise exceptions.DataError(_("Unknown panel type [%s]") % panel_type) | 1357 raise exceptions.DataError(_("Unknown panel type [%s]") % panel_type) |
1359 if panel_type == C.XMLUI_FORM and submit_id is None: | 1358 if panel_type == C.XMLUI_FORM and submit_id is None: |
1360 raise exceptions.DataError(_("form XMLUI need a submit_id")) | 1359 raise exceptions.DataError(_("form XMLUI need a submit_id")) |
1361 if not isinstance(container, basestring): | 1360 if not isinstance(container, str): |
1362 raise exceptions.DataError(_("container argument must be a string")) | 1361 raise exceptions.DataError(_("container argument must be a string")) |
1363 if dialog_opt is not None and panel_type != C.XMLUI_DIALOG: | 1362 if dialog_opt is not None and panel_type != C.XMLUI_DIALOG: |
1364 raise exceptions.DataError( | 1363 raise exceptions.DataError( |
1365 _("dialog_opt can only be used with dialog panels") | 1364 _("dialog_opt can only be used with dialog panels") |
1366 ) | 1365 ) |
1408 # in bin/sat script then evaluated | 1407 # in bin/sat script then evaluated |
1409 # bin/sat should be refactored | 1408 # bin/sat should be refactored |
1410 # log.debug(u'introspecting XMLUI widgets and containers') | 1409 # log.debug(u'introspecting XMLUI widgets and containers') |
1411 cls._containers = {} | 1410 cls._containers = {} |
1412 cls._widgets = {} | 1411 cls._widgets = {} |
1413 for obj in globals().values(): | 1412 for obj in list(globals().values()): |
1414 try: | 1413 try: |
1415 if issubclass(obj, Widget): | 1414 if issubclass(obj, Widget): |
1416 if obj.__name__ == "Widget": | 1415 if obj.__name__ == "Widget": |
1417 continue | 1416 continue |
1418 cls._widgets[obj.type] = obj | 1417 cls._widgets[obj.type] = obj |
1419 creator_name = u"add" + obj.__name__ | 1418 creator_name = "add" + obj.__name__ |
1420 if creator_name.endswith('Widget'): | 1419 if creator_name.endswith('Widget'): |
1421 creator_name = creator_name[:-6] | 1420 creator_name = creator_name[:-6] |
1422 is_input = issubclass(obj, InputWidget) | 1421 is_input = issubclass(obj, InputWidget) |
1423 # FIXME: cf. above comment | 1422 # FIXME: cf. above comment |
1424 # log.debug(u"Adding {creator_name} creator (is_input={is_input}))" | 1423 # log.debug(u"Adding {creator_name} creator (is_input={is_input}))" |
1523 def changeContainer(self, container, **kwargs): | 1522 def changeContainer(self, container, **kwargs): |
1524 """Change the current container | 1523 """Change the current container |
1525 | 1524 |
1526 @param container: either container type (container it then created), | 1525 @param container: either container type (container it then created), |
1527 or an Container instance""" | 1526 or an Container instance""" |
1528 if isinstance(container, basestring): | 1527 if isinstance(container, str): |
1529 self.current_container = self._createContainer( | 1528 self.current_container = self._createContainer( |
1530 container, | 1529 container, |
1531 self.current_container.getParentContainer() or self.main_container, | 1530 self.current_container.getParentContainer() or self.main_container, |
1532 **kwargs | 1531 **kwargs |
1533 ) | 1532 ) |
1631 profile=profile, | 1630 profile=profile, |
1632 ) | 1631 ) |
1633 return xmlui_d | 1632 return xmlui_d |
1634 | 1633 |
1635 | 1634 |
1636 def deferDialog(host, message, title=u"Please confirm", type_=C.XMLUI_DIALOG_CONFIRM, | 1635 def deferDialog(host, message, title="Please confirm", type_=C.XMLUI_DIALOG_CONFIRM, |
1637 options=None, action_extra=None, security_limit=C.NO_SECURITY_LIMIT, chained=False, | 1636 options=None, action_extra=None, security_limit=C.NO_SECURITY_LIMIT, chained=False, |
1638 profile=C.PROF_KEY_NONE): | 1637 profile=C.PROF_KEY_NONE): |
1639 """Create a submitable dialog and manage it with a deferred | 1638 """Create a submitable dialog and manage it with a deferred |
1640 | 1639 |
1641 @param message(unicode): message to display | 1640 @param message(unicode): message to display |
1687 return new_elt | 1686 return new_elt |
1688 | 1687 |
1689 | 1688 |
1690 def isXHTMLField(field): | 1689 def isXHTMLField(field): |
1691 """Check if a data_form.Field is an XHTML one""" | 1690 """Check if a data_form.Field is an XHTML one""" |
1692 return (field.fieldType is None and field.ext_type == u"xml" and | 1691 return (field.fieldType is None and field.ext_type == "xml" and |
1693 field.value.uri == C.NS_XHTML) | 1692 field.value.uri == C.NS_XHTML) |
1694 | 1693 |
1695 | 1694 |
1696 class ElementParser(object): | 1695 class ElementParser(object): |
1697 """callable class to parse XML string into Element""" | 1696 """callable class to parse XML string into Element""" |
1703 if entity in XML_ENTITIES: | 1702 if entity in XML_ENTITIES: |
1704 # we don't escape XML entities | 1703 # we don't escape XML entities |
1705 return matchobj.group(0) | 1704 return matchobj.group(0) |
1706 else: | 1705 else: |
1707 try: | 1706 try: |
1708 return unichr(htmlentitydefs.name2codepoint[entity]) | 1707 return chr(html.entities.name2codepoint[entity]) |
1709 except KeyError: | 1708 except KeyError: |
1710 log.warning(u"removing unknown entity {}".format(entity)) | 1709 log.warning("removing unknown entity {}".format(entity)) |
1711 return u"" | 1710 return "" |
1712 | 1711 |
1713 def __call__(self, raw_xml, force_spaces=False, namespace=None): | 1712 def __call__(self, raw_xml, force_spaces=False, namespace=None): |
1714 """ | 1713 """ |
1715 @param raw_xml(unicode): the raw XML | 1714 @param raw_xml(unicode): the raw XML |
1716 @param force_spaces (bool): if True, replace occurrences of '\n' and '\t' | 1715 @param force_spaces (bool): if True, replace occurrences of '\n' and '\t' |
1719 element | 1718 element |
1720 """ | 1719 """ |
1721 # we need to wrap element in case | 1720 # we need to wrap element in case |
1722 # there is not a unique one on the top | 1721 # there is not a unique one on the top |
1723 if namespace is not None: | 1722 if namespace is not None: |
1724 raw_xml = u"<div xmlns='{}'>{}</div>".format(namespace, raw_xml) | 1723 raw_xml = "<div xmlns='{}'>{}</div>".format(namespace, raw_xml) |
1725 else: | 1724 else: |
1726 raw_xml = u"<div>{}</div>".format(raw_xml) | 1725 raw_xml = "<div>{}</div>".format(raw_xml) |
1727 | 1726 |
1728 # avoid ParserError on HTML escaped chars | 1727 # avoid ParserError on HTML escaped chars |
1729 raw_xml = html_entity_re.sub(self._escapeHTML, raw_xml) | 1728 raw_xml = html_entity_re.sub(self._escapeHTML, raw_xml) |
1730 | 1729 |
1731 self.result = None | 1730 self.result = None |
1765 """ | 1764 """ |
1766 data = [] | 1765 data = [] |
1767 for child in node.childNodes: | 1766 for child in node.childNodes: |
1768 if child.nodeType == child.TEXT_NODE: | 1767 if child.nodeType == child.TEXT_NODE: |
1769 data.append(child.wholeText) | 1768 data.append(child.wholeText) |
1770 return u"".join(data) | 1769 return "".join(data) |
1771 | 1770 |
1772 | 1771 |
1773 def findAll(elt, namespaces=None, names=None): | 1772 def findAll(elt, namespaces=None, names=None): |
1774 """Find child element at any depth matching criteria | 1773 """Find child element at any depth matching criteria |
1775 | 1774 |
1778 None to accept every names | 1777 None to accept every names |
1779 @param namespace(iterable[unicode], basestring, None): URIs to match | 1778 @param namespace(iterable[unicode], basestring, None): URIs to match |
1780 None to accept every namespaces | 1779 None to accept every namespaces |
1781 @return ((G)domish.Element): found elements | 1780 @return ((G)domish.Element): found elements |
1782 """ | 1781 """ |
1783 if isinstance(namespaces, basestring): | 1782 if isinstance(namespaces, str): |
1784 namespaces = tuple((namespaces,)) | 1783 namespaces = tuple((namespaces,)) |
1785 if isinstance(names, basestring): | 1784 if isinstance(names, str): |
1786 names = tuple((names,)) | 1785 names = tuple((names,)) |
1787 | 1786 |
1788 for child in elt.elements(): | 1787 for child in elt.elements(): |
1789 if ( | 1788 if ( |
1790 domish.IElement.providedBy(child) | 1789 domish.IElement.providedBy(child) |