File Coverage

blib/lib/AnyEvent/XMPP/Ext/DataForm.pm
Criterion Covered Total %
statement 6 193 3.1
branch 0 52 0.0
condition 0 6 0.0
subroutine 2 21 9.5
pod 16 17 94.1
total 24 289 8.3


line stmt bran cond sub pod time code
1             package AnyEvent::XMPP::Ext::DataForm;
2 1     1   1408 use strict;
  1         2  
  1         46  
3 1     1   6 use AnyEvent::XMPP::Namespaces qw/xmpp_ns/;
  1         3  
  1         2684  
4              
5             =head1 NAME
6              
7             AnyEvent::XMPP::Ext::DataForm - XEP-0004 DataForm
8              
9             =head1 SYNOPSIS
10              
11             =head1 DESCRIPTION
12              
13             This module represents a Data Form as specified in XEP-0004.
14              
15             =head1 METHODS
16              
17             =over 4
18              
19             =item B
20              
21             =cut
22              
23             sub new {
24 0     0 1   my $this = shift;
25 0   0       my $class = ref($this) || $this;
26 0           my $self = bless { @_ }, $class;
27 0           $self->init;
28 0           $self
29             }
30              
31             sub init {
32 0     0 0   my ($self) = @_;
33 0           $self->{fields} = [];
34 0           $self->{field_var} = {};
35 0           $self->{items} = [];
36 0           $self->{reported} = [];
37 0           delete $self->{type};
38 0           delete $self->{title};
39 0           delete $self->{instructions};
40             }
41              
42             =item B
43              
44             This method appends a field to the form.
45             C<$field> must have the structure as described in L below.
46              
47             =cut
48              
49             sub append_field {
50 0     0 1   my ($self, $field) = @_;
51 0 0         $self->{fields} = [] unless $self->{fields};
52 0 0         $self->{field_var} = {} unless $self->{field_var};
53 0           push @{$self->{fields}}, $field;
  0            
54 0 0         $self->{field_var}->{$field->{var}} = $field if defined $field->{var};
55             }
56              
57             =item B
58              
59             This method interprets the L object in C<$node> as
60             data form XML node and reads out the fields and all associated information.
61              
62             (C<$node> must be the XML node of the tag).
63              
64             =cut
65              
66             sub _extract_field {
67 0     0     my ($field) = @_;
68              
69 0           my $fo = {
70             label => $field->attr ('label'),
71             var => $field->attr ('var'),
72             type => $field->attr ('type'),
73             };
74              
75 0           my ($desc) = $field->find_all ([qw/data_form desc/]);
76 0 0         if ($desc) {
77 0           $fo->{desc} = $desc->text;
78             }
79 0 0         if ($field->find_all ([qw/data_form required/])) {
80 0           $fo->{required} = 1;
81             }
82 0           my (@vals) = $field->find_all ([qw/data_form value/]);
83 0           $fo->{values} = [];
84 0           for (@vals) {
85 0           push @{$fo->{values}}, $_->text;
  0            
86             }
87 0           my (@opts) = $field->find_all ([qw/data_form option/]);
88 0           $fo->{options} = [];
89 0           for my $o (@opts) {
90 0           my (@v) = $o->find_all ([qw/data_form value/]);
91 0           my $vals = [];
92 0           for my $val (@v) {
93 0           push @$vals, $val->text;
94             }
95 0           push @{$fo->{options}}, [$o->attr ('label'), $vals];
  0            
96             }
97              
98             $fo
99 0           }
100              
101             sub from_node {
102 0     0 1   my ($self, $node) = @_;
103              
104 0           $self->init;
105              
106 0           my ($title) = $node->find_all ([qw/data_form title/]);
107 0           my ($instr) = $node->find_all ([qw/data_form instructions/]);
108              
109 0           $self->{type} = $node->attr ('type');
110 0 0         $self->{title} = $title->text if $title;
111 0 0         $self->{instructions} = $instr->text if $instr;
112              
113 0           for my $field ($node->find_all ([qw/data_form field/])) {
114 0           my $fo = _extract_field ($field);
115 0           $self->append_field ($fo);
116             }
117              
118 0           my ($rep) = $node->find_all ([qw/data_form reported/]);
119 0 0         if ($rep) {
120 0           for my $field ($rep->find_all ([qw/data_form field/])) {
121 0           my $fo = {
122             label => $field->attr ('label'),
123             var => $field->attr ('var'),
124             type => $field->attr ('type'),
125             };
126 0           push @{$self->{reported}}, $fo;
  0            
127             }
128             }
129              
130 0           for my $item ($node->find_all ([qw/data_form item/])) {
131 0           my $flds = [];
132 0           for my $field ($item->find_all ([qw/data_form field/])) {
133 0           my $fo = _extract_field ($field);
134 0           push @$flds, $fo;
135             }
136 0           push @{$self->{items}}, $flds;
  0            
137             }
138             }
139              
140             =item B
141              
142             This method initializes this form with default answers and
143             other neccessary fields from C<$request_form>, which must be
144             of type L or compatible.
145              
146             The result will be a form with a copy of all fields which are not of
147             type C. The fields will also have the default value copied over.
148              
149             The form type will be set to C.
150              
151             The idea is: this creates a template answer form from C<$request_form>.
152              
153             To strip out the unneccessary fields later you don't need call the
154             C method.
155              
156             =cut
157              
158             sub make_answer_form {
159 0     0 1   my ($self, $reqform) = @_;
160              
161 0           $self->set_form_type ('submit');
162              
163 0           for my $field ($reqform->get_fields) {
164 0 0         next if $field->{type} eq 'fixed';
165              
166 0           my $fo = {
167             var => $field->{var},
168             type => $field->{type},
169 0           values => [ @{$field->{values}} ],
170             options => [],
171             };
172              
173 0           $self->append_field ($fo);
174             }
175             }
176              
177             =item B
178              
179             This method removes all fields that have no values and options.
180              
181             =cut
182              
183             sub clear_empty_fields {
184 0     0 1   my ($self) = @_;
185              
186 0           my @dead;
187 0           for ($self->get_fields) {
188 0 0 0       unless (@{$_->{values}} || @{$_->{options}}) {
  0            
  0            
189 0           push @dead, $_;
190             }
191             }
192 0           $self->remove_field ($_) for @dead;
193             }
194              
195             =item B
196              
197             This method removes a field either by it's unique name or
198             by reference. C<$field_or_var> can either be the unique name or
199             the actual field hash reference you get from C or C.
200              
201             =cut
202              
203             sub remove_field {
204 0     0 1   my ($self, $field) = @_;
205 0 0         unless (ref $field) {
206 0 0         $field = $self->get_field ($field) or return;
207             }
208 0           @{$self->{fields}} = grep { $_ ne $field } @{$self->{fields}};
  0            
  0            
  0            
209 0 0         if (defined $field->{var}) {
210 0           delete $self->{field_var}->{$field->{var}};
211             }
212             }
213              
214             =item B
215              
216             This method sets the type of the form, which must be one of:
217              
218             form, submit, cancel, result
219              
220             =cut
221              
222             sub set_form_type {
223 0     0 1   my ($self, $type) = @_;
224 0           $self->{type} = $type;
225             }
226              
227             =item B
228              
229             This method returns the type of the form, which is one of the
230             options described in C above or undef if no type
231             was yet set.
232              
233             =cut
234              
235 0     0 1   sub form_type { return $_[0]->{type} }
236              
237             =item B
238              
239             If this is a search result this method returns more than one element
240             here. The returned list consists of fields as described in L,
241             only that they lack values and options.
242              
243             See also the C method.
244              
245             =cut
246              
247             sub get_reported_fields {
248 0     0 1   my ($self) = @_;
249 0           @{$self->{reported}}
  0            
250             }
251              
252             =item B
253              
254             If this form is a search result this method returns the list of
255             items of that search.
256              
257             An item is a array ref of fields (field structure is described in L).
258             This method returns a list of items.
259              
260             =cut
261              
262             sub get_items {
263 0     0 1   my ($self) = @_;
264 0           @{$self->{items}};
  0            
265             }
266              
267             =item B
268              
269             This method returns a list of fields. Each field has the structure as described
270             in L.
271              
272             =cut
273              
274             sub get_fields {
275 0     0 1   my ($self) = @_;
276 0           @{$self->{fields}}
  0            
277             }
278              
279             =item B
280              
281             Returns the field with the unique field name C<$var> or
282             undef if no such field is in this form.
283              
284             =cut
285              
286             sub get_field {
287 0     0 1   my ($self, $var) = @_;
288 0           $self->{field_var}->{$var}
289             }
290              
291             =item B
292              
293             This method sets the value of the field with the unique name C<$var>.
294             If the field has supports multiple values all values will be removed
295             and only C<$value> will be added, if C<$value> is undefined the field's
296             value will be deleted.
297              
298             =cut
299              
300             sub set_field_value {
301 0     0 1   my ($self, $var, $val) = @_;
302 0 0         my $f = $self->get_field ($var) or return;
303 0 0         $f->{values} = defined $val ? [ $val ] : [];
304             }
305              
306             =item B
307              
308             This method adds the C<$value> to the field with the unique name C<$var>.
309             If the field doesn't support multiple values this method has the same
310             effect as C.
311              
312             =cut
313              
314             sub add_field_value {
315 0     0 1   my ($self, $var, $val) = @_;
316 0 0         my $f = $self->get_field ($var) or return;
317 0 0         if (grep { $f->{type} eq $_ } qw/jid-multi list-multi text-multi/) {
  0            
318 0           push @{$f->{values}}, $val;
  0            
319             } else {
320 0           $self->set_field_value ($var, $val);
321             }
322             }
323              
324             =item B
325              
326             This method converts the form to a data strcuture
327             that you can pass as C argument to the C
328             function which is documented in L.
329              
330             Example call might be:
331              
332             my $node = $form->to_simxml;
333             simxml ($w, defns => $node->{ns}, node => $node);
334              
335             B The returned simxml node has the C field set
336             so that no prefixes are generated for the namespace it is in.
337              
338             =cut
339              
340             sub _field_to_simxml {
341 0     0     my ($f) = @_;
342              
343 0           my $ofa = [];
344 0           my $ofc = [];
345 0           my $of = { name => 'field', attrs => $ofa, childs => $ofc };
346              
347 0 0         push @$ofa, (label => $f->{label}) if defined $f->{label};
348 0 0         push @$ofa, (var => $f->{var}) if defined $f->{var};
349 0 0         push @$ofa, (type => $f->{type}) if defined $f->{type};
350              
351 0           for (@{$f->{values}}) {
  0            
352 0           push @$ofc, { name => 'value', childs => [ $_ ] }
353             }
354              
355 0           for (@{$f->{options}}) {
  0            
356 0           my $at = [];
357 0           my $chlds = [];
358 0           push @$ofc, {
359             name => 'option', attrs => $at, childs => $chlds
360             };
361 0           for (@{$_->[1]}) {
  0            
362 0           push @$chlds, { name => 'value', childs => [ $_ ] }
363             }
364 0 0         if (defined $_->[0]) { push @$at, (label => $_->[0]) }
  0            
365             }
366              
367 0 0         if ($f->{desc}) {
368 0           push @$ofc, { name => 'desc', childs => [ $f->{desc} ] }
369             }
370              
371 0 0         if ($f->{required}) {
372 0           push @$ofc, { name => 'required' }
373             }
374              
375             $of
376 0           }
377              
378             sub to_simxml {
379 0     0 1   my ($self) = @_;
380              
381 0           my $fields = [];
382 0           my $top = {
383             ns => 'data_form',
384             dns => 'data_form',
385             name => 'x',
386             attrs => [],
387             childs => $fields,
388             };
389              
390 0           push @{$top->{attrs}}, ( type => $self->{type} );
  0            
391              
392 0 0         if (defined $self->{title}) {
393 0           push @$fields, {
394             name => 'title', childs => [ $self->{title} ]
395             }
396             }
397              
398 0 0         if (defined $self->{instructions}) {
399 0           push @$fields, {
400             name => 'instructions', childs => [ $self->{instructions} ]
401             }
402             }
403              
404 0           for my $f ($self->get_fields) {
405 0           push @$fields, _field_to_simxml ($f);
406             }
407              
408 0           my $repchld = [];
409 0           for my $rf ($self->get_reported_fields) {
410 0           push @$repchld, _field_to_simxml ($rf);
411             }
412              
413 0 0         if (@$repchld) {
414 0           push @$fields, {
415             name => 'reported',
416             childs => $repchld
417             };
418             }
419              
420 0           for my $itf ($self->get_items) {
421 0           my $itfields = [];
422              
423 0           for my $f (@$itf) {
424 0           push @$itfields, _field_to_simxml ($f);
425             }
426              
427 0           push @$fields, {
428             name => 'item',
429             childs => $itfields
430             }
431             }
432              
433             $top
434 0           }
435              
436             =item B
437              
438             This method returns a string that represents the form.
439             Only for debugging purposes.
440              
441             =cut
442              
443             sub as_debug_string {
444 0     0 1   my ($self) = @_;
445              
446 0           my $str;
447 0           $str .= "title: $self->{title}\n"
448             ."instructions: $self->{instructions}\n"
449             ."type: $self->{type}\n";
450 0           for my $f ($self->get_fields) {
451 0           $str .= sprintf "- var : %-50s label: %s\n type: %-10s required: %d\n",
452             $f->{var}, $f->{label}, $f->{type}, $f->{required};
453 0           for (@{$f->{values}}) {
  0            
454 0           $str .= sprintf " * val : %s\n", $_
455             }
456 0           for (@{$f->{options}}) {
  0            
457 0           $str .= sprintf " * opt lbl: %-50s text: %s\n", @$_
458             }
459             }
460              
461 0           $str .= "reported:\n";
462 0           for my $f (@{$self->{reported}}) {
  0            
463 0           $str .= sprintf "- var: %-50s label: %-30s type: %-10s %d\n",
464             $f->{var}, $f->{label}, $f->{type};
465             }
466              
467 0           $str .= "items:\n";
468 0           for my $i (@{$self->{items}}) {
  0            
469 0           $str .= "-" x 60 . "\n";
470 0           for my $f (@$i) {
471 0           $str .= sprintf "- var : %-50s\n", $f->{var};
472 0           for (@{$f->{values}}) {
  0            
473 0           $str .= sprintf " * val : %s\n", $_
474             }
475 0           for (@{$f->{options}}) {
  0            
476 0           $str .= sprintf " * opt lbl: %-50s text: %s\n", @$_
477             }
478             }
479             }
480              
481             $str
482 0           }
483              
484             =back
485              
486             =head1 FIELD STRUCTURE
487              
488             {
489             label => 'field label',
490             type => 'field type',
491             var => '(unique) field name'
492             required => true or false value,
493             values => [
494             'value text',
495             ...
496             ],
497             options => [
498             ['option label', 'option text'],
499             ...
500             ]
501             }
502              
503             For the semantics of all fields please consult XEP 0004.
504              
505             =head1 SEE ALSO
506              
507             XEP 0004
508              
509             =head1 AUTHOR
510              
511             Robin Redeker, C<< >>, JID: C<< >>
512              
513             =head1 COPYRIGHT & LICENSE
514              
515             Copyright 2007, 2008 Robin Redeker, all rights reserved.
516              
517             This program is free software; you can redistribute it and/or modify it
518             under the same terms as Perl itself.
519              
520             =cut
521              
522             1;