pyxmpp.jabber.dataforms

Package pyxmpp :: Package jabber :: Module dataforms |
[hide private]

# Source Code for Module pyxmpp.jabber.dataforms

      1  #
      2  # (C) Copyright 2005-2010 Jacek Konieczny <jajcus@jajcus.net>
      3  #
      4  # This program is free software; you can redistribute it and/or modify
      5  # it under the terms of the GNU Lesser General Public License Version
      6  # 2.1 as published by the Free Software Foundation.
      7  #
      8  # This program is distributed in the hope that it will be useful,
      9  # but WITHOUT ANY WARRANTY; without even the implied warranty of
     10  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     11  # GNU Lesser General Public License for more details.
     12  #
     13  # You should have received a copy of the GNU Lesser General Public
     14  # License along with this program; if not, write to the Free Software
     15  # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
     16  #
     17  """Jabber Data Forms support.
     18
     19  Normative reference:
     20    - `JEP 4 <http://www.jabber.org/jeps/jep-0004.html>`__
     21  """
     22
     23  __revision__="$Id: disco.py 513 2005-01-09 16:34:00Z jajcus $"
     24  __docformat__="restructuredtext en"
     25
     26  import copy
     27  import libxml2
     28  import warnings
     29  from pyxmpp.objects import StanzaPayloadObject
     30  from pyxmpp.utils import from_utf8, to_utf8
     31  from pyxmpp.xmlextra import xml_element_ns_iter
     32  from pyxmpp.jid import JID
     33  from pyxmpp.exceptions import BadRequestProtocolError
     34
     35  DATAFORM_NS = "jabber:x:data"
     36
     37 -class Option(StanzaPayloadObject):
     38      """One of optional data form field values.
     39
     40      :Ivariables:
     41          - `label`: option label.
     42          - `value`: option value.
     43      :Types:
     44          - `label`: `unicode`
     45          - `value`: `unicode`
     46      """
     47      xml_element_name = "option"
     48      xml_element_namespace = DATAFORM_NS
     49
     50 -    def __init__(self, value = None, label = None, values = None):
     51          """Initialize an `Option` object.
     52
     53          :Parameters:
     54              - `value`: option value (mandatory).
     55              - `label`: option label (human-readable description).
     56              - `values`: for backward compatibility only.
     57          :Types:
     58              - `label`: `unicode`
     59              - `value`: `unicode`
     60          """
     61          self.label = label
     62
     63          if value:
     64              self.value = value
     65          elif values:
     66              warnings.warn("Option constructor accepts only single value now.", DeprecationWarning, stacklevel=1)
     67              self.value = values[0]
     68          else:
     69              raise TypeError, "value argument to pyxmpp.dataforms.Option is required"
     70
     71
     72      @property
     73 -    def values(self):
     74          """Return list of option values (always single element). Obsolete. For
     75          backwards compatibility only."""
     76          return [self.value]
     77
     78 -    def complete_xml_element(self, xmlnode, doc):
     79          """Complete the XML node with `self` content.
     80
     81          :Parameters:
     82              - `xmlnode`: XML node with the element being built. It has already
     83                right name and namespace, but no attributes or content.
     84              - `doc`: document to which the element belongs.
     85          :Types:
     86              - `xmlnode`: `libxml2.xmlNode`
     87              - `doc`: `libxml2.xmlDoc`"""
     88          _unused = doc
     89          if self.label is not None:
     90              xmlnode.setProp("label", self.label.encode("utf-8"))
     91          xmlnode.newTextChild(xmlnode.ns(), "value", self.value.encode("utf-8"))
     92          return xmlnode
     93
     94      @classmethod
     95 -    def _new_from_xml(cls, xmlnode):
     96          """Create a new `Option` object from an XML element.
     97
     98          :Parameters:
     99              - `xmlnode`: the XML element.
    100          :Types:
    101              - `xmlnode`: `libxml2.xmlNode`
    102
    103          :return: the object created.
    104          :returntype: `Option`
    105          """
    106          label = from_utf8(xmlnode.prop("label"))
    107          child = xmlnode.children
    108          value = None
    109          for child in xml_element_ns_iter(xmlnode.children, DATAFORM_NS):
    110              if child.name == "value":
    111                  value = from_utf8(child.getContent())
    112                  break
    113          if value is None:
    114              raise BadRequestProtocolError, "No value in <option/> element"
    115          return cls(value, label)
    116
    117 -class Field(StanzaPayloadObject):
    118      """A data form field.
    119
    120      :Ivariables:
    121          - `name`: field name.
    122          - `values`: field values.
    123          - `value`: field value parsed according to the form type.
    124          - `label`: field label (human-readable description).
    125          - `type`: field type ("boolean", "fixed", "hidden", "jid-multi",
    126            "jid-single", "list-multi", "list-single", "text-multi",
    127            "text-private" or "text-single").
    128          - `options`: field options (for "list-multi" or "list-single" fields).
    129          - `required`: `True` when the field is required.
    130          - `desc`: natural-language description of the field.
    131      :Types:
    132          - `name`: `unicode`
    133          - `values`: `list` of `unicode`
    134          - `value`: `bool` for "boolean" field, `JID` for "jid-single", `list` of `JID`
    135            for "jid-multi", `list` of `unicode` for "list-multi" and "text-multi"
    136            and `unicode` for other field types.
    137          - `label`: `unicode`
    138          - `type`: `str`
    139          - `options`: `Option`
    140          - `required`: `boolean`
    141          - `desc`: `unicode`
    142      """
    143      xml_element_name = "field"
    144      xml_element_namespace = DATAFORM_NS
    145      allowed_types = ("boolean", "fixed", "hidden", "jid-multi",
    146                  "jid-single", "list-multi", "list-single", "text-multi",
    147                  "text-private", "text-single")
    148 -    def __init__(self, name = None, values = None, field_type = None, label = None,
    149              options = None, required = False, desc = None, value = None):
    150          """Initialize a `Field` object.
    151
    152          :Parameters:
    153              - `name`: field name.
    154              - `values`: raw field values. Not to be used together with `value`.
    155              - `field_type`: field type.
    156              - `label`: field label.
    157              - `options`: optional values for the field.
    158              - `required`: `True` if the field is required.
    159              - `desc`: natural-language description of the field.
    160              - `value`: field value or values in a field_type-specific type. May be used only
    161                if `values` parameter is not provided.
    162          :Types:
    163              - `name`: `unicode`
    164              - `values`: `list` of `unicode`
    165              - `field_type`: `str`
    166              - `label`: `unicode`
    167              - `options`: `list` of `Option`
    168              - `required`: `bool`
    169              - `desc`: `unicode`
    170              - `value`: `bool` for "boolean" field, `JID` for "jid-single", `list` of `JID`
    171                for "jid-multi", `list` of `unicode` for "list-multi" and "text-multi"
    172                and `unicode` for other field types.
    173          """
    174          self.name = name
    175          if field_type is not None and field_type not in self.allowed_types:
    176              raise ValueError, "Invalid form field type: %r" % (field_type,)
    177          self.type = field_type
    178          if value is not None:
    179              if values:
    180                  raise ValueError, "values or value must be given, not both"
    181              self.value = value
    182          elif not values:
    183              self.values = []
    184          else:
    185              self.values = list(values)
    186          if field_type and not field_type.endswith("-multi") and len(self.values) > 1:
    187              raise ValueError, "Multiple values for a single-value field"
    188          self.label = label
    189          if not options:
    190              self.options = []
    191          elif field_type and not field_type.startswith("list-"):
    192              raise ValueError, "Options not allowed for non-list fields"
    193          else:
    194              self.options = list(options)
    195          self.required = required
    196          self.desc = desc
    197
    198 -    def __getattr__(self, name):
    199          if name != "value":
    200              raise AttributeError, "'Field' object has no attribute %r" % (name,)
    201          values = self.values
    202          t = self.type
    203          l = len(values)
    204          if t is not None:
    205              if t == "boolean":
    206                  if l == 0:
    207                      return None
    208                  elif l == 1:
    209                      v = values[0]
    210                      if v in ("0","false"):
    211                          return False
    212                      elif v in ("1","true"):
    213                          return True
    214                  raise ValueError, "Bad boolean value"
    215              elif t.startswith("jid-"):
    216                  values = [JID(v) for v in values]
    217              if t.endswith("-multi"):
    218                  return values
    219          if l == 0:
    220              return None
    221          elif l == 1:
    222              return values[0]
    223          else:
    224              raise ValueError, "Multiple values of a single-value field"
    225
    226 -    def __setattr__(self, name, value):
    227          if name != "value":
    228              self.__dict__[name] = value
    229              return
    230          if value is None:
    231              self.values = []
    232              return
    233          t = self.type
    234          if t == "boolean":
    235              if value:
    236                  self.values = ["1"]
    237              else:
    238                  self.values = ["0"]
    239              return
    240          if t and t.endswith("-multi"):
    241              values = list(value)
    242          else:
    243              values = [value]
    244          if t and t.startswith("jid-"):
    245              values = [JID(v).as_unicode() for v in values]
    246          self.values = values
    247
    248 -    def add_option(self, value, label):
    249          """Add an option for the field.
    250
    251          :Parameters:
    252              - `value`: option values.
    253              - `label`: option label (human-readable description).
    254          :Types:
    255              - `value`: `list` of `unicode`
    256              - `label`: `unicode`
    257          """
    258          if type(value) is list:
    259              warnings.warn(".add_option() accepts single value now.", DeprecationWarning, stacklevel=1)
    260              value = value[0]
    261          if self.type not in ("list-multi", "list-single"):
    262              raise ValueError, "Options are allowed only for list types."
    263          option = Option(value, label)
    264          self.options.append(option)
    265          return option
    266
    267 -    def complete_xml_element(self, xmlnode, doc):
    268          """Complete the XML node with `self` content.
    269
    270          :Parameters:
    271              - `xmlnode`: XML node with the element being built. It has already
    272                right name and namespace, but no attributes or content.
    273              - `doc`: document to which the element belongs.
    274          :Types:
    275              - `xmlnode`: `libxml2.xmlNode`
    276              - `doc`: `libxml2.xmlDoc`"""
    277          if self.type is not None and self.type not in self.allowed_types:
    278              raise ValueError, "Invalid form field type: %r" % (self.type,)
    279          if self.type is not None:
    280              xmlnode.setProp("type", self.type)
    281          if not self.label is None:
    282              xmlnode.setProp("label", to_utf8(self.label))
    283          if not self.name is None:
    284              xmlnode.setProp("var", to_utf8(self.name))
    285          if self.values:
    286              if self.type and len(self.values) > 1 and not self.type.endswith(u"-multi"):
    287                  raise ValueError, "Multiple values not allowed for %r field" % (self.type,)
    288              for value in self.values:
    289                  xmlnode.newTextChild(xmlnode.ns(), "value", to_utf8(value))
    290          for option in self.options:
    291              option.as_xml(xmlnode, doc)
    292          if self.required:
    293              xmlnode.newChild(xmlnode.ns(), "required", None)
    294          if self.desc:
    295              xmlnode.newTextChild(xmlnode.ns(), "desc", to_utf8(self.desc))
    296          return xmlnode
    297
    298      @classmethod
    299 -    def _new_from_xml(cls, xmlnode):
    300          """Create a new `Field` object from an XML element.
    301
    302          :Parameters:
    303              - `xmlnode`: the XML element.
    304          :Types:
    305              - `xmlnode`: `libxml2.xmlNode`
    306
    307          :return: the object created.
    308          :returntype: `Field`
    309          """
    310          field_type = xmlnode.prop("type")
    311          label = from_utf8(xmlnode.prop("label"))
    312          name = from_utf8(xmlnode.prop("var"))
    313          child = xmlnode.children
    314          values = []
    315          options = []
    316          required = False
    317          desc = None
    318          while child:
    319              if child.type != "element" or child.ns().content != DATAFORM_NS:
    320                  pass
    321              elif child.name == "required":
    322                  required = True
    323              elif child.name == "desc":
    324                  desc = from_utf8(child.getContent())
    325              elif child.name == "value":
    326                  values.append(from_utf8(child.getContent()))
    327              elif child.name == "option":
    328                  options.append(Option._new_from_xml(child))
    329              child = child.next
    330          if field_type and not field_type.endswith("-multi") and len(values) > 1:
    331              raise BadRequestProtocolError, "Multiple values for a single-value field"
    332          return cls(name, values, field_type, label, options, required, desc)
    333
    334 -class Item(StanzaPayloadObject):
    335      """An item of multi-item form data (e.g. a search result).
    336
    337      Additionally to the direct access to the contained fields via the `fields` attribute,
    338      `Item` object provides an iterator and mapping interface for field access. E.g.::
    339
    340          for field in item:
    341              ...
    342
    343      or::
    344
    345          field = item['field_name']
    346
    347      or::
    348
    349          if 'field_name' in item:
    350              ...
    351
    352      :Ivariables:
    353          - `fields`: the fields of the item.
    354      :Types:
    355          - `fields`: `list` of `Field`.
    356      """
    357      xml_element_name = "item"
    358      xml_element_namespace = DATAFORM_NS
    359
    360 -    def __init__(self, fields = None):
    361          """Initialize an `Item` object.
    362
    363          :Parameters:
    364              - `fields`: item fields.
    365          :Types:
    366              - `fields`: `list` of `Field`.
    367          """
    368          if fields is None:
    369              self.fields = []
    370          else:
    371              self.fields = list(fields)
    372
    373 -    def __getitem__(self, name_or_index):
    374          if isinstance(name_or_index, int):
    375              return self.fields[name_or_index]
    376          for f in self.fields:
    377              if f.name == name_or_index:
    378                  return f
    379          raise KeyError, name_or_index
    380
    381 -    def __contains__(self, name):
    382          for f in self.fields:
    383              if f.name == name:
    384                  return True
    385          return False
    386
    387 -    def __iter__(self):
    388          for field in self.fields:
    389              yield field
    390
    391 -    def add_field(self, name = None, values = None, field_type = None,
    392              label = None, options = None, required = False, desc = None, value = None):
    393          """Add a field to the item.
    394
    395          :Parameters:
    396              - `name`: field name.
    397              - `values`: raw field values. Not to be used together with `value`.
    398              - `field_type`: field type.
    399              - `label`: field label.
    400              - `options`: optional values for the field.
    401              - `required`: `True` if the field is required.
    402              - `desc`: natural-language description of the field.
    403              - `value`: field value or values in a field_type-specific type. May be used only
    404                if `values` parameter is not provided.
    405          :Types:
    406              - `name`: `unicode`
    407              - `values`: `list` of `unicode`
    408              - `field_type`: `str`
    409              - `label`: `unicode`
    410              - `options`: `list` of `Option`
    411              - `required`: `bool`
    412              - `desc`: `unicode`
    413              - `value`: `bool` for "boolean" field, `JID` for "jid-single", `list` of `JID`
    414                for "jid-multi", `list` of `unicode` for "list-multi" and "text-multi"
    415                and `unicode` for other field types.
    416
    417          :return: the field added.
    418          :returntype: `Field`
    419          """
    420          field = Field(name, values, field_type, label, options, required, desc, value)
    421          self.fields.append(field)
    422          return field
    423
    424 -    def complete_xml_element(self, xmlnode, doc):
    425          """Complete the XML node with `self` content.
    426
    427          :Parameters:
    428              - `xmlnode`: XML node with the element being built. It has already
    429                right name and namespace, but no attributes or content.
    430              - `doc`: document to which the element belongs.
    431          :Types:
    432              - `xmlnode`: `libxml2.xmlNode`
    433              - `doc`: `libxml2.xmlDoc`"""
    434          for field in self.fields:
    435              field.as_xml(xmlnode, doc)
    436
    437      @classmethod
    438 -    def _new_from_xml(cls, xmlnode):
    439          """Create a new `Item` object from an XML element.
    440
    441          :Parameters:
    442              - `xmlnode`: the XML element.
    443          :Types:
    444              - `xmlnode`: `libxml2.xmlNode`
    445
    446          :return: the object created.
    447          :returntype: `Item`
    448          """
    449          child = xmlnode.children
    450          fields = []
    451          while child:
    452              if child.type != "element" or child.ns().content != DATAFORM_NS:
    453                  pass
    454              elif child.name == "field":
    455                  fields.append(Field._new_from_xml(child))
    456              child = child.next
    457          return cls(fields)
    458
    459 -class Form(StanzaPayloadObject):
    460      """A JEP-0004 compliant data form.
    461
    462      Additionally to the direct access to the contained fields via the `fields` attribute,
    463      `Form` object provides an iterator and mapping interface for field access. E.g.::
    464
    465          for field in form:
    466              ...
    467
    468      or::
    469
    470          field = form['field_name']
    471
    472      :Ivariables:
    473          - `type`: form type ("form", "submit", "cancel" or "result").
    474          - `title`: form title.
    475          - `instructions`: instructions for a form user.
    476          - `fields`: the fields in the form.
    477          - `reported_fields`: list of fields returned in a multi-item data form.
    478          - `items`: items in a multi-item data form.
    479      :Types:
    480          - `title`: `unicode`
    481          - `instructions`: `unicode`
    482          - `fields`: `list` of `Field`
    483          - `reported_fields`: `list` of `Field`
    484          - `items`: `list` of `Item`
    485      """
    486      allowed_types = ("form", "submit", "cancel", "result")
    487      xml_element_name = "x"
    488      xml_element_namespace = DATAFORM_NS
    489
    490 -    def __init__(self, xmlnode_or_type = "form", title = None, instructions = None,
    491              fields = None, reported_fields = None, items = None):
    492          """Initialize a `Form` object.
    493
    494          :Parameters:
    495              - `xmlnode_or_type`: XML element to parse or a form title.
    496              - `title`: form title.
    497              - `instructions`: instructions for the form.
    498              - `fields`: form fields.
    499              - `reported_fields`: fields reported in multi-item data.
    500              - `items`: items of multi-item data.
    501          :Types:
    502              - `xmlnode_or_type`: `libxml2.xmlNode` or `str`
    503              - `title`: `unicode`
    504              - `instructions`: `unicode`
    505              - `fields`: `list` of `Field`
    506              - `reported_fields`: `list` of `Field`
    507              - `items`: `list` of `Item`
    508          """
    509          if isinstance(xmlnode_or_type, libxml2.xmlNode):
    510              self.__from_xml(xmlnode_or_type)
    511          elif xmlnode_or_type not in self.allowed_types:
    512              raise ValueError, "Form type %r not allowed." % (xmlnode_or_type,)
    513          else:
    514              self.type = xmlnode_or_type
    515              self.title = title
    516              self.instructions = instructions
    517              if fields:
    518                  self.fields = list(fields)
    519              else:
    520                  self.fields = []
    521              if reported_fields:
    522                  self.reported_fields = list(reported_fields)
    523              else:
    524                  self.reported_fields = []
    525              if items:
    526                  self.items = list(items)
    527              else:
    528                  self.items = []
    529
    530 -    def __getitem__(self, name_or_index):
    531          if isinstance(name_or_index, int):
    532              return self.fields[name_or_index]
    533          for f in self.fields:
    534              if f.name == name_or_index:
    535                  return f
    536          raise KeyError, name_or_index
    537
    538 -    def __contains__(self, name):
    539          for f in self.fields:
    540              if f.name == name:
    541                  return True
    542          return False
    543
    544 -    def __iter__(self):
    545          for field in self.fields:
    546              yield field
    547
    548 -    def add_field(self, name = None, values = None, field_type = None,
    549              label = None, options = None, required = False, desc = None, value = None):
    550          """Add a field to the form.
    551
    552          :Parameters:
    553              - `name`: field name.
    554              - `values`: raw field values. Not to be used together with `value`.
    555              - `field_type`: field type.
    556              - `label`: field label.
    557              - `options`: optional values for the field.
    558              - `required`: `True` if the field is required.
    559              - `desc`: natural-language description of the field.
    560              - `value`: field value or values in a field_type-specific type. May be used only
    561                if `values` parameter is not provided.
    562          :Types:
    563              - `name`: `unicode`
    564              - `values`: `list` of `unicode`
    565              - `field_type`: `str`
    566              - `label`: `unicode`
    567              - `options`: `list` of `Option`
    568              - `required`: `bool`
    569              - `desc`: `unicode`
    570              - `value`: `bool` for "boolean" field, `JID` for "jid-single", `list` of `JID`
    571                for "jid-multi", `list` of `unicode` for "list-multi" and "text-multi"
    572                and `unicode` for other field types.
    573
    574          :return: the field added.
    575          :returntype: `Field`
    576          """
    577          field = Field(name, values, field_type, label, options, required, desc, value)
    578          self.fields.append(field)
    579          return field
    580
    581 -    def add_item(self, fields = None):
    582          """Add and item to the form.
    583
    584          :Parameters:
    585              - `fields`: fields of the item (they may be added later).
    586          :Types:
    587              - `fields`: `list` of `Field`
    588
    589          :return: the item added.
    590          :returntype: `Item`
    591          """
    592          item = Item(fields)
    593          self.items.append(item)
    594          return item
    595
    596 -    def make_submit(self, keep_types = False):
    597          """Make a "submit" form using data in `self`.
    598
    599          Remove uneeded information from the form. The information removed
    600          includes: title, instructions, field labels, fixed fields etc.
    601
    602          :raise ValueError: when any required field has no value.
    603
    604          :Parameters:
    605              - `keep_types`: when `True` field type information will be included
    606                in the result form. That is usually not needed.
    607          :Types:
    608              - `keep_types`: `bool`
    609
    610          :return: the form created.
    611          :returntype: `Form`"""
    612          result = Form("submit")
    613          for field in self.fields:
    614              if field.type == "fixed":
    615                  continue
    616              if not field.values:
    617                  if field.required:
    618                      raise ValueError, "Required field with no value!"
    619                  continue
    620              if keep_types:
    621                  result.add_field(field.name, field.values, field.type)
    622              else:
    623                  result.add_field(field.name, field.values)
    624          return result
    625
    626 -    def copy(self):
    627          """Get a deep copy of `self`.
    628
    629          :return: a deep copy of `self`.
    630          :returntype: `Form`"""
    631          return copy.deepcopy(self)
    632
    633 -    def complete_xml_element(self, xmlnode, doc):
    634          """Complete the XML node with `self` content.
    635
    636          :Parameters:
    637              - `xmlnode`: XML node with the element being built. It has already
    638                right name and namespace, but no attributes or content.
    639              - `doc`: document to which the element belongs.
    640          :Types:
    641              - `xmlnode`: `libxml2.xmlNode`
    642              - `doc`: `libxml2.xmlDoc`"""
    643          if self.type not in self.allowed_types:
    644              raise ValueError, "Form type %r not allowed." % (self.type,)
    645          xmlnode.setProp("type", self.type)
    646          if self.type == "cancel":
    647              return
    648          ns = xmlnode.ns()
    649          if self.title is not None:
    650              xmlnode.newTextChild(ns, "title", to_utf8(self.title))
    651          if self.instructions is not None:
    652              xmlnode.newTextChild(ns, "instructions", to_utf8(self.instructions))
    653          for field in self.fields:
    654              field.as_xml(xmlnode, doc)
    655          if self.type != "result":
    656              return
    657          if self.reported_fields:
    658              reported = xmlnode.newChild(ns, "reported", None)
    659              for field in self.reported_fields:
    660                  field.as_xml(reported, doc)
    661          for item in self.items:
    662              item.as_xml(xmlnode, doc)
    663
    664 -    def __from_xml(self, xmlnode):
    665          """Initialize a `Form` object from an XML element.
    666
    667          :Parameters:
    668              - `xmlnode`: the XML element.
    669          :Types:
    670              - `xmlnode`: `libxml2.xmlNode`
    671          """
    672          self.fields = []
    673          self.reported_fields = []
    674          self.items = []
    675          self.title = None
    676          self.instructions = None
    677          if (xmlnode.type != "element" or xmlnode.name != "x"
    678                  or xmlnode.ns().content != DATAFORM_NS):
    679              raise ValueError, "Not a form: " + xmlnode.serialize()
    680          self.type = xmlnode.prop("type")
    681          if not self.type in self.allowed_types:
    682              raise BadRequestProtocolError, "Bad form type: %r" % (self.type,)
    683          child = xmlnode.children
    684          while child:
    685              if child.type != "element" or child.ns().content != DATAFORM_NS:
    686                  pass
    687              elif child.name == "title":
    688                  self.title = from_utf8(child.getContent())
    689              elif child.name == "instructions":
    690                  self.instructions = from_utf8(child.getContent())
    691              elif child.name == "field":
    692                  self.fields.append(Field._new_from_xml(child))
    693              elif child.name == "item":
    694                  self.items.append(Item._new_from_xml(child))
    695              elif child.name == "reported":
    696                  self.__get_reported(child)
    697              child = child.next
    698
    699 -    def __get_reported(self, xmlnode):
    700          """Parse the <reported/> element of the form.
    701
    702          :Parameters:
    703              - `xmlnode`: the element to parse.
    704          :Types:
    705              - `xmlnode`: `libxml2.xmlNode`"""
    706          child = xmlnode.children
    707          while child:
    708              if child.type != "element" or child.ns().content != DATAFORM_NS:
    709                  pass
    710              elif child.name == "field":
    711                  self.reported_fields.append(Field._new_from_xml(child))
    712              child = child.next
    713  # vi: sts=4 et sw=4
    714