diff options
-rw-r--r-- | sleekxmpp/plugins/xep_0004.py | 395 | ||||
-rw-r--r-- | sleekxmpp/plugins/xep_0004/__init__.py | 11 | ||||
-rw-r--r-- | sleekxmpp/plugins/xep_0004/dataforms.py | 60 | ||||
-rw-r--r-- | sleekxmpp/plugins/xep_0004/stanza/__init__.py | 10 | ||||
-rw-r--r-- | sleekxmpp/plugins/xep_0004/stanza/field.py | 167 | ||||
-rw-r--r-- | sleekxmpp/plugins/xep_0004/stanza/form.py | 250 | ||||
-rw-r--r-- | tests/test_stanza_xep_0004.py | 41 | ||||
-rw-r--r-- | tests/test_stanza_xep_0060.py | 34 |
8 files changed, 538 insertions, 430 deletions
diff --git a/sleekxmpp/plugins/xep_0004.py b/sleekxmpp/plugins/xep_0004.py deleted file mode 100644 index 5a49d70f..00000000 --- a/sleekxmpp/plugins/xep_0004.py +++ /dev/null @@ -1,395 +0,0 @@ -""" - SleekXMPP: The Sleek XMPP Library - Copyright (C) 2010 Nathanael C. Fritz, Lance J.T. Stout - This file is part of SleekXMPP. - - See the file LICENSE for copying permission. -""" - -import logging -import copy -from . import base -from .. xmlstream.handler.callback import Callback -from .. xmlstream.matcher.xpath import MatchXPath -from .. xmlstream.stanzabase import registerStanzaPlugin, ElementBase, ET, JID -from .. stanza.message import Message - - -log = logging.getLogger(__name__) - - -class Form(ElementBase): - namespace = 'jabber:x:data' - name = 'x' - plugin_attrib = 'form' - interfaces = set(('fields', 'instructions', 'items', 'reported', 'title', 'type', 'values')) - sub_interfaces = set(('title',)) - form_types = set(('cancel', 'form', 'result', 'submit')) - - def __init__(self, *args, **kwargs): - title = None - if 'title' in kwargs: - title = kwargs['title'] - del kwargs['title'] - ElementBase.__init__(self, *args, **kwargs) - if title is not None: - self['title'] = title - self.field = FieldAccessor(self) - - def setup(self, xml=None): - if ElementBase.setup(self, xml): #if we had to generate xml - self['type'] = 'form' - - def addField(self, var='', ftype=None, label='', desc='', required=False, value=None, options=None, **kwargs): - kwtype = kwargs.get('type', None) - if kwtype is None: - kwtype = ftype - - field = FormField(parent=self) - field['var'] = var - field['type'] = kwtype - field['label'] = label - field['desc'] = desc - field['required'] = required - field['value'] = value - if options is not None: - field['options'] = options - return field - - def getXML(self, type='submit'): - self['type'] = type - log.warning("Form.getXML() is deprecated API compatibility with plugins/old_0004.py") - return self.xml - - def fromXML(self, xml): - log.warning("Form.fromXML() is deprecated API compatibility with plugins/old_0004.py") - n = Form(xml=xml) - return n - - def addItem(self, values): - itemXML = ET.Element('{%s}item' % self.namespace) - self.xml.append(itemXML) - reported_vars = self['reported'].keys() - for var in reported_vars: - fieldXML = ET.Element('{%s}field' % FormField.namespace) - itemXML.append(fieldXML) - field = FormField(xml=fieldXML) - field['var'] = var - field['value'] = values.get(var, None) - - def addReported(self, var, ftype=None, label='', desc='', **kwargs): - kwtype = kwargs.get('type', None) - if kwtype is None: - kwtype = ftype - reported = self.xml.find('{%s}reported' % self.namespace) - if reported is None: - reported = ET.Element('{%s}reported' % self.namespace) - self.xml.append(reported) - fieldXML = ET.Element('{%s}field' % FormField.namespace) - reported.append(fieldXML) - field = FormField(xml=fieldXML) - field['var'] = var - field['type'] = kwtype - field['label'] = label - field['desc'] = desc - return field - - def cancel(self): - self['type'] = 'cancel' - - def delFields(self): - fieldsXML = self.xml.findall('{%s}field' % FormField.namespace) - for fieldXML in fieldsXML: - self.xml.remove(fieldXML) - - def delInstructions(self): - instsXML = self.xml.findall('{%s}instructions') - for instXML in instsXML: - self.xml.remove(instXML) - - def delItems(self): - itemsXML = self.xml.find('{%s}item' % self.namespace) - for itemXML in itemsXML: - self.xml.remove(itemXML) - - def delReported(self): - reportedXML = self.xml.find('{%s}reported' % self.namespace) - if reportedXML is not None: - self.xml.remove(reportedXML) - - def getFields(self, use_dict=False): - fields = {} if use_dict else [] - fieldsXML = self.xml.findall('{%s}field' % FormField.namespace) - for fieldXML in fieldsXML: - field = FormField(xml=fieldXML) - if use_dict: - fields[field['var']] = field - else: - fields.append((field['var'], field)) - return fields - - def getInstructions(self): - instructions = '' - instsXML = self.xml.findall('{%s}instructions' % self.namespace) - return "\n".join([instXML.text for instXML in instsXML]) - - def getItems(self): - items = [] - itemsXML = self.xml.findall('{%s}item' % self.namespace) - for itemXML in itemsXML: - item = {} - fieldsXML = itemXML.findall('{%s}field' % FormField.namespace) - for fieldXML in fieldsXML: - field = FormField(xml=fieldXML) - item[field['var']] = field['value'] - items.append(item) - return items - - def getReported(self): - fields = {} - fieldsXML = self.xml.findall('{%s}reported/{%s}field' % (self.namespace, - FormField.namespace)) - for fieldXML in fieldsXML: - field = FormField(xml=fieldXML) - fields[field['var']] = field - return fields - - def getValues(self): - values = {} - fields = self.getFields(use_dict=True) - for var in fields: - values[var] = fields[var]['value'] - return values - - def reply(self): - if self['type'] == 'form': - self['type'] = 'submit' - elif self['type'] == 'submit': - self['type'] = 'result' - - def setFields(self, fields, default=None): - del self['fields'] - for field_data in fields: - var = field_data[0] - field = field_data[1] - field['var'] = var - - self.addField(**field) - - def setInstructions(self, instructions): - del self['instructions'] - if instructions in [None, '']: - return - instructions = instructions.split('\n') - for instruction in instructions: - inst = ET.Element('{%s}instructions' % self.namespace) - inst.text = instruction - self.xml.append(inst) - - def setItems(self, items): - for item in items: - self.addItem(item) - - def setReported(self, reported, default=None): - for var in reported: - field = reported[var] - field['var'] = var - self.addReported(var, **field) - - def setValues(self, values): - fields = self.getFields(use_dict=True) - for field in values: - fields[field]['value'] = values[field] - - def merge(self, other): - new = copy.copy(self) - if type(other) == dict: - new.setValues(other) - return new - nfields = new.getFields(use_dict=True) - ofields = other.getFields(use_dict=True) - nfields.update(ofields) - new.setFields([(x, nfields[x]) for x in nfields]) - return new - -class FieldAccessor(object): - def __init__(self, form): - self.form = form - - def __getitem__(self, key): - return self.form.getFields(use_dict=True)[key] - - def __contains__(self, key): - return key in self.form.getFields(use_dict=True) - - def has_key(self, key): - return key in self.form.getFields(use_dict=True) - - -class FormField(ElementBase): - namespace = 'jabber:x:data' - name = 'field' - plugin_attrib = 'field' - interfaces = set(('answer', 'desc', 'required', 'value', 'options', 'label', 'type', 'var')) - sub_interfaces = set(('desc',)) - field_types = set(('boolean', 'fixed', 'hidden', 'jid-multi', 'jid-single', 'list-multi', - 'list-single', 'text-multi', 'text-private', 'text-single')) - multi_value_types = set(('hidden', 'jid-multi', 'list-multi', 'text-multi')) - multi_line_types = set(('hidden', 'text-multi')) - option_types = set(('list-multi', 'list-single')) - true_values = set((True, '1', 'true')) - - def addOption(self, label='', value=''): - if self['type'] in self.option_types: - opt = FieldOption(parent=self) - opt['label'] = label - opt['value'] = value - else: - raise ValueError("Cannot add options to a %s field." % self['type']) - - def delOptions(self): - optsXML = self.xml.findall('{%s}option' % self.namespace) - for optXML in optsXML: - self.xml.remove(optXML) - - def delRequired(self): - reqXML = self.xml.find('{%s}required' % self.namespace) - if reqXML is not None: - self.xml.remove(reqXML) - - def delValue(self): - valsXML = self.xml.findall('{%s}value' % self.namespace) - for valXML in valsXML: - self.xml.remove(valXML) - - def getAnswer(self): - return self.getValue() - - def getOptions(self): - options = [] - optsXML = self.xml.findall('{%s}option' % self.namespace) - for optXML in optsXML: - opt = FieldOption(xml=optXML) - options.append({'label': opt['label'], 'value':opt['value']}) - return options - - def getRequired(self): - reqXML = self.xml.find('{%s}required' % self.namespace) - return reqXML is not None - - def getValue(self): - valsXML = self.xml.findall('{%s}value' % self.namespace) - if len(valsXML) == 0: - return None - elif self['type'] == 'boolean': - return valsXML[0].text in self.true_values - elif self['type'] in self.multi_value_types: - values = [] - for valXML in valsXML: - if valXML.text is None: - valXML.text = '' - values.append(valXML.text) - if self['type'] == 'text-multi': - values = "\n".join(values) - return values - else: - return valsXML[0].text - - def setAnswer(self, answer): - self.setValue(answer) - - def setFalse(self): - self.setValue(False) - - def setOptions(self, options): - for value in options: - if isinstance(value, dict): - self.addOption(**value) - else: - self.addOption(value=value) - - def setRequired(self, required): - exists = self.getRequired() - if not exists and required: - self.xml.append(ET.Element('{%s}required' % self.namespace)) - elif exists and not required: - self.delRequired() - - def setTrue(self): - self.setValue(True) - - def setValue(self, value): - self.delValue() - valXMLName = '{%s}value' % self.namespace - - if self['type'] == 'boolean': - if value in self.true_values: - valXML = ET.Element(valXMLName) - valXML.text = '1' - self.xml.append(valXML) - else: - valXML = ET.Element(valXMLName) - valXML.text = '0' - self.xml.append(valXML) - elif self['type'] in self.multi_value_types or self['type'] in ['', None]: - if self['type'] in self.multi_line_types and isinstance(value, str): - value = value.split('\n') - if not isinstance(value, list): - value = [value] - for val in value: - if self['type'] in ['', None] and val in self.true_values: - val = '1' - valXML = ET.Element(valXMLName) - valXML.text = val - self.xml.append(valXML) - else: - if isinstance(value, list): - raise ValueError("Cannot add multiple values to a %s field." % self['type']) - valXML = ET.Element(valXMLName) - valXML.text = value - self.xml.append(valXML) - - -class FieldOption(ElementBase): - namespace = 'jabber:x:data' - name = 'option' - plugin_attrib = 'option' - interfaces = set(('label', 'value')) - sub_interfaces = set(('value',)) - - -class xep_0004(base.base_plugin): - """ - XEP-0004: Data Forms - """ - - def plugin_init(self): - self.xep = '0004' - self.description = 'Data Forms' - - self.xmpp.registerHandler( - Callback('Data Form', - MatchXPath('{%s}message/{%s}x' % (self.xmpp.default_ns, - Form.namespace)), - self.handle_form)) - - registerStanzaPlugin(FormField, FieldOption) - registerStanzaPlugin(Form, FormField) - registerStanzaPlugin(Message, Form) - - def makeForm(self, ftype='form', title='', instructions=''): - f = Form() - f['type'] = ftype - f['title'] = title - f['instructions'] = instructions - return f - - def post_init(self): - base.base_plugin.post_init(self) - self.xmpp.plugin['xep_0030'].add_feature('jabber:x:data') - - def handle_form(self, message): - self.xmpp.event("message_xform", message) - - def buildForm(self, xml): - return Form(xml=xml) diff --git a/sleekxmpp/plugins/xep_0004/__init__.py b/sleekxmpp/plugins/xep_0004/__init__.py new file mode 100644 index 00000000..aad4e15f --- /dev/null +++ b/sleekxmpp/plugins/xep_0004/__init__.py @@ -0,0 +1,11 @@ +""" + SleekXMPP: The Sleek XMPP Library + Copyright (C) 2011 Nathanael C. Fritz, Lance J.T. Stout + This file is part of SleekXMPP. + + See the file LICENSE for copying permission. +""" + +from sleekxmpp.plugins.xep_0004.stanza import Form +from sleekxmpp.plugins.xep_0004.stanza import FormField, FieldOption +from sleekxmpp.plugins.xep_0004.dataforms import xep_0004 diff --git a/sleekxmpp/plugins/xep_0004/dataforms.py b/sleekxmpp/plugins/xep_0004/dataforms.py new file mode 100644 index 00000000..5414be5c --- /dev/null +++ b/sleekxmpp/plugins/xep_0004/dataforms.py @@ -0,0 +1,60 @@ +""" + SleekXMPP: The Sleek XMPP Library + Copyright (C) 2011 Nathanael C. Fritz, Lance J.T. Stout + This file is part of SleekXMPP. + + See the file LICENSE for copying permission. +""" + +import copy + +from sleekxmpp.thirdparty import OrderedDict + +from sleekxmpp import Message +from sleekxmpp.xmlstream import register_stanza_plugin, ElementBase, ET +from sleekxmpp.xmlstream.handler import Callback +from sleekxmpp.xmlstream.matcher import StanzaPath +from sleekxmpp.plugins.base import base_plugin +from sleekxmpp.plugins.xep_0004 import stanza +from sleekxmpp.plugins.xep_0004.stanza import Form, FormField, FieldOption + + +class xep_0004(base_plugin): + """ + XEP-0004: Data Forms + """ + + def plugin_init(self): + self.xep = '0004' + self.description = 'Data Forms' + self.stanza = stanza + + self.xmpp.registerHandler( + Callback('Data Form', + StanzaPath('message/form'), + self.handle_form)) + + register_stanza_plugin(FormField, FieldOption, iterable=True) + register_stanza_plugin(Form, FormField, iterable=True) + register_stanza_plugin(Message, Form) + + def make_form(self, ftype='form', title='', instructions=''): + f = Form() + f['type'] = ftype + f['title'] = title + f['instructions'] = instructions + return f + + def post_init(self): + base_plugin.post_init(self) + self.xmpp.plugin['xep_0030'].add_feature('jabber:x:data') + + def handle_form(self, message): + self.xmpp.event("message_xform", message) + + def build_form(self, xml): + return Form(xml=xml) + + +xep_0004.makeForm = xep_0004.make_form +xep_0004.buildForm = xep_0004.build_form diff --git a/sleekxmpp/plugins/xep_0004/stanza/__init__.py b/sleekxmpp/plugins/xep_0004/stanza/__init__.py new file mode 100644 index 00000000..6ad35298 --- /dev/null +++ b/sleekxmpp/plugins/xep_0004/stanza/__init__.py @@ -0,0 +1,10 @@ +""" + SleekXMPP: The Sleek XMPP Library + Copyright (C) 2011 Nathanael C. Fritz, Lance J.T. Stout + This file is part of SleekXMPP. + + See the file LICENSE for copying permission. +""" + +from sleekxmpp.plugins.xep_0004.stanza.field import FormField, FieldOption +from sleekxmpp.plugins.xep_0004.stanza.form import Form diff --git a/sleekxmpp/plugins/xep_0004/stanza/field.py b/sleekxmpp/plugins/xep_0004/stanza/field.py new file mode 100644 index 00000000..9bb92311 --- /dev/null +++ b/sleekxmpp/plugins/xep_0004/stanza/field.py @@ -0,0 +1,167 @@ +""" + SleekXMPP: The Sleek XMPP Library + Copyright (C) 2011 Nathanael C. Fritz, Lance J.T. Stout + This file is part of SleekXMPP. + + See the file LICENSE for copying permission. +""" + +from sleekxmpp.xmlstream import ElementBase, ET + + +class FormField(ElementBase): + namespace = 'jabber:x:data' + name = 'field' + plugin_attrib = 'field' + interfaces = set(('answer', 'desc', 'required', 'value', + 'options', 'label', 'type', 'var')) + sub_interfaces = set(('desc',)) + plugin_tag_map = {} + plugin_attrib_map = {} + + field_types = set(('boolean', 'fixed', 'hidden', 'jid-multi', + 'jid-single', 'list-multi', 'list-single', + 'text-multi', 'text-private', 'text-single')) + + true_values = set((True, '1', 'true')) + option_types = set(('list-multi', 'list-single')) + multi_line_types = set(('hidden', 'text-multi')) + multi_value_types = set(('hidden', 'jid-multi', + 'list-multi', 'text-multi')) + + def add_option(self, label='', value=''): + if self['type'] in self.option_types: + opt = FieldOption(parent=self) + opt['label'] = label + opt['value'] = value + else: + raise ValueError("Cannot add options to " + \ + "a %s field." % self['type']) + + def del_options(self): + optsXML = self.xml.findall('{%s}option' % self.namespace) + for optXML in optsXML: + self.xml.remove(optXML) + + def del_required(self): + reqXML = self.xml.find('{%s}required' % self.namespace) + if reqXML is not None: + self.xml.remove(reqXML) + + def del_value(self): + valsXML = self.xml.findall('{%s}value' % self.namespace) + for valXML in valsXML: + self.xml.remove(valXML) + + def get_answer(self): + return self['value'] + + def get_options(self): + options = [] + optsXML = self.xml.findall('{%s}option' % self.namespace) + for optXML in optsXML: + opt = FieldOption(xml=optXML) + options.append({'label': opt['label'], 'value': opt['value']}) + return options + + def get_required(self): + reqXML = self.xml.find('{%s}required' % self.namespace) + return reqXML is not None + + def get_value(self): + valsXML = self.xml.findall('{%s}value' % self.namespace) + if len(valsXML) == 0: + return None + elif self['type'] == 'boolean': + return valsXML[0].text in self.true_values + elif self['type'] in self.multi_value_types: + values = [] + for valXML in valsXML: + if valXML.text is None: + valXML.text = '' + values.append(valXML.text) + if self['type'] == 'text-multi': + values = "\n".join(values) + return values + else: + return valsXML[0].text + + def set_answer(self, answer): + self['value'] = answer + + def set_false(self): + self['value'] = False + + def set_options(self, options): + for value in options: + if isinstance(value, dict): + self.add_option(**value) + else: + self.add_option(value=value) + + def set_required(self, required): + exists = self['required'] + if not exists and required: + self.xml.append(ET.Element('{%s}required' % self.namespace)) + elif exists and not required: + del self['required'] + + def set_true(self): + self['value'] = True + + def set_value(self, value): + del self['value'] + valXMLName = '{%s}value' % self.namespace + + if self['type'] == 'boolean': + if value in self.true_values: + valXML = ET.Element(valXMLName) + valXML.text = '1' + self.xml.append(valXML) + else: + valXML = ET.Element(valXMLName) + valXML.text = '0' + self.xml.append(valXML) + elif self['type'] in self.multi_value_types or not self['type']: + if not isinstance(value, list): + if self['type'] in self.multi_line_types: + value = value.split('\n') + else: + value = [value] + for val in value: + if self['type'] in ['', None] and val in self.true_values: + val = '1' + valXML = ET.Element(valXMLName) + valXML.text = val + self.xml.append(valXML) + else: + if isinstance(value, list): + raise ValueError("Cannot add multiple values " + \ + "to a %s field." % self['type']) + valXML = ET.Element(valXMLName) + valXML.text = value + self.xml.append(valXML) + + +class FieldOption(ElementBase): + namespace = 'jabber:x:data' + name = 'option' + plugin_attrib = 'option' + interfaces = set(('label', 'value')) + sub_interfaces = set(('value',)) + + +FormField.addOption = FormField.add_option +FormField.delOptions = FormField.del_options +FormField.delRequired = FormField.del_required +FormField.delValue = FormField.del_value +FormField.getAnswer = FormField.get_answer +FormField.getOptions = FormField.get_options +FormField.getRequired = FormField.get_required +FormField.getValue = FormField.get_value +FormField.setAnswer = FormField.set_answer +FormField.setFalse = FormField.set_false +FormField.setOptions = FormField.set_options +FormField.setRequired = FormField.set_required +FormField.setTrue = FormField.set_true +FormField.setValue = FormField.set_value diff --git a/sleekxmpp/plugins/xep_0004/stanza/form.py b/sleekxmpp/plugins/xep_0004/stanza/form.py new file mode 100644 index 00000000..d85266fc --- /dev/null +++ b/sleekxmpp/plugins/xep_0004/stanza/form.py @@ -0,0 +1,250 @@ +""" + SleekXMPP: The Sleek XMPP Library + Copyright (C) 2011 Nathanael C. Fritz, Lance J.T. Stout + This file is part of SleekXMPP. + + See the file LICENSE for copying permission. +""" + +import copy +import logging + +from sleekxmpp.thirdparty import OrderedDict + +from sleekxmpp.xmlstream import ElementBase, ET +from sleekxmpp.plugins.xep_0004.stanza import FormField + + +log = logging.getLogger(__name__) + + +class Form(ElementBase): + namespace = 'jabber:x:data' + name = 'x' + plugin_attrib = 'form' + interfaces = set(('fields', 'instructions', 'items', + 'reported', 'title', 'type', 'values')) + sub_interfaces = set(('title',)) + form_types = set(('cancel', 'form', 'result', 'submit')) + + def __init__(self, *args, **kwargs): + title = None + if 'title' in kwargs: + title = kwargs['title'] + del kwargs['title'] + ElementBase.__init__(self, *args, **kwargs) + if title is not None: + self['title'] = title + + def setup(self, xml=None): + if ElementBase.setup(self, xml): + # If we had to generate xml + self['type'] = 'form' + + def set_type(self, ftype): + self._set_attr('type', ftype) + if ftype == 'submit': + fields = self['fields'] + for var in fields: + field = fields[var] + del field['type'] + del field['label'] + del field['desc'] + del field['required'] + del field['options'] + elif ftype == 'cancel': + del self['fields'] + + def add_field(self, var='', ftype=None, label='', desc='', + required=False, value=None, options=None, **kwargs): + kwtype = kwargs.get('type', None) + if kwtype is None: + kwtype = ftype + + field = FormField(parent=self) + field['var'] = var + field['type'] = kwtype + field['value'] = value + if self['type'] in ('form', 'result'): + field['label'] = label + field['desc'] = desc + field['required'] = required + if options is not None: + field['options'] = options + else: + del field['type'] + return field + + def getXML(self, type='submit'): + self['type'] = type + log.warning("Form.getXML() is deprecated API compatibility " + \ + "with plugins/old_0004.py") + return self.xml + + def fromXML(self, xml): + log.warning("Form.fromXML() is deprecated API compatibility " + \ + "with plugins/old_0004.py") + n = Form(xml=xml) + return n + + def add_item(self, values): + itemXML = ET.Element('{%s}item' % self.namespace) + self.xml.append(itemXML) + reported_vars = self['reported'].keys() + for var in reported_vars: + fieldXML = ET.Element('{%s}field' % FormField.namespace) + itemXML.append(fieldXML) + field = FormField(xml=fieldXML) + field['var'] = var + field['value'] = values.get(var, None) + + def add_reported(self, var, ftype=None, label='', desc='', **kwargs): + kwtype = kwargs.get('type', None) + if kwtype is None: + kwtype = ftype + reported = self.xml.find('{%s}reported' % self.namespace) + if reported is None: + reported = ET.Element('{%s}reported' % self.namespace) + self.xml.append(reported) + fieldXML = ET.Element('{%s}field' % FormField.namespace) + reported.append(fieldXML) + field = FormField(xml=fieldXML) + field['var'] = var + field['type'] = kwtype + field['label'] = label + field['desc'] = desc + return field + + def cancel(self): + self['type'] = 'cancel' + + def del_fields(self): + fieldsXML = self.xml.findall('{%s}field' % FormField.namespace) + for fieldXML in fieldsXML: + self.xml.remove(fieldXML) + + def del_instructions(self): + instsXML = self.xml.findall('{%s}instructions') + for instXML in instsXML: + self.xml.remove(instXML) + + def del_items(self): + itemsXML = self.xml.find('{%s}item' % self.namespace) + for itemXML in itemsXML: + self.xml.remove(itemXML) + + def del_reported(self): + reportedXML = self.xml.find('{%s}reported' % self.namespace) + if reportedXML is not None: + self.xml.remove(reportedXML) + + def get_fields(self, use_dict=False): + fields = OrderedDict() + fieldsXML = self.xml.findall('{%s}field' % FormField.namespace) + for fieldXML in fieldsXML: + field = FormField(xml=fieldXML) + fields[field['var']] = field + return fields + + def get_instructions(self): + instructions = '' + instsXML = self.xml.findall('{%s}instructions' % self.namespace) + return "\n".join([instXML.text for instXML in instsXML]) + + def get_items(self): + items = [] + itemsXML = self.xml.findall('{%s}item' % self.namespace) + for itemXML in itemsXML: + item = {} + fieldsXML = itemXML.findall('{%s}field' % FormField.namespace) + for fieldXML in fieldsXML: + field = FormField(xml=fieldXML) + item[field['var']] = field['value'] + items.append(item) + return items + + def get_reported(self): + fields = {} + xml = self.xml.findall('{%s}reported/{%s}field' % (self.namespace, + FormField.namespace)) + for field in xml: + field = FormField(xml=field) + fields[field['var']] = field + return fields + + def get_values(self): + values = {} + fields = self['fields'] + for var in fields: + values[var] = fields[var]['value'] + return values + + def reply(self): + if self['type'] == 'form': + self['type'] = 'submit' + elif self['type'] == 'submit': + self['type'] = 'result' + + def set_fields(self, fields): + del self['fields'] + if not isinstance(fields, list): + fields = fields.items() + for var, field in fields: + field['var'] = var + self.add_field(**field) + + def set_instructions(self, instructions): + del self['instructions'] + if instructions in [None, '']: + return + instructions = instructions.split('\n') + for instruction in instructions: + inst = ET.Element('{%s}instructions' % self.namespace) + inst.text = instruction + self.xml.append(inst) + + def set_items(self, items): + for item in items: + self.add_item(item) + + def set_reported(self, reported): + for var in reported: + field = reported[var] + field['var'] = var + self.add_reported(var, **field) + + def set_values(self, values): + fields = self['fields'] + for field in values: + fields[field]['value'] = values[field] + + def merge(self, other): + new = copy.copy(self) + if type(other) == dict: + new['values'] = other + return new + nfields = new['fields'] + ofields = other['fields'] + nfields.update(ofields) + new['fields'] = nfields + return new + + +Form.setType = Form.set_type +Form.addField = Form.add_field +Form.addItem = Form.add_item +Form.addReported = Form.add_reported +Form.delFields = Form.del_fields +Form.delInstructions = Form.del_instructions +Form.delItems = Form.del_items +Form.delReported = Form.del_reported +Form.getFields = Form.get_fields +Form.getInstructions = Form.get_instructions +Form.getItems = Form.get_items +Form.getReported = Form.get_reported +Form.getValues = Form.get_values +Form.setFields = Form.set_fields +Form.setInstructions = Form.set_instructions +Form.setItems = Form.set_items +Form.setReported = Form.set_reported +Form.setValues = Form.set_values diff --git a/tests/test_stanza_xep_0004.py b/tests/test_stanza_xep_0004.py index bdc4a878..22f8b77d 100644 --- a/tests/test_stanza_xep_0004.py +++ b/tests/test_stanza_xep_0004.py @@ -1,4 +1,6 @@ from sleekxmpp.test import * +from sleekxmpp.thirdparty import OrderedDict + import sleekxmpp.plugins.xep_0004 as xep_0004 @@ -47,21 +49,25 @@ class TestDataForms(SleekTest): </message> """) - form['fields'] = [('f1', {'type': 'text-single', - 'label': 'Username', - 'required': True}), - ('f2', {'type': 'text-private', - 'label': 'Password', - 'required': True}), - ('f3', {'type': 'text-multi', - 'label': 'Message', - 'value': 'Enter message.\nA long one even.'}), - ('f4', {'type': 'list-single', - 'label': 'Message Type', - 'options': [{'label': 'Cool!', - 'value': 'cool'}, - {'label': 'Urgh!', - 'value': 'urgh'}]})] + fields = OrderedDict() + fields['f1'] = {'type': 'text-single', + 'label': 'Username', + 'required': True} + fields['f2'] = {'type': 'text-private', + 'label': 'Password', + 'required': True} + fields['f3'] = {'type': 'text-multi', + 'label': 'Message', + 'value': 'Enter message.\nA long one even.'} + fields['f4'] = {'type': 'list-single', + 'label': 'Message Type', + 'options': [{'label': 'Cool!', + 'value': 'cool'}, + {'label': 'Urgh!', + 'value': 'urgh'}]} + form['fields'] = fields + + self.check(msg, """ <message> <x xmlns="jabber:x:data" type="form"> @@ -92,9 +98,8 @@ class TestDataForms(SleekTest): msg = self.Message() form = msg['form'] - form.setFields([ - ('foo', {'type': 'text-single'}), - ('bar', {'type': 'list-multi'})]) + form.add_field(var='foo', ftype='text-single') + form.add_field(var='bar', ftype='list-multi') form.setValues({'foo': 'Foo!', 'bar': ['a', 'b']}) diff --git a/tests/test_stanza_xep_0060.py b/tests/test_stanza_xep_0060.py index d42c11bd..2427b787 100644 --- a/tests/test_stanza_xep_0060.py +++ b/tests/test_stanza_xep_0060.py @@ -182,7 +182,7 @@ class TestPubsubStanzas(SleekTest): <subscribe node="cheese" jid="fritzy@netflint.net/sleekxmpp"> <options node="cheese" jid="fritzy@netflint.net/sleekxmpp"> <x xmlns="jabber:x:data" type="submit"> - <field var="pubsub#title" type="text-single"> + <field var="pubsub#title"> <value>this thing is awesome</value> </field> </x> @@ -306,42 +306,42 @@ class TestPubsubStanzas(SleekTest): <create node="testnode2" /> <configure> <x xmlns="jabber:x:data" type="submit"> - <field var="FORM_TYPE" type="hidden"> + <field var="FORM_TYPE"> <value>http://jabber.org/protocol/pubsub#node_config</value> </field> - <field var="pubsub#node_type" type="list-single" label="Select the node type"> + <field var="pubsub#node_type"> <value>leaf</value> </field> - <field var="pubsub#title" type="text-single" label="A friendly name for the node" /> - <field var="pubsub#deliver_notifications" type="boolean" label="Deliver event notifications"> + <field var="pubsub#title" /> + <field var="pubsub#deliver_notifications"> <value>1</value> </field> - <field var="pubsub#deliver_payloads" type="boolean" label="Deliver payloads with event notifications"> + <field var="pubsub#deliver_payloads"> <value>1</value> </field> - <field var="pubsub#notify_config" type="boolean" label="Notify subscribers when the node configuration changes" /> - <field var="pubsub#notify_delete" type="boolean" label="Notify subscribers when the node is deleted" /> - <field var="pubsub#notify_retract" type="boolean" label="Notify subscribers when items are removed from the node"> + <field var="pubsub#notify_config" /> + <field var="pubsub#notify_delete" /> + <field var="pubsub#notify_retract"> <value>1</value> </field> - <field var="pubsub#notify_sub" type="boolean" label="Notify owners about new subscribers and unsubscribes" /> - <field var="pubsub#persist_items" type="boolean" label="Persist items in storage" /> - <field var="pubsub#max_items" type="text-single" label="Max # of items to persist"> + <field var="pubsub#notify_sub" /> + <field var="pubsub#persist_items" /> + <field var="pubsub#max_items"> <value>10</value> </field> - <field var="pubsub#subscribe" type="boolean" label="Whether to allow subscriptions"> + <field var="pubsub#subscribe"> <value>1</value> </field> - <field var="pubsub#access_model" type="list-single" label="Specify the subscriber model"> + <field var="pubsub#access_model"> <value>open</value> </field> - <field var="pubsub#publish_model" type="list-single" label="Specify the publisher model"> + <field var="pubsub#publish_model"> <value>publishers</value> </field> - <field var="pubsub#send_last_published_item" type="list-single" label="Send last published item"> + <field var="pubsub#send_last_published_item"> <value>never</value> </field> - <field var="pubsub#presence_based_delivery" type="boolean" label="Deliver notification only to available users" /> + <field var="pubsub#presence_based_delivery" /> </x> </configure> </pubsub> |