summaryrefslogtreecommitdiff
path: root/slixmpp/plugins/xep_0004/stanza
diff options
context:
space:
mode:
Diffstat (limited to 'slixmpp/plugins/xep_0004/stanza')
-rw-r--r--slixmpp/plugins/xep_0004/stanza/__init__.py10
-rw-r--r--slixmpp/plugins/xep_0004/stanza/field.py185
-rw-r--r--slixmpp/plugins/xep_0004/stanza/form.py275
3 files changed, 470 insertions, 0 deletions
diff --git a/slixmpp/plugins/xep_0004/stanza/__init__.py b/slixmpp/plugins/xep_0004/stanza/__init__.py
new file mode 100644
index 00000000..9daaab75
--- /dev/null
+++ b/slixmpp/plugins/xep_0004/stanza/__init__.py
@@ -0,0 +1,10 @@
+"""
+ Slixmpp: The Slick XMPP Library
+ Copyright (C) 2011 Nathanael C. Fritz, Lance J.T. Stout
+ This file is part of Slixmpp.
+
+ See the file LICENSE for copying permission.
+"""
+
+from slixmpp.plugins.xep_0004.stanza.field import FormField, FieldOption
+from slixmpp.plugins.xep_0004.stanza.form import Form
diff --git a/slixmpp/plugins/xep_0004/stanza/field.py b/slixmpp/plugins/xep_0004/stanza/field.py
new file mode 100644
index 00000000..42f1210b
--- /dev/null
+++ b/slixmpp/plugins/xep_0004/stanza/field.py
@@ -0,0 +1,185 @@
+"""
+ Slixmpp: The Slick XMPP Library
+ Copyright (C) 2011 Nathanael C. Fritz, Lance J.T. Stout
+ This file is part of Slixmpp.
+
+ See the file LICENSE for copying permission.
+"""
+
+from slixmpp.xmlstream import ElementBase, ET
+
+
+class FormField(ElementBase):
+ namespace = 'jabber:x:data'
+ name = 'field'
+ plugin_attrib = 'field'
+ plugin_multi_attrib = 'fields'
+ interfaces = set(('answer', 'desc', 'required', 'value',
+ '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 setup(self, xml=None):
+ if ElementBase.setup(self, xml):
+ self._type = None
+ else:
+ self._type = self['type']
+
+ def set_type(self, value):
+ self._set_attr('type', value)
+ if value:
+ self._type = value
+
+ def add_option(self, label='', value=''):
+ if self._type is None or self._type in self.option_types:
+ opt = FieldOption()
+ opt['label'] = label
+ opt['value'] = value
+ self.append(opt)
+ 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, convert=True):
+ valsXML = self.xml.findall('{%s}value' % self.namespace)
+ if len(valsXML) == 0:
+ return None
+ elif self._type == 'boolean':
+ if convert:
+ return valsXML[0].text in self.true_values
+ return valsXML[0].text
+ elif self._type in self.multi_value_types or len(valsXML) > 1:
+ values = []
+ for valXML in valsXML:
+ if valXML.text is None:
+ valXML.text = ''
+ values.append(valXML.text)
+ if self._type == 'text-multi' and convert:
+ values = "\n".join(values)
+ return values
+ else:
+ if valsXML[0].text is None:
+ return ''
+ 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 self._type in ('', None):
+ if isinstance(value, bool):
+ value = [value]
+ if not isinstance(value, list):
+ value = value.replace('\r', '')
+ value = value.split('\n')
+ 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',))
+ plugin_multi_attrib = 'options'
+
+
+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/slixmpp/plugins/xep_0004/stanza/form.py b/slixmpp/plugins/xep_0004/stanza/form.py
new file mode 100644
index 00000000..0c67acc6
--- /dev/null
+++ b/slixmpp/plugins/xep_0004/stanza/form.py
@@ -0,0 +1,275 @@
+"""
+ Slixmpp: The Slick XMPP Library
+ Copyright (C) 2011 Nathanael C. Fritz, Lance J.T. Stout
+ This file is part of Slixmpp.
+
+ See the file LICENSE for copying permission.
+"""
+
+import copy
+import logging
+
+from collections import OrderedDict
+from slixmpp.thirdparty import OrderedSet
+
+from slixmpp.xmlstream import ElementBase, ET
+from slixmpp.plugins.xep_0004.stanza import FormField
+
+
+log = logging.getLogger(__name__)
+
+
+class Form(ElementBase):
+ namespace = 'jabber:x:data'
+ name = 'x'
+ plugin_attrib = 'form'
+ interfaces = OrderedSet(('instructions', 'reported', 'title', 'type', 'items', ))
+ 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'
+
+ @property
+ def field(self):
+ return self.get_fields()
+
+ def set_type(self, ftype):
+ self._set_attr('type', ftype)
+ if ftype == 'submit':
+ fields = self.get_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()
+ 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:
+ for option in options:
+ field.add_option(**option)
+ else:
+ del field['type']
+ self.append(field)
+ 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:
+ field = FormField()
+ field._type = self['reported'][var]['type']
+ field['var'] = var
+ field['value'] = values.get(var, None)
+ itemXML.append(field.xml)
+
+ 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()
+ for stanza in self['substanzas']:
+ if isinstance(stanza, FormField):
+ fields[stanza['var']] = stanza
+ return fields
+
+ def get_instructions(self):
+ 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 = OrderedDict()
+ 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 = OrderedDict()
+ 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 = OrderedDict()
+ fields = self.get_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(
+ var = field.get('var'),
+ label = field.get('label'),
+ desc = field.get('desc'),
+ required = field.get('required'),
+ value = field.get('value'),
+ options = field.get('options'),
+ type = field.get('type'))
+
+ def set_instructions(self, instructions):
+ del self['instructions']
+ if instructions in [None, '']:
+ return
+ if not isinstance(instructions, list):
+ 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):
+ """
+ This either needs a dictionary or dictionaries or a dictionary of form fields.
+ :param reported:
+ :return:
+ """
+ for var in reported:
+ field = reported[var]
+
+ if isinstance(field, dict):
+ self.add_reported(**field)
+ else:
+ 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)
+ new_field = FormField(xml=fieldXML)
+ new_field.values = field.values
+
+ def set_values(self, values):
+ fields = self.get_fields()
+ for field in values:
+ if field not in self.get_fields():
+ fields[field] = self.add_field(var=field)
+ self.get_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.addField = Form.add_field
+Form.addReported = Form.add_reported
+Form.delFields = Form.del_fields
+Form.delInstructions = Form.del_instructions
+Form.delReported = Form.del_reported
+Form.getFields = Form.get_fields
+Form.getInstructions = Form.get_instructions
+Form.getReported = Form.get_reported
+Form.getValues = Form.get_values
+Form.setFields = Form.set_fields
+Form.setInstructions = Form.set_instructions
+Form.setReported = Form.set_reported
+Form.setValues = Form.set_values