anotherim-desktop/xmpp-vala/src/module/xep/0004_data_forms.vala
Michel Le Bihan e06cc08425
Improve IBR form handling (#990)
* Change register account dialog to show instructions

Change data forms to handle title and instructions elements

* Change register account dialog to show fixed fields

Closes #988

* Change form switch to halign start

Closes #992

* Change register account dialog to markup URLs
2021-02-17 22:57:53 +01:00

247 lines
7.7 KiB
Vala

using Gee;
namespace Xmpp.Xep.DataForms {
public const string NS_URI = "jabber:x:data";
public class DataForm {
public StanzaNode stanza_node { get; set; }
public Gee.List<Field> fields = new ArrayList<Field>();
public string? form_type = null;
public string? instructions = null;
public string? title = null;
public StanzaNode get_submit_node() {
stanza_node.set_attribute("type", "submit");
return stanza_node;
}
public enum Type {
BOOLEAN,
FIXED,
HIDDEN,
JID_MULTI,
LIST_SINGLE,
LIST_MULTI,
TEXT_PRIVATE,
TEXT_SINGLE,
}
public class Option {
public string label { get; set; }
public string value { get; set; }
public Option(string label, string value) {
this.label = label;
this.value = value;
}
}
public class Field {
public StanzaNode node { get; set; }
public string? label {
get { return node.get_attribute("label", NS_URI); }
set { node.set_attribute("label", value); }
}
public virtual Type? type_ { get; internal set; default=null; }
public string? var {
get { return node.get_attribute("var", NS_URI); }
set { node.set_attribute("var", value); }
}
public Field() {
this.node = new StanzaNode.build("field", NS_URI);
}
public Field.from_node(StanzaNode node) {
this.node = node;
}
internal Gee.List<string> get_values() {
Gee.List<string> ret = new ArrayList<string>();
Gee.List<StanzaNode> value_nodes = node.get_subnodes("value", NS_URI);
foreach (StanzaNode node in value_nodes) {
ret.add(node.get_string_content());
}
return ret;
}
public string get_value_string() {
Gee.List<string> values = get_values();
return values.size > 0 ? values[0] : "";
}
public void set_value_string(string val) {
StanzaNode? value_node = node.get_subnode("value", NS_URI);
if (value_node == null) {
value_node = new StanzaNode.build("value", NS_URI);
node.put_node(value_node);
}
value_node.sub_nodes.clear();
value_node.put_node(new StanzaNode.text(val));
}
internal void add_value_string(string val) {
StanzaNode node = new StanzaNode.build("value");
node.put_node(new StanzaNode.text(val));
}
internal Gee.List<Option>? get_options() {
Gee.List<Option> ret = new ArrayList<Option>();
Gee.List<StanzaNode> option_nodes = node.get_subnodes("option", NS_URI);
foreach (StanzaNode node in option_nodes) {
Option option = new Option(node.get_attribute("label", NS_URI), node.get_subnode("value").get_string_content());
ret.add(option);
}
return ret;
}
}
public class BooleanField : Field {
public bool value {
get { return get_value_string() == "1"; }
set { set_value_string(value ? "1" : "0"); }
}
public BooleanField(StanzaNode node) {
base.from_node(node);
type_ = Type.BOOLEAN;
}
}
public class FixedField : Field {
public string value {
owned get { return get_value_string(); }
set { set_value_string(value); }
}
public FixedField(StanzaNode node) {
base.from_node(node);
type_ = Type.FIXED;
}
}
public class HiddenField : Field {
public HiddenField() {
base();
type_ = Type.HIDDEN;
node.put_attribute("type", "hidden");
}
public HiddenField.from_node(StanzaNode node) {
base.from_node(node);
type_ = Type.HIDDEN;
}
}
public class JidMultiField : Field {
public Gee.List<Option> options { owned get { return get_options(); } }
public Gee.List<string> value { get; set; }
public JidMultiField(StanzaNode node) {
base.from_node(node);
type_ = Type.JID_MULTI;
}
}
public class ListSingleField : Field {
public Gee.List<Option> options { owned get { return get_options(); } }
public string value {
owned get { return get_value_string(); }
set { set_value_string(value); }
}
public ListSingleField(StanzaNode node) {
base.from_node(node);
type_ = Type.LIST_SINGLE;
node.set_attribute("type", "list-single");
}
}
public class ListMultiField : Field {
public Gee.List<Option> options { owned get { return get_options(); } }
public Gee.List<string> value { get; set; }
public ListMultiField(StanzaNode node) {
base.from_node(node);
type_ = Type.LIST_MULTI;
}
}
public class TextPrivateField : Field {
public string value {
owned get { return get_value_string(); }
set { set_value_string(value); }
}
public TextPrivateField(StanzaNode node) {
base.from_node(node);
type_ = Type.TEXT_PRIVATE;
}
}
public class TextSingleField : Field {
public string value {
owned get { return get_value_string(); }
set { set_value_string(value); }
}
public TextSingleField(StanzaNode node) {
base.from_node(node);
type_ = Type.TEXT_SINGLE;
}
}
// TODO text-multi
internal DataForm.from_node(StanzaNode node) {
this.stanza_node = node;
Gee.List<StanzaNode> field_nodes = node.get_subnodes("field", NS_URI);
foreach (StanzaNode field_node in field_nodes) {
string? type = field_node.get_attribute("type", NS_URI);
switch (type) {
case "boolean":
fields.add(new BooleanField(field_node)); break;
case "fixed":
fields.add(new FixedField(field_node)); break;
case "hidden":
HiddenField field = new HiddenField.from_node(field_node);
if (field.var == "FORM_TYPE") {
this.form_type = field.get_value_string();
break;
}
fields.add(field); break;
case "jid-multi":
fields.add(new JidMultiField(field_node)); break;
case "list-single":
fields.add(new ListSingleField(field_node)); break;
case "list-multi":
fields.add(new ListMultiField(field_node)); break;
case "text-private":
fields.add(new TextPrivateField(field_node)); break;
case "text-single":
fields.add(new TextSingleField(field_node)); break;
}
}
StanzaNode? instructions_node = node.get_subnode("instructions", NS_URI);
if (instructions_node != null) {
instructions = instructions_node.get_string_content();
}
StanzaNode? title_node = node.get_subnode("title", NS_URI);
if (title_node != null) {
title = title_node.get_string_content();
}
}
internal DataForm() {
this.stanza_node = new StanzaNode.build("x", NS_URI).add_self_xmlns();
}
public static DataForm? create_from_node(StanzaNode node) {
return new DataForm.from_node(node);
}
public void add_field(Field field) {
fields.add(field);
stanza_node.put_node(field.node);
}
}
}