Mercurial > libervia-backend
comparison src/tools/xml_tools.py @ 1444:8ce9924fa92c
tools (xml_tools): better PEP-8 compliance
author | souliane <souliane@mailoo.org> |
---|---|
date | Tue, 21 Jul 2015 15:36:40 +0200 |
parents | 3265a2639182 |
children | a13be5c22334 |
comparison
equal
deleted
inserted
replaced
1443:32d1089df687 | 1444:8ce9924fa92c |
---|---|
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 log = getLogger(__name__) | 23 log = getLogger(__name__) |
24 | |
24 from xml.dom import minidom, NotFoundErr | 25 from xml.dom import minidom, NotFoundErr |
25 from wokkel import data_form | 26 from wokkel import data_form |
26 from twisted.words.xish import domish | 27 from twisted.words.xish import domish |
27 from sat.core import exceptions | 28 from sat.core import exceptions |
28 | 29 |
29 | 30 |
30 """This library help manage XML used in SàT (parameters, registration, etc) """ | 31 """This library help manage XML used in SàT (parameters, registration, etc)""" |
31 | 32 |
32 SAT_FORM_PREFIX = "SAT_FORM_" | 33 SAT_FORM_PREFIX = "SAT_FORM_" |
33 SAT_PARAM_SEPARATOR = "_XMLUI_PARAM_" # used to have unique elements names | 34 SAT_PARAM_SEPARATOR = "_XMLUI_PARAM_" # used to have unique elements names |
34 | 35 |
35 | 36 |
36 # Helper functions | 37 # Helper functions |
37 | 38 |
38 def _dataFormField2XMLUIData(field, read_only=False): | 39 def _dataFormField2XMLUIData(field, read_only=False): |
39 """ Get data needed to create an XMLUI's Widget from Wokkel's data_form's Field | 40 """Get data needed to create an XMLUI's Widget from Wokkel's data_form's Field. |
40 field can be modified (if it's fixed and it has no value) | 41 |
41 @param field: data_form.Field (it uses field.value, field.fieldType, field.label and field.var) | 42 The attribute field can be modified (if it's fixed and it has no value). |
42 @param read_only: if True and it make sens, create a read only input widget | 43 |
43 @return: widget_type, widget_args, widget_kwargs | 44 @param field (data_form.Field): a field with attributes "value", "fieldType", "label" and "var" |
44 | 45 @param read_only (bool): if True and it makes sense, create a read only input widget |
46 @return: a tuple (widget_type, widget_args, widget_kwargs) | |
45 """ | 47 """ |
46 widget_args = [field.value] | 48 widget_args = [field.value] |
47 widget_kwargs = {} | 49 widget_kwargs = {} |
48 if field.fieldType == 'fixed' or field.fieldType is None: | 50 if field.fieldType == 'fixed' or field.fieldType is None: |
49 widget_type = 'text' | 51 widget_type = 'text' |
86 if field.var: | 88 if field.var: |
87 widget_kwargs["name"] = field.var | 89 widget_kwargs["name"] = field.var |
88 | 90 |
89 return widget_type, widget_args, widget_kwargs | 91 return widget_type, widget_args, widget_kwargs |
90 | 92 |
93 | |
91 def dataForm2Widgets(form_ui, form, read_only=False): | 94 def dataForm2Widgets(form_ui, form, read_only=False): |
92 """Complete an existing XMLUI with widget converted frot XEP-0004 data forms | 95 """Complete an existing XMLUI with widget converted from XEP-0004 data forms. |
93 | 96 |
94 @param form_ui: XMLUI instance | 97 @param form_ui (XMLUI): XMLUI instance |
95 @param form: Wokkel's implementation of data form | 98 @param form (data_form.Form): Wokkel's implementation of data form |
96 @return: completed xml_ui | 99 @param read_only (bool): if True and it makes sense, create a read only input widget |
100 @return: the completed XMLUI instance | |
97 """ | 101 """ |
98 if form.instructions: | 102 if form.instructions: |
99 form_ui.addText('\n'.join(form.instructions), 'instructions') | 103 form_ui.addText('\n'.join(form.instructions), 'instructions') |
100 | 104 |
101 form_ui.changeContainer("pairs") | 105 form_ui.changeContainer("pairs") |
110 | 114 |
111 form_ui.addWidget(widget_type, *widget_args, **widget_kwargs) | 115 form_ui.addWidget(widget_type, *widget_args, **widget_kwargs) |
112 | 116 |
113 return form_ui | 117 return form_ui |
114 | 118 |
119 | |
115 def dataForm2XMLUI(form, submit_id, session_id=None, read_only=False): | 120 def dataForm2XMLUI(form, submit_id, session_id=None, read_only=False): |
116 """Take a data form (xep-0004, Wokkel's implementation) and convert it to a SàT XML | 121 """Take a data form (XEP-0004, Wokkel's implementation) and convert it to a SàT XMLUI. |
117 | 122 |
118 @param submit_id: callback id to call when submitting form | 123 @param form (data_form.Form): a Form instance |
119 @param session_id: id to return with the data | 124 @param submit_id (unicode): callback id to call when submitting form |
125 @param session_id (unicode): session id to return with the data | |
126 @param read_only (bool): if True and it makes sense, create a read only input widget | |
127 @return: XMLUI instance | |
120 """ | 128 """ |
121 form_ui = XMLUI("form", "vertical", submit_id=submit_id, session_id=session_id) | 129 form_ui = XMLUI("form", "vertical", submit_id=submit_id, session_id=session_id) |
122 return dataForm2Widgets(form_ui, form, read_only=read_only) | 130 return dataForm2Widgets(form_ui, form, read_only=read_only) |
123 | 131 |
132 | |
124 def dataFormResult2AdvancedList(xmlui, form_xml): | 133 def dataFormResult2AdvancedList(xmlui, form_xml): |
125 """Take a raw data form (not parsed by XEP-0004) and convert it to an advanced list | 134 """Take a raw data form (not parsed by XEP-0004) and convert it to an advanced list. |
126 raw data form is used because Wokkel doesn't manage result items parsing yet | 135 |
127 @param xmlui: the XMLUI where the AdvancedList will be added | 136 The raw data form is used because Wokkel doesn't manage result items parsing yet. |
128 @param form_xml: domish.Element of the data form | 137 @param xmlui (XMLUI): the XMLUI where the AdvancedList will be added |
138 @param form_xml (domish.Element): element of the data form | |
129 @return: AdvancedList element | 139 @return: AdvancedList element |
130 """ | 140 """ |
131 headers = {} | 141 headers = {} |
132 try: | 142 try: |
133 reported_elt = form_xml.elements('jabber:x:data', 'reported').next() | 143 reported_elt = form_xml.elements('jabber:x:data', 'reported').next() |
136 | 146 |
137 for elt in reported_elt.elements(): | 147 for elt in reported_elt.elements(): |
138 if elt.name != "field": | 148 if elt.name != "field": |
139 raise exceptions.DataError("Unexpected tag") | 149 raise exceptions.DataError("Unexpected tag") |
140 name = elt["var"] | 150 name = elt["var"] |
141 label = elt.attributes.get('label','') | 151 label = elt.attributes.get('label', '') |
142 type_ = elt.attributes.get('type') | 152 type_ = elt.attributes.get('type') |
143 headers[name] = (label, type_) | 153 headers[name] = (label, type_) |
144 | 154 |
145 if not headers: | 155 if not headers: |
146 raise exceptions.DataError("No reported fields (see XEP-0004 §3.4)") | 156 raise exceptions.DataError("No reported fields (see XEP-0004 §3.4)") |
160 widget_type, widget_args, widget_kwargs = _dataFormField2XMLUIData(field) | 170 widget_type, widget_args, widget_kwargs = _dataFormField2XMLUIData(field) |
161 xmlui.addWidget(widget_type, *widget_args, **widget_kwargs) | 171 xmlui.addWidget(widget_type, *widget_args, **widget_kwargs) |
162 | 172 |
163 return xmlui | 173 return xmlui |
164 | 174 |
175 | |
165 def dataFormResult2XMLUI(form_elt, session_id=None): | 176 def dataFormResult2XMLUI(form_elt, session_id=None): |
166 """Take a raw data form (not parsed by XEP-0004) and convert it to a SàT XMLUI | 177 """Take a raw data form (not parsed by XEP-0004) and convert it to a SàT XMLUI. |
167 raw data form is used because Wokkel doesn't manage result items parsing yet | 178 |
168 @param form_elt: domish.Element of the data form | 179 The raw data form is used because Wokkel doesn't manage result items parsing yet. |
169 @return: XMLUI interface | 180 @param form_elt (domish.Element): element of the data form |
170 """ | 181 @param session_id (unicode): session id to return with the data |
171 | 182 @return: XMLUI instance |
183 """ | |
172 xml_ui = XMLUI("window", "vertical", session_id=session_id) | 184 xml_ui = XMLUI("window", "vertical", session_id=session_id) |
173 try: | 185 try: |
174 dataFormResult2AdvancedList(xml_ui, form_elt) | 186 dataFormResult2AdvancedList(xml_ui, form_elt) |
175 except exceptions.DataError: | 187 except exceptions.DataError: |
176 parsed_form = data_form.Form.fromElement(form_elt) | 188 parsed_form = data_form.Form.fromElement(form_elt) |
177 dataForm2Widgets(xml_ui, parsed_form, read_only=True) | 189 dataForm2Widgets(xml_ui, parsed_form, read_only=True) |
178 return xml_ui | 190 return xml_ui |
179 | 191 |
192 | |
180 def _cleanValue(value): | 193 def _cleanValue(value): |
181 """Workaround method to avoid DBus types with D-Bus bridge | 194 """Workaround method to avoid DBus types with D-Bus bridge. |
182 | 195 |
183 @param value: value to clean | 196 @param value: value to clean |
184 @return: value in a non DBus type (only clean string yet) | 197 @return: value in a non DBus type (only clean string yet) |
185 """ | 198 """ |
186 # XXX: must be removed when DBus types will no cause problems anymore | 199 # XXX: must be removed when DBus types will no cause problems anymore |
187 # FIXME: should be cleaned inside D-Bus bridge itself | 200 # FIXME: should be cleaned inside D-Bus bridge itself |
188 if isinstance(value, basestring): | 201 if isinstance(value, basestring): |
189 return unicode(value) | 202 return unicode(value) |
190 return value | 203 return value |
191 | 204 |
205 | |
192 def XMLUIResult2DataFormResult(xmlui_data): | 206 def XMLUIResult2DataFormResult(xmlui_data): |
193 """ Extract form data from a XMLUI return | 207 """ Extract form data from a XMLUI return. |
194 @xmlui_data: data returned by frontends for XMLUI form | 208 |
195 @return: dict of data usable by Wokkel's dataform | 209 @param xmlui_data (dict): data returned by frontends for XMLUI form |
210 @return: dict of data usable by Wokkel's data form | |
196 """ | 211 """ |
197 return {key[len(SAT_FORM_PREFIX):]: _cleanValue(value) for key, value in xmlui_data.iteritems() if key.startswith(SAT_FORM_PREFIX)} | 212 return {key[len(SAT_FORM_PREFIX):]: _cleanValue(value) for key, value in xmlui_data.iteritems() if key.startswith(SAT_FORM_PREFIX)} |
198 | 213 |
214 | |
199 def formEscape(name): | 215 def formEscape(name): |
200 """ Return escaped name for forms """ | 216 """Return escaped name for forms. |
201 return u"%s%s" % (SAT_FORM_PREFIX, name) | 217 |
218 @param name (unicode): form name | |
219 @return: unicode | |
220 """ | |
221 return u"%s%s" % (SAT_FORM_PREFIX, name) | |
222 | |
202 | 223 |
203 def XMLUIResultToElt(xmlui_data): | 224 def XMLUIResultToElt(xmlui_data): |
204 """ Construct result domish.Element from XMLUI result | 225 """Construct result domish.Element from XMLUI result. |
205 @xmlui_data: data returned by frontends for XMLUI form | 226 |
227 @param xmlui_data (dict): data returned by frontends for XMLUI form | |
228 @return: domish.Element | |
206 """ | 229 """ |
207 form = data_form.Form('submit') | 230 form = data_form.Form('submit') |
208 form.makeFields(XMLUIResult2DataFormResult(xmlui_data)) | 231 form.makeFields(XMLUIResult2DataFormResult(xmlui_data)) |
209 return form.toElement() | 232 return form.toElement() |
210 | 233 |
234 | |
211 def tupleList2dataForm(values): | 235 def tupleList2dataForm(values): |
212 """convert a list of tuples (name,value) to a wokkel submit data form""" | 236 """Convert a list of tuples (name, value) to a wokkel submit data form. |
237 | |
238 @param values (list): list of tuples | |
239 @return: data_form.Form | |
240 """ | |
213 form = data_form.Form('submit') | 241 form = data_form.Form('submit') |
214 for value in values: | 242 for value in values: |
215 field = data_form.Field(var=value[0], value=value[1]) | 243 field = data_form.Field(var=value[0], value=value[1]) |
216 form.addField(field) | 244 form.addField(field) |
217 | 245 |
218 return form | 246 return form |
219 | 247 |
248 | |
220 def paramsXML2XMLUI(xml): | 249 def paramsXML2XMLUI(xml): |
221 """Convert the xml for parameter to a SàT XML User Interface""" | 250 """Convert the XML for parameter to a SàT XML User Interface. |
251 | |
252 @param xml (unicode) | |
253 @return: XMLUI | |
254 """ | |
222 params_doc = minidom.parseString(xml.encode('utf-8')) | 255 params_doc = minidom.parseString(xml.encode('utf-8')) |
223 top = params_doc.documentElement | 256 top = params_doc.documentElement |
224 if top.nodeName != 'params': | 257 if top.nodeName != 'params': |
225 raise exceptions.DataError(_('INTERNAL ERROR: parameters xml not valid')) | 258 raise exceptions.DataError(_('INTERNAL ERROR: parameters xml not valid')) |
226 | 259 |
272 | 305 |
273 return param_ui.toXml() | 306 return param_ui.toXml() |
274 | 307 |
275 | 308 |
276 def _getParamListOptions(param): | 309 def _getParamListOptions(param): |
277 """Retrieve the options for list element. The <option/> tags | 310 """Retrieve the options for list element. |
278 must be direct children of <param/>.""" | 311 |
312 The <option/> tags must be direct children of <param/>. | |
313 @param param (domish.Element): element | |
314 @return: a tuple (options, selected_value) | |
315 """ | |
279 if len(param.getElementsByTagName("options")) > 0: | 316 if len(param.getElementsByTagName("options")) > 0: |
280 raise exceptions.DataError(_("The 'options' tag is not allowed in parameter of type 'list'!")) | 317 raise exceptions.DataError(_("The 'options' tag is not allowed in parameter of type 'list'!")) |
281 elems = param.getElementsByTagName("option") | 318 elems = param.getElementsByTagName("option") |
282 if len(elems) == 0: | 319 if len(elems) == 0: |
283 return [] | 320 return [] |
284 options = [elem.getAttribute("value") for elem in elems] | 321 options = [elem.getAttribute("value") for elem in elems] |
285 selected = [elem.getAttribute("value") for elem in elems if elem.getAttribute("selected") == 'true'] | 322 selected = [elem.getAttribute("value") for elem in elems if elem.getAttribute("selected") == 'true'] |
286 return (options, selected) | 323 return (options, selected) |
287 | 324 |
288 | 325 |
289 ## XMLUI Elements | 326 ## XMLUI Elements |
290 | 327 |
291 | 328 |
293 """ Base XMLUI element """ | 330 """ Base XMLUI element """ |
294 type = None | 331 type = None |
295 | 332 |
296 def __init__(self, xmlui, parent=None): | 333 def __init__(self, xmlui, parent=None): |
297 """Create a container element | 334 """Create a container element |
335 | |
298 @param xmlui: XMLUI instance | 336 @param xmlui: XMLUI instance |
299 @parent: parent element | 337 @parent: parent element |
300 """ | 338 """ |
301 assert(self.type) is not None | 339 assert(self.type) is not None |
302 if not hasattr(self, 'elem'): | 340 if not hasattr(self, 'elem'): |
319 self.elem = xmlui.doc.documentElement | 357 self.elem = xmlui.doc.documentElement |
320 super(TopElement, self).__init__(xmlui) | 358 super(TopElement, self).__init__(xmlui) |
321 | 359 |
322 | 360 |
323 class TabElement(Element): | 361 class TabElement(Element): |
324 """ Used by TabsContainer to give name and label to tabs """ | 362 """ Used by TabsContainer to give name and label to tabs.""" |
325 type = 'tab' | 363 type = 'tab' |
326 | 364 |
327 def __init__(self, parent, name, label): | 365 def __init__(self, parent, name, label): |
328 if not isinstance(parent, TabsContainer): | 366 if not isinstance(parent, TabsContainer): |
329 raise exceptions.DataError(_("TabElement must be a child of TabsContainer")) | 367 raise exceptions.DataError(_("TabElement must be a child of TabsContainer")) |
445 while(not isinstance(current, (Container)) and | 483 while(not isinstance(current, (Container)) and |
446 current is not None): | 484 current is not None): |
447 current = current.parent | 485 current = current.parent |
448 return current | 486 return current |
449 | 487 |
488 | |
450 class VerticalContainer(Container): | 489 class VerticalContainer(Container): |
451 type = "vertical" | 490 type = "vertical" |
452 | 491 |
453 | 492 |
454 class HorizontalContainer(Container): | 493 class HorizontalContainer(Container): |
480 | 519 |
481 | 520 |
482 class AdvancedListContainer(Container): | 521 class AdvancedListContainer(Container): |
483 type = "advanced_list" | 522 type = "advanced_list" |
484 | 523 |
485 def __init__(self, xmlui, callback_id=None, name=None, headers=None, items=None, columns=None, selectable = 'no', auto_index = False, parent=None): | 524 def __init__(self, xmlui, callback_id=None, name=None, headers=None, items=None, columns=None, selectable='no', auto_index=False, parent=None): |
486 """Create an advanced list | 525 """Create an advanced list |
487 @param headers: optional headers information | 526 @param headers: optional headers information |
488 @param callback_id: id of the method to call when selection is done | 527 @param callback_id: id of the method to call when selection is done |
489 @param items: list of widgets to add (just the first row) | 528 @param items: list of widgets to add (just the first row) |
490 @param columns: number of columns in this table, or None to autodetect | 529 @param columns: number of columns in this table, or None to autodetect |
525 def addHeaders(self, headers): | 564 def addHeaders(self, headers): |
526 for header in headers: | 565 for header in headers: |
527 self.addHeader(header) | 566 self.addHeader(header) |
528 | 567 |
529 def addHeader(self, header): | 568 def addHeader(self, header): |
530 pass # TODO | 569 pass # TODO |
531 | 570 |
532 def addItems(self, items): | 571 def addItems(self, items): |
533 for item in items: | 572 for item in items: |
534 self.append(item) | 573 self.append(item) |
535 | 574 |
621 | 660 |
622 class LabelWidget(Widget): | 661 class LabelWidget(Widget): |
623 """Used for one line blob of text, | 662 """Used for one line blob of text, |
624 most of time to display the desciption or name of the next widget | 663 most of time to display the desciption or name of the next widget |
625 """ | 664 """ |
626 type='label' | 665 type = 'label' |
627 | 666 |
628 def __init__(self, xmlui, label, name=None, parent=None): | 667 def __init__(self, xmlui, label, name=None, parent=None): |
629 super(LabelWidget, self).__init__(xmlui, name, parent) | 668 super(LabelWidget, self).__init__(xmlui, name, parent) |
630 self.elem.setAttribute('value', label) | 669 self.elem.setAttribute('value', label) |
631 | 670 |
632 | 671 |
633 class JidWidget(Widget): | 672 class JidWidget(Widget): |
634 """Used to display a Jabber ID, some specific methods can be added""" | 673 """Used to display a Jabber ID, some specific methods can be added""" |
635 type='jid' | 674 type = 'jid' |
636 | 675 |
637 def __init__(self, xmlui, jid, name=None, parent=None): | 676 def __init__(self, xmlui, jid, name=None, parent=None): |
638 super(JidWidget, self).__init__(xmlui, name, parent) | 677 super(JidWidget, self).__init__(xmlui, name, parent) |
639 try: | 678 try: |
640 self.elem.setAttribute('value', jid.full()) | 679 self.elem.setAttribute('value', jid.full()) |
903 if panel_type == C.XMLUI_DIALOG: | 942 if panel_type == C.XMLUI_DIALOG: |
904 if dialog_opt is None: | 943 if dialog_opt is None: |
905 dialog_opt = {} | 944 dialog_opt = {} |
906 self._createDialog(dialog_opt) | 945 self._createDialog(dialog_opt) |
907 return | 946 return |
908 self.main_container = self._createContainer(container, TopElement(self)) | 947 self.main_container = self._createContainer(container, TopElement(self)) |
909 self.current_container = self.main_container | 948 self.current_container = self.main_container |
910 | 949 |
911 def _introspect(self): | 950 def _introspect(self): |
912 """ Introspect module to find Widgets and Containers """ | 951 """ Introspect module to find Widgets and Containers """ |
913 self._containers = {} | 952 self._containers = {} |
927 | 966 |
928 def __del__(self): | 967 def __del__(self): |
929 self.doc.unlink() | 968 self.doc.unlink() |
930 | 969 |
931 def __getattr__(self, name): | 970 def __getattr__(self, name): |
932 if name.startswith("add") and name not in ('addWidget',): # addWidgetName(...) create an instance of WidgetName | 971 if name.startswith("add") and name not in ('addWidget',): # addWidgetName(...) create an instance of WidgetName |
933 if self.type == C.XMLUI_DIALOG: | 972 if self.type == C.XMLUI_DIALOG: |
934 raise exceptions.InternalError(_("addXXX can't be used with dialogs")) | 973 raise exceptions.InternalError(_("addXXX can't be used with dialogs")) |
935 class_name = name[3:]+"Widget" | 974 class_name = name[3:] + "Widget" |
936 if class_name in globals(): | 975 if class_name in globals(): |
937 cls = globals()[class_name] | 976 cls = globals()[class_name] |
938 if issubclass(cls, Widget): | 977 if issubclass(cls, Widget): |
939 def createWidget(*args, **kwargs): | 978 def createWidget(*args, **kwargs): |
940 if "parent" not in kwargs: | 979 if "parent" not in kwargs: |
941 kwargs["parent"] = self.current_container | 980 kwargs["parent"] = self.current_container |
942 if "name" not in kwargs and issubclass(cls, InputWidget): # name can be given as first argument or in keyword arguments for InputWidgets | 981 if "name" not in kwargs and issubclass(cls, InputWidget): # name can be given as first argument or in keyword arguments for InputWidgets |
943 args = list(args) | 982 args = list(args) |
944 kwargs["name"] = args.pop(0) | 983 kwargs["name"] = args.pop(0) |
945 return cls(self, *args, **kwargs) | 984 return cls(self, *args, **kwargs) |
946 return createWidget | 985 return createWidget |
947 return object.__getattribute__(self, name) | 986 return object.__getattribute__(self, name) |
958 if value is None: | 997 if value is None: |
959 try: | 998 try: |
960 top_element.removeAttribute("submit") | 999 top_element.removeAttribute("submit") |
961 except NotFoundErr: | 1000 except NotFoundErr: |
962 pass | 1001 pass |
963 elif value: # submit_id can be the empty string to bypass form restriction | 1002 elif value: # submit_id can be the empty string to bypass form restriction |
964 top_element.setAttribute("submit", value) | 1003 top_element.setAttribute("submit", value) |
965 | 1004 |
966 @property | 1005 @property |
967 def session_id(self): | 1006 def session_id(self): |
968 top_element = self.doc.documentElement | 1007 top_element = self.doc.documentElement |
1048 | 1087 |
1049 @param message: body of the note | 1088 @param message: body of the note |
1050 @param title: title of the note | 1089 @param title: title of the note |
1051 @param level: one of C.XMLUI_DATA_LVL_* | 1090 @param level: one of C.XMLUI_DATA_LVL_* |
1052 """ | 1091 """ |
1053 note_xmlui = XMLUI(C.XMLUI_DIALOG, dialog_opt = { | 1092 note_xmlui = XMLUI(C.XMLUI_DIALOG, dialog_opt={ |
1054 C.XMLUI_DATA_TYPE: C.XMLUI_DIALOG_NOTE, | 1093 C.XMLUI_DATA_TYPE: C.XMLUI_DIALOG_NOTE, |
1055 C.XMLUI_DATA_MESS: message, | 1094 C.XMLUI_DATA_MESS: message, |
1056 C.XMLUI_DATA_LVL: level}, | 1095 C.XMLUI_DATA_LVL: level}, |
1057 title=title | 1096 title=title |
1058 ) | 1097 ) |
1059 return note_xmlui | 1098 return note_xmlui |
1060 | 1099 |
1061 # Misc other funtions | 1100 # Misc other funtions |
1062 | 1101 |
1063 | 1102 |