File Coverage

blib/lib/Tags/HTML/Login/Access.pm
Criterion Covered Total %
statement 80 80 100.0
branch 14 14 100.0
condition n/a
subroutine 16 16 100.0
pod 1 1 100.0
total 111 111 100.0


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