File Coverage

blib/lib/Tags/HTML/ChangePassword.pm
Criterion Covered Total %
statement 88 88 100.0
branch 16 16 100.0
condition n/a
subroutine 18 18 100.0
pod 1 1 100.0
total 123 123 100.0


line stmt bran cond sub pod time code
1             package Tags::HTML::ChangePassword;
2              
3 8     8   780722 use base qw(Tags::HTML);
  8         16  
  8         4549  
4 8     8   54391 use strict;
  8         17  
  8         204  
5 8     8   42 use warnings;
  8         27  
  8         505  
6              
7 8     8   2291 use Class::Utils qw(set_params split_params);
  8         28  
  8         448  
8 8     8   42 use Error::Pure qw(err);
  8         17  
  8         365  
9 8     8   44 use List::Util qw(none);
  8         14  
  8         532  
10 8     8   4795 use Mo::utils::CSS 0.07 qw(check_css_unit);
  8         85873  
  8         274  
11 8     8   3502 use Mo::utils::Language 0.05 qw(check_language_639_2);
  8         1643965  
  8         304  
12 8     8   515 use Readonly;
  8         18  
  8         398  
13 8     8   4813 use Tags::HTML::Messages;
  8         22025  
  8         12652  
14              
15             Readonly::Array our @FORM_METHODS => qw(post get);
16             Readonly::Array our @TEXT_KEYS => qw(change_password old_password_label password1_label password2_label submit);
17              
18             our $VERSION = 0.08;
19              
20             # Constructor.
21             sub new {
22 25     25 1 2113931 my ($class, @params) = @_;
23              
24             # Create object.
25 25         280 my ($object_params_ar, $other_params_ar) = split_params(
26             ['css_change_password', 'form_method', 'lang', 'link', 'text', 'width'], @params);
27 25         992 my $self = $class->SUPER::new(@{$other_params_ar});
  25         217  
28              
29             # CSS style for change password box.
30 22         1064 $self->{'css_change_password'} = 'form-change-password';
31              
32             # Form method.
33 22         133 $self->{'form_method'} = 'post';
34              
35             # Language.
36 22         107 $self->{'lang'} = 'eng';
37              
38             # Language texts.
39 22         294 $self->{'text'} = {
40             'eng' => {
41             'change_password' => 'Change password',
42             'old_password_label' => 'Old password',
43             'password1_label' => 'New password',
44             'password2_label' => 'Confirm new password',
45             'submit' => 'Save Changes',
46             },
47             };
48              
49             # Login box width.
50 22         120 $self->{'width'} = '300px';
51              
52             # Process params.
53 22         80 set_params($self, @{$object_params_ar});
  22         108  
54              
55             # Check form method.
56 22 100   23   548 if (none { $self->{'form_method'} eq $_ } @FORM_METHODS) {
  23         344  
57 1         17 err "Parameter 'form_method' has bad value.";
58             }
59              
60             # Check lang.
61 21         457 check_language_639_2($self, 'lang');
62              
63             # Check text.
64 20 100       264224 if (! defined $self->{'text'}) {
65 1         15 err "Parameter 'text' is required.";
66             }
67 19 100       131 if (ref $self->{'text'} ne 'HASH') {
68 1         15 err "Parameter 'text' must be a hash with language texts.";
69             }
70 18 100       141 if (! exists $self->{'text'}->{$self->{'lang'}}) {
71 1         19 err "Texts for language '$self->{'lang'}' doesn't exist.";
72             }
73 17 100       227 if (@TEXT_KEYS != keys %{$self->{'text'}->{$self->{'lang'}}}) {
  17         221  
74 1         11 err "Number of texts isn't same as expected.";
75             }
76 16         68 foreach my $req_text_key (@TEXT_KEYS) {
77 76 100       1040 if (! exists $self->{'text'}->{$self->{'lang'}}->{$req_text_key}) {
78 1         28 err "Text for lang '$self->{'lang'}' and key '$req_text_key' doesn't exist.";
79             }
80             }
81              
82 15         261 check_css_unit($self, 'width');
83              
84             $self->{'_tags_messages'} = Tags::HTML::Messages->new(
85             'css' => $self->{'css'},
86             'flag_no_messages' => 0,
87 12         1434 'tags' => $self->{'tags'},
88             );
89              
90             # Object.
91 12         141200 return $self;
92             }
93              
94             sub _cleanup {
95 1     1   22 my $self = shift;
96              
97 1         4 delete $self->{'_messages'};
98 1         3 delete $self->{'_message_types'};
99              
100 1         4 return;
101             }
102              
103             sub _prepare {
104 4     4   118 my ($self, $message_types_hr) = @_;
105              
106 4 100       30 if (! defined $message_types_hr) {
107 1         13 err 'No message types to prepare.';
108             }
109              
110 3         14 $self->{'_message_types'} = $message_types_hr;
111              
112 3         10 return;
113             }
114              
115             sub _init {
116 3     3   471 my ($self, $messages_ar) = @_;
117              
118 3 100       13 if (! defined $messages_ar) {
119 1         6 err 'No messages to init.';
120             }
121              
122 2         7 $self->{'_messages'} = $messages_ar;
123              
124 2         5 return;
125             }
126              
127             # Process 'Tags'.
128             sub _process {
129 1     1   19 my $self = shift;
130              
131 1         2 my $old_password_id = 'old_password';
132 1         3 my $password1_id = 'password1';
133 1         5 my $password2_id = 'password2';
134              
135             # Main content.
136             $self->{'tags'}->put(
137             ['b', 'form'],
138             ['a', 'class', $self->{'css_change_password'}],
139 1         15 ['a', 'method', $self->{'form_method'}],
140              
141             ['b', 'fieldset'],
142             ['b', 'legend'],
143             ['d', $self->_text('change_password')],
144             ['e', 'legend'],
145              
146             ['b', 'p'],
147             ['b', 'label'],
148             ['a', 'for', $old_password_id],
149             ['d', $self->_text('old_password_label')],
150             ['e', 'label'],
151             ['b', 'input'],
152             ['a', 'type', 'password'],
153             ['a', 'name', $old_password_id],
154             ['a', 'id', $old_password_id],
155             ['a', 'autofocus', 'autofocus'],
156             ['e', 'input'],
157             ['e', 'p'],
158              
159             ['b', 'p'],
160             ['b', 'label'],
161             ['a', 'for', $password1_id],
162             ['d', $self->_text('password1_label')],
163             ['e', 'label'],
164             ['b', 'input'],
165             ['a', 'type', 'password'],
166             ['a', 'name', $password1_id],
167             ['a', 'id', $password1_id],
168             ['e', 'input'],
169             ['e', 'p'],
170              
171             ['b', 'p'],
172             ['b', 'label'],
173             ['a', 'for', $password2_id],
174             ['d', $self->_text('password2_label')],
175             ['e', 'label'],
176             ['b', 'input'],
177             ['a', 'type', 'password'],
178             ['a', 'name', $password2_id],
179             ['a', 'id', $password2_id],
180             ['e', 'input'],
181             ['e', 'p'],
182              
183             ['b', 'p'],
184             ['b', 'button'],
185             ['a', 'type', 'submit'],
186             ['a', 'name', 'change_password'],
187             ['a', 'value', 'change_password'],
188             ['d', $self->_text('submit')],
189             ['e', 'button'],
190             ['e', 'p'],
191              
192             ['e', 'fieldset'],
193             );
194              
195 1         1782 $self->{'_tags_messages'}->process($self->{'_messages'});
196              
197 1         349 $self->{'tags'}->put(
198             ['e', 'form'],
199             );
200              
201 1         45 return;
202             }
203              
204             # Process 'CSS::Struct'.
205             sub _process_css {
206 3     3   99 my $self = shift;
207              
208             $self->{'css'}->put(
209             ['s', '.'.$self->{'css_change_password'}],
210             ['d', 'width', $self->{'width'}],
211             ['d', 'background-color', '#f2f2f2'],
212             ['d', 'padding', '20px'],
213             ['d', 'border-radius', '5px'],
214             ['d', 'box-shadow', '0 0 10px rgba(0, 0, 0, 0.2)'],
215             ['e'],
216              
217             ['s', '.'.$self->{'css_change_password'}.' fieldset'],
218             ['d', 'border', 'none'],
219             ['d', 'padding', 0],
220             ['d', 'margin-bottom', '20px'],
221             ['e'],
222              
223             ['s', '.'.$self->{'css_change_password'}.' legend'],
224             ['d', 'font-weight', 'bold'],
225             ['d', 'margin-bottom', '10px'],
226             ['e'],
227              
228             ['s', '.'.$self->{'css_change_password'}.' p'],
229             ['d', 'margin', 0],
230             ['d', 'padding', '10px 0'],
231             ['e'],
232              
233             ['s', '.'.$self->{'css_change_password'}.' label'],
234             ['d', 'display', 'block'],
235             ['d', 'font-weight', 'bold'],
236             ['d', 'margin-bottom', '5px'],
237             ['e'],
238              
239             ['s', '.'.$self->{'css_change_password'}.' input[type="text"]'],
240             ['s', '.'.$self->{'css_change_password'}.' input[type="password"]'],
241             ['d', 'width', '100%'],
242             ['d', 'padding', '8px'],
243             ['d', 'border', '1px solid #ccc'],
244             ['d', 'border-radius', '3px'],
245             ['e'],
246              
247             ['s', '.'.$self->{'css_change_password'}.' button[type="submit"]'],
248             ['d', 'width', '100%'],
249             ['d', 'padding', '10px'],
250             ['d', 'background-color', '#4CAF50'],
251             ['d', 'color', '#fff'],
252             ['d', 'border', 'none'],
253             ['d', 'border-radius', '3px'],
254             ['d', 'cursor', 'pointer'],
255             ['e'],
256              
257             ['s', '.'.$self->{'css_change_password'}.' button[type="submit"]:hover'],
258             ['d', 'background-color', '#45a049'],
259             ['e'],
260              
261 3         290 ['s', '.'.$self->{'css_change_password'}.' .messages'],
262             ['d', 'text-align', 'center'],
263             ['e'],
264             );
265              
266 3         4128 $self->{'_tags_messages'}->process_css($self->{'_message_types'});
267              
268 3         297 return;
269             }
270              
271             sub _text {
272 5     5   12 my ($self, $key) = @_;
273              
274 5         87 return $self->{'text'}->{$self->{'lang'}}->{$key};
275             }
276              
277              
278             1;
279              
280             __END__
281              
282             =pod
283              
284             =encoding utf8
285              
286             =head1 NAME
287              
288             Tags::HTML::ChangePassword - Tags helper for change password.
289              
290             =head1 SYNOPSIS
291              
292             use Tags::HTML::ChangePassword;
293              
294             my $obj = Tags::HTML::ChangePassword->new(%params);
295             $obj->cleanup;
296             $obj->init($messages_ar);
297             $obj->prepare($message_types_hr);
298             $obj->process;
299             $obj->process_css;
300              
301             =head1 DESCRIPTION
302              
303             L<Tags> helper to print HTML page of form for changing of password.
304              
305             The page contains optional logo, fields for old and new password and button to
306             process workflow. All texts on the page are translatable.
307              
308             This helper is created for usage in L<Plack::App::ChangePassword> plack
309             application which is full application for changing of password.
310              
311             =head1 METHODS
312              
313             =head2 C<new>
314              
315             my $obj = Tags::HTML::ChangePassword->new(%params);
316              
317             Constructor.
318              
319             =over 8
320              
321             =item * C<css>
322              
323             L<CSS::Struct::Output> object for L<process_css> processing.
324              
325             Default value is undef.
326              
327             =item * C<css_change_password>
328              
329             CSS class for form.
330              
331             Default value is 'form-change-password'.
332              
333             =item * C<form_method>
334              
335             Form method.
336              
337             Possible values are 'post' and 'get'.
338              
339             Default value is 'post'.
340              
341             =item * C<lang>
342              
343             Language in ISO 639-3 code.
344              
345             Default value is 'eng'.
346              
347             =item * C<tags>
348              
349             L<Tags::Output> object.
350              
351             Default value is undef.
352              
353             =item * C<text>
354              
355             Hash reference with keys defined language in ISO 639-2 code and value with hash
356             reference with texts.
357              
358             Required keys are 'change_password', 'old_password_label', 'password1_label', 'password2_label' and 'submit'.
359              
360             Default value is:
361              
362             {
363             'eng' => {
364             'change_password' => 'Change password',
365             'old_password_label' => 'Old password',
366             'password1_label' => 'New password',
367             'password2_label' => 'Confirm new password',
368             'submit' => 'Save Changes',
369             },
370             }
371              
372             =back
373              
374             Returns instance of object.
375              
376             =head2 C<cleanup>
377              
378             $obj->cleanup;
379              
380             Process cleanup after page run.
381              
382             Returns undef.
383              
384             =head2 C<init>
385              
386             $obj->init($messages_ar);
387              
388             Initialize object.
389             Variable C<$message_ar> is reference to array with L<Data::Message::Simple>
390             instances.
391              
392             Returns undef.
393              
394             =head2 C<prepare>
395              
396             $obj->prepare($message_types_hr);
397              
398             Prepare object.
399             Variable C<$message_types_hr> is reference to hash with message type keys and
400             CSS color as value. Message types are defined in L<Data::Message::Simple>.
401              
402             Returns undef.
403              
404             =head2 C<process>
405              
406             $obj->process;
407              
408             Process L<Tags> structure for change password form.
409              
410             Returns undef.
411              
412             =head2 C<process_css>
413              
414             $obj->process_css;
415              
416             Process L<CSS::Struct> structure for change password form.
417              
418             Returns undef.
419              
420             =head1 ERRORS
421              
422             new():
423             From Class::Utils::set_params():
424             Unknown parameter '%s'.
425             From Mo::utils::CSS::check_css_unit():
426             Parameter 'width' doesn't contain unit number.
427             Value: %s
428             Parameter 'width' doesn't contain unit name.
429             Value: %s
430             Parameter 'width' contain bad unit.
431             Unit: %s
432             Value: %s
433             From Mo::utils::Language::check_language_639_2():
434             Parameter 'lang' doesn't contain valid ISO 639-2 code.
435             Codeset: %s
436             Value: %s
437             From Tags::HTML::new():
438             Parameter 'css' must be a 'CSS::Struct::Output::*' class.
439             Parameter 'tags' must be a 'Tags::Output::*' class.
440             Text for lang '%s' and key '%s' doesn't exist.
441              
442             init():
443             No messages to init.
444              
445             prepare():
446             No message types to prepare.
447              
448             process():
449             From Tags::HTML::process():
450             Parameter 'tags' isn't defined.
451             Bad message data object.
452              
453             process_css():
454             From Tags::HTML::process_css():
455             Parameter 'css' isn't defined.
456             Message types must be a hash reference.
457              
458             =head1 EXAMPLE1
459              
460             =for comment filename=print_block_html_and_css.pl
461              
462             use strict;
463             use warnings;
464              
465             use CSS::Struct::Output::Indent;
466             use Tags::HTML::ChangePassword;
467             use Tags::Output::Indent;
468              
469             # Object.
470             my $css = CSS::Struct::Output::Indent->new;
471             my $tags = Tags::Output::Indent->new;
472             my $obj = Tags::HTML::ChangePassword->new(
473             'css' => $css,
474             'tags' => $tags,
475             );
476              
477             # Process change password form.
478             $obj->process_css;
479             $obj->process;
480              
481             # Print out.
482             print "CSS\n";
483             print $css->flush."\n\n";
484             print "HTML\n";
485             print $tags->flush."\n";
486              
487             # Output:
488             # CSS
489             # .form-change-password {
490             # width: 300px;
491             # background-color: #f2f2f2;
492             # padding: 20px;
493             # border-radius: 5px;
494             # box-shadow: 0 0 10px rgba(0, 0, 0, 0.2);
495             # }
496             # .form-change-password fieldset {
497             # border: none;
498             # padding: 0;
499             # margin-bottom: 20px;
500             # }
501             # .form-change-password legend {
502             # font-weight: bold;
503             # margin-bottom: 10px;
504             # }
505             # .form-change-password p {
506             # margin: 0;
507             # padding: 10px 0;
508             # }
509             # .form-change-password label {
510             # display: block;
511             # font-weight: bold;
512             # margin-bottom: 5px;
513             # }
514             # .form-change-password input[type="text"], .form-change-password input[type="password"] {
515             # width: 100%;
516             # padding: 8px;
517             # border: 1px solid #ccc;
518             # border-radius: 3px;
519             # }
520             # .form-change-password button[type="submit"] {
521             # width: 100%;
522             # padding: 10px;
523             # background-color: #4CAF50;
524             # color: #fff;
525             # border: none;
526             # border-radius: 3px;
527             # cursor: pointer;
528             # }
529             # .form-change-password button[type="submit"]:hover {
530             # background-color: #45a049;
531             # }
532             # .form-change-password .messages {
533             # text-align: center;
534             # }
535             #
536             # HTML
537             # <form class="form-change-password" method="post">
538             # <fieldset>
539             # <legend>
540             # Change password
541             # </legend>
542             # <p>
543             # <label for="old_password">
544             # Old password
545             # </label>
546             # <input type="password" name="old_password" id="old_password" autofocus=
547             # "autofocus">
548             # </input>
549             # </p>
550             # <p>
551             # <label for="password1">
552             # New password
553             # </label>
554             # <input type="password" name="password1" id="password1">
555             # </input>
556             # </p>
557             # <p>
558             # <label for="password2">
559             # Confirm new password
560             # </label>
561             # <input type="password" name="password2" id="password2">
562             # </input>
563             # </p>
564             # <p>
565             # <button type="submit" name="change_password" value="change_password">
566             # Save Changes
567             # </button>
568             # </p>
569             # </fieldset>
570             # </form>
571              
572             =head1 EXAMPLE2
573              
574             =for comment filename=plack_app_change_password.pl
575              
576             use strict;
577             use warnings;
578            
579             use CSS::Struct::Output::Indent;
580             use Plack::App::Tags::HTML;
581             use Plack::Runner;
582             use Tags::HTML::ChangePassword;
583             use Tags::Output::Indent;
584            
585             my $css = CSS::Struct::Output::Indent->new;
586             my $tags = Tags::Output::Indent->new(
587             'xml' => 1,
588             'preserved' => ['style'],
589             );
590             my $change_password = Tags::HTML::ChangePassword->new(
591             'css' => $css,
592             'tags' => $tags,
593             );
594             $change_password->process_css;
595             my $app = Plack::App::Tags::HTML->new(
596             'component' => 'Tags::HTML::Container',
597             'data' => [sub {
598             my $self = shift;
599             $change_password->process;
600             return;
601             }],
602             'css' => $css,
603             'tags' => $tags,
604             )->to_app;
605             Plack::Runner->new->run($app);
606              
607             # Output screenshot is in images/ directory.
608              
609             =begin html
610              
611             <a href="https://raw.githubusercontent.com/michal-josef-spacek/Tags-HTML-ChangePassword/master/images/plack_app_change_password.png">
612             <img src="https://raw.githubusercontent.com/michal-josef-spacek/Tags-HTML-ChangePassword/master/images/plack_app_change_password.png" alt="Web app example" width="300px" height="300px" />
613             </a>
614              
615             =end html
616              
617             =head1 DEPENDENCIES
618              
619             L<Class::Utils>,
620             L<Error::Pure>,
621             L<List::Util>,
622             L<Mo::utils::CSS>,
623             L<Mo::utils::Language>,
624             L<Readonly>,
625             L<Tags::HTML>,
626             L<Tags::HTML::Messages>.
627              
628             =head1 SEE ALSO
629              
630             =over
631              
632             =item L<Tags::HTML::Login::Access>
633              
634             Tags helper for login access.
635              
636             =item L<Tags::HTML::Login::Button>
637              
638             Tags helper for login button.
639              
640             =item L<Tags::HTML::Login::Register>
641              
642             Tags helper for login register.
643              
644             =back
645              
646             =head1 REPOSITORY
647              
648             L<https://github.com/michal-josef-spacek/Tags-HTML-ChangePassword>
649              
650             =head1 AUTHOR
651              
652             Michal Josef Špaček L<mailto:skim@cpan.org>
653              
654             L<http://skim.cz>
655              
656             =head1 LICENSE AND COPYRIGHT
657              
658             © 2024 Michal Josef Špaček
659              
660             BSD 2-Clause License
661              
662             =head1 VERSION
663              
664             0.08
665              
666             =cut