File Coverage

blib/lib/HTML/FormBuilder/Validation.pm
Criterion Covered Total %
statement 161 172 93.6
branch 52 62 83.8
condition 38 57 66.6
subroutine 20 22 90.9
pod 8 8 100.0
total 279 321 86.9


line stmt bran cond sub pod time code
1             package HTML::FormBuilder::Validation;
2              
3 2     2   19888 use strict;
  2         3  
  2         43  
4 2     2   6 use warnings;
  2         2  
  2         35  
5              
6 2     2   6 use Carp;
  2         2  
  2         73  
7 2     2   829 use Class::Std::Utils;
  2         4015  
  2         7  
8              
9 2     2   997 use Encode;
  2         14366  
  2         111  
10 2     2   808 use URI::Escape;
  2         1746  
  2         90  
11 2     2   404 use HTML::Entities;
  2         3472  
  2         92  
12              
13 2     2   424 use Moo;
  2         8723  
  2         10  
14 2     2   1600 use namespace::clean;
  2         7612  
  2         8  
15             extends qw(HTML::FormBuilder);
16              
17             our $VERSION = '0.12'; ## VERSION
18              
19             has has_error_of => (
20             is => 'rw',
21             default => 0,
22             isa => sub {
23             croak('has_error_of should be 0 or 1') unless $_[0] == 0 || $_[0] == 1;
24             },
25             );
26              
27             has custom_server_side_check_of => (
28             is => 'rw',
29             isa => sub {
30             croak('custom_server_side_check_of should be code')
31             unless ref $_[0] eq 'CODE';
32             });
33              
34             has onsubmit_js_error => (
35             is => 'rw',
36             default => '',
37             );
38              
39             ########################################################################
40             # Usage : $form_validation_obj->set_input_fields(\%input);
41             # Purpose : Set input fields value based on last submit form.
42             # Returns : none
43             # Parameters : \%input: HASH ref to %input
44             # Comments : Public
45             # NOTE: This subroutine can use only if fields have same
46             # name and id.
47             # (i.e. <input id="name" name="name" type="text" />)
48             # See Also : n / a
49             ########################################################################
50             sub set_input_fields {
51 3     3 1 8 my $self = shift;
52 3         4 my $input = shift;
53              
54 3         4 for my $element_id (keys %{$input}) {
  3         10  
55 8 100       13 if ($element_id eq 'csrftoken') {
56 2         4 $self->{__input_csrftoken} = $input->{$element_id};
57             } else {
58 6         10 $self->set_field_value($element_id, $input->{$element_id});
59             }
60             }
61 3         7 return;
62             }
63              
64             sub build {
65 2     2 1 21 my $self = shift;
66 2         3 my $print_fieldset_index = shift;
67              
68 2         2 my $javascript_validation = '';
69              
70             # build the fieldset, if $print_fieldset_index is specifed then we only generate that praticular fieldset with that index
71 2         2 my @fieldsets;
72 2 50       6 if (defined $print_fieldset_index) {
73 0         0 push @fieldsets, $self->{'fieldsets'}->[$print_fieldset_index];
74             } else {
75 2         4 @fieldsets = @{$self->{'fieldsets'}};
  2         6  
76             }
77              
78             # build the form fieldset
79 2         4 foreach my $fieldset (@fieldsets) {
80 2         4 foreach my $input_field (@{$fieldset->{'fields'}}) {
  2         5  
81              
82             # build inputs javascript validation
83 7   100     18 my $validation = $self->_build_javascript_validation({'input_field' => $input_field})
84             || '';
85 7         43 $javascript_validation .= $validation;
86             }
87             }
88              
89 2         14 my $onsubmit_js_error = $self->onsubmit_js_error;
90 2 100       7 if ($onsubmit_js_error) {
91 1         4 $onsubmit_js_error = "if (bResult == false) { $onsubmit_js_error; }";
92             }
93 2         28 $self->{data}{'onsubmit'} = "return (function () { var bResult = true; $javascript_validation; $onsubmit_js_error return bResult; })();";
94              
95 2         14 return $self->SUPER::build();
96             }
97              
98             ########################################################################
99             # Usage : $form_validation_obj->validate();
100             # Purpose : Validate form input
101             # Returns : true (No ERROR) / false
102             # Parameters : none
103             # Comments : Public
104             # See Also : n / a
105             ########################################################################
106             sub validate {
107 10     10 1 62 my $self = shift;
108              
109 10 100       32 if ($self->csrftoken) {
110 2 100       4 $self->validate_csrf() or return 0;
111             }
112              
113 9         5 my @fieldsets = @{$self->{'fieldsets'}};
  9         19  
114 9         13 foreach my $fieldset (@fieldsets) {
115             INPUT_FIELD:
116 9         7 foreach my $input_field (@{$fieldset->{'fields'}}) {
  9         14  
117 49         39 my $data = $input_field->{data};
118 49 100 66     113 if ($data->{'input'} and $data->{'error'}->{'id'}) {
119 32         19 foreach my $input_element (@{$data->{'input'}}) {
  32         48  
120 40 50 33     23 if (eval { $input_element->{'input'}->can('value') }
  40         277  
121             and (not defined $self->get_field_value($input_element->{'id'})))
122             {
123 0         0 $self->set_field_error_message($input_element->{'id'}, $self->_localize('Invalid amount'));
124 0         0 next INPUT_FIELD;
125             }
126             }
127             }
128              
129             # Validate each field
130 49 50 66     127 if ( defined $data->{'validation'}
      33        
131             and $data->{'input'}
132             and $data->{'error'}->{'id'})
133             {
134             $self->_validate_field({
135             'validation' => $data->{'validation'},
136 32         83 'input_element' => $data->{'input'},
137             });
138             }
139             }
140             }
141              
142 9 50       110 if ($self->custom_server_side_check_of) {
143 0         0 $self->custom_server_side_check_of->();
144             }
145              
146 9 100       848 return ($self->get_has_error) ? 0 : 1;
147             }
148              
149             sub validate_csrf {
150 2     2 1 3 my ($self) = @_;
151              
152 2 100 50     11 if (($self->{__input_csrftoken} // '') eq $self->csrftoken) {
153 1         4 return 1;
154             }
155              
156 1         4 $self->_set_has_error();
157 1         5 return 0;
158             }
159              
160             sub is_error_found_in {
161 0     0 1 0 my $self = shift;
162 0         0 my $input_element_id = shift;
163              
164 0         0 return $self->get_field_error_message($input_element_id);
165             }
166              
167             ########################################################################
168             # Usage : $self->_set_has_error();
169             # Purpose : Set has error to indicate form has error and should be
170             # rebuild again.
171             # Returns : none
172             # Parameters : none
173             # Comments : Private
174             # See Also : n / a
175             ########################################################################
176             sub _set_has_error {
177 11     11   13 my $self = shift;
178              
179 11         175 $self->has_error_of(1);
180 11         60 return;
181             }
182              
183             ########################################################################
184             # Usage : $form_validation_obj->get_has_error();
185             # Purpose : Check if form has error
186             # Returns : 0 / 1
187             # Parameters : none
188             # Comments : Public
189             # See Also : n / a
190             ########################################################################
191             sub get_has_error {
192 11     11 1 12 my $self = shift;
193 11         126 return $self->has_error_of;
194             }
195              
196             sub set_field_error_message {
197 10     10 1 10 my $self = shift;
198 10         3 my $element_id = shift;
199 10         11 my $error_message = shift;
200              
201 10         23 $self->SUPER::set_field_error_message($element_id, $error_message);
202 10 50       19 if ($error_message) {
203 10         12 $self->_set_has_error();
204             }
205 10         9 return;
206             }
207              
208             ########################################################################
209             # Usage : $self->_build_javascript_validation
210             # ({
211             # 'validation' => $input_field->{'validation'},
212             # 'input_element' => $input_field->{'input'},
213             # 'error_element_id' => $input_field->{'error'}->{'id'},
214             # });
215             # Purpose : Create javascript validation code.
216             # Returns : text (Javascript code)
217             # Parameters : $arg_ref:
218             # {
219             # 'validation': ARRAY ref to $input_field->{'validation'}
220             # 'input_element': HASH ref to input element
221             # 'error_element_id': error element id
222             # }
223             # Comments : Private
224             # See Also : build()
225             ########################################################################
226             sub _build_javascript_validation {
227 7     7   3 my $self = shift;
228 7         8 my $arg_ref = shift;
229 7         6 my $javascript;
230              
231 7         6 my $input_field = $arg_ref->{'input_field'};
232              
233 7         8 my $data = $input_field->{data};
234 7 100 66     40 if ( defined $data->{'validation'}
    100 33        
      66        
235             and $data->{'input'}
236             and $data->{'error'}->{'id'})
237             {
238              
239 4         4 my @validations = @{$data->{'validation'}};
  4         7  
240 4         5 my $input_element = $data->{'input'};
241 4         4 my $error_element_id = $data->{'error'}->{'id'};
242              
243 4         3 my $input_element_id;
244             my $input_element_conditions;
245              
246 4         3 foreach my $input_field (@{$input_element}) {
  4         6  
247 5 50       9 if (defined $input_field->{'id'}) {
248 5         3 $input_element_id = $input_field->{'id'};
249 5         10 $javascript .= "var input_element_$input_element_id = document.getElementById('$input_element_id');";
250 5         8 $input_element_conditions .= "input_element_$input_element_id && ";
251             }
252             }
253              
254             $javascript .=
255 4         13 "var error_element_$error_element_id = document.getElementById('$error_element_id');"
256             . "document.getElementById('$error_element_id').innerHTML = '';"
257             . "if ($input_element_conditions error_element_$error_element_id) {"
258             . 'var regexp;'
259             . 'var bInputResult = true;';
260              
261 4         5 foreach my $validation (@validations) {
262             next
263 10 50       34 unless ($validation->{'type'} =~ /(?:regexp|min_amount|max_amount|checkbox_checked|custom)/);
264 10         13 $javascript .= $self->_build_single_javascript_validation($validation, $input_element_id, $error_element_id);
265             }
266              
267 4         6 $javascript .= 'if (!bInputResult)' . '{' . 'bResult = bInputResult;' . '}' . '}';
268              
269             }
270              
271             # get the general error field (contain only error message without input)
272             elsif ( defined $data->{'error'}
273             and defined $data->{'error'}->{'id'})
274             {
275 1         2 my $error_id = $data->{'error'}->{'id'};
276              
277 1         6 $javascript = "var error_element_$error_id = document.getElementById('$error_id');" . "document.getElementById('$error_id').innerHTML = '';";
278             }
279              
280 7         20 return $javascript;
281             }
282              
283             ########################################################################
284             # Usage : $self->_build_single_validation
285             # ($validation, $input_element_id, $error_element_id);
286             # Purpose : Create javascript validation code for a validation.
287             # Returns : text (Javascript code)
288             # Parameters : validation, input_element_id, error_element_id
289             # Comments : Private
290             # See Also : build()
291             ########################################################################
292             sub _build_single_javascript_validation {
293 10     10   9 my $self = shift;
294 10         6 my $validation = shift;
295 10         7 my $input_element_id = shift;
296 10         7 my $error_element_id = shift;
297              
298 10         8 my $javascript = '';
299 10         13 my $err_msg = _encode_text($validation->{'err_msg'});
300              
301             # if the id define in the validation hash, meaning input has more than 1 fields, the validation is validated against the id
302 10 100 66     24 if ($validation->{'id'} and length $validation->{'id'} > 0) {
303 3         4 $input_element_id = $validation->{'id'};
304             }
305              
306 10 50       14 my $error_if_true = $validation->{error_if_true} ? '' : '!';
307 10         9 my $test = '';
308 10 100       25 if ($validation->{'type'} eq 'regexp') {
    100          
    100          
    50          
309 5         5 my $regexp = $validation->{'regexp'};
310 5         19 $regexp =~ s/(\\|')/\\$1/g;
311             $javascript .=
312 5 100       72 ($validation->{'case_insensitive'})
313             ? "regexp = new RegExp('$regexp', 'i');"
314             : "regexp = new RegExp('$regexp');";
315              
316 5         10 $test = qq[${error_if_true}regexp.test(input_element_$input_element_id.value)];
317             }
318              
319             # Min Max amount checking
320             elsif ($validation->{'type'} =~ /^(min|max)_amount$/) {
321 3 100       6 my $op = $1 eq 'min' ? '<' : '>';
322 3         8 $test = qq[input_element_$input_element_id.value $op $validation->{amount}];
323             }
324              
325             # checkbox checked checking
326             elsif ($validation->{'type'} eq 'checkbox_checked') {
327 1         2 $test = qq[input_element_$input_element_id.checked === false];
328             }
329              
330             # Custom checking
331             elsif ($validation->{'type'} eq 'custom') {
332 1         2 $test = qq[${error_if_true}$validation->{function}];
333             }
334 10         20 $javascript .= qq[if (bInputResult && $test){error_element_$error_element_id.innerHTML = decodeURIComponent('$err_msg');bInputResult = false;}];
335              
336 10         22 return $javascript;
337             }
338              
339             ########################################################################
340             # Usage : $form_validation_obj->set_server_side_checks($custom_server_side_sub_ref);
341             # Purpose : Set custom server side validation
342             # Returns : none
343             # Parameters : $server_side_check_sub_ref: sub ref
344             # Comments : Public
345             # See Also : n / a
346             ########################################################################
347             sub set_server_side_checks {
348 0     0 1 0 my $self = shift;
349 0         0 my $server_side_check_sub_ref = shift;
350 0         0 $self->custom_server_side_check_of($server_side_check_sub_ref);
351 0         0 return;
352             }
353              
354             ########################################################################
355             # Usage : $self->_validate_field({
356             # 'validation' => $input_field->{'validation'},
357             # 'input_element' => $input_field->{'input'},
358             # });
359             # Purpose : Server side validation base on type of validation
360             # Returns : none
361             # Parameters : $arg_ref:
362             # {
363             # 'validation': ARRAY ref to $input_field->{'validation'}
364             # 'input_element': HASH ref to input element
365             # }
366             # Comments : Private
367             # See Also : validate()
368             ########################################################################
369             sub _validate_field {
370 32     32   26 my $self = shift;
371 32         19 my $arg_ref = shift;
372              
373 32         27 my @validations = @{$arg_ref->{'validation'}};
  32         51  
374 32         25 my $input_element = $arg_ref->{'input_element'};
375 32         22 my $input_element_id;
376             my $field_value;
377              
378 32         31 foreach my $validation (@validations) {
379 65 100 66     317 if ( $validation->{'type'}
380             and $validation->{'type'} =~ /(?:regexp|min_amount|max_amount|checkbox_checked)/)
381             {
382              
383             # The input_element must be an array. so if validation no 'id', then we use the first element's id
384             # because the array should be just one element.
385 61   66     105 $input_element_id = $validation->{id} || $input_element->[0]{id};
386              
387             # Check with whitespace trimmed from both ends to make sure that it's reasonable.
388 61   100     118 $field_value = $self->get_field_value($input_element_id) || '';
389             # $field_value =~ s/^\s+|\s+$//g;
390 61         83 $field_value =~ s/\A\s+//;
391 61         62 $field_value =~ s/\s+\z//;
392              
393 61 100 100     224 if ($validation->{'type'} eq 'regexp') {
    100 100        
    100 66        
      100        
394             my $regexp =
395 37 100       213 ($validation->{'case_insensitive'})
396             ? qr{$validation->{'regexp'}}i
397             : qr{$validation->{'regexp'}};
398 37 100 33     282 if ($validation->{error_if_true} && $field_value =~ $regexp
      66        
      66        
399             || !$validation->{error_if_true} && $field_value !~ $regexp)
400             {
401 6         14 $self->set_field_error_message($input_element_id, $validation->{'err_msg'});
402 6         15 return 0;
403             }
404             }
405              
406             # Min amount checking
407             elsif ($validation->{'type'} eq 'min_amount' && $field_value < $validation->{'amount'}
408             || $validation->{'type'} eq 'max_amount' && $field_value > $validation->{'amount'})
409             {
410 3         9 $self->set_field_error_message($input_element_id, $validation->{'err_msg'});
411 3         8 return 0;
412             }
413              
414             elsif ($validation->{'type'} eq 'checkbox_checked' && !$field_value) {
415 1         3 $self->set_field_error_message($input_element_id, $validation->{'err_msg'});
416 1         2 return 0;
417             }
418             }
419             }
420 22         50 return 1;
421             }
422              
423             sub _encode_text {
424 10     10   6 my $text = shift;
425              
426 10 50       13 return unless ($text);
427              
428             # javascript cant load html entities
429 10         35 $text = Encode::encode("UTF-8", HTML::Entities::decode_entities($text));
430 10         340 $text = URI::Escape::uri_escape($text);
431 10         140 $text =~ s/(['"\\])/\\$1/g;
432              
433 10         11 return $text;
434             }
435              
436             1;
437              
438             =head1 NAME
439              
440             HTML::FormBuilder::Validation - An extension of the Form object, to allow for javascript-side validation of inputs
441             and also server-side validation after the form is POSTed
442              
443             =head1 SYNOPSIS
444              
445             First, create the Form object. The keys in the HASH reference is the attributes
446             of the form.
447              
448             # Form attributes require to create new form object
449             my $form_attributes =
450             {
451             'name' => 'name_test_form',
452             'id' => 'id_test_form',
453             'method' => 'post',
454             'action' => "http://www.domain.com/contact.cgi",
455             'class' => 'formObject',
456             };
457             my $form_obj = new HTML::FormBuilder::Validation(data => $form_attributes);
458              
459             my $fieldset = $form_obj->add_fieldset({});
460              
461              
462             =head2 Create the input fields with validation
463              
464             This is quite similar to creating input field in Form object. Likewise you can
465             add validation to HASH reference as the attribute of input field.
466              
467             Below you can see the sample included four types of validation:
468              
469             1. regexp: Just write the reqular expression that should be apply to the value
470              
471             2. min_amount: Needs both type=min_amount and also minimum amount that declared
472             in amount
473              
474             3. max_amount: Just like min_amount
475              
476             4. checkbox_checked: Ensure checkbox is checked by user
477              
478             5. custom: Just the javascript function call with parameters should be given to.
479             It only specifies client side validation.
480              
481             my $input_field_amount =
482             {
483             'label' =>
484             {
485             'text' => 'Amount',
486             'for' => 'amount',
487             'optional' => '0',
488             },
489             'input' =>
490             {
491             'type' => 'text',
492             'id' => 'amount',
493             'name' => 'amount',
494             'maxlength' => 40,
495             'value' => '',
496             },
497             'error' =>
498             {
499             'text' => '',
500             'id' => 'error_amount',
501             'class' => 'errorfield',
502             },
503             'validation' =>
504             [
505             {
506             'type' => 'regexp',
507             'regexp' => '\w+',
508             'err_msg' => 'Not empty',
509             },
510             {
511             'type' => 'regexp',
512             'regexp' => '\d+',
513             'err_msg' => 'Must be digit',
514             },
515             {
516             'type' => 'min_amount',
517             'amount' => 50,
518             'err_msg' => 'Too little',
519             },
520             {
521             'type' => 'max_amount',
522             'amount' => 500,
523             'err_msg' => 'Too much',
524             },
525             {
526             'type' => 'custom',
527             'function' => 'custom_amount_validation()',
528             'err_msg' => 'It is not good',
529             },
530             ],
531             };
532              
533             my $terms_and_condition_checkbox =
534             {
535             'label' =>
536             {
537             'text' => 'I have read & agree to the terms & condition of the site',
538             'for' => 'tnc',
539             },
540             'input' =>
541             {
542             'type' => 'checkbox',
543             'id' => 'tnc',
544             'name' => 'tnc',
545             'value' => '1', # optional
546             },
547             'error' =>
548             {
549             'id' => 'error_tnc',
550             'class' => 'errorfield',
551             },
552             'validation' =>
553             [
554             {
555             'type' => 'checkbox_checked',
556             'err_msg' => 'In order to proceed, you need to agree to the terms & condition',
557             },
558             ],
559             };
560              
561             Below is another example with two different fields. In this matter we need to
562             indicate the id of each field in validation attributes.
563              
564             my $select_curr =
565             {
566             'id' => 'select_text_curr',
567             'name' => 'select_text_curr',
568             'type' => 'select',
569             'options' => '<option value=""></option><option value="USD">USD</option><option value="EUR">EUR</option>',
570             };
571             my $input_amount =
572             {
573             'id' => 'select_text_amount',
574             'name' => 'select_text_amount',
575             'type' => 'text',
576             'value' => ''
577             };
578             my $input_field_select_text =
579             {
580             'label' =>
581             {
582             'text' => 'select_text',
583             'for' => 'select_text',
584             },
585             'input' => [ $select_curr, $input_amount ],
586             'error' =>
587             {
588             'text' => '',
589             'id' => 'error_select_text',
590             'class' => 'errorfield',
591             },
592             'validation' =>
593             [
594             {
595             'type' => 'regexp',
596             'id' => 'select_text_curr',
597             'regexp' => '\w+',
598             'err_msg' => 'Must be select',
599             },
600             {
601             'type' => 'regexp',
602             'id' => 'select_text_amount',
603             'regexp' => '\d+',
604             'err_msg' => 'Must be digits',
605             },
606             {
607             'type' => 'min_amount',
608             'id' => 'select_text_amount',
609             'amount' => 50,
610             'err_msg' => 'Too little',
611             },
612             ],
613             };
614              
615             my $general_error_field =
616             {
617             'error' =>
618             {
619             'text' => '',
620             'id' => 'error_general',
621             'class' => 'errorfield'
622             },
623             };
624              
625             =head2 Adding input fields to form object
626              
627             Here is just add fields to the form object like before.
628              
629             $form_obj->add_field($fieldset_index, $general_error_field);
630             $form_obj->add_field($fieldset_index, $input_field_amount);
631             $form_obj->add_field($fieldset_index, $input_field_select_text);
632              
633             =head2 Define Javascript code to be run, during onsubmit input validation error
634              
635             This javascript code will be run before onsubmit return false
636              
637             $form_obj->onsubmit_js_error("\$('#residence').attr('disabled', true);");
638             $form_obj->onsubmit_js_error('onsubmit_error_disable_fields()');
639              
640             =head2 Custom javascript validation
641              
642             Custom javascript validation should be defined and assigned to the form object.
643             Note that, the name and parameters should be the same as the way you indicate
644             function call in validation attributes.
645              
646             You can see a sample below:
647              
648             my $custom_javascript = qq~
649             function custom_amount_validation()
650             {
651             var input_amount = document.getElementById('amount');
652             if (input_amount.value == 100)
653             {
654             return false;
655             }
656             return true;
657             }~;
658              
659             =head2 Custom server side validation
660              
661             The custom server side validation is quite similar to javascript. A reference to
662             a subrotine should be pass to form object.
663              
664             my $custom_server_side_sub_ref = sub {
665             if ($form_obj->get_field_value('name') eq 'felix')
666             {
667             $form_obj->set_field_error_message('name', 'felix is not allow to use this page');
668             $form_obj->set_field_error_message('error_general', 'There is an error !!!');
669             }
670             };
671              
672             $form_obj->set_server_side_checks($custom_server_side_sub_ref);
673              
674             =head2 Use form object in cgi files
675              
676             Somewhere in cgi files you can just print the result of build().
677              
678             print $form_obj->build();
679              
680             In submit you need to fill form values, use set_input_fields(\%input) and pass
681             %input HASH and then show what ever you want in result of validation. Just like
682             below:
683              
684             if (not $form_obj->validate())
685             {
686             print '<h1>Test Form</h1>';
687             print $form_obj->build();
688             }
689             else
690             {
691             print '<h1>Success !!!</h1>';
692             }
693              
694             code_exit();
695              
696             =head1 Attributes
697              
698             =head2 has_error_of
699              
700             The tag that error happened during validation
701              
702             =head2 custom_server_side_check_of
703              
704             The custom server side subroutine that will be run on server side.
705              
706             =head2 onsubmit_js_error
707              
708             javasript code to run during onsubmit error by javasript validation
709              
710             =head1 METHODS
711              
712             =head2 set_input_fields
713              
714             $form_validation_obj->set_input_fields({username => $username});
715              
716             assign value to the input fields
717              
718             =head2 validate
719              
720             $form_validation_obj->validate();
721              
722             validate form input and return true or false
723              
724             =head2 is_error_found_in
725              
726             $form_validation_obj->is_error_found_in($input_element_id);
727              
728             check the erorr is founded in the input element or not
729              
730             =head2 get_has_error
731              
732             =head2 set_field_error_message
733              
734             =head2 set_server_side_checks
735              
736             =head2 validate_csrf
737              
738             =head1 CROSS SITE REQUEST FORGERY PROTECTION
739              
740             for plain CGI or other framework, read Dancer example below.
741              
742             =head2 CSRF and Dancer
743              
744             =over 4
745              
746             =item * create form HTML and store csrftoken in session
747              
748             my $form = HTML::FormBuilder::Validation->new(data => $form_attributes, csrftoken => 1);
749             ...
750             my $html = $form->build;
751              
752             # save csrf token in session or cookie
753             session(__csrftoken => $form->csrftoken);
754              
755             =item * validate csrftoken on form submit
756              
757             my $csrftoken = session('__csrftoken');
758             my $form = HTML::FormBuilder::Validation->new(data => $form_attributes, csrftoken => $csrftoken);
759             $form->validate_csrf() or die 'CSRF failed.';
760             # or call
761             if ( $form->validate() ) { # it calls validate_csrf inside
762             # Yap! it's ok
763             } else {
764             # NOTE we do not have error for csrf on form HTML build
765             # show form again with $form->build
766             }
767              
768             =back
769              
770             =head2 CSRF and Mojolicious
771              
772             if you're using Mojolicious and have DefaultHelpers plugin enabled, it's simple to add csrftoken in Validation->new as below:
773              
774             my $form = HTML::FormBuilder::Validation->new(data => $form_attributes, csrftoken => $c->csrf_token);
775              
776             Mojolicious $c->csrf_token will handle the session part for you.
777              
778             =head1 AUTHOR
779              
780             Chylli L<mailto:chylli@binary.com>
781              
782             =head1 CONTRIBUTOR
783              
784             Fayland Lam L<mailto:fayland@binary.com>
785              
786             Tee Shuwn Yuan L<mailto:shuwnyuan@binary.com>
787              
788             =head1 COPYRIGHT AND LICENSE
789              
790             =cut