diff options
author | André Apitzsch <git@apitzsch.eu> | 2017-06-21 00:09:12 +0300 |
---|---|---|
committer | André Apitzsch <git@apitzsch.eu> | 2017-07-02 18:25:47 +0300 |
commit | 125ce523e44f11620de2cb09c55bce95d03f0993 (patch) | |
tree | 59fcf5f02b403d48c61da8707324cc7e19a4dd7f /src/common/dataforms.py | |
parent | 68a57e7c917244be40d569700f09ab67f11db45a (diff) |
Rename src directory
Diffstat (limited to 'src/common/dataforms.py')
-rw-r--r-- | src/common/dataforms.py | 754 |
1 files changed, 0 insertions, 754 deletions
diff --git a/src/common/dataforms.py b/src/common/dataforms.py deleted file mode 100644 index 6dd6b896c..000000000 --- a/src/common/dataforms.py +++ /dev/null @@ -1,754 +0,0 @@ -# this will go to src/common/xmpp later, for now it is in src/common -# -*- coding:utf-8 -*- -## src/common/dataforms.py -## -## Copyright (C) 2006-2007 Tomasz Melcer <liori AT exroot.org> -## Copyright (C) 2006-2014 Yann Leboulanger <asterix AT lagaule.org> -## Copyright (C) 2007 Stephan Erb <steve-e AT h3c.de> -## -## This file is part of Gajim. -## -## Gajim 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; version 3 only. -## -## Gajim 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 Gajim. If not, see <http://www.gnu.org/licenses/>. -## - -""" -This module contains wrappers for different parts of data forms (JEP 0004). For -information how to use them, read documentation -""" - -import nbxmpp -from common import helpers - -# exceptions used in this module -# base class -class Error(Exception): pass -# when we get nbxmpp.Node which we do not understand -class UnknownDataForm(Error): pass -# when we get nbxmpp.Node which contains bad fields -class WrongFieldValue(Error): pass - -# helper class to change class of already existing object -class ExtendedNode(nbxmpp.Node, object): - @classmethod - def __new__(cls, *a, **b): - if 'extend' not in b.keys() or not b['extend']: - 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): - ''' Helper function to create a field of given type. ''' - f = { - 'boolean': BooleanField, - 'fixed': StringField, - 'hidden': StringField, - 'text-private': StringField, - 'text-single': StringField, - 'jid-multi': JidMultiField, - 'jid-single': JidSingleField, - 'list-multi': ListMultiField, - 'list-single': ListSingleField, - 'text-multi': TextMultiField, - }[typ](typ=typ, **attrs) - return f - -def ExtendField(node): - """ - Helper function to extend a node to field of appropriate type - """ - # when validation (XEP-122) will go in, we could have another classes - # like DateTimeField - so that dicts in Field() and ExtendField() will - # be different... - typ=node.getAttr('type') - f = { - 'boolean': BooleanField, - 'fixed': StringField, - 'hidden': StringField, - 'text-private': StringField, - 'text-single': StringField, - 'jid-multi': JidMultiField, - 'jid-single': JidSingleField, - 'list-multi': ListMultiField, - 'list-single': ListSingleField, - 'text-multi': TextMultiField, - } - if typ not in f: - typ = 'text-single' - return f[typ](extend=node) - -def ExtendForm(node): - """ - Helper function to extend a node to form of appropriate type - """ - if node.getTag('reported') is not None: - return MultipleDataForm(extend=node) - else: - return SimpleDataForm(extend=node) - -class DataField(ExtendedNode): - """ - Keeps data about one field - var, field type, labels, instructions... Base - class for different kinds of fields. Use Field() function to construct one - of these - """ - - def __init__(self, typ=None, var=None, value=None, label=None, desc=None, - required=False, options=None, extend=None): - - if extend is None: - ExtendedNode.__init__(self, 'field') - - self.type_ = typ - self.var = var - if value is not None: - self.value = value - if label is not None: - self.label = label - if desc is not None: - 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, str) - 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, str) - self.setAttr('var', value) - - def fdel(self): - self.delAttr('var') - - return locals() - - @nested_property - def label(): - """ - Human-readable field name - """ - def fget(self): - l = self.getAttr('label') - if not l: - l = self.var - return l - - def fset(self, value): - assert isinstance(value, str) - self.setAttr('label', value) - - def fdel(self): - if self.getAttr('label'): - self.delAttr('label') - - return locals() - - @nested_property - def description(): - """ - Human-readable description of field meaning - """ - def fget(self): - return self.getTagData('desc') or '' - - def fset(self, value): - assert isinstance(value, str) - 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 bool(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() - - @nested_property - def media(): - """ - Media data - """ - def fget(self): - media = self.getTag('media', namespace=nbxmpp.NS_DATA_MEDIA) - if media: - return Media(media) - - def fset(self, value): - fdel(self) - self.addChild(node=value) - - def fdel(self): - t = self.getTag('media') - if t is not None: - self.delChild(t) - - return locals() - - def is_valid(self): - return True - -class Uri(nbxmpp.Node): - def __init__(self, uri_tag): - nbxmpp.Node.__init__(self, node=uri_tag) - - @nested_property - def type_(): - """ - uri type - """ - def fget(self): - return self.getAttr('type') - - def fset(self, value): - self.setAttr('type', value) - - def fdel(self): - self.delAttr('type') - - return locals() - - @nested_property - def uri_data(): - """ - uri data - """ - def fget(self): - return self.getData() - - def fset(self, value): - self.setData(value) - - def fdel(self): - self.setData(None) - - return locals() - -class Media(nbxmpp.Node): - def __init__(self, media_tag): - nbxmpp.Node.__init__(self, node=media_tag) - - @nested_property - def uris(): - """ - URIs of the media element. - """ - def fget(self): - return map(Uri, self.getTags('uri')) - - def fset(self, value): - fdel(self) - for uri in value: - self.addChild(node=uri) - - def fdel(self, value): - for element in self.getTags('uri'): - self.delChild(element) - - 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 False # default value is False - 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 string - """ - def fget(self): - return self.getTagData('value') or '' - - def fset(self, value): - assert isinstance(value, str) - if value == '' and not self.required: - return fdel(self) - self.setTagData('value', value) - - def fdel(self): - try: - self.delChild(self.getTag('value')) - except ValueError: # if there already were no value tag - pass - - return locals() - -class ListField(DataField): - """ - Covers fields of types: jid-multi, jid-single, list-multi, list-single - """ - - @nested_property - def options(): - """ - Options - """ - def fget(self): - options = [] - for element in self.getTags('option'): - v = element.getTagData('value') - if v is None: - raise WrongFieldValue - l = element.getAttr('label') - if not l: - l = v - options.append((l, v)) - return options - - def fset(self, values): - fdel(self) - for value, label in values: - self.addChild('option', {'label': label}).setTagData('value', value) - - def fdel(self): - for element in self.getTags('option'): - self.delChild(element) - - return locals() - - def iter_options(self): - for element in self.iterTags('option'): - v = element.getTagData('value') - if v is None: - raise WrongFieldValue - l = element.getAttr('label') - if not l: - l = v - yield (v, l) - -class ListSingleField(ListField, StringField): - """ - Covers list-single field - """ - def is_valid(self): - if not self.required: - return True - if not self.value: - return False - return True - -class JidSingleField(ListSingleField): - """ - Covers jid-single fields - """ - def is_valid(self): - if self.value: - try: - helpers.parse_jid(self.value) - return True - except: - return False - if self.required: - return False - return True - -class ListMultiField(ListField): - """ - Covers list-multi fields - """ - - @nested_property - def values(): - """ - Values held in field - """ - def fget(self): - values = [] - for element in self.getTags('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 self.getTags('value'): - self.delChild(element) - - return locals() - - def iter_values(self): - for element in self.getTags('value'): - yield element.getData() - - def is_valid(self): - if not self.required: - return True - if not self.values: - return False - return True - -class JidMultiField(ListMultiField): - """ - Covers jid-multi fields - """ - def is_valid(self): - if len(self.values): - for value in self.values: - try: - helpers.parse_jid(value) - except: - return False - return True - if self.required: - return False - return True - -class TextMultiField(DataField): - @nested_property - def value(): - """ - Value held in field - """ - def fget(self): - value = '' - for element in self.iterTags('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 self.getTags('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 - nbxmpp.Node.__init__(self) - - if fields is not None: - self.fields = fields - else: - # we already have nbxmpp.Node inside - try to convert all - # fields into DataField objects - if fields is None: - for field in self.iterTags('field'): - if not isinstance(field, DataField): - ExtendField(field) - self.vars[field.var] = field - else: - for field in self.getTags('field'): - self.delChild(field) - self.fields = fields - - @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): - ExtendField(extend=field) - self.addChild(node=field) - - def fdel(self): - for element in self.getTags('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] - - def is_valid(self): - for f in self.iter_fields(): - if not f.is_valid(): - return False - return True - -class DataForm(ExtendedNode): - def __init__(self, type_=None, title=None, instructions=None, extend=None): - if extend is None: - # we have to build form from scratch - nbxmpp.Node.__init__(self, 'x', attrs={'xmlns': nbxmpp.NS_DATA}) - - if type_ is not None: - self.type_=type_ - if title is not None: - self.title=title - if instructions is not None: - self.instructions=instructions - - @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 will be able soon to do: - filledform = DataForm(replyto=thisform) - """ - def fget(self): - return self.getAttr('type') - - def fset(self, type_): - 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, title): - 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 = '' - for valuenode in self.getTags('instructions'): - value += '\n' + valuenode.getData() - return value[1:] - - def fset(self, value): - fdel(self) - if value == '': return - for line in value.split('\n'): - self.addChild('instructions').setData(line) - - def fdel(self): - for value in self.getTags('instructions'): - self.delChild(value) - - return locals() - -class SimpleDataForm(DataForm, DataRecord): - def __init__(self, type_=None, title=None, instructions=None, fields=None, \ - extend=None): - DataForm.__init__(self, type_=type_, title=title, - instructions=instructions, extend=extend) - DataRecord.__init__(self, fields=fields, extend=self, associated=self) - - def get_purged(self): - c = SimpleDataForm(extend=self) - del c.title - c.instructions = '' - to_be_removed = [] - for f in c.iter_fields(): - if f.required: - # add <value> if there is not - if hasattr(f, 'value') and not f.value: - f.value = '' - # Keep all required fields - continue - if (hasattr(f, 'value') and not f.value and f.value != 0) or ( - hasattr(f, 'values') and len(f.values) == 0): - to_be_removed.append(f) - else: - del f.label - del f.description - del f.media - for f in to_be_removed: - c.delChild(f) - return c - -class MultipleDataForm(DataForm): - def __init__(self, type_=None, title=None, instructions=None, items=None, - extend=None): - DataForm.__init__(self, type_=type_, title=title, - instructions=instructions, extend=extend) - # all records, recorded into DataRecords - if extend is None: - if items is not None: - self.items = items - else: - # we already have nbxmpp.Node inside - try to convert all - # fields into DataField objects - if items is None: - self.items = list(self.iterTags('item')) - else: - for item in self.getTags('item'): - self.delChild(item) - self.items = items - reported_tag = self.getTag('reported') - self.reported = DataRecord(extend=reported_tag) - - @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.getTags('item'): - self.delChild(record) - - return locals() - - def iter_records(self): - for record in self.getTags('item'): - yield record - -# @nested_property -# def reported(): -# """ -# DataRecord that contains descriptions of fields in records -# """ -# def fget(self): -# return self.getTag('reported') -# def fset(self, record): -# try: -# self.delChild('reported') -# except: -# pass -# -# record.setName('reported') -# self.addChild(node=record) -# return locals() |