util.dataforms

This module implements XEP-0004 data forms. Forms are a way to request and receive data from other XMPP entities. Sometimes this is automated, and sometimes the forms are rendered by clients and presented to a human.

Forms contain a list of fields. The core field types are defined in XEP-0004, with some special field types defined in later XEPs.

Usage

Define a form layout

Whether you will be receiving or sending data using forms (or both), the first step is to define your form’s “layout”, which describes all the fields in your form, and specifies their type.

    local dataforms = require "util.dataforms";

    local my_layout = {
      -- Specify the form's title and instructions
      title = "Request a Pony";
      instructions = "To request a pony, fill out this form";

      -- Now the fields are defined

      -- It is conventional to start every form with a hidden field
      -- called FORM_TYPE which lets anyone receiving the form know
      -- what kind of form this is. The value should be a unique URI.
      -- If they understand the form type, clients could render a special
      -- UI, for example. You can read more about FORM_TYPE in XEP-0068.
      {
        type = "hidden";
        name = "FORM_TYPE";
        value = "xmpp:example.com/forms/pony_request";
      };

      {
        type = "text-single"; -- The type of this field
        name = "pony_name"; -- The unique internal name of this field
        label = "Pony's name"; -- The label of this field (displayed to the user)
        required = true; -- This is a mandatory field
      };

      {
        type = "list-single";
        name = "pony_breed";
        options = {
          "Shetland";
          "Welsh";
          "Connemara";
          "Fjord";
          "Other";
        };
      };

      {
        type = "boolean";
        name = "pony_experience";
        label = "Do you have prior experience with ponies?";
      };
    };

    -- Now create a form object based on this layout
    local myform = dataforms.new(my_layout);

Sending forms

If a client wants to submit a form, it will typically request it first. Then the server will generate the form XML and send it to the client.

Use the :form() method to generate the form element which is suitable for embedding in a stanza. The returned element will be the <x xmlns='jabber:x:data'> element, containing all your form’s fields.


    -- If the client requests a form, we can generate the form XML to send them,
    -- using the :form() method. In this example we're sending the form in a
    -- message stanza, but most commonly the form is embedded in an iq response.

    local some_stanza = st.stanza("message");
    some_stanza:add_child(myform:form());

Note: if you want to send a form where the fields are already populated with data, you can provide that data to the :form() method as a table. This example is just sending an empty form to the client, so the client knows what the form fields are.

Receiving form submissions

Next, the client will submit the completed form to you. You can use the form object to parse the form XML and turn it into a table of data.

    -- Get the submitted form from the stanza
    local submitted_form = some_stanza:get_child("x", "jabber:x:data");

    -- Parse the form using the :data() method
    local data, errors = my_form:data(submitted_form);
    if not data then
      -- Failed to parse the form!
      -- 'errors' will be a map of [field_name] = error_string
      for field_name, error_string in pairs(errors) do
        module:log("warn", "Form error in field %s: %s", field_name, error_string);
      end
      return;
    end

    -- We have the submitted data
    module:log("info", "Received request for pony %s", data.pony_name);

Reference

new(layout)

Creates and returns a new form object using the given form layout. For a detailed description of the layout object, see Form layouts.

get_type(value)

Checks whether the provided value is a valid form element. If it is a form, it returns the value of the FORM_TYPE field, or an empty string. If it’s not, it returns nil plus an error message.

This could be useful when you don’t know if a form was provided at all, or if there are multiple forms (e.g. in XEP-0030) and you need to pick out one in particular based on the FORM_TYPE.

form:form(data, type)

This method returns the form element (<x xmlns='jabber:x:data'>) which can be embedded into a stanza object (e.g using stanza:add_child()).

If data is not passed, the form fields will be empty or use default values from the layout.

If data is passed, it must be a table where the keys are the field names, and the values are appropriate values for each field type. Any fields which are not included will be empty or the default value will be used if one is specified in the layout.

The type parameter specifies the intention of the form. Per XEP-0004, it should be one of "form", "submit" or "result". If not specified, it defaults to "form". This parameter should not be confused with the unrelated FORM_TYPE field which is often included in data forms!

form:data(form_element, current_values)

This method accepts a form element (e.g. the <x xmlns='jabber:x:data'> element extracted from a stanza), and parses and validates it according to the form’s layout. If successful, it returns the data that was found in the form’s fields. This data will be returned as a table, where each key is the field name, and the value is the submitted value in a data type appropriate for that field type.

If provided, current_values is a table of data that will be used as a default if any fields specified in the layout are not provided in the form. This allows partial form submissions where existing data remains unchanged.

If form validation fails, the returned data will be nil. The second result will be a table of errors, where the keys are field names that failed to parse and the values are strings describing the error.

Finally, the third result is always a table which contains as keys all the field names that were present in the parsed form, and the value true.

Form layouts

A form layout is a table, which can contain some key/value pairs, and also an array of fields.

Properties are:

title
The title of the form
instructions
Human-readable instructions related to the form

Fields

Each field is represented as a table.

Common field properties:

type
A string which specified the field types (known types are listed below).
name
The field ‘name’ is usually an internal string such as pony_name and is used to refer to the field in data tables that are used as input/output of the form methods.
var
If specified, this overrides the field name in the form’s XML format (the name is still used in data tables). If not specified, the field name is used in the XML.
label
A human-readable string which names the field (e.g. Name of pony).
desc
An optional human-readable description of the field.
datatype
Specify a field “subtype” used for validation. Valid types are specified in XEP-0122.
range_min
If appropriate for the data type, specifies a minimum acceptable value for this field. See XEP-0122 for details.
range_max
If appropriate for the data type, specifies a maximum acceptale value for this field. See XEP-0122 for details.
value
The value of the field. If specified in the layout, this is used as a default value when rendering the form.
options
For list data types (list-single or list-multi), this specifies an array of possible options in the list. Each entry in the list should be a string or a table of the form { label = "Value label", value = "my_value" }.

Field types

Valid field types:

hidden
String, not presented to the user when rendering the form.
boolean
Boolean value, typically rendered as a checkbox.
- fixed
String, not editable by the user.
jid-single
String, must be a valid JID.
jid-multi
Array of strings, all entries must be valid JIDs.
text-single
String. Arbitrary single line of text.
text-multi
String, multi-line.
list-single
String. Allows selecting one item from a list of possible options.
list-multi
Array. Allows selecting zero or more items from a list of possible options.