Skip to content
Snippets Groups Projects
Commit 68703a1d authored by Liorithiel's avatar Liorithiel
Browse files

'Server' part switched to use new forms.

parent fafc555b
No related branches found
No related tags found
No related merge requests found
......@@ -53,8 +53,8 @@ class AdHocCommand:
cmd.addChild('actions', attrs, actions)
return response, cmd
def badRequest(self):
self.connection.connection.send(xmpp.Error(xmpp.NS_STANZAS+' bad-request'))
def badRequest(self, stanza):
self.connection.connection.send(xmpp.Error(stanza, xmpp.NS_STANZAS+' bad-request'))
def cancel(self, request):
response, cmd = self.buildResponse(request, status='canceled')
......@@ -68,17 +68,20 @@ class ChangeStatusCommand(AdHocCommand):
@staticmethod
def isVisibleFor(samejid):
''' Change status is visible only if the entity has the same bare jid. '''
print 'isVisibleFor', samejid
return True # TODO: Remove that!
return samejid
def execute(self, request):
# first query...
response, cmd = self.buildResponse(request, defaultaction='execute', actions=['execute'])
cmd.addChild(node=dataforms.DataForm(
cmd.addChild(node=dataforms.SimpleDataForm(
title='Change status',
instructions='Set the presence type and description',
fields=[
dataforms.DataField('list-single', 'presence-type',
dataforms.Field('list-single',
var='presence-type',
label='Type of presence:',
options=[
(u'free-for-chat', u'Free for chat'),
......@@ -89,7 +92,8 @@ class ChangeStatusCommand(AdHocCommand):
(u'offline', u'Offline - disconnect')],
value='online',
required=True),
dataforms.DataField('text-multi', 'presence-desc',
dataforms.Field('text-multi',
var='presence-desc',
label='Presence description:')]))
self.connection.connection.send(response)
......@@ -102,23 +106,25 @@ class ChangeStatusCommand(AdHocCommand):
def changestatus(self, request):
# check if the data is correct
try:
form=dataforms.DataForm(node=request.getTag('command').getTag('x'))
except TypeError:
self.badRequest()
form=dataforms.SimpleDataForm(extend=request.getTag('command').getTag('x'))
except:
self.badRequest(request)
return False
try:
presencetype = form['presence-type']
if not presencetype in ('free-for-chat', 'online', 'away', 'xa', 'dnd', 'offline'):
self.badRequest()
presencetype = form['presence-type'].value
if not presencetype in \
('free-for-chat', 'online', 'away', 'xa', 'dnd', 'offline'):
self.badRequest(request)
return False
except KeyError:
self.badRequest()
except: # KeyError if there's no presence-type field in form or
# AttributeError if that field is of wrong type
self.badRequest(request)
return False
try:
presencedesc = form['presence-desc']
except KeyError:
presencedesc = form['presence-desc'].value
except: # same exceptions as in last comment
presencedesc = u''
response, cmd = self.buildResponse(request, status='completed')
......@@ -150,6 +156,7 @@ class ConnectionCommands:
return xmpp.JID(jid).getStripped() == self.getOurBareJID()
def commandListQuery(self, con, iq_obj):
print 'commandListQuery'
iq = iq_obj.buildReply('result')
jid = helpers.get_full_jid_from_iq(iq_obj)
q = iq.getTag('query')
......@@ -224,11 +231,12 @@ class ConnectionCommands:
obj = self.__sessions[magictuple]
try:
if action == 'cancel': rc = obj.cancel(iq_obj)
elif action == 'prev': rc = obj.prev(iq_obj)
elif action == 'next': rc = obj.next(iq_obj)
elif action == 'execute': rc = obj.execute(iq_obj)
elif action == 'complete': rc = obj.complete(iq_obj)
if action == 'cancel': rc = obj.cancel(iq_obj)
elif action == 'prev': rc = obj.prev(iq_obj)
elif action == 'next': rc = obj.next(iq_obj)
elif action == 'execute' or action is None:
rc = obj.execute(iq_obj)
elif action == 'complete': rc = obj.complete(iq_obj)
else:
# action is wrong. stop the session, send error
raise AttributeError
......
# this will go to src/common/xmpp later, for now it is in src/common
""" This module contains wrappers for different parts of data forms (JEP 0004). For information
how to use them, read documentation. """
import xmpp
# exceptions used in this module
class Error(Exception): pass # base class
class UnknownDataForm(Error): pass # when we get xmpp.Node which we do not understand
class WrongFieldValue(Error): pass # when we get xmpp.Node which contains bad fields
# helper class to change class of already existing object
class ExtendedNode(xmpp.Node, object):
def __new__(cls, *a, **b):
print 'Extends.__new__'
if 'extend' not in b.keys():
return object.__new__(cls)
extend = b['extend']
assert issubclass(cls, extend.__class__)
extend.__class__ = cls
return extend
# helper decorator to create properties in cleaner way
def nested_property(f):
ret = f()
p = {'doc': f.__doc__}
for v in ('fget', 'fset', 'fdel', 'doc'):
if v in ret.keys(): p[v]=ret[v]
return property(**p)
# helper to create fields from scratch
def Field(typ, **attrs):
f = {
'boolean': BooleanField,
'fixed': StringField,
'hidden': StringField,
'text-private': StringField,
'text-single': StringField,
'jid-multi': ListField,
'jid-single': ListField,
'list-multi': ListField,
'list-single': ListField,
'text-multi': TextMultiField,
}[typ]
for key, value in attrs.iteritems():
f.setattr(key, value)
return f
class DataField(ExtendedNode):
""" Keeps data about one field - var, field type, labels, instructions... """
def __init__(self, typ=None, var=None, value=None, label=None, desc=None, required=None,
options=None, extend=None):
if extend is None:
super(DataField, self).__init__(self, 'field')
self.type = typ
self.var = var
self.value = value
self.label = label
self.desc = desc
self.required = required
self.options = options
@nested_property
def type():
'''Type of field. Recognized values are: 'boolean', 'fixed', 'hidden', 'jid-multi',
'jid-single', 'list-multi', 'list-single', 'text-multi', 'text-private',
'text-single'. If you set this to something different, DataField will store
given name, but treat all data as text-single.'''
def fget(self):
t = self.getAttr('type')
if t is None: return 'text-single'
return t
def fset(self, value):
assert isinstance(value, basestring)
self.setAttr('type', value)
return locals()
@nested_property
def var():
'''Field identifier.'''
def fget(self):
return self.getAttr('var')
def fset(self, value):
assert isinstance(value, basestring)
self.setAttr('var', value)
def fdel(self):
self.delAttr('var')
return locals()
@nested_property
def label():
'''Human-readable field name.'''
def fget(self):
return self.getAttr('label')
def fset(self, value):
assert isinstance(value, basestring)
self.setAttr('label', value)
def fdel(self):
self.delAttr('label')
return locals()
@nested_property
def description():
'''Human-readable description of field meaning.'''
def fget(self):
return self.getTagData('desc') or u''
def fset(self, value):
assert isinstance(value, basestring)
if value == '':
fdel(self)
else:
self.setTagData('desc', value)
def fdel(self):
t = self.getTag('desc')
if t is not None:
self.delChild(t)
return locals()
@nested_property
def required():
'''Controls whether this field required to fill. Boolean.'''
def fget(self):
return boolean(self.getTag('required'))
def fset(self, value):
t = self.getTag('required')
if t and not value:
self.delChild(t)
elif not t and value:
self.addChild('required')
return locals()
class BooleanField(DataField):
@nested_property
def value():
'''Value of field. May contain True, False or None.'''
def fget(self):
v = self.getTagData('value')
if v in ('0', 'false'): return False
if v in ('1', 'true'): return True
if v is None: return None
raise WrongFieldValue
def fset(self, value):
self.setTagData('value', value and '1' or '0')
def fdel(self, value):
t = self.getTag('value')
if t is not None:
self.delChild(t)
return locals()
class StringField(DataField):
''' Covers fields of types: fixed, hidden, text-private, text-single. '''
@nested_property
def value():
'''Value of field. May be any unicode string.'''
def fget(self):
return self.getTagData('value') or u''
def fset(self, value):
assert isinstance(value, basestring)
if value == '':
return fdel(self)
self.setTagData('value', value)
def fdel(self):
t = self.getTag('value')
if t is not None:
self.delChild(t)
return locals()
class ListField(DataField):
''' Covers fields of types: jid-multi, jid-single, list-multi, list-single. '''
@nested_property
def values():
'''Values held in field.'''
def fget(self):
values = []
for element in iter_elements(self, 'value'):
values.append(element.getData())
return values
def fset(self, values):
fdel(self)
for value in values:
self.addChild('value').setData(value)
def fdel(self):
for element in iter_elements(self, 'value'):
self.delChild(element)
return locals()
def iter_values():
for element in iter_elements(self, 'value'):
yield element.getData()
@nested_property
def options():
'''Options.'''
def fget(self):
options = []
for element in iter_elements(self, 'option'):
v = element.getTagData('value')
if v is None: raise WrongFieldValue
options.append((element.getAttr('label'), v))
return options
def fset(self, values):
fdel(self)
for label, value in values:
self.addChild('option', {'label': label}).setTagData('value', value)
def fdel(self):
for element in iter_elements(self, 'option'):
self.delChild(element)
return locals()
def iter_options(self):
for element in iter_elements(self, 'option'):
v = element.getTagData('value')
if v is None: raise WrongFieldValue
yield (element.getAttr('label'), v)
class TextMultiField(DataField):
@nested_property
def value():
'''Value held in field.'''
def fget(self):
value = u''
for element in iter_elements(self, 'value'):
value += '\n' + element.getData()
return value[1:]
def fset(self, value):
fdel(self)
if value == '': return
for line in value.split('\n'):
self.addChild('value').setData(line)
def fdel(self):
for element in iter_elements(self, 'value'):
self.delChild(element)
return locals()
class DataRecord(ExtendedNode):
'''The container for data fields - an xml element which has DataField
elements as children.'''
def __init__(self, fields=None, associated=None, extend=None):
self.associated = associated
self.vars = {}
if extend is None:
# we have to build this object from scratch
xmpp.Node.__init__(self)
if fields is not None: self.fields = fields
else:
# we already have xmpp.Node inside - try to convert all
# fields into DataField objects
for field in self.iterTags('field'):
if not isinstance(field, DataField):
DataField(extend=field)
self.vars[field.var] = field
@nested_property
def fields():
'''List of fields in this record.'''
def fget(self):
return self.getTags('field')
def fset(self, fields):
fdel(self)
for field in fields:
if not isinstance(field, DataField):
DataField(extend=field)
self.addChild(node=field)
def fdel(self):
for element in self.iterTags('field'):
self.delChild(element)
return locals()
def iter_fields(self):
''' Iterate over fields in this record. Do not take associated
into account. '''
for field in self.iterTags('field'):
yield field
def iter_with_associated(self):
''' Iterate over associated, yielding both our field and
associated one together. '''
for field in self.associated.iter_fields():
yield self[field.var], field
def __getitem__(self, item):
return self.vars[item]
class DataForm(ExtendedNode):
def __init__(self, replyto=None, extend=None):
if extend is None:
# we have to build form from scratch
xmpp.Node.__init__(self, 'x', attrs={'xmlns': xmpp.NS_DATA})
@nested_property
def type():
''' Type of the form. Must be one of: 'form', 'submit', 'cancel', 'result'.
'form' - this form is to be filled in; you can do:
filledform = DataForm(replyto=thisform)...'''
def fget(self):
return self.getAttr('type')
def fset(self):
assert type in ('form', 'submit', 'cancel', 'result')
self.setAttr('type', type)
return locals()
@nested_property
def title():
''' Title of the form. Human-readable, should not contain any \\r\\n.'''
def fget(self):
return self.getTagData('title')
def fset(self):
self.setTagData('title', title)
def fdel(self):
try:
self.delChild('title')
except ValueError:
pass
return locals()
@nested_property
def instructions():
''' Instructions for this form. Human-readable, may contain \\r\\n. '''
# TODO: the same code is in TextMultiField. join them
def fget(self):
value = u''
for value in self.iterTags('value'):
value += '\n' + value.getData()
return value[1:]
def fset(self, value):
fdel(self)
if value == '': return
for line in value.split('\n'):
self.addChild('value').setData(line)
def fdel(self):
for value in self.iterTags('value'):
self.delChild(value)
return locals()
class SimpleDataForm(DataForm, DataRecord):
def __init__(self, extend=None):
if extend is None:
# we have to build form from scratch
DataForm.__init__(self)
DataRecord.__init__(self, extend=self, associated=self)
else:
# we already have node, just change it to real form object
pass
class MultipleDataForm(DataForm):
def __init__(self):
# all records, recorded into DataRecords
pass
@nested_property
def items():
''' A list of all records. '''
def fget(self):
return list(self.iter_records())
def fset(self, records):
fdel(self)
for record in records:
if not isinstance(record, DataRecord):
DataRecord(extend=record)
self.addChild(node=record)
def fdel(self):
for record in self.iterTags('record'):
self.delChild(record)
return locals()
def iter_records():
for record in self.iterTags('item'):
yield item
@nested_property
def recorded():
''' DataRecord that contains descriptions of fields in records.'''
def fget(self):
return self.getTag('recorded')
def fset(self, record):
try:
self.delChild('recorded')
except:
pass
record.setName('recorded')
self.addChild(node=record)
return locals()
This diff is collapsed.
......@@ -33,7 +33,7 @@ def ustr(what):
if type(r)<>type(u''): return unicode(r,ENCODING)
return r
class Node:
class Node(object):
""" Node class describes syntax of separate XML Node. It have a constructor that permits node creation
from set of "namespace name", attributes and payload of text strings and other nodes.
It does not natively support building node from text string and uses NodeBuilder class for that purpose.
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment