util/dataforms.lua

1
-- Prosody IM
2
-- Copyright (C) 2008-2010 Matthew Wild
3
-- Copyright (C) 2008-2010 Waqas Hussain
4
-- 
5
-- This project is MIT/X11 licensed. Please see the
6
-- COPYING file in the source package for more information.
7
--
8
 
9
local setmetatable = setmetatable;
10
local pairs, ipairs = pairs, ipairs;
11
local tostring, type = tostring, type;
12
local t_concat = table.concat;
13
local st = require "util.stanza";
14
 
15
module "dataforms"
16
 
17
local xmlns_forms = 'jabber:x:data';
18
 
19
local form_t = {};
20
local form_mt = { __index = form_t };
21
 
22
function new(layout)
23
	return setmetatable(layout, form_mt);
24
end
25
 
26
function form_t.form(layout, data, formtype)
27
	local form = st.stanza("x", { xmlns = xmlns_forms, type = formtype or "form" });
28
	if layout.title then
29
		form:tag("title"):text(layout.title):up();
30
	end
31
	if layout.instructions then
32
		form:tag("instructions"):text(layout.instructions):up();
33
	end
34
	for n, field in ipairs(layout) do
35
		local field_type = field.type or "text-single";
36
		-- Add field tag
37
		form:tag("field", { type = field_type, var = field.name, label = field.label });
38
 
39
		local value = (data and data[field.name]) or field.value;
40
		
41
		if value then
42
			-- Add value, depending on type
43
			if field_type == "hidden" then
44
				if type(value) == "table" then
45
					-- Assume an XML snippet
46
					form:tag("value")
47
						:add_child(value)
48
						:up();
49
				else
50
					form:tag("value"):text(tostring(value)):up();
51
				end
52
			elseif field_type == "boolean" then
53
				form:tag("value"):text((value and "1") or "0"):up();
54
			elseif field_type == "fixed" then
55
				
56
			elseif field_type == "jid-multi" then
57
				for _, jid in ipairs(value) do
58
					form:tag("value"):text(jid):up();
59
				end
60
			elseif field_type == "jid-single" then
61
				form:tag("value"):text(value):up();
62
			elseif field_type == "text-single" or field_type == "text-private" then
63
				form:tag("value"):text(value):up();
64
			elseif field_type == "text-multi" then
65
				-- Split into multiple <value> tags, one for each line
66
				for line in value:gmatch("([^\r\n]+)\r?\n*") do
67
					form:tag("value"):text(line):up();
68
				end
69
			elseif field_type == "list-single" then
70
				local has_default = false;
71
				for _, val in ipairs(value) do
72
					if type(val) == "table" then
73
						form:tag("option", { label = val.label }):tag("value"):text(val.value):up():up();
74
						if val.default and (not has_default) then
75
							form:tag("value"):text(val.value):up();
76
							has_default = true;
77
						end
78
					else
79
						form:tag("option", { label= val }):tag("value"):text(tostring(val)):up():up();
80
					end
81
				end
82
			elseif field_type == "list-multi" then
83
				for _, val in ipairs(value) do
84
					if type(val) == "table" then
85
						form:tag("option", { label = val.label }):tag("value"):text(val.value):up():up();
86
						if val.default then
87
							form:tag("value"):text(val.value):up();
88
						end
89
					else
90
						form:tag("option", { label= val }):tag("value"):text(tostring(val)):up():up();
91
					end
92
				end
93
			end
94
		end
95
		
96
		if field.required then
97
			form:tag("required"):up();
98
		end
99
		
100
		-- Jump back up to list of fields
101
		form:up();
102
	end
103
	return form;
104
end
105
 
106
local field_readers = {};
107
 
108
function form_t.data(layout, stanza)
109
	local data = {};
110
	
111
	for field_tag in stanza:childtags() do
112
		local field_type;
113
		for n, field in ipairs(layout) do
114
			if field.name == field_tag.attr.var then
115
				field_type = field.type;
116
				break;
117
			end
118
		end
119
		
120
		local reader = field_readers[field_type];
121
		if reader then
122
			data[field_tag.attr.var] = reader(field_tag);
123
		end
124
		
125
	end
126
	return data;
127
end
128
 
129
field_readers["text-single"] =
130
	function (field_tag)
131
		local value = field_tag:child_with_name("value");
132
		if value then
133
			return value[1];
134
		end
135
	end
136
 
137
field_readers["text-private"] =
138
	field_readers["text-single"];
139
 
140
field_readers["jid-single"] =
141
	field_readers["text-single"];
142
 
143
field_readers["jid-multi"] =
144
	function (field_tag)
145
		local result = {};
146
		for value_tag in field_tag:childtags() do
147
			if value_tag.name == "value" then
148
				result[#result+1] = value_tag[1];
149
			end
150
		end
151
		return result;
152
	end
153
 
154
field_readers["text-multi"] =
155
	function (field_tag)
156
		local result = {};
157
		for value_tag in field_tag:childtags() do
158
			if value_tag.name == "value" then
159
				result[#result+1] = value_tag[1];
160
			end
161
		end
162
		return t_concat(result, "\n");
163
	end
164
 
165
field_readers["list-single"] =
166
	field_readers["text-single"];
167
 
168
field_readers["list-multi"] =
169
	function (field_tag)
170
		local result = {};
171
		for value_tag in field_tag:childtags() do
172
			if value_tag.name == "value" then
173
				result[#result+1] = value_tag[1];
174
			end
175
		end
176
		return result;
177
	end
178
 
179
field_readers["boolean"] =
180
	function (field_tag)
181
		local value = field_tag:child_with_name("value");
182
		if value then
183
			if value[1] == "1" or value[1] == "true" then
184
				return true;
185
			else
186
				return false;
187
			end
188
		end
189
	end
190
 
191
field_readers["hidden"] =
192
	function (field_tag)
193
		local value = field_tag:child_with_name("value");
194
		if value then
195
			return value[1];
196
		end
197
	end
198
	
199
return _M;
200
 
201
 
202
--[=[
203
 
204
Layout:
205
{
206
 
207
	title = "MUC Configuration",
208
	instructions = [[Use this form to configure options for this MUC room.]],
209
 
210
	{ name = "FORM_TYPE", type = "hidden", required = true };
211
	{ name = "field-name", type = "field-type", required = false };
212
}
213
 
214
 
215
--]=]