Пример #1
0
class Command(xso.XSO):
    TAG = (namespaces.xep0050_commands, "command")

    actions = xso.Child([Actions])

    notes = xso.ChildList([Note])

    action = xso.Attr(
        "action",
        type_=xso.EnumCDataType(ActionType),
        default=ActionType.EXECUTE,
    )

    status = xso.Attr(
        "status",
        type_=xso.EnumCDataType(CommandStatus),
        default=None,
    )

    sessionid = xso.Attr(
        "sessionid",
        default=None,
    )

    node = xso.Attr(
        "node",
    )

    payload = xso.ChildList([
        aioxmpp.forms.Data,
    ])

    def __init__(self, node, *,
                 action=ActionType.EXECUTE,
                 status=None,
                 sessionid=None,
                 payload=[],
                 notes=[],
                 actions=None):
        super().__init__()
        self.node = node
        self.action = action
        self.status = status
        self.sessionid = sessionid
        if not isinstance(payload, collections.abc.Iterable):
            self.payload[:] = [payload]
        else:
            self.payload[:] = payload
        self.notes[:] = notes
        self.actions = actions

    @property
    def first_payload(self):
        try:
            return self.payload[0]
        except IndexError:
            return
Пример #2
0
class Open(xso.XSO):
    TAG = (namespaces.xep0047, "open")

    block_size = xso.Attr("block-size", type_=xso.Integer())

    # XXX: sid should be restricted to NMTOKEN
    sid = xso.Attr("sid", type_=xso.String())

    stanza = xso.Attr(
        "stanza",
        type_=xso.EnumCDataType(IBBStanzaType),
        default=IBBStanzaType.IQ,
    )
Пример #3
0
class Status(xso.XSO):
    TAG = (namespaces.xep0045_muc_user, "status")

    code = xso.Attr("code",
                    type_=xso.EnumCDataType(
                        StatusCode,
                        xso.Integer(),
                        allow_coerce=True,
                        pass_unknown=True,
                    ))

    def __init__(self, code):
        super().__init__()
        self.code = code
Пример #4
0
class Actions(xso.XSO):
    TAG = (namespaces.xep0050_commands, "actions")

    next_is_allowed = xso.ChildFlag(
        (namespaces.xep0050_commands, "next"),
    )

    prev_is_allowed = xso.ChildFlag(
        (namespaces.xep0050_commands, "prev"),
    )

    complete_is_allowed = xso.ChildFlag(
        (namespaces.xep0050_commands, "complete"),
    )

    execute = xso.Attr(
        "execute",
        type_=xso.EnumCDataType(ActionType),
        validator=xso.RestrictToSet({
            ActionType.NEXT,
            ActionType.PREV,
            ActionType.COMPLETE,
        }),
        default=None,
    )

    @property
    def allowed_actions(self):
        result = [ActionType.EXECUTE, ActionType.CANCEL]
        if self.prev_is_allowed:
            result.append(ActionType.PREV)
        if self.next_is_allowed:
            result.append(ActionType.NEXT)
        if self.complete_is_allowed:
            result.append(ActionType.COMPLETE)
        return frozenset(result)

    @allowed_actions.setter
    def allowed_actions(self, values):
        values = frozenset(values)
        if ActionType.EXECUTE not in values:
            raise ValueError("EXECUTE must always be allowed")
        if ActionType.CANCEL not in values:
            raise ValueError("CANCEL must always be allowed")
        self.prev_is_allowed = ActionType.PREV in values
        self.next_is_allowed = ActionType.NEXT in values
        self.complete_is_allowed = ActionType.COMPLETE in values
Пример #5
0
class Note(xso.XSO):
    TAG = (namespaces.xep0050_commands, "note")

    body = xso.Text(
        default=None,
    )

    type_ = xso.Attr(
        "type",
        type_=xso.EnumCDataType(
            NoteType,
        ),
        default=NoteType.INFO,
    )

    def __init__(self, type_, body):
        super().__init__()
        self.type_ = type_
        self.body = body
Пример #6
0
class Data(AbstractItem):
    """
    A :xep:`4` ``x`` element, that is, a Data Form.

    :param type_: Initial value for the :attr:`type_` attribute.

    .. attribute:: type_

       The ``type`` attribute of the form, represented by one of the members of
       the :class:`DataType` enumeration.

    .. attribute:: title

       The (optional) title of the form. Either a :class:`str` or :data:`None`.

    .. attribute:: instructions

       A sequence of strings which represent the instructions elements on the
       form.

    .. attribute:: fields

       If the :class:`Data` is a form, this is a sequence of :class:`Field`
       elements which represent the fields to be filled in.

       This does not make sense on :attr:`.DataType.RESULT` typed objects.

    .. attribute:: items

       If the :class:`Data` is a table, this is a sequence of :class:`Item`
       instances which represent the table rows.

       This only makes sense on :attr:`.DataType.RESULT` typed objects.

    .. attribute:: reported

       If the :class:`Data` is a table, this is a :class:`Reported` object
       representing the table header.

       This only makes sense on :attr:`.DataType.RESULT` typed objects.

    .. automethod:: get_form_type
    """

    TAG = (namespaces.xep0004_data, "x")

    type_ = xso.Attr("type", type_=xso.EnumCDataType(DataType))

    title = xso.ChildText(
        (namespaces.xep0004_data, "title"),
        default=None,
    )

    instructions = xso.ChildValueList(type_=InstructionsElement())

    items = xso.ChildList([Item])

    reported = xso.Child([Reported], required=False)

    def __init__(self, type_):
        super().__init__()
        self.type_ = type_

    def _validate_result(self):
        if self.fields:
            raise ValueError("field in report result")

        fieldvars = {field.var for field in self.reported.fields}

        if not fieldvars:
            raise ValueError("empty report header")

        for item in self.items:
            itemvars = {field.var for field in item.fields}
            if itemvars != fieldvars:
                raise ValueError("field mismatch between row and header")

    def validate(self):
        super().validate()

        if (self.type_ != DataType.RESULT
                and (self.reported is not None or self.items)):
            raise ValueError("report in non-result")

        if (self.type_ == DataType.RESULT
                and (self.reported is not None or self.items)):
            self._validate_result()

    def get_form_type(self):
        """
        Extract the ``FORM_TYPE`` from the fields.

        :return: ``FORM_TYPE`` value or :data:`None`
        :rtype: :class:`str` or :data:`None`

        Return :data:`None` if no well-formed ``FORM_TYPE`` field is found in
        the list of fields.

        .. versionadded:: 0.8
        """

        for field in self.fields:
            if field.var == "FORM_TYPE" and field.type_ == FieldType.HIDDEN:
                if len(field.values) != 1:
                    return None
                return field.values[0]
