File Coverage

lib/CallBackery/GuiPlugin/AbstractForm.pm
Criterion Covered Total %
statement 21 142 14.7
branch 0 60 0.0
condition 0 18 0.0
subroutine 7 19 36.8
pod 8 8 100.0
total 36 247 14.5


line stmt bran cond sub pod time code
1             package CallBackery::GuiPlugin::AbstractForm;
2 1     1   630 use Carp qw(carp croak);
  1         2  
  1         66  
3 1     1   4 use CallBackery::Translate qw(trm);
  1         2  
  1         49  
4 1     1   4 use CallBackery::Exception qw(mkerror);
  1         2  
  1         74  
5 1     1   7 use Mojo::Promise;
  1         2  
  1         10  
6 1     1   33 use Mojo::Util qw(dumper);
  1         2  
  1         46  
7 1     1   5 use Time::HiRes;
  1         1  
  1         7  
8              
9             =head1 NAME
10              
11             CallBackery::GuiPlugin::AbstractForm - form base class
12              
13             =head1 SYNOPSIS
14              
15             use Mojo::Base 'CallBackery::GuiPlugin::AbstractForm', -signatures;
16              
17             =head1 DESCRIPTION
18              
19             The base class for gui forms.
20              
21             =cut
22              
23 1     1   51 use Mojo::Base 'CallBackery::GuiPlugin::AbstractAction', -signatures;
  1         1  
  1         10  
24              
25             =head1 ATTRIBUTES
26              
27             The attributes of the L class plus:
28              
29             =head2 screenCfg
30              
31             Returns a configuration structure for the form. The output from this
32             method is fed to the callbackery.ui.form.Auto object to build the
33             Qooxdoo form.
34              
35             =cut
36              
37             has screenCfg => sub ($self) {
38             my $cfg = $self->SUPER::screenCfg;
39             $cfg->{type} = 'form';
40             $cfg->{form} = $self->formCfg;
41             return $cfg;
42             };
43              
44             =head2 formCfg
45              
46             Returns the content of the form.
47              
48             =cut
49              
50             has formCfg => sub {
51             [];
52             };
53              
54             =head2 formCfg
55              
56             TODOC
57              
58             =cut
59              
60             has formCfgMap => sub ($self) {
61             my %map;
62             for my $row (@{$self->formCfg}){
63             next unless $row->{key};
64             $map{$row->{key}} = $row;
65             }
66             return \%map;
67             };
68              
69              
70             =head2 formData ()
71              
72             Return the form data independently from the form phase.
73              
74             =cut
75              
76 0     0 1   sub formData ($self) {
  0            
  0            
77 0   0       my $args = $self->args || {};
78 0 0         return {} if ref $args->{location} eq 'HASH';
79 0   0       return $args->{currentFormData} || $args->{formData} || $args;
80             };
81              
82              
83             =head2 formPhase ()
84              
85             Return the form phase.
86              
87             =cut
88              
89 0     0 1   sub formPhase ($self) {
  0            
  0            
90 0           my $args = $self->args;
91              
92              
93             # data
94 0 0         if ($args->{currentFormData}) {
95 0 0         return 'reconfigure' if $args->{triggerField};
96 0           return 'initialize';
97             }
98              
99             # actions
100 0 0         if ($args->{formData}) {
101 0 0         return "action:$args->{key}" if $args->{key};
102             }
103              
104             # if called from CardList or Table plugins
105 0 0         if (ref $args eq 'HASH') {
106 0 0         return 'pluginConfig' if not keys $args->%*;
107 0           return 'instanciate';
108             }
109              
110             # catch all, if ever reached
111 0           $self->log->warn('Unknown form phase, args=', dumper $args);
112 0           return 'unknown';
113             };
114              
115              
116             =head1 METHODS
117              
118             All the methods of L plus:
119              
120             =cut
121              
122             =head2 validateData(fieldName,formData)
123              
124             If the given value is valid for the field, return undef else return
125             an error message.
126              
127             =cut
128              
129             sub validateData {
130 0     0 1   my $self = shift;
131 0           my $fieldName = shift;
132 0   0       my $formData = shift || {};
133 0           $self->args($formData);
134 0           my $entry = $self->formCfgMap->{$fieldName};
135 0 0         if (not ref $entry){
136 0           die mkerror(4095,trm("sorry, don't know the field you are talking about"));
137             }
138 0   0       my $fieldIsEmpty = (not defined $formData->{$fieldName} or length($formData->{$fieldName}) == 0);
139 0 0 0       return if not $entry->{set}{required} and $fieldIsEmpty;
140 0 0 0       if ($entry->{validator}){
    0          
141 0           my $start = time;
142 0           my $data = $entry->{validator}->($formData->{$fieldName},$fieldName,$formData);
143 0           $self->log->debug(sprintf("validator %s: %0.2fs",$fieldName,time-$start));
144 0           return $data;
145             }
146             # if there is no validator but the field is required, complain
147             # if the content is empty
148             elsif ($entry->{set}{required} and $fieldIsEmpty){
149 0           return trm('The %1 field is required',$fieldName);
150             }
151 0           return;
152             }
153              
154             =head2 processData($args)
155              
156             The default behavior of the method is to validate all the form fields
157             and then store the data into the config database.
158              
159             =cut
160              
161 0     0 1   sub processData ($self, $args, $extraArgs=undef) {
  0            
  0            
  0            
  0            
162 0 0         $self->args($args) if $args;
163 0           my $form = $self->formCfgMap;
164 0           my $formData = $args->{formData};
165             # this is only to be sure ... data should be pre-validated
166 0           for my $key (keys %$form){
167 0 0         if (my $error = $self->validateData($key,$formData)){
168 0           die mkerror(7492,$error);
169             }
170             }
171 0 0         if ($args->{key}){
172 0           my $handler = $self->actionCfgMap->{$args->{key}}{actionHandler};
173 0 0         if (ref $handler eq 'CODE'){
174 0           return $handler->($self,$formData);
175             }
176 0           $handler = $self->actionCfgMap->{$args->{key}}{handler};
177 0 0         if (ref $handler eq 'CODE'){
178 0           $self->log->warn("Using handler properties in actionCfg is deprecated. Use actionHandler instead.");
179 0           return $handler->($formData);
180             }
181 0           $self->log->error('Plugin instance '.$self->name." action $args->{key} has a broken handler");
182 0           die mkerror(7623,'Plugin instance '.$self->name." action $args->{key} has a broken handler");
183             }
184             }
185              
186              
187             =head2 saveFormDataToConfig(data)
188              
189             Save all the form fields for which is available to the config
190             database. Keys will be prefixed by the plugin instance name
191             (C).
192              
193             =cut
194              
195 0     0 1   sub saveFormDataToConfig ($self, $formData) {
  0            
  0            
  0            
196 0           my $form = $self->formCfgMap;
197 0           for my $key (keys %$form){
198 0 0         next if not exists $formData->{$key};
199 0           $self->setConfigValue($self->name.'::'.$key,$formData->{$key});
200             }
201             }
202              
203             =head2 getFieldValue(field)
204              
205             Fetch the current value of the field. This will either use the getter
206             method supplied in the form config or try to fetch the value from the
207             config database.
208              
209             =cut
210              
211 0     0 1   sub getFieldValue ($self, $field) {
  0            
  0            
  0            
212 0           my $entry = $self->formCfgMap->{$field};
213 0           my $log = $self->log;
214 0 0         return undef unless ref $entry eq 'HASH';
215 0 0         if ($entry->{getter}){
216 0 0         if (ref $entry->{getter} eq 'CODE'){
217 0           my $start = time;
218 0           my $data = $entry->{getter}->($self);
219 0 0         if (eval { blessed $data && $data->isa('Mojo::Promise')}){
  0 0          
220 0     0     $data = $data->then(sub ($value) {
  0            
  0            
221 0           my $delay = time-$start;
222 0 0         $log->debug(sprintf("slow async getter %s: %0.2fs",$field,$delay)) if $delay > 2;
223 0           return $value;
224 0           });
225             }
226             else {
227 0           my $delay = time-$start;
228 0 0         $log->debug(sprintf("slow getter %s: %0.2fs",$field,$delay)) if $delay > 2;
229             }
230 0           return $data;
231             }
232             else {
233 0           $log->warn('Plugin instance'.$self->name." field $field has a broken getter\n");
234             }
235             }
236 0           return $self->getConfigValue($self->name.'::'.$field);
237             }
238              
239             =head2 getAllFieldValues
240              
241             Return all field values of the form.
242              
243             =cut
244              
245 0     0 1   sub getAllFieldValues ($self, $parentForm, $currentForm, $args) {
  0            
  0            
  0            
  0            
  0            
246 0           my %map;
247             my @promises;
248 0 0         $self->args($currentForm) if $currentForm;
249 0           for my $key (keys %{$self->formCfgMap}){
  0            
250 0           my $value = $self->getFieldValue($key);
251 0 0         if (eval { blessed $value && $value->isa('Mojo::Promise')}){
  0 0          
252 0           push @promises, $value;
253             $value->then(
254             sub{
255 0     0     $map{$key} = shift;
256             },
257             sub {
258 0     0     die shift;
259             }
260 0           );
261             }
262             else {
263 0           $map{$key} = $self->getFieldValue($key);
264             }
265             }
266 0 0         if (@promises){
267             return Mojo::Promise->new->all(@promises)->then(sub {
268 0     0     return \%map;
269 0           });
270             }
271 0           return \%map;
272             }
273              
274              
275             =head2 getData (type,field)
276              
277             Return the value of the given field. If no field name is specified
278             return a hash with all the current data known to the plugin.
279              
280             =cut
281              
282 0     0 1   sub getData ($self, $type, @args) {
  0            
  0            
  0            
  0            
283 0 0         if ($type eq 'field'){
    0          
284 0           return $self->getFieldValue(@args);
285             }
286             elsif ($type eq 'allFields') {
287 0           return $self->getAllFieldValues(@args);
288             }
289             else {
290 0   0       die mkerror(38334, 'Requested unknown data type ' . ($type // 'unknown'));
291             }
292             }
293              
294             1;
295              
296             __END__