#!/usr/bin/python # -*- coding: utf-8 -*- # SAT: a jabber client # Copyright (C) 2009, 2010, 2011, 2012, 2013 Jérôme Poisson (goffi@goffi.org) # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Affero General Public License for more details. # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . from sat.core.i18n import _ from logging import debug, info, error, warning from xml.dom import minidom, NotFoundErr from wokkel import data_form from twisted.words.xish import domish from sat.core import exceptions """This library help manage XML used in SàT (parameters, registration, etc) """ SAT_FORM_PREFIX = "SAT_FORM_" SAT_PARAM_SEPARATOR = "_XMLUI_PARAM_" # used to have unique elements names def dataForm2XMLUI(form, submit_id, session_id=None): """Take a data form (xep-0004, Wokkel's implementation) and convert it to a SàT xml""" form_ui = XMLUI("form", "vertical", submit_id=submit_id, session_id=session_id) if form.instructions: form_ui.addText('\n'.join(form.instructions), 'instructions') labels = [field for field in form.fieldList if field.label] if labels: # if there is no label, we don't need to use pairs form_ui.changeLayout("pairs") for field in form.fieldList: if field.fieldType == 'fixed': __field_type = 'text' elif field.fieldType == 'text-single': __field_type = "string" elif field.fieldType == 'text-private': __field_type = "password" elif field.fieldType == 'list-single': __field_type = "list" else: error(u"FIXME FIXME FIXME: Type [%s] is not managed yet by SàT" % field.fieldType) __field_type = "string" if labels: if field.label: form_ui.addLabel(field.label) else: form_ui.addEmpty() form_ui.addElement(__field_type, field.var, field.value, [option.value for option in field.options]) return form_ui def dataFormResult2AdvancedList(form_ui, form_xml): """Take a raw data form (not parsed by XEP-0004) and convert it to an advanced list raw data form is used because Wokkel doesn't manage result items parsing yet @param form_ui: the XMLUI where the AdvancedList will be added @param form_xml: domish.Element of the data form @return: AdvancedList element """ headers = [] items = [] try: reported_elt = form_xml.elements('jabber:x:data', 'reported').next() except StopIteration: raise exceptions.DataError("Couldn't find expected tag") for elt in reported_elt.elements(): if elt.name != "field": raise exceptions.DataError("Unexpected tag") name = elt["var"] label = elt.attributes.get('label','') type_ = elt.attributes.get('type','') # TODO headers.append(Header(name, label)) if not headers: raise exceptions.DataError("No reported fields (see XEP-0004 §3.4)") item_elts = form_xml.elements('jabber:x:data', 'item') for item_elt in item_elts: fields = [] for elt in item_elt.elements(): if elt.name != 'field': warning("Unexpected tag (%s)" % elt.name) continue name = elt['var'] child_elt = elt.firstChildElement() if child_elt.name != "value": raise exceptions.DataError('Was expecting tag') value = unicode(child_elt) fields.append(Field(name, value)) items.append(Item(' | '.join((field.value for field in fields if field)), fields)) return form_ui.addAdvancedList(None, headers, items) def dataFormResult2XMLUI(form_xml, session_id=None): """Take a raw data form (not parsed by XEP-0004) and convert it to a SàT XMLUI raw data form is used because Wokkel doesn't manage result items parsing yet @param form_xml: domish.Element of the data form @return: XMLUI interface """ form_ui = XMLUI("window", "vertical", session_id=session_id) dataFormResult2AdvancedList(form_ui, form_xml) return form_ui def XMLUIResult2DataFormResult(xmlui_data): """ Extract form data from a XMLUI return @xmlui_data: data returned by frontends for XMLUI form @return: dict of data usable by Wokkel's dataform """ return {key[len(SAT_FORM_PREFIX):]: value for key, value in xmlui_data.iteritems() if key.startswith(SAT_FORM_PREFIX)} def XMLUIResultToElt(xmlui_data): """ Construct result domish.Element from XMLUI result @xmlui_data: data returned by frontends for XMLUI form """ form = data_form.Form('result') form.makeFields(XMLUIResult2DataFormResult(xmlui_data)) return form.toElement() def tupleList2dataForm(values): """convert a list of tuples (name,value) to a wokkel submit data form""" form = data_form.Form('submit') for value in values: field = data_form.Field(var=value[0], value=value[1]) form.addField(field) return form def paramsXml2xmlUI(xml): """Convert the xml for parameter to a SàT XML User Interface""" params_doc = minidom.parseString(xml.encode('utf-8')) top = params_doc.documentElement if top.nodeName != 'params': error(_('INTERNAL ERROR: parameters xml not valid')) assert(False) param_ui = XMLUI("param", "tabs") for category in top.getElementsByTagName("category"): category_name = category.getAttribute('name') label = category.getAttribute('label') if not category_name: error(_('INTERNAL ERROR: params categories must have a name')) assert(False) param_ui.addCategory(category_name, 'pairs', label=label) for param in category.getElementsByTagName("param"): param_name = param.getAttribute('name') label = param.getAttribute('label') if not param_name: error(_('INTERNAL ERROR: params must have a name')) assert(False) type_ = param.getAttribute('type') value = param.getAttribute('value') or None options = getOptions(param) callback_id = param.getAttribute('callback_id') or None if type_ == "button": param_ui.addEmpty() else: param_ui.addLabel(label or param_name) param_ui.addElement(name="%s%s%s" % (category_name, SAT_PARAM_SEPARATOR, param_name), type_=type_, value=value, options=options, callback_id=callback_id) return param_ui.toXml() def getOptions(param): """Retrieve the options for list element. Allow listing the