Пример #7
0
class Field(xso.XSO):
    """
    Represent a single field in a Data Form.

    :param type_: Field type, must be one of the valid field types specified in
                  :xep:`4`.
    :type type_: :class:`FieldType`
    :param options: A mapping of values to labels defining the options in a
                    ``list-*`` field.
    :type options: :class:`dict` mapping :class:`str` to :class:`str`
    :param values: A sequence of values currently given for the field. Having
                   more than one value is only valid in ``*-multi`` fields.
    :type values: :class:`list` of :class:`str`
    :param desc: Description which can be shown in a tool-tip or similar,
                 without newlines.
    :type desc: :class:`str` or :data:`None`s
    :param label: Human-readable label to be shown next to the field input
    :type label: :class:`str` or :data:`None`
    :param required: Flag to indicate that the field is required
    :type required: :class:`bool`
    :param var: "ID" identifying the field uniquely inside the form. Only
                required for fields carrying a meaning (thus, not for
                ``fixed``).
    :type var: :class:`str` or :data:`None`

    The semantics of a :class:`Field` are different depending on where it
    occurs: in a :class:`Data`, it is a form field to be filled in, in a
    :class:`Item` it is a cell of a row and in a :class:`Reported` it
    represents a column header.

    .. attribute:: required

       A boolean flag indicating whether the field is required.

       If true, the XML serialisation will contain the corresponding
       ``<required/>`` tag.

    .. attribute:: desc

       Single line of description for the field. This attribute represents the
       ``<desc/>`` element from :xep:`4`.

    .. attribute:: values

       A sequence of strings representing the ``<value/>`` elements of the
       field, one string for each value.

       .. note::

          Since the requirements on the sequence of strings in :attr:`values`
          change depending on the :attr:`type_` attribute, validation and type
          conversion on assignment is very lax. The attribute accepts all
          sequences of strings, even if the field is for example a
          :attr:`FieldType.BOOLEAN` field, which allows for at most one string
          of a well-defined format (see the documentation there for the
          details).

          This makes it easy to inadvertendly generate invalid forms, which is
          why you should be using :class:`Form` subclasses when accessing forms
          from within normal code and some other, generic mechanism taking care
          of these details when showing forms in a UI framework to users. Note
          that devising such a mechanism is out of scope for :mod:`aioxmpp`, as
          every UI framework has different requirements.

    .. attribute:: options

       A dictionary mapping values to human-readable labels, representing the
       ``<option/>`` elements of the field.

    .. attribute:: var

       The uniquely identifying string of the (valued, that is,
       non-:attr:`FieldType.FIXED` field). Represents the ``var`` attribute of
       the field.

    .. attribute:: type_

       The type of the field. The :attr:`type_` must be a :class:`FieldType`
       enumeration value and determines restrictions and constraints on other
       attributes. See the :class:`FieldType` enumeration and :xep:`4` for
       details.

    .. attribute:: label

       The human-readable label for the field, representing the ``label``
       attribute of the field. May be :data:`None` if the label is omitted.

    """

    TAG = (namespaces.xep0004_data, "field")

    required = xso.ChildFlag((namespaces.xep0004_data, "required"), )

    desc = xso.ChildText((namespaces.xep0004_data, "desc"), default=None)

    values = xso.ChildValueList(type_=ValueElement())

    options = xso.ChildValueMap(
        type_=OptionElement(),
        mapping_type=collections.OrderedDict,
    )

    var = xso.Attr((None, "var"), default=None)

    type_ = xso.Attr(
        (None, "type"),
        type_=xso.EnumCDataType(FieldType, ),
        default=FieldType.TEXT_SINGLE,
    )

    label = xso.Attr((None, "label"), default=None)

    def __init__(self,
                 *,
                 type_=FieldType.TEXT_SINGLE,
                 options={},
                 values=[],
                 desc=None,
                 label=None,
                 required=False,
                 var=None):
        super().__init__()
        self.type_ = type_
        self.options.update(options)
        self.values[:] = values
        self.desc = desc
        self.label = label
        self.required = required
        self.var = var

    def validate(self):
        super().validate()

        if self.type_ != FieldType.FIXED and not self.var:
            raise ValueError("missing attribute var")

        if not self.type_.has_options and self.options:
            raise ValueError("unexpected option on non-list field")

        if not self.type_.is_multivalued and len(self.values) > 1:
            raise ValueError("too many values on non-multi field")

        values_list = [opt for opt in self.options.values()]
        values_set = set(values_list)

        if len(values_list) != len(values_set):
            raise ValueError("duplicate option value")