mirror of
https://github.com/correl/SleekXMPP.git
synced 2024-11-30 19:19:55 +00:00
made Lance's new XEP-4 stanzas the default, and put xep-0004 as old_0004
This commit is contained in:
parent
d5e42ac0e7
commit
16104b6e56
3 changed files with 740 additions and 740 deletions
|
@ -1,330 +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.txt 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
|
|
||||||
|
|
||||||
|
|
||||||
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 addField(self, var, ftype='text-single', label='', desc='', required=False, value=None, options=None):
|
|
||||||
field = FormField(parent=self)
|
|
||||||
field['var'] = var
|
|
||||||
field['type'] = ftype
|
|
||||||
field['label'] = label
|
|
||||||
field['desc'] = desc
|
|
||||||
field['required'] = required
|
|
||||||
field['value'] = value
|
|
||||||
if options is not None:
|
|
||||||
field['options'] = options
|
|
||||||
return field
|
|
||||||
|
|
||||||
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='text-single', label='', desc=''):
|
|
||||||
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'] = ftype
|
|
||||||
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):
|
|
||||||
fields = {}
|
|
||||||
fieldsXML = self.xml.findall('{%s}field' % FormField.namespace)
|
|
||||||
for fieldXML in fieldsXML:
|
|
||||||
field = FormField(xml=fieldXML)
|
|
||||||
fields[field['var']] = field
|
|
||||||
return fields
|
|
||||||
|
|
||||||
def getInstructions(self):
|
|
||||||
instructions = ''
|
|
||||||
instsXML = self.xml.findall('{%s}instructions')
|
|
||||||
for instXML in instsXML:
|
|
||||||
instructions += instXML.text
|
|
||||||
|
|
||||||
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()
|
|
||||||
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):
|
|
||||||
del self['fields']
|
|
||||||
for var in fields:
|
|
||||||
field = fields[var]
|
|
||||||
|
|
||||||
# Remap 'type' to 'ftype' to match the addField method
|
|
||||||
ftype = field.get('type', 'text-single')
|
|
||||||
field['type'] = ftype
|
|
||||||
del field['type']
|
|
||||||
field['ftype'] = ftype
|
|
||||||
|
|
||||||
self.addField(var, **field)
|
|
||||||
|
|
||||||
def setInstructions(self, instructions):
|
|
||||||
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):
|
|
||||||
for var in reported:
|
|
||||||
field = reported[var]
|
|
||||||
|
|
||||||
# Remap 'type' to 'ftype' to match the addReported method
|
|
||||||
ftype = field.get('type', 'text-single')
|
|
||||||
field['type'] = ftype
|
|
||||||
del field['type']
|
|
||||||
field['ftype'] = ftype
|
|
||||||
|
|
||||||
self.addReported(var, **field)
|
|
||||||
|
|
||||||
def setValues(self, values):
|
|
||||||
fields = self.getFields()
|
|
||||||
for field in values:
|
|
||||||
fields[field]['value'] = values[field]
|
|
||||||
|
|
||||||
|
|
||||||
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 = 'true'
|
|
||||||
self.xml.append(valXML)
|
|
||||||
else:
|
|
||||||
valXML = ET.Element(valXMLName)
|
|
||||||
valXML.text = 'true'
|
|
||||||
self.xml.append(valXML)
|
|
||||||
if self['type'] in self.multi_value_types:
|
|
||||||
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:
|
|
||||||
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 alt_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 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)
|
|
427
sleekxmpp/plugins/old_0004.py
Normal file
427
sleekxmpp/plugins/old_0004.py
Normal file
|
@ -0,0 +1,427 @@
|
||||||
|
"""
|
||||||
|
SleekXMPP: The Sleek XMPP Library
|
||||||
|
Copyright (C) 2007 Nathanael C. Fritz
|
||||||
|
This file is part of SleekXMPP.
|
||||||
|
|
||||||
|
SleekXMPP is free software; you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation; either version 2 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
SleekXMPP 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 General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with SleekXMPP; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
"""
|
||||||
|
from . import base
|
||||||
|
import logging
|
||||||
|
from xml.etree import cElementTree as ET
|
||||||
|
import copy
|
||||||
|
#TODO support item groups and results
|
||||||
|
|
||||||
|
class old_0004(base.base_plugin):
|
||||||
|
|
||||||
|
def plugin_init(self):
|
||||||
|
self.xep = '0004'
|
||||||
|
self.description = '*Deprecated Data Forms'
|
||||||
|
self.xmpp.add_handler("<message><x xmlns='jabber:x:data' /></message>", self.handler_message_xform)
|
||||||
|
|
||||||
|
def post_init(self):
|
||||||
|
base.base_plugin.post_init(self)
|
||||||
|
self.xmpp.plugin['xep_0030'].add_feature('jabber:x:data')
|
||||||
|
|
||||||
|
def handler_message_xform(self, xml):
|
||||||
|
object = self.handle_form(xml)
|
||||||
|
self.xmpp.event("message_form", object)
|
||||||
|
|
||||||
|
def handler_presence_xform(self, xml):
|
||||||
|
object = self.handle_form(xml)
|
||||||
|
self.xmpp.event("presence_form", object)
|
||||||
|
|
||||||
|
def handle_form(self, xml):
|
||||||
|
xmlform = xml.find('{jabber:x:data}x')
|
||||||
|
object = self.buildForm(xmlform)
|
||||||
|
self.xmpp.event("message_xform", object)
|
||||||
|
return object
|
||||||
|
|
||||||
|
def buildForm(self, xml):
|
||||||
|
form = Form(ftype=xml.attrib['type'])
|
||||||
|
form.fromXML(xml)
|
||||||
|
return form
|
||||||
|
|
||||||
|
def makeForm(self, ftype='form', title='', instructions=''):
|
||||||
|
return Form(self.xmpp, ftype, title, instructions)
|
||||||
|
|
||||||
|
class FieldContainer(object):
|
||||||
|
def __init__(self, stanza = 'form'):
|
||||||
|
self.fields = []
|
||||||
|
self.field = {}
|
||||||
|
self.stanza = stanza
|
||||||
|
|
||||||
|
def addField(self, var, ftype='text-single', label='', desc='', required=False, value=None):
|
||||||
|
self.field[var] = FormField(var, ftype, label, desc, required, value)
|
||||||
|
self.fields.append(self.field[var])
|
||||||
|
return self.field[var]
|
||||||
|
|
||||||
|
def buildField(self, xml):
|
||||||
|
self.field[xml.get('var', '__unnamed__')] = FormField(xml.get('var', '__unnamed__'), xml.get('type', 'text-single'))
|
||||||
|
self.fields.append(self.field[xml.get('var', '__unnamed__')])
|
||||||
|
self.field[xml.get('var', '__unnamed__')].buildField(xml)
|
||||||
|
|
||||||
|
def buildContainer(self, xml):
|
||||||
|
self.stanza = xml.tag
|
||||||
|
for field in xml.findall('{jabber:x:data}field'):
|
||||||
|
self.buildField(field)
|
||||||
|
|
||||||
|
def getXML(self, ftype):
|
||||||
|
container = ET.Element(self.stanza)
|
||||||
|
for field in self.fields:
|
||||||
|
container.append(field.getXML(ftype))
|
||||||
|
return container
|
||||||
|
|
||||||
|
class Form(FieldContainer):
|
||||||
|
types = ('form', 'submit', 'cancel', 'result')
|
||||||
|
def __init__(self, xmpp=None, ftype='form', title='', instructions=''):
|
||||||
|
if not ftype in self.types:
|
||||||
|
raise ValueError("Invalid Form Type")
|
||||||
|
FieldContainer.__init__(self)
|
||||||
|
self.xmpp = xmpp
|
||||||
|
self.type = ftype
|
||||||
|
self.title = title
|
||||||
|
self.instructions = instructions
|
||||||
|
self.reported = []
|
||||||
|
self.items = []
|
||||||
|
|
||||||
|
def merge(self, form2):
|
||||||
|
form1 = Form(ftype=self.type)
|
||||||
|
form1.fromXML(self.getXML(self.type))
|
||||||
|
for field in form2.fields:
|
||||||
|
if not field.var in form1.field:
|
||||||
|
form1.addField(field.var, field.type, field.label, field.desc, field.required, field.value)
|
||||||
|
else:
|
||||||
|
form1.field[field.var].value = field.value
|
||||||
|
for option, label in field.options:
|
||||||
|
if (option, label) not in form1.field[field.var].options:
|
||||||
|
form1.fields[field.var].addOption(option, label)
|
||||||
|
return form1
|
||||||
|
|
||||||
|
def copy(self):
|
||||||
|
newform = Form(ftype=self.type)
|
||||||
|
newform.fromXML(self.getXML(self.type))
|
||||||
|
return newform
|
||||||
|
|
||||||
|
def update(self, form):
|
||||||
|
values = form.getValues()
|
||||||
|
for var in values:
|
||||||
|
if var in self.fields:
|
||||||
|
self.fields[var].setValue(self.fields[var])
|
||||||
|
|
||||||
|
def getValues(self):
|
||||||
|
result = {}
|
||||||
|
for field in self.fields:
|
||||||
|
value = field.value
|
||||||
|
if len(value) == 1:
|
||||||
|
value = value[0]
|
||||||
|
result[field.var] = value
|
||||||
|
return result
|
||||||
|
|
||||||
|
def setValues(self, values={}):
|
||||||
|
for field in values:
|
||||||
|
if field in self.field:
|
||||||
|
if isinstance(values[field], list) or isinstance(values[field], tuple):
|
||||||
|
for value in values[field]:
|
||||||
|
self.field[field].setValue(value)
|
||||||
|
else:
|
||||||
|
self.field[field].setValue(values[field])
|
||||||
|
|
||||||
|
def fromXML(self, xml):
|
||||||
|
self.buildForm(xml)
|
||||||
|
|
||||||
|
def addItem(self):
|
||||||
|
newitem = FieldContainer('item')
|
||||||
|
self.items.append(newitem)
|
||||||
|
return newitem
|
||||||
|
|
||||||
|
def buildItem(self, xml):
|
||||||
|
newitem = self.addItem()
|
||||||
|
newitem.buildContainer(xml)
|
||||||
|
|
||||||
|
def addReported(self):
|
||||||
|
reported = FieldContainer('reported')
|
||||||
|
self.reported.append(reported)
|
||||||
|
return reported
|
||||||
|
|
||||||
|
def buildReported(self, xml):
|
||||||
|
reported = self.addReported()
|
||||||
|
reported.buildContainer(xml)
|
||||||
|
|
||||||
|
def setTitle(self, title):
|
||||||
|
self.title = title
|
||||||
|
|
||||||
|
def setInstructions(self, instructions):
|
||||||
|
self.instructions = instructions
|
||||||
|
|
||||||
|
def setType(self, ftype):
|
||||||
|
self.type = ftype
|
||||||
|
|
||||||
|
def getXMLMessage(self, to):
|
||||||
|
msg = self.xmpp.makeMessage(to)
|
||||||
|
msg.append(self.getXML())
|
||||||
|
return msg
|
||||||
|
|
||||||
|
def buildForm(self, xml):
|
||||||
|
self.type = xml.get('type', 'form')
|
||||||
|
if xml.find('{jabber:x:data}title') is not None:
|
||||||
|
self.setTitle(xml.find('{jabber:x:data}title').text)
|
||||||
|
if xml.find('{jabber:x:data}instructions') is not None:
|
||||||
|
self.setInstructions(xml.find('{jabber:x:data}instructions').text)
|
||||||
|
for field in xml.findall('{jabber:x:data}field'):
|
||||||
|
self.buildField(field)
|
||||||
|
for reported in xml.findall('{jabber:x:data}reported'):
|
||||||
|
self.buildReported(reported)
|
||||||
|
for item in xml.findall('{jabber:x:data}item'):
|
||||||
|
self.buildItem(item)
|
||||||
|
|
||||||
|
#def getXML(self, tostring = False):
|
||||||
|
def getXML(self, ftype=None):
|
||||||
|
if ftype:
|
||||||
|
self.type = ftype
|
||||||
|
form = ET.Element('{jabber:x:data}x')
|
||||||
|
form.attrib['type'] = self.type
|
||||||
|
if self.title and self.type in ('form', 'result'):
|
||||||
|
title = ET.Element('{jabber:x:data}title')
|
||||||
|
title.text = self.title
|
||||||
|
form.append(title)
|
||||||
|
if self.instructions and self.type == 'form':
|
||||||
|
instructions = ET.Element('{jabber:x:data}instructions')
|
||||||
|
instructions.text = self.instructions
|
||||||
|
form.append(instructions)
|
||||||
|
for field in self.fields:
|
||||||
|
form.append(field.getXML(self.type))
|
||||||
|
for reported in self.reported:
|
||||||
|
form.append(reported.getXML('{jabber:x:data}reported'))
|
||||||
|
for item in self.items:
|
||||||
|
form.append(item.getXML(self.type))
|
||||||
|
#if tostring:
|
||||||
|
# form = self.xmpp.tostring(form)
|
||||||
|
return form
|
||||||
|
|
||||||
|
def getXHTML(self):
|
||||||
|
form = ET.Element('{http://www.w3.org/1999/xhtml}form')
|
||||||
|
if self.title:
|
||||||
|
title = ET.Element('h2')
|
||||||
|
title.text = self.title
|
||||||
|
form.append(title)
|
||||||
|
if self.instructions:
|
||||||
|
instructions = ET.Element('p')
|
||||||
|
instructions.text = self.instructions
|
||||||
|
form.append(instructions)
|
||||||
|
for field in self.fields:
|
||||||
|
form.append(field.getXHTML())
|
||||||
|
for field in self.reported:
|
||||||
|
form.append(field.getXHTML())
|
||||||
|
for field in self.items:
|
||||||
|
form.append(field.getXHTML())
|
||||||
|
return form
|
||||||
|
|
||||||
|
|
||||||
|
def makeSubmit(self):
|
||||||
|
self.setType('submit')
|
||||||
|
|
||||||
|
class FormField(object):
|
||||||
|
types = ('boolean', 'fixed', 'hidden', 'jid-multi', 'jid-single', 'list-multi', 'list-single', 'text-multi', 'text-private', 'text-single')
|
||||||
|
listtypes = ('jid-multi', 'jid-single', 'list-multi', 'list-single')
|
||||||
|
lbtypes = ('fixed', 'text-multi')
|
||||||
|
def __init__(self, var, ftype='text-single', label='', desc='', required=False, value=None):
|
||||||
|
if not ftype in self.types:
|
||||||
|
raise ValueError("Invalid Field Type")
|
||||||
|
self.type = ftype
|
||||||
|
self.var = var
|
||||||
|
self.label = label
|
||||||
|
self.desc = desc
|
||||||
|
self.options = []
|
||||||
|
self.required = False
|
||||||
|
self.value = []
|
||||||
|
if self.type in self.listtypes:
|
||||||
|
self.islist = True
|
||||||
|
else:
|
||||||
|
self.islist = False
|
||||||
|
if self.type in self.lbtypes:
|
||||||
|
self.islinebreak = True
|
||||||
|
else:
|
||||||
|
self.islinebreak = False
|
||||||
|
if value:
|
||||||
|
self.setValue(value)
|
||||||
|
|
||||||
|
def addOption(self, value, label):
|
||||||
|
if self.islist:
|
||||||
|
self.options.append((value, label))
|
||||||
|
else:
|
||||||
|
raise ValueError("Cannot add options to non-list type field.")
|
||||||
|
|
||||||
|
def setTrue(self):
|
||||||
|
if self.type == 'boolean':
|
||||||
|
self.value = [True]
|
||||||
|
|
||||||
|
def setFalse(self):
|
||||||
|
if self.type == 'boolean':
|
||||||
|
self.value = [False]
|
||||||
|
|
||||||
|
def require(self):
|
||||||
|
self.required = True
|
||||||
|
|
||||||
|
def setDescription(self, desc):
|
||||||
|
self.desc = desc
|
||||||
|
|
||||||
|
def setValue(self, value):
|
||||||
|
if self.type == 'boolean':
|
||||||
|
if value in ('1', 1, True, 'true', 'True', 'yes'):
|
||||||
|
value = True
|
||||||
|
else:
|
||||||
|
value = False
|
||||||
|
if self.islinebreak and value is not None:
|
||||||
|
self.value += value.split('\n')
|
||||||
|
else:
|
||||||
|
if len(self.value) and (not self.islist or self.type == 'list-single'):
|
||||||
|
self.value = [value]
|
||||||
|
else:
|
||||||
|
self.value.append(value)
|
||||||
|
|
||||||
|
def delValue(self, value):
|
||||||
|
if type(self.value) == type([]):
|
||||||
|
try:
|
||||||
|
idx = self.value.index(value)
|
||||||
|
if idx != -1:
|
||||||
|
self.value.pop(idx)
|
||||||
|
except ValueError:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
self.value = ''
|
||||||
|
|
||||||
|
def setAnswer(self, value):
|
||||||
|
self.setValue(value)
|
||||||
|
|
||||||
|
def buildField(self, xml):
|
||||||
|
self.type = xml.get('type', 'text-single')
|
||||||
|
self.label = xml.get('label', '')
|
||||||
|
for option in xml.findall('{jabber:x:data}option'):
|
||||||
|
self.addOption(option.find('{jabber:x:data}value').text, option.get('label', ''))
|
||||||
|
for value in xml.findall('{jabber:x:data}value'):
|
||||||
|
self.setValue(value.text)
|
||||||
|
if xml.find('{jabber:x:data}required') is not None:
|
||||||
|
self.require()
|
||||||
|
if xml.find('{jabber:x:data}desc') is not None:
|
||||||
|
self.setDescription(xml.find('{jabber:x:data}desc').text)
|
||||||
|
|
||||||
|
def getXML(self, ftype):
|
||||||
|
field = ET.Element('{jabber:x:data}field')
|
||||||
|
if ftype != 'result':
|
||||||
|
field.attrib['type'] = self.type
|
||||||
|
if self.type != 'fixed':
|
||||||
|
if self.var:
|
||||||
|
field.attrib['var'] = self.var
|
||||||
|
if self.label:
|
||||||
|
field.attrib['label'] = self.label
|
||||||
|
if ftype == 'form':
|
||||||
|
for option in self.options:
|
||||||
|
optionxml = ET.Element('{jabber:x:data}option')
|
||||||
|
optionxml.attrib['label'] = option[1]
|
||||||
|
optionval = ET.Element('{jabber:x:data}value')
|
||||||
|
optionval.text = option[0]
|
||||||
|
optionxml.append(optionval)
|
||||||
|
field.append(optionxml)
|
||||||
|
if self.required:
|
||||||
|
required = ET.Element('{jabber:x:data}required')
|
||||||
|
field.append(required)
|
||||||
|
if self.desc:
|
||||||
|
desc = ET.Element('{jabber:x:data}desc')
|
||||||
|
desc.text = self.desc
|
||||||
|
field.append(desc)
|
||||||
|
for value in self.value:
|
||||||
|
valuexml = ET.Element('{jabber:x:data}value')
|
||||||
|
if value is True or value is False:
|
||||||
|
if value:
|
||||||
|
valuexml.text = '1'
|
||||||
|
else:
|
||||||
|
valuexml.text = '0'
|
||||||
|
else:
|
||||||
|
valuexml.text = value
|
||||||
|
field.append(valuexml)
|
||||||
|
return field
|
||||||
|
|
||||||
|
def getXHTML(self):
|
||||||
|
field = ET.Element('div', {'class': 'xmpp-xforms-%s' % self.type})
|
||||||
|
if self.label:
|
||||||
|
label = ET.Element('p')
|
||||||
|
label.text = "%s: " % self.label
|
||||||
|
else:
|
||||||
|
label = ET.Element('p')
|
||||||
|
label.text = "%s: " % self.var
|
||||||
|
field.append(label)
|
||||||
|
if self.type == 'boolean':
|
||||||
|
formf = ET.Element('input', {'type': 'checkbox', 'name': self.var})
|
||||||
|
if len(self.value) and self.value[0] in (True, 'true', '1'):
|
||||||
|
formf.attrib['checked'] = 'checked'
|
||||||
|
elif self.type == 'fixed':
|
||||||
|
formf = ET.Element('p')
|
||||||
|
try:
|
||||||
|
formf.text = ', '.join(self.value)
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
field.append(formf)
|
||||||
|
formf = ET.Element('input', {'type': 'hidden', 'name': self.var})
|
||||||
|
try:
|
||||||
|
formf.text = ', '.join(self.value)
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
elif self.type == 'hidden':
|
||||||
|
formf = ET.Element('input', {'type': 'hidden', 'name': self.var})
|
||||||
|
try:
|
||||||
|
formf.text = ', '.join(self.value)
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
elif self.type in ('jid-multi', 'list-multi'):
|
||||||
|
formf = ET.Element('select', {'name': self.var})
|
||||||
|
for option in self.options:
|
||||||
|
optf = ET.Element('option', {'value': option[0], 'multiple': 'multiple'})
|
||||||
|
optf.text = option[1]
|
||||||
|
if option[1] in self.value:
|
||||||
|
optf.attrib['selected'] = 'selected'
|
||||||
|
formf.append(option)
|
||||||
|
elif self.type in ('jid-single', 'text-single'):
|
||||||
|
formf = ET.Element('input', {'type': 'text', 'name': self.var})
|
||||||
|
try:
|
||||||
|
formf.attrib['value'] = ', '.join(self.value)
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
elif self.type == 'list-single':
|
||||||
|
formf = ET.Element('select', {'name': self.var})
|
||||||
|
for option in self.options:
|
||||||
|
optf = ET.Element('option', {'value': option[0]})
|
||||||
|
optf.text = option[1]
|
||||||
|
if not optf.text:
|
||||||
|
optf.text = option[0]
|
||||||
|
if option[1] in self.value:
|
||||||
|
optf.attrib['selected'] = 'selected'
|
||||||
|
formf.append(optf)
|
||||||
|
elif self.type == 'text-multi':
|
||||||
|
formf = ET.Element('textarea', {'name': self.var})
|
||||||
|
try:
|
||||||
|
formf.text = ', '.join(self.value)
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
if not formf.text:
|
||||||
|
formf.text = ' '
|
||||||
|
elif self.type == 'text-private':
|
||||||
|
formf = ET.Element('input', {'type': 'password', 'name': self.var})
|
||||||
|
try:
|
||||||
|
formf.attrib['value'] = ', '.join(self.value)
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
label.append(formf)
|
||||||
|
return field
|
||||||
|
|
|
@ -1,427 +1,330 @@
|
||||||
"""
|
"""
|
||||||
SleekXMPP: The Sleek XMPP Library
|
SleekXMPP: The Sleek XMPP Library
|
||||||
Copyright (C) 2007 Nathanael C. Fritz
|
Copyright (C) 2010 Nathanael C. Fritz, Lance J.T. Stout
|
||||||
This file is part of SleekXMPP.
|
This file is part of SleekXMPP.
|
||||||
|
|
||||||
SleekXMPP is free software; you can redistribute it and/or modify
|
See the file license.txt for copying permission.
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation; either version 2 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
SleekXMPP 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 General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with SleekXMPP; if not, write to the Free Software
|
|
||||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
||||||
"""
|
"""
|
||||||
from . import base
|
|
||||||
import logging
|
import logging
|
||||||
from xml.etree import cElementTree as ET
|
|
||||||
import copy
|
import copy
|
||||||
#TODO support item groups and results
|
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
|
||||||
|
|
||||||
|
|
||||||
|
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 addField(self, var, ftype='text-single', label='', desc='', required=False, value=None, options=None):
|
||||||
|
field = FormField(parent=self)
|
||||||
|
field['var'] = var
|
||||||
|
field['type'] = ftype
|
||||||
|
field['label'] = label
|
||||||
|
field['desc'] = desc
|
||||||
|
field['required'] = required
|
||||||
|
field['value'] = value
|
||||||
|
if options is not None:
|
||||||
|
field['options'] = options
|
||||||
|
return field
|
||||||
|
|
||||||
|
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='text-single', label='', desc=''):
|
||||||
|
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'] = ftype
|
||||||
|
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):
|
||||||
|
fields = {}
|
||||||
|
fieldsXML = self.xml.findall('{%s}field' % FormField.namespace)
|
||||||
|
for fieldXML in fieldsXML:
|
||||||
|
field = FormField(xml=fieldXML)
|
||||||
|
fields[field['var']] = field
|
||||||
|
return fields
|
||||||
|
|
||||||
|
def getInstructions(self):
|
||||||
|
instructions = ''
|
||||||
|
instsXML = self.xml.findall('{%s}instructions')
|
||||||
|
for instXML in instsXML:
|
||||||
|
instructions += instXML.text
|
||||||
|
|
||||||
|
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()
|
||||||
|
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):
|
||||||
|
del self['fields']
|
||||||
|
for var in fields:
|
||||||
|
field = fields[var]
|
||||||
|
|
||||||
|
# Remap 'type' to 'ftype' to match the addField method
|
||||||
|
ftype = field.get('type', 'text-single')
|
||||||
|
field['type'] = ftype
|
||||||
|
del field['type']
|
||||||
|
field['ftype'] = ftype
|
||||||
|
|
||||||
|
self.addField(var, **field)
|
||||||
|
|
||||||
|
def setInstructions(self, instructions):
|
||||||
|
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):
|
||||||
|
for var in reported:
|
||||||
|
field = reported[var]
|
||||||
|
|
||||||
|
# Remap 'type' to 'ftype' to match the addReported method
|
||||||
|
ftype = field.get('type', 'text-single')
|
||||||
|
field['type'] = ftype
|
||||||
|
del field['type']
|
||||||
|
field['ftype'] = ftype
|
||||||
|
|
||||||
|
self.addReported(var, **field)
|
||||||
|
|
||||||
|
def setValues(self, values):
|
||||||
|
fields = self.getFields()
|
||||||
|
for field in values:
|
||||||
|
fields[field]['value'] = values[field]
|
||||||
|
|
||||||
|
|
||||||
|
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 = 'true'
|
||||||
|
self.xml.append(valXML)
|
||||||
|
else:
|
||||||
|
valXML = ET.Element(valXMLName)
|
||||||
|
valXML.text = 'true'
|
||||||
|
self.xml.append(valXML)
|
||||||
|
if self['type'] in self.multi_value_types:
|
||||||
|
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:
|
||||||
|
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):
|
class xep_0004(base.base_plugin):
|
||||||
|
"""
|
||||||
|
XEP-0004: Data Forms
|
||||||
|
"""
|
||||||
|
|
||||||
def plugin_init(self):
|
def plugin_init(self):
|
||||||
self.xep = '0004'
|
self.xep = '0004'
|
||||||
self.description = 'Data Forms'
|
self.description = 'Data Forms'
|
||||||
self.xmpp.add_handler("<message><x xmlns='jabber:x:data' /></message>", self.handler_message_xform)
|
|
||||||
|
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 post_init(self):
|
def post_init(self):
|
||||||
base.base_plugin.post_init(self)
|
base.base_plugin.post_init(self)
|
||||||
self.xmpp.plugin['xep_0030'].add_feature('jabber:x:data')
|
self.xmpp.plugin['xep_0030'].add_feature('jabber:x:data')
|
||||||
|
|
||||||
def handler_message_xform(self, xml):
|
def handle_form(self, message):
|
||||||
object = self.handle_form(xml)
|
self.xmpp.event("message_xform", message)
|
||||||
self.xmpp.event("message_form", object)
|
|
||||||
|
|
||||||
def handler_presence_xform(self, xml):
|
|
||||||
object = self.handle_form(xml)
|
|
||||||
self.xmpp.event("presence_form", object)
|
|
||||||
|
|
||||||
def handle_form(self, xml):
|
|
||||||
xmlform = xml.find('{jabber:x:data}x')
|
|
||||||
object = self.buildForm(xmlform)
|
|
||||||
self.xmpp.event("message_xform", object)
|
|
||||||
return object
|
|
||||||
|
|
||||||
def buildForm(self, xml):
|
|
||||||
form = Form(ftype=xml.attrib['type'])
|
|
||||||
form.fromXML(xml)
|
|
||||||
return form
|
|
||||||
|
|
||||||
def makeForm(self, ftype='form', title='', instructions=''):
|
|
||||||
return Form(self.xmpp, ftype, title, instructions)
|
|
||||||
|
|
||||||
class FieldContainer(object):
|
|
||||||
def __init__(self, stanza = 'form'):
|
|
||||||
self.fields = []
|
|
||||||
self.field = {}
|
|
||||||
self.stanza = stanza
|
|
||||||
|
|
||||||
def addField(self, var, ftype='text-single', label='', desc='', required=False, value=None):
|
|
||||||
self.field[var] = FormField(var, ftype, label, desc, required, value)
|
|
||||||
self.fields.append(self.field[var])
|
|
||||||
return self.field[var]
|
|
||||||
|
|
||||||
def buildField(self, xml):
|
|
||||||
self.field[xml.get('var', '__unnamed__')] = FormField(xml.get('var', '__unnamed__'), xml.get('type', 'text-single'))
|
|
||||||
self.fields.append(self.field[xml.get('var', '__unnamed__')])
|
|
||||||
self.field[xml.get('var', '__unnamed__')].buildField(xml)
|
|
||||||
|
|
||||||
def buildContainer(self, xml):
|
|
||||||
self.stanza = xml.tag
|
|
||||||
for field in xml.findall('{jabber:x:data}field'):
|
|
||||||
self.buildField(field)
|
|
||||||
|
|
||||||
def getXML(self, ftype):
|
|
||||||
container = ET.Element(self.stanza)
|
|
||||||
for field in self.fields:
|
|
||||||
container.append(field.getXML(ftype))
|
|
||||||
return container
|
|
||||||
|
|
||||||
class Form(FieldContainer):
|
|
||||||
types = ('form', 'submit', 'cancel', 'result')
|
|
||||||
def __init__(self, xmpp=None, ftype='form', title='', instructions=''):
|
|
||||||
if not ftype in self.types:
|
|
||||||
raise ValueError("Invalid Form Type")
|
|
||||||
FieldContainer.__init__(self)
|
|
||||||
self.xmpp = xmpp
|
|
||||||
self.type = ftype
|
|
||||||
self.title = title
|
|
||||||
self.instructions = instructions
|
|
||||||
self.reported = []
|
|
||||||
self.items = []
|
|
||||||
|
|
||||||
def merge(self, form2):
|
|
||||||
form1 = Form(ftype=self.type)
|
|
||||||
form1.fromXML(self.getXML(self.type))
|
|
||||||
for field in form2.fields:
|
|
||||||
if not field.var in form1.field:
|
|
||||||
form1.addField(field.var, field.type, field.label, field.desc, field.required, field.value)
|
|
||||||
else:
|
|
||||||
form1.field[field.var].value = field.value
|
|
||||||
for option, label in field.options:
|
|
||||||
if (option, label) not in form1.field[field.var].options:
|
|
||||||
form1.fields[field.var].addOption(option, label)
|
|
||||||
return form1
|
|
||||||
|
|
||||||
def copy(self):
|
|
||||||
newform = Form(ftype=self.type)
|
|
||||||
newform.fromXML(self.getXML(self.type))
|
|
||||||
return newform
|
|
||||||
|
|
||||||
def update(self, form):
|
|
||||||
values = form.getValues()
|
|
||||||
for var in values:
|
|
||||||
if var in self.fields:
|
|
||||||
self.fields[var].setValue(self.fields[var])
|
|
||||||
|
|
||||||
def getValues(self):
|
|
||||||
result = {}
|
|
||||||
for field in self.fields:
|
|
||||||
value = field.value
|
|
||||||
if len(value) == 1:
|
|
||||||
value = value[0]
|
|
||||||
result[field.var] = value
|
|
||||||
return result
|
|
||||||
|
|
||||||
def setValues(self, values={}):
|
|
||||||
for field in values:
|
|
||||||
if field in self.field:
|
|
||||||
if isinstance(values[field], list) or isinstance(values[field], tuple):
|
|
||||||
for value in values[field]:
|
|
||||||
self.field[field].setValue(value)
|
|
||||||
else:
|
|
||||||
self.field[field].setValue(values[field])
|
|
||||||
|
|
||||||
def fromXML(self, xml):
|
|
||||||
self.buildForm(xml)
|
|
||||||
|
|
||||||
def addItem(self):
|
|
||||||
newitem = FieldContainer('item')
|
|
||||||
self.items.append(newitem)
|
|
||||||
return newitem
|
|
||||||
|
|
||||||
def buildItem(self, xml):
|
|
||||||
newitem = self.addItem()
|
|
||||||
newitem.buildContainer(xml)
|
|
||||||
|
|
||||||
def addReported(self):
|
|
||||||
reported = FieldContainer('reported')
|
|
||||||
self.reported.append(reported)
|
|
||||||
return reported
|
|
||||||
|
|
||||||
def buildReported(self, xml):
|
|
||||||
reported = self.addReported()
|
|
||||||
reported.buildContainer(xml)
|
|
||||||
|
|
||||||
def setTitle(self, title):
|
|
||||||
self.title = title
|
|
||||||
|
|
||||||
def setInstructions(self, instructions):
|
|
||||||
self.instructions = instructions
|
|
||||||
|
|
||||||
def setType(self, ftype):
|
|
||||||
self.type = ftype
|
|
||||||
|
|
||||||
def getXMLMessage(self, to):
|
|
||||||
msg = self.xmpp.makeMessage(to)
|
|
||||||
msg.append(self.getXML())
|
|
||||||
return msg
|
|
||||||
|
|
||||||
def buildForm(self, xml):
|
|
||||||
self.type = xml.get('type', 'form')
|
|
||||||
if xml.find('{jabber:x:data}title') is not None:
|
|
||||||
self.setTitle(xml.find('{jabber:x:data}title').text)
|
|
||||||
if xml.find('{jabber:x:data}instructions') is not None:
|
|
||||||
self.setInstructions(xml.find('{jabber:x:data}instructions').text)
|
|
||||||
for field in xml.findall('{jabber:x:data}field'):
|
|
||||||
self.buildField(field)
|
|
||||||
for reported in xml.findall('{jabber:x:data}reported'):
|
|
||||||
self.buildReported(reported)
|
|
||||||
for item in xml.findall('{jabber:x:data}item'):
|
|
||||||
self.buildItem(item)
|
|
||||||
|
|
||||||
#def getXML(self, tostring = False):
|
|
||||||
def getXML(self, ftype=None):
|
|
||||||
if ftype:
|
|
||||||
self.type = ftype
|
|
||||||
form = ET.Element('{jabber:x:data}x')
|
|
||||||
form.attrib['type'] = self.type
|
|
||||||
if self.title and self.type in ('form', 'result'):
|
|
||||||
title = ET.Element('{jabber:x:data}title')
|
|
||||||
title.text = self.title
|
|
||||||
form.append(title)
|
|
||||||
if self.instructions and self.type == 'form':
|
|
||||||
instructions = ET.Element('{jabber:x:data}instructions')
|
|
||||||
instructions.text = self.instructions
|
|
||||||
form.append(instructions)
|
|
||||||
for field in self.fields:
|
|
||||||
form.append(field.getXML(self.type))
|
|
||||||
for reported in self.reported:
|
|
||||||
form.append(reported.getXML('{jabber:x:data}reported'))
|
|
||||||
for item in self.items:
|
|
||||||
form.append(item.getXML(self.type))
|
|
||||||
#if tostring:
|
|
||||||
# form = self.xmpp.tostring(form)
|
|
||||||
return form
|
|
||||||
|
|
||||||
def getXHTML(self):
|
|
||||||
form = ET.Element('{http://www.w3.org/1999/xhtml}form')
|
|
||||||
if self.title:
|
|
||||||
title = ET.Element('h2')
|
|
||||||
title.text = self.title
|
|
||||||
form.append(title)
|
|
||||||
if self.instructions:
|
|
||||||
instructions = ET.Element('p')
|
|
||||||
instructions.text = self.instructions
|
|
||||||
form.append(instructions)
|
|
||||||
for field in self.fields:
|
|
||||||
form.append(field.getXHTML())
|
|
||||||
for field in self.reported:
|
|
||||||
form.append(field.getXHTML())
|
|
||||||
for field in self.items:
|
|
||||||
form.append(field.getXHTML())
|
|
||||||
return form
|
|
||||||
|
|
||||||
|
|
||||||
def makeSubmit(self):
|
|
||||||
self.setType('submit')
|
|
||||||
|
|
||||||
class FormField(object):
|
|
||||||
types = ('boolean', 'fixed', 'hidden', 'jid-multi', 'jid-single', 'list-multi', 'list-single', 'text-multi', 'text-private', 'text-single')
|
|
||||||
listtypes = ('jid-multi', 'jid-single', 'list-multi', 'list-single')
|
|
||||||
lbtypes = ('fixed', 'text-multi')
|
|
||||||
def __init__(self, var, ftype='text-single', label='', desc='', required=False, value=None):
|
|
||||||
if not ftype in self.types:
|
|
||||||
raise ValueError("Invalid Field Type")
|
|
||||||
self.type = ftype
|
|
||||||
self.var = var
|
|
||||||
self.label = label
|
|
||||||
self.desc = desc
|
|
||||||
self.options = []
|
|
||||||
self.required = False
|
|
||||||
self.value = []
|
|
||||||
if self.type in self.listtypes:
|
|
||||||
self.islist = True
|
|
||||||
else:
|
|
||||||
self.islist = False
|
|
||||||
if self.type in self.lbtypes:
|
|
||||||
self.islinebreak = True
|
|
||||||
else:
|
|
||||||
self.islinebreak = False
|
|
||||||
if value:
|
|
||||||
self.setValue(value)
|
|
||||||
|
|
||||||
def addOption(self, value, label):
|
|
||||||
if self.islist:
|
|
||||||
self.options.append((value, label))
|
|
||||||
else:
|
|
||||||
raise ValueError("Cannot add options to non-list type field.")
|
|
||||||
|
|
||||||
def setTrue(self):
|
|
||||||
if self.type == 'boolean':
|
|
||||||
self.value = [True]
|
|
||||||
|
|
||||||
def setFalse(self):
|
|
||||||
if self.type == 'boolean':
|
|
||||||
self.value = [False]
|
|
||||||
|
|
||||||
def require(self):
|
|
||||||
self.required = True
|
|
||||||
|
|
||||||
def setDescription(self, desc):
|
|
||||||
self.desc = desc
|
|
||||||
|
|
||||||
def setValue(self, value):
|
|
||||||
if self.type == 'boolean':
|
|
||||||
if value in ('1', 1, True, 'true', 'True', 'yes'):
|
|
||||||
value = True
|
|
||||||
else:
|
|
||||||
value = False
|
|
||||||
if self.islinebreak and value is not None:
|
|
||||||
self.value += value.split('\n')
|
|
||||||
else:
|
|
||||||
if len(self.value) and (not self.islist or self.type == 'list-single'):
|
|
||||||
self.value = [value]
|
|
||||||
else:
|
|
||||||
self.value.append(value)
|
|
||||||
|
|
||||||
def delValue(self, value):
|
|
||||||
if type(self.value) == type([]):
|
|
||||||
try:
|
|
||||||
idx = self.value.index(value)
|
|
||||||
if idx != -1:
|
|
||||||
self.value.pop(idx)
|
|
||||||
except ValueError:
|
|
||||||
pass
|
|
||||||
else:
|
|
||||||
self.value = ''
|
|
||||||
|
|
||||||
def setAnswer(self, value):
|
|
||||||
self.setValue(value)
|
|
||||||
|
|
||||||
def buildField(self, xml):
|
|
||||||
self.type = xml.get('type', 'text-single')
|
|
||||||
self.label = xml.get('label', '')
|
|
||||||
for option in xml.findall('{jabber:x:data}option'):
|
|
||||||
self.addOption(option.find('{jabber:x:data}value').text, option.get('label', ''))
|
|
||||||
for value in xml.findall('{jabber:x:data}value'):
|
|
||||||
self.setValue(value.text)
|
|
||||||
if xml.find('{jabber:x:data}required') is not None:
|
|
||||||
self.require()
|
|
||||||
if xml.find('{jabber:x:data}desc') is not None:
|
|
||||||
self.setDescription(xml.find('{jabber:x:data}desc').text)
|
|
||||||
|
|
||||||
def getXML(self, ftype):
|
|
||||||
field = ET.Element('{jabber:x:data}field')
|
|
||||||
if ftype != 'result':
|
|
||||||
field.attrib['type'] = self.type
|
|
||||||
if self.type != 'fixed':
|
|
||||||
if self.var:
|
|
||||||
field.attrib['var'] = self.var
|
|
||||||
if self.label:
|
|
||||||
field.attrib['label'] = self.label
|
|
||||||
if ftype == 'form':
|
|
||||||
for option in self.options:
|
|
||||||
optionxml = ET.Element('{jabber:x:data}option')
|
|
||||||
optionxml.attrib['label'] = option[1]
|
|
||||||
optionval = ET.Element('{jabber:x:data}value')
|
|
||||||
optionval.text = option[0]
|
|
||||||
optionxml.append(optionval)
|
|
||||||
field.append(optionxml)
|
|
||||||
if self.required:
|
|
||||||
required = ET.Element('{jabber:x:data}required')
|
|
||||||
field.append(required)
|
|
||||||
if self.desc:
|
|
||||||
desc = ET.Element('{jabber:x:data}desc')
|
|
||||||
desc.text = self.desc
|
|
||||||
field.append(desc)
|
|
||||||
for value in self.value:
|
|
||||||
valuexml = ET.Element('{jabber:x:data}value')
|
|
||||||
if value is True or value is False:
|
|
||||||
if value:
|
|
||||||
valuexml.text = '1'
|
|
||||||
else:
|
|
||||||
valuexml.text = '0'
|
|
||||||
else:
|
|
||||||
valuexml.text = value
|
|
||||||
field.append(valuexml)
|
|
||||||
return field
|
|
||||||
|
|
||||||
def getXHTML(self):
|
|
||||||
field = ET.Element('div', {'class': 'xmpp-xforms-%s' % self.type})
|
|
||||||
if self.label:
|
|
||||||
label = ET.Element('p')
|
|
||||||
label.text = "%s: " % self.label
|
|
||||||
else:
|
|
||||||
label = ET.Element('p')
|
|
||||||
label.text = "%s: " % self.var
|
|
||||||
field.append(label)
|
|
||||||
if self.type == 'boolean':
|
|
||||||
formf = ET.Element('input', {'type': 'checkbox', 'name': self.var})
|
|
||||||
if len(self.value) and self.value[0] in (True, 'true', '1'):
|
|
||||||
formf.attrib['checked'] = 'checked'
|
|
||||||
elif self.type == 'fixed':
|
|
||||||
formf = ET.Element('p')
|
|
||||||
try:
|
|
||||||
formf.text = ', '.join(self.value)
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
field.append(formf)
|
|
||||||
formf = ET.Element('input', {'type': 'hidden', 'name': self.var})
|
|
||||||
try:
|
|
||||||
formf.text = ', '.join(self.value)
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
elif self.type == 'hidden':
|
|
||||||
formf = ET.Element('input', {'type': 'hidden', 'name': self.var})
|
|
||||||
try:
|
|
||||||
formf.text = ', '.join(self.value)
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
elif self.type in ('jid-multi', 'list-multi'):
|
|
||||||
formf = ET.Element('select', {'name': self.var})
|
|
||||||
for option in self.options:
|
|
||||||
optf = ET.Element('option', {'value': option[0], 'multiple': 'multiple'})
|
|
||||||
optf.text = option[1]
|
|
||||||
if option[1] in self.value:
|
|
||||||
optf.attrib['selected'] = 'selected'
|
|
||||||
formf.append(option)
|
|
||||||
elif self.type in ('jid-single', 'text-single'):
|
|
||||||
formf = ET.Element('input', {'type': 'text', 'name': self.var})
|
|
||||||
try:
|
|
||||||
formf.attrib['value'] = ', '.join(self.value)
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
elif self.type == 'list-single':
|
|
||||||
formf = ET.Element('select', {'name': self.var})
|
|
||||||
for option in self.options:
|
|
||||||
optf = ET.Element('option', {'value': option[0]})
|
|
||||||
optf.text = option[1]
|
|
||||||
if not optf.text:
|
|
||||||
optf.text = option[0]
|
|
||||||
if option[1] in self.value:
|
|
||||||
optf.attrib['selected'] = 'selected'
|
|
||||||
formf.append(optf)
|
|
||||||
elif self.type == 'text-multi':
|
|
||||||
formf = ET.Element('textarea', {'name': self.var})
|
|
||||||
try:
|
|
||||||
formf.text = ', '.join(self.value)
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
if not formf.text:
|
|
||||||
formf.text = ' '
|
|
||||||
elif self.type == 'text-private':
|
|
||||||
formf = ET.Element('input', {'type': 'password', 'name': self.var})
|
|
||||||
try:
|
|
||||||
formf.attrib['value'] = ', '.join(self.value)
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
label.append(formf)
|
|
||||||
return field
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue