File Coverage

blib/lib/Mojolicious/Plugin/FormFieldsFromJSON.pm
Criterion Covered Total %
statement 460 477 96.4
branch 210 254 82.6
condition 152 253 60.0
subroutine 33 33 100.0
pod 1 1 100.0
total 856 1018 84.0


line stmt bran cond sub pod time code
1             package Mojolicious::Plugin::FormFieldsFromJSON;
2 87     87   88050 use Mojo::Base 'Mojolicious::Plugin';
  87         209  
  87         696  
3              
4             # ABSTRACT: create form fields based on a definition in a JSON file
5              
6             our $VERSION = '1.03';
7              
8 87     87   21289 use Carp;
  87         201  
  87         5650  
9 87     87   560 use File::Basename;
  87         190  
  87         5068  
10 87     87   529 use File::Spec;
  87         213  
  87         2367  
11 87     87   48870 use IO::Dir;
  87         144161  
  87         4761  
12 87     87   652 use List::Util qw(first);
  87         203  
  87         5259  
13              
14 87     87   543 use Mojo::Asset::File;
  87         190  
  87         1482  
15 87     87   2442 use Mojo::Collection;
  87         197  
  87         3168  
16 87     87   455 use Mojo::ByteStream;
  87         199  
  87         3194  
17 87     87   527 use Mojo::File qw(path);
  87         184  
  87         3596  
18 87     87   468 use Mojo::JSON qw(decode_json);
  87         187  
  87         3533  
19              
20 87     87   495 use Mojolicious ();
  87         190  
  87         579344  
21              
22             has dir => sub { ["."] };
23              
24             sub register {
25 87     87 1 4789 my ( $self, $app, $config ) = @_;
26              
27 87   50     454 $config //= {};
28              
29 87 100       501 if ( $config->{template_file} ) {
30 1         9 $config->{template} = path( $app->home, 'templates', $config->{template_file} )->slurp;
31 1   33     263 $config->{template} //= $app->renderer->get_data_template( $config->{template_file} );
32             }
33              
34 87         249 my %configs;
35 87 100       386 if ( ref $config->{dir} eq "ARRAY" ) {
36 1         2 @{ $self->dir } = @{ $config->{dir} };
  1         3  
  1         3  
37             }
38             else {
39 86         205 @{ $self->dir } = ( $config->{dir} );
  86         338  
40             }
41              
42             my %valid_types = (
43 87 100       213 %{ $config->{types} || {} },
  87         1236  
44             text => 1,
45             checkbox => 1,
46             select => 1,
47             radio => 1,
48             hidden => 1,
49             textarea => 1,
50             password => 1,
51             );
52              
53 87         265 my %configfiles;
54             $app->helper(
55             forms => sub {
56 1 50   1   20100 if (%configfiles) {
57 0         0 my @sorted_configfiles = sort keys %configfiles;
58 0         0 return @sorted_configfiles;
59             }
60              
61 1         4 for my $dir ( @{ $self->dir } ) {
  1         7  
62 1         18 my $dir = IO::Dir->new($dir);
63              
64             FILE:
65 1         204 while ( my $file = $dir->read ) {
66 5 100       123 next FILE if $file !~ m{\.json\z};
67              
68 3         105 my $filename = basename $file;
69 3         24 $filename =~ s{\.json\z}{};
70              
71 3         16 $configfiles{$filename} = 1;
72             }
73             }
74              
75 1         79 my @sorted_configfiles = sort keys %configfiles;
76 1         7 return @sorted_configfiles;
77             }
78 87         1183 );
79              
80 87     1   12895 $app->helper( "form_fields_from_json_dir" => sub { return $self->dir } );
  1         4278  
81              
82             $app->helper(
83             fields => sub {
84 2     2   28116 my ( $c, $file, $params ) = @_;
85              
86 2 100       10 if ( !$configs{$file} ) {
87 1         4 $self->_load_config_from_file( $c, \%configs, $file );
88             }
89              
90 2         5 my @fields;
91             my @fields_long;
92 2         3 for my $field ( @{ $configs{$file} } ) {
  2         7  
93 4   33     13 my $name = $field->{label} // $field->{name} // '';
      0        
94 4         10 push @fields, $name;
95 4   50     18 push @fields_long, +{ label => $name, name => $field->{name} // '' };
96             }
97              
98 2 100 66     13 if ( $params and $params->{hash} ) {
99 1         6 return @fields_long;
100             }
101              
102 1         16 return @fields;
103             }
104 87         8051 );
105              
106             $app->helper(
107             'validate_form_fields' => sub {
108 35     35   419821 my ( $c, $file ) = @_;
109              
110 35 50       173 return '' if !$file;
111              
112 35 100       153 if ( !$configs{$file} ) {
113 12         58 $self->_load_config_from_file( $c, \%configs, $file );
114             }
115              
116 35 50       143 return '' if !$configs{$file};
117              
118 35         107 my $config = $configs{$file};
119              
120 35         259 my $validation = $c->validation;
121              
122 35         22242 my $params_hash = $c->req->params->to_hash;
123 35 50       1296 my @param_names = keys %{ $params_hash || {} };
  35         216  
124              
125 35         100 my %params;
126 35         94 for my $name ( @param_names ) {
127 31         127 my $param_values = $c->every_param($name);
128              
129 31 50       2462 $params{$name} = @{$param_values || [] } > 1 ?
  31 100       297  
130             $param_values : $param_values->[-1];
131             }
132              
133 35         172 $validation->input( \%params );
134              
135 35         231 my %errors;
136              
137             FIELD:
138 35         71 for my $field ( @{$config} ) {
  35         90  
139 35 50       127 if ( 'HASH' ne ref $field ) {
140 0         0 $app->log->error('Field definition must be a HASH - skipping field');
141 0         0 next FIELD;
142             }
143              
144 35 50       120 if ( !$field->{validation} ) {
145 0         0 next FIELD;
146             }
147              
148 35 50       119 if ( 'HASH' ne ref $field->{validation} ) {
149 0         0 $app->log->warn('Validation settings must be a HASH - skipping field');
150 0         0 next FIELD;
151             }
152              
153 35         97 my $filters = $field->{validation}->{filters};
154              
155 35   33     146 my $name = $field->{name} // $field->{label} // '';
      0        
156 35         116 my $global_error = 1;
157              
158 35 100       116 if ( $field->{validation}->{required} ) {
159 12 100       26 $validation->required($name, @{ $filters || [] });
  12         99  
160              
161 12         878 my $value = $field->{validation}->{required};
162 12 100 66     74 if ( ref $value && 'HASH' eq ref $value ) {
163 2   50     7 $global_error = $value->{msg} // 1;
164             }
165             }
166             else {
167 23 100       47 $validation->optional($name, @{ $filters || [] });
  23         175  
168             }
169              
170             RULE:
171 35         1192 for my $rule ( sort keys %{ $field->{validation} } ) {
  35         236  
172 54 100       250 last RULE if !defined $params{$name};
173              
174 48 100       162 next RULE if $rule eq 'required';
175 41 100       125 next RULE if $rule eq 'filters';
176              
177 30         94 my $value = $field->{validation}->{$rule};
178 30         70 my $ref = ref $value;
179 30         57 my $method = $rule;
180 30         66 my $error = 1;
181              
182 30         71 my @params;
183              
184 30 100       121 if ( !$ref ) {
    100          
    50          
185 3         6 @params = $value;
186             }
187             elsif ( $ref eq 'ARRAY' ) {
188 17         84 @params = @{$value};
  17         63  
189             }
190             elsif ( $ref eq 'HASH' ) {
191 10 50       29 @params = ref $value->{args} ? @{ $value->{args} } : $value->{args};
  10         27  
192 10   50     42 $error = $value->{msg} // 1;
193             }
194              
195             eval {
196 30         163 $validation->check( $method, @params );
197 30         1654 1;
198 30 50       85 } or do {
199 0         0 $app->log->error("Validating $name with rule $method failed: $@");
200             };
201              
202 30 100       122 if ( $validation->has_error($name) ) {
203 10         62 $errors{$name} = $error;
204 10         64 last RULE;
205             }
206             }
207              
208 35 100 100     184 if ( $validation->has_error($name) && !defined $errors{$name} ) {
209 5         57 $errors{$name} = $global_error;
210             }
211             }
212              
213 35         383 return %errors;
214             }
215 87         7823 );
216              
217             $app->helper(
218             'form_fields' => sub {
219 107     107   1523686 my ( $c, $file, %params ) = @_;
220              
221             # get form config
222 107 100       627 return '' if !$self->_load_config_from_file( $c, \%configs, $file );
223 106 50 66     469 return '' if !$configs{$file} && !ref $file;
224 106   66     499 my $field_config = $configs{$file} || $file;
225              
226 106         263 my @fields;
227              
228 106 100       266 my %fields_to_show = map { $_ => 1 } @{ $params{fields} || [] };
  2         8  
  106         970  
229              
230             FIELD:
231 106         309 for my $field ( @{$field_config} ) {
  106         350  
232 145 100 100     567 next FIELD if %fields_to_show && !$fields_to_show{ $field->{name} };
233              
234 143         691 my $field_content = $self->_build_form_field( $c, $field, \%params, $config, \%valid_types );
235              
236 143 50       511 if ( length $field_content ) {
237 143         1294 push @fields, $field_content;
238             }
239             }
240              
241 106         468 return Mojo::ByteStream->new( join "\n\n", @fields );
242             }
243 87         7691 );
244              
245             $app->helper(
246             'form_field_by_name' => sub {
247 4     4   64625 my ( $c, $file, $field_name, %params ) = @_;
248              
249             # get form config
250 4 50       43 return '' if !$self->_load_config_from_file( $c, \%configs, $file );
251 4 0 33     30 return '' if !$configs{$file} && !ref $file;
252 4   33     20 my $field_config = $configs{$file} || $file;
253              
254             # find field config
255 4         12 my @fields_filtered = grep { $_->{name} eq $field_name } @{$field_config};
  8         55  
  4         22  
256              
257 4 50       18 return '' if !( scalar @fields_filtered );
258              
259 4         29 return $self->_build_form_field( $c, $fields_filtered[0], \%params, $config, \%valid_types );
260             }
261 87         8330 );
262              
263 87         7347 return 1;
264             }
265              
266             sub _load_config_from_file {
267 124     124   445 my ( $self, $c, $configs, $file ) = @_;
268              
269 124 50       646 return 0 if !$file;
270              
271 124 100 100     1035 if ( !$configs->{$file} && !ref $file ) {
272 87         280 my $path;
273              
274             # search until first match
275 87         226 my $i = 0;
276             do {
277 91         589 my $_path = File::Spec->catfile( $self->dir->[$i], $file . '.json' );
278 91 100       7129 $path = $_path if -r $_path;
279 87   100     243 } while ( not defined $path and ++$i <= $#{ $self->dir } );
  5         26  
280              
281 87 100       577 if ( not defined $path ) {
282 1         5 $c->app->log->error("FORMFIELDS $file: not found in directories");
283 1         22 $c->app->log->error(" $_") for @{ $self->dir };
  1         3  
284 1         31 return 0;
285             }
286              
287             eval {
288 86         1080 my $content = Mojo::Asset::File->new( path => $path )->slurp;
289 86         31284 $configs->{$file} = decode_json $content;
290 86 50       254 } or do {
291 0         0 $c->app->log->error("FORMFIELDS $file: $@");
292 0         0 return 0;
293             };
294              
295 86 50       34454 if ( 'ARRAY' ne ref $configs->{$file} ) {
296 0         0 $c->app->log->error('Definition JSON must be an ARRAY');
297 0         0 return 0;
298             }
299             }
300              
301 123         645 return 1;
302             }
303              
304             sub _build_form_field {
305 147     147   535 my ( $self, $c, $field, $params, $plugin_config, $valid_types ) = @_;
306              
307 147 50       619 if ( 'HASH' ne ref $field ) {
308 0         0 $c->app->log->error('Field definition must be an HASH - skipping field');
309 0         0 return '';
310             }
311              
312 147         659 my $type = lc $field->{type};
313 147         395 my $orig_type = $type;
314              
315 147 100 100     583 if ( $plugin_config->{alias} && $plugin_config->{alias}->{$type} ) {
316 3         9 $type = $plugin_config->{alias}->{$type};
317             }
318              
319 147 50       778 if ( !$valid_types->{$type} ) {
320 0         0 $c->app->log->warn("Invalid field type $type - falling back to 'text'");
321 0         0 $type = 'text';
322             }
323              
324 147 100 100     1023 if ( $plugin_config->{global_attributes}
      66        
325             && $type ne 'hidden'
326             && 'HASH' eq ref $plugin_config->{global_attributes} )
327             {
328              
329             ATTRIBUTE:
330 24         45 for my $attribute ( keys %{ $plugin_config->{global_attributes} } ) {
  24         81  
331 24   100     110 $field->{attributes}->{$attribute} //= '';
332              
333 24         63 my $field_attr = $field->{attributes}->{$attribute};
334 24         48 my $global_attr = $plugin_config->{global_attributes}->{$attribute};
335              
336 24 100       180 next ATTRIBUTE if $field_attr =~ m{\Q$global_attr};
337              
338 18 100       67 my $space = length $field_attr ? ' ' : '';
339              
340 18         67 $field->{attributes}->{$attribute} .= $space . $global_attr;
341             }
342             }
343              
344 147 50 66     586 if ( $field->{translate_sublabels}
      33        
345             && $plugin_config->{translation_method}
346             && !$field->{translation_method} )
347             {
348 2         6 $field->{translation_method} = $plugin_config->{translation_method};
349             }
350              
351 147         1700 my $sub = $self->can( '_' . $type );
352 147         489 my $form_field = $self->$sub( $c, $field, %{$params} );
  147         695  
353              
354 147         34544 $form_field = Mojo::ByteStream->new($form_field);
355              
356             my $template = $field->{template}
357             // $plugin_config->{templates}->{$orig_type}
358 147   100     3452 // $plugin_config->{template};
      100        
359              
360 147 100 66     723 if ( $template && $type ne 'hidden' ) {
361 23   50     135 my $label = $field->{label} // '';
362 23         69 my $loc = $plugin_config->{translation_method};
363              
364 23 50 66     133 if ( $plugin_config->{translate_labels} && $loc && 'CODE' eq ref $loc ) {
      66        
365 1         6 $label = $loc->( $c, $label );
366             }
367              
368             $form_field = Mojo::ByteStream->new(
369             $c->render_to_string(
370             inline => $template,
371             id => $field->{id} // $field->{name} // $field->{label} // '',
372             label => $label,
373             field => $form_field,
374             message => $field->{msg} // '',
375 23   33     429 info => $field->{info} // '',
      33        
      0        
      100        
      100        
376             )
377             );
378              
379             # $c->app->log->debug("rendered formfield: ".$form_field);
380             }
381              
382 147         59801 return $form_field;
383             }
384              
385             sub _hidden {
386 8     8   37 my ( $self, $c, $field, %params ) = @_;
387              
388 8   33     34 my $name = $field->{name} // $field->{label} // '';
      0        
389              
390 8         30 my $from_stash_key = $params{from_stash};
391             my $from_stash =
392             $from_stash_key
393 8 50       26 ? $c->stash($from_stash_key)->{$name}
394             : undef;
395              
396             my $value = $params{$name}->{data} // $from_stash // $c->stash($name) // $c->param($name)
397 8   66     105 // $field->{data} // '';
      66        
      66        
      66        
      50        
398              
399 8   33     987 my $id = $field->{id} // $name;
400 8 50       18 my %attrs = %{ $field->{attributes} || {} };
  8         63  
401              
402 8         114 return $c->hidden_field( $name, $value, id => $id, %attrs );
403             }
404              
405             sub _text {
406 33     33   126 my ( $self, $c, $field, %params ) = @_;
407              
408 33   33     198 my $name = $field->{name} // $field->{label} // '';
      0        
409              
410 33         91 my $from_stash_key = $params{from_stash};
411             my $from_stash =
412             $from_stash_key
413 33 100       138 ? $c->stash($from_stash_key)->{$name}
414             : undef;
415              
416             my $value = $params{$name}->{data} // $from_stash // $c->stash($name) // $c->param($name)
417 33   100     552 // $field->{data} // '';
      100        
      100        
      100        
      100        
418              
419 33   33     7278 my $id = $field->{id} // $name;
420 33 100       79 my %attrs = %{ $field->{attributes} || {} };
  33         303  
421              
422 33         436 return $c->text_field( $name, $value, id => $id, %attrs );
423             }
424              
425             sub _select {
426 38     38   152 my ( $self, $c, $field, %params ) = @_;
427              
428 38   33     220 my $name = $field->{name} // $field->{label} // '';
      0        
429              
430 38         103 my $from_stash_key = $params{from_stash};
431             my $from_stash =
432             $from_stash_key
433 38 100       150 ? $c->stash($from_stash_key)->{$name}
434             : undef;
435              
436 38   100     440 my $field_params = $params{$name} || {},
437              
438             my %select_params = (
439             disabled => $self->_get_highlighted_values( $field, 'disabled' ),
440             selected => $self->_get_highlighted_values( $field, 'selected' ),
441             );
442              
443 38         239 my $stash_values = $c->every_param($name);
444 38 50 100     8813 if ( scalar( @{ $stash_values || [] } ) == 0 && defined( $c->stash($name) ) ) {
  38 100       330  
445 3         37 my $local_stash = $c->stash($name);
446 3 100       27 $stash_values = ref $local_stash ? $local_stash : [$local_stash];
447             }
448              
449 38 100       529 if ($from_stash) {
450 1 50       30 $stash_values = ref $from_stash ? $from_stash : [$from_stash];
451             }
452              
453 38         89 my $reset;
454 38 50       87 if ( @{ $stash_values || [] } ) {
  38 100       166  
455             $select_params{selected} =
456 13         55 $self->_get_highlighted_values( +{ selected => $stash_values }, 'selected', );
457              
458 13         36 $reset = 1;
459             }
460              
461 38         127 for my $key (qw/disabled selected/) {
462 76         229 my $hashref = $self->_get_highlighted_values( $field_params, $key );
463 76 100       160 if ( keys %{$hashref} ) {
  76         408  
464 7         22 $select_params{$key} = $hashref;
465             }
466             }
467              
468 38 100       190 if ( $field_params->{data} ) {
469 5         14 $select_params{data} = $field_params->{data};
470             }
471              
472 38         227 my @values = $self->_get_select_values( $c, $field, %select_params );
473 38   33     254 my $id = $field->{id} // $name;
474 38 100       115 my %attrs = %{ $field->{attributes} || {} };
  38         326  
475              
476 38 100       202 if ( $field->{multiple} ) {
477 4         12 $attrs{multiple} = 'multiple';
478 4   50     14 $attrs{size} = $field->{size} || 5;
479             }
480              
481 38         87 my @selected = keys %{ $select_params{selected} };
  38         158  
482 38 100       165 if (@selected) {
483 18         34 my $single = scalar @selected;
484 18 100       71 my $param = $single == 1 ? $selected[0] : \@selected;
485 18         83 $c->param( $name, $param );
486             }
487              
488 38         693 my $select_field = $c->select_field( $name, [@values], id => $id, %attrs );
489              
490             # reset parameters
491 38 100       37066 if ($reset) {
492 13         53 my $single = scalar @{$stash_values};
  13         99  
493 13 100       52 my $param = $single == 1 ? $stash_values->[0] : $stash_values;
494 13         77 $c->param( $name, $param );
495             }
496              
497 38         448 return $select_field;
498             }
499              
500             sub _get_highlighted_values {
501 386     386   933 my ( $self, $field, $key ) = @_;
502              
503 386 100       1649 return +{} if !$field->{$key};
504              
505 69         129 my %highlighted;
506              
507 69 100       290 if ( !ref $field->{$key} ) {
    50          
508 25         86 my $value = $field->{$key};
509 25         74 $highlighted{$value} = 1;
510             }
511             elsif ( 'ARRAY' eq ref $field->{$key} ) {
512 44         88 for my $value ( @{ $field->{$key} } ) {
  44         133  
513 52         224 $highlighted{$value} = 1;
514             }
515             }
516              
517 69         245 return \%highlighted;
518             }
519              
520             sub _get_select_values {
521 39     39   160 my ( $self, $c, $field, %params ) = @_;
522              
523 39   100     352 my $data = $params{data} || $field->{data} || [];
524 39 100       152 if ( $field->{data_cb} ) {
525 1         8 my @parts = split /::/, $field->{data_cb};
526 1         3 my $subname = pop @parts;
527 1         3 my $class = join '::', @parts;
528 1         14 my $sub = $class->can($subname);
529 1 50       12 $data = $sub->() if $sub;
530             }
531              
532 39         142 my @values;
533 39 100       243 if ( 'ARRAY' eq ref $data ) {
    50          
534 35         208 @values = $self->_transform_array_values( $data, %params );
535             }
536             elsif ( 'HASH' eq ref $data ) {
537 4         27 @values = $self->_transform_hash_values( $c, $data, %params );
538             }
539              
540 39         157 return @values;
541             }
542              
543             sub _transform_hash_values {
544 4     4   22 my ( $self, $c, $data, %params ) = @_;
545              
546 4         9 my @values;
547 4         14 my $numeric = 1;
548 4         57 my $counter = 0;
549 4         9 my %mapping;
550              
551             KEY:
552 4         9 for my $key ( keys %{$data} ) {
  4         16  
553 7 100       21 if ( ref $data->{$key} ) {
554 1         10 my @group_values = $self->_get_select_values( $c, +{ data => $data->{$key} }, %params );
555 1         17 $values[$counter] = Mojo::Collection->new( $key => \@group_values );
556 1         10 $mapping{$key} = $counter;
557             }
558             else {
559 6         13 my %opts;
560              
561 6 50       27 $opts{disabled} = 'disabled' if $params{disabled}->{$key};
562 6 50       16 $opts{selected} = undef if $params{selected}->{$key};
563              
564             #$opts{selected} = undef if $params{selected}->{$key};
565              
566 6         20 $values[$counter] = [ $data->{$key} => $key, %opts ];
567 6         19 $mapping{$key} = $counter;
568             }
569              
570 7         15 $counter++;
571             }
572              
573 4 100   5   57 if ( first { $_ =~ m{[^0-9]} } keys %mapping ) {
  5         28  
574 3         14 $numeric = 0;
575             }
576              
577             my @sorted_keys =
578             $numeric
579 1         7 ? sort { $a <=> $b } keys %mapping
580 4 100       43 : sort { $a cmp $b } keys %mapping;
  2         15  
581              
582 4         15 my @indexes = @mapping{@sorted_keys};
583              
584 4         10 my @sorted_values = @values[@indexes];
585              
586 4         17 return @sorted_values;
587             }
588              
589             sub _transform_array_values {
590 35     35   130 my ( $self, $data, %params ) = @_;
591              
592 35         81 my @values;
593 35         82 my $numeric = 1;
594              
595 35         289 for my $value ( @{$data} ) {
  35         127  
596 93 100 66     488 if ( $numeric && $value =~ m{[^0-9]} ) {
597 35         96 $numeric = 0;
598             }
599              
600 93         158 my %opts;
601              
602 93 100       279 $opts{disabled} = 'disabled' if $params{disabled}->{$value};
603 93 100       249 $opts{selected} = undef if $params{selected}->{$value};
604              
605             #$opts{selected} = undef if $params{selected}->{$value};
606              
607 93         406 push @values, [ $value => $value, %opts ];
608             }
609              
610             @values = $numeric
611 0         0 ? sort { $a->[0] <=> $b->[0] } @values
612 35 50       289 : sort { $a->[0] cmp $b->[0] } @values;
  80         289  
613              
614 35         162 return @values;
615             }
616              
617             sub _radio {
618 25     25   100 my ( $self, $c, $field, %params ) = @_;
619              
620 25   33     117 my $name = $field->{name} // $field->{label} // '';
      0        
621 25   33     230 my $id = $field->{id} // $name;
622 25 100       56 my %attrs = %{ $field->{attributes} || {} };
  25         218  
623              
624 25   66     261 my $data = $params{$name}->{data} // $field->{data} // [];
      50        
625 25 100       126 my @values = ref $data ? @{$data} : ($data);
  17         74  
626              
627 25   50     167 my $field_params = $params{$name} || {},
628              
629             my %select_params = (
630             disabled => $self->_get_highlighted_values( $field, 'disabled' ),
631             selected => $self->_get_highlighted_values( $field, 'selected' ),
632             );
633              
634 25         187 my $stash_values = $c->every_param($name);
635 25 50 100     4796 if ( scalar( @{ $stash_values || [] } ) == 0 && defined( $c->stash($name) ) ) {
  25 100       281  
636 1         22 my $local_stash = $c->stash($name);
637 1 50       11 $stash_values = ref $local_stash ? $local_stash : [$local_stash];
638             }
639              
640 25         370 my $reset;
641 25 50       53 if ( @{ $stash_values || [] } ) {
  25 100       139  
642 4         17 $select_params{selected} = $self->_get_highlighted_values(
643             +{ selected => $stash_values },
644             'selected',
645             );
646 4         9 $reset = 1;
647             }
648              
649 25         75 for my $key (qw/disabled selected/) {
650 50         139 my $hashref = $self->_get_highlighted_values( $field_params, $key );
651 50 100       95 if ( keys %{$hashref} ) {
  50         274  
652 5         16 $select_params{$key} = $hashref;
653             }
654             }
655              
656 25         62 my @selected = keys %{ $select_params{selected} };
  25         117  
657 25 100       306 if (@selected) {
658 6         15 my $single = scalar @selected;
659 6 50       18 my $param = $single == 1 ? $selected[0] : \@selected;
660 6         25 $c->param( $name, $param );
661             }
662              
663 25         169 my $radiobuttons = '';
664 25         73 for my $radio_value (@values) {
665 42         81 my %value_attributes;
666              
667 42 100       149 if ( $select_params{disabled}->{$radio_value} ) {
668 3         8 $value_attributes{disabled} = 'disabled';
669             }
670              
671 42 100       168 if ( $select_params{selected}->{$radio_value} ) {
672 6         17 $value_attributes{checked} = undef;
673             }
674              
675 42         201 my $local_label = '';
676 42 100       185 if ( $field->{show_value} ) {
677 4         8 $local_label = $radio_value;
678             }
679              
680 42         84 my $loc = $field->{translation_method};
681 42 50 100     123 if ( length $local_label && $field->{translate_sublabels} && $loc && 'CODE' eq ref $loc ) {
      66        
      66        
682 2         15 $local_label = $loc->( $c, $local_label );
683             }
684              
685 42 100       152 $local_label = " " . $local_label if length $local_label;
686              
687 42         353 $radiobuttons .= $c->radio_button(
688             $name => $radio_value,
689             id => $id,
690             %attrs,
691             %value_attributes,
692             ) . "$local_label\n";
693              
694 42 100       20304 if ( defined $field->{after_element} ) {
695 2         6 $radiobuttons .= $field->{after_element};
696             }
697             }
698              
699 25 100       115 if ($reset) {
700 4         7 my $single = scalar @{$stash_values};
  4         11  
701 4 50       18 my $param = $single == 1 ? $stash_values->[0] : $stash_values;
702 4         20 $c->param( $name, $param );
703             }
704              
705 25         182 return $radiobuttons;
706             }
707              
708             sub _checkbox {
709 28     28   102 my ( $self, $c, $field, %params ) = @_;
710              
711 28   33     130 my $name = $field->{name} // $field->{label} // '';
      0        
712 28   33     157 my $id = $field->{id} // $name;
713 28 100       59 my %attrs = %{ $field->{attributes} || {} };
  28         205  
714              
715 28   66     232 my $data = $params{$name}->{data} // $field->{data} // [];
      50        
716 28 100       123 my @values = ref $data ? @{$data} : ($data);
  15         46  
717              
718 28   50     207 my $field_params = $params{$name} || {},
719              
720             my %select_params = (
721             disabled => $self->_get_highlighted_values( $field, 'disabled' ),
722             selected => $self->_get_highlighted_values( $field, 'selected' ),
723             );
724              
725 28         162 my $stash_values = $c->every_param($name);
726 28 50 100     5169 if ( scalar( @{ $stash_values || [] } ) == 0 && defined( $c->stash($name) ) ) {
  28 100       250  
727 2         26 my $local_stash = $c->stash($name);
728 2 100       19 $stash_values = ref $local_stash ? $local_stash : [$local_stash];
729             }
730              
731 28         325 my $reset;
732 28 50       55 if ( @{ $stash_values || [] } ) {
  28 100       165  
733             $select_params{selected} =
734 5         17 $self->_get_highlighted_values( +{ selected => $stash_values }, 'selected', );
735 5         24 $c->param( $name, '' );
736 5         81 $reset = 1;
737             }
738              
739 28         88 for my $key (qw/disabled selected/) {
740 56         195 my $hashref = $self->_get_highlighted_values( $field_params, $key );
741 56 100       86 if ( keys %{$hashref} ) {
  56         251  
742 6         21 $select_params{$key} = $hashref;
743             }
744             }
745              
746 28         63 my @selected = keys %{ $select_params{selected} };
  28         125  
747 28 100       196 if (@selected) {
748 8         15 my $single = scalar @selected;
749 8 100       23 my $param = $single == 1 ? $selected[0] : \@selected;
750 8         34 $c->param( $name, $param );
751             }
752              
753 28         159 my $checkboxes = '';
754 28         84 for my $checkbox_value (@values) {
755 44         78 my %value_attributes;
756              
757 44 100       134 if ( $select_params{disabled}->{$checkbox_value} ) {
758 3         9 $value_attributes{disabled} = 'disabled';
759             }
760              
761 44 100       161 if ( $select_params{selected}->{$checkbox_value} ) {
762 9         30 $value_attributes{checked} = undef;
763             }
764              
765 44         247 my $local_label = '';
766 44 100       219 if ( $field->{show_value} ) {
767 4         7 $local_label = $checkbox_value;
768             }
769              
770 44         95 my $loc = $field->{translation_method};
771 44 50 100     135 if ( length $local_label && $field->{translate_sublabels} && $loc && 'CODE' eq ref $loc ) {
      66        
      66        
772 2         15 $local_label = $loc->( $c, $local_label );
773             }
774              
775 44 100       145 $local_label = " " . $local_label if length $local_label;
776              
777 44         328 $checkboxes .= $c->check_box(
778             $name => $checkbox_value,
779             id => $id,
780             %attrs,
781             %value_attributes,
782             ) . "$local_label\n";
783              
784 44 100       20309 if ( defined $field->{after_element} ) {
785 3         9 $checkboxes .= $field->{after_element};
786             }
787             }
788              
789 28 100       99 if ($reset) {
790 5         7 my $single = scalar @{$stash_values};
  5         13  
791 5 100       18 my $param = $single == 1 ? $stash_values->[0] : $stash_values;
792 5         17 $c->param( $name, $param );
793             }
794              
795 28         185 return $checkboxes;
796             }
797              
798             sub _textarea {
799 8     8   43 my ( $self, $c, $field, %params ) = @_;
800              
801 8   33     69 my $name = $field->{name} // $field->{label} // '';
      0        
802 8   66     93 my $value = $params{$name}->{data} // $c->stash($name) // $c->param($name) // $field->{data} // '';
      100        
      100        
      100        
803 8   33     1642 my $id = $field->{id} // $name;
804 8 100       25 my %attrs = %{ $field->{attributes} || {} };
  8         65  
805              
806 8         103 return $c->text_area( $name, $value, id => $id, %attrs );
807             }
808              
809             sub _password {
810 6     6   25 my ( $self, $c, $field, %params ) = @_;
811              
812 6   33     32 my $name = $field->{name} // $field->{label} // '';
      0        
813 6   33     90 my $value = $params{$name}->{data} // $c->stash($name) // $c->param($name) // $field->{data} // '';
      66        
      66        
      50        
814 6   33     1120 my $id = $field->{id} // $name;
815 6 100       16 my %attrs = %{ $field->{attributes} || {} };
  6         40  
816              
817 6         72 return $c->password_field( $name, value => $value, id => $id, %attrs );
818             }
819              
820             1;
821              
822             __END__