File Coverage

blib/lib/Plack/App/Login/Request.pm
Criterion Covered Total %
statement 35 77 45.4
branch 6 22 27.2
condition 0 9 0.0
subroutine 10 16 62.5
pod n/a
total 51 124 41.1


line stmt bran cond sub pod time code
1             package Plack::App::Login::Request;
2              
3 3     3   177358 use base qw(Plack::Component::Tags::HTML);
  3         7  
  3         1894  
4 3     3   277094 use strict;
  3         7  
  3         90  
5 3     3   44 use warnings;
  3         9  
  3         175  
6              
7 3     3   1815 use Plack::Request;
  3         205262  
  3         218  
8 3     3   1843 use Plack::Response;
  3         4313  
  3         128  
9 3     3   1680 use Plack::Session;
  3         1823  
  3         182  
10 3         20 use Plack::Util::Accessor qw(generator lang login_request_cb logo_image_url
11 3     3   21 message_cb redirect_login redirect_error text title);
  3         5  
12 3     3   2230 use Tags::HTML::Container;
  3         53191  
  3         140  
13 3     3   1992 use Tags::HTML::Login::Request;
  3         917153  
  3         2797  
14              
15             our $VERSION = 0.03;
16              
17             sub _css {
18 0     0   0 my $self = shift;
19              
20 0         0 $self->{'_tags_container'}->process_css;
21 0         0 $self->{'_tags_login_request'}->process_css({
22             'error' => 'red',
23             'info' => 'blue',
24             });
25              
26 0         0 return;
27             }
28              
29             sub _login_check {
30 0     0   0 my ($self, $env, $body_parameters_hr) = @_;
31              
32 0 0 0     0 if (! exists $body_parameters_hr->{'login_request'}
33             || $body_parameters_hr->{'login_request'} ne 'login_request') {
34              
35 0         0 $self->_message($env, 'error', 'There is no login request POST.');
36 0         0 return 0;
37             }
38 0 0 0     0 if (! defined $body_parameters_hr->{'email'} || ! $body_parameters_hr->{'email'}) {
39 0         0 $self->_message($env, 'error', "Missing email.");
40 0         0 return 0;
41             }
42              
43 0         0 return 1;
44             }
45              
46             sub _message {
47 0     0   0 my ($self, $env, $message_type, $message) = @_;
48              
49 0 0       0 if (defined $self->message_cb) {
50 0         0 $self->message_cb->($env, $message_type, $message);
51             }
52              
53 0         0 return;
54             }
55              
56             sub _prepare_app {
57 5     5   370173 my $self = shift;
58              
59             # Defaults which rewrite defaults in module which I am inheriting.
60 5 50       24 if (! $self->generator) {
61 5         90 $self->generator(__PACKAGE__.'; Version: '.$VERSION);
62             }
63              
64 5 50       33 if (! $self->title) {
65 5         23 $self->title('Login request page');
66             }
67              
68             # Inherite defaults.
69 5         37 $self->SUPER::_prepare_app;
70              
71             # Defaults from this module.
72 3         985 my %p = (
73             'css' => $self->css,
74             'tags' => $self->tags,
75             );
76              
77             # Tags helper for login button.
78 3 100       47 $self->{'_tags_login_request'} = Tags::HTML::Login::Request->new(
    100          
79             %p,
80             defined $self->lang ? (
81             'lang' => $self->lang,
82             ) : (),
83             'logo_image_url' => $self->logo_image_url,
84             defined $self->text ? (
85             'text' => $self->text,
86             ) : (),
87             );
88 0           $self->{'_tags_container'} = Tags::HTML::Container->new(%p);
89              
90 0           return;
91             }
92              
93             sub _process_actions {
94 0     0     my ($self, $env) = @_;
95              
96 0 0 0       if (defined $self->login_request_cb && $env->{'REQUEST_METHOD'} eq 'POST') {
97 0           my $req = Plack::Request->new($env);
98 0           my $body_params_hr = $req->body_parameters;
99 0           my ($status, $messages_ar) = $self->_login_check($env, $body_params_hr);
100 0           my $res = Plack::Response->new;
101 0 0         if ($status) {
102 0 0         if ($self->login_request_cb->($env, $body_params_hr->{'email'})) {
103 0           $self->_message($env, 'info',
104             "Login information for email '$body_params_hr->{'email'}' was sent.");
105 0           $res->redirect($self->redirect_login);
106             } else {
107 0           $self->_message($env, 'error', 'Bad login email.');
108 0           $res->redirect($self->redirect_error);
109             }
110             } else {
111 0           $res->redirect($self->redirect_error);
112             }
113 0           $self->psgi_app($res->finalize);
114             }
115              
116 0           return;
117             }
118              
119             sub _tags_middle {
120 0     0     my ($self, $env) = @_;
121              
122 0           my $messages_ar = [];
123 0 0         if (exists $env->{'psgix.session'}) {
124 0           my $session = Plack::Session->new($env);
125 0           $messages_ar = $session->get('messages');
126 0           $session->set('messages', []);
127             }
128             $self->{'_tags_container'}->process(
129             sub {
130 0     0     $self->{'_tags_login_request'}->process($messages_ar);
131             },
132 0           );
133              
134 0           return;
135             }
136              
137             1;
138              
139             __END__
140              
141             =pod
142              
143             =encoding utf8
144              
145             =head1 NAME
146              
147             Plack::App::Login::Request - Plack application for request of login information.
148              
149             =head1 SYNOPSIS
150              
151             use Plack::App::Login::Request;
152              
153             my $obj = Plack::App::Login::Request->new(%parameters);
154             my $psgi_ar = $obj->call($env);
155             my $app = $obj->to_app;
156              
157             =head1 METHODS
158              
159             =head2 C<new>
160              
161             my $obj = Plack::App::Login::Request->new(%parameters);
162              
163             Constructor.
164              
165             =over 8
166              
167             =item * C<author>
168              
169             Author string to HTML head.
170              
171             Default value is undef.
172              
173             =item * C<content_type>
174              
175             Content type for output.
176              
177             Default value is 'text/html; charset=__ENCODING__'.
178              
179             =item * C<css>
180              
181             Instance of L<CSS::Struct::Output> object.
182              
183             Default value is L<CSS::Struct::Output::Raw> instance.
184              
185             =item * C<css_init>
186              
187             Reference to array with L<CSS::Struct> structure.
188              
189             Default value is CSS initialization from L<Tags::HTML::Page::Begin> like
190              
191             * {
192             box-sizing: border-box;
193             margin: 0;
194             padding: 0;
195             }
196              
197             =item * C<encoding>
198              
199             Set encoding for output.
200              
201             Default value is 'utf-8'.
202              
203             =item * C<favicon>
204              
205             Link to favicon.
206              
207             Default value is undef.
208              
209             =item * C<flag_begin>
210              
211             Flag that means begin of html writing via L<Tags::HTML::Page::Begin>.
212              
213             Default value is 1.
214              
215             =item * C<flag_end>
216              
217             Flag that means end of html writing via L<Tags::HTML::Page::End>.
218              
219             Default value is 1.
220              
221             =item * C<generator>
222              
223             HTML generator string.
224              
225             Default value is 'Plack::App::Login; Version: __VERSION__'.
226              
227             =item * C<lang>
228              
229             Language in ISO 639-2 code.
230              
231             Default value is undef.
232              
233             =item * C<psgi_app>
234              
235             PSGI application to run instead of normal process.
236             Intent of this is change application in C<_process_actions> method.
237              
238             Default value is undef.
239              
240             =item * C<script_js>
241              
242             Reference to array with Javascript code strings.
243              
244             Default value is [].
245              
246             =item * C<script_js_src>
247              
248             Reference to array with Javascript URLs.
249              
250             Default value is [].
251              
252             =item * C<status_code>
253              
254             HTTP status code.
255              
256             Default value is 200.
257              
258             =item * C<tags>
259              
260             Instance of L<Tags::Output> object.
261              
262             Default value is
263              
264             Tags::Output::Raw->new(
265             'xml' => 1,
266             'no_simple' => ['script', 'textarea'],
267             'preserved' => ['pre', 'style'],
268             );
269              
270             =item * C<text>
271              
272             Hash reference with keys defined language in ISO 639-2 code and value with hash reference with texts.
273              
274             Required keys are 'login_request', 'email_label' and 'submit'.
275              
276             See more in L<Tags::HTML::Login::Request>.
277              
278             Default value is undef.
279              
280             =item * C<title>
281              
282             Page title.
283              
284             Default value is 'Login page'.
285              
286             =back
287              
288             Returns instance of object.
289              
290             =head2 C<call>
291              
292             my $psgi_ar = $obj->call($env);
293              
294             Implementation of login request page.
295              
296             Returns reference to array (PSGI structure).
297              
298             =head2 C<to_app>
299              
300             my $app = $obj->to_app;
301              
302             Creates Plack application.
303              
304             Returns Plack::Component object.
305              
306             =head1 EXAMPLE
307              
308             =for comment filename=plack_app_login_request.pl
309              
310             use strict;
311             use warnings;
312              
313             use CSS::Struct::Output::Indent;
314             use Plack::App::Login::Request;
315             use Data::Message::Simple;
316             use Plack::Builder;
317             use Plack::Runner;
318             use Plack::Session;
319             use Tags::Output::Indent;
320             use Unicode::UTF8 qw(decode_utf8);
321              
322             my $message_cb = sub {
323             my ($env, $message_type, $message) = @_;
324             my $session = Plack::Session->new($env);
325             my $m = Data::Message::Simple->new(
326             'text' => $message,
327             'type' => $message_type,
328             );
329             my $messages_ar = $session->get('messages');
330             if (defined $messages_ar) {
331             push @{$messages_ar}, $m;
332             } else {
333             $session->set('messages', [$m]);
334             }
335             return;
336             };
337              
338             # Run application.
339             my $app = Plack::App::Login::Request->new(
340             'css' => CSS::Struct::Output::Indent->new,
341             'generator' => 'Plack::App::Login::Request',
342             'login_request_cb' => sub {
343             my ($env, $email) = @_;
344             if ($email eq 'skim@skim.cz') {
345             return 1;
346             } else {
347             return 0;
348             }
349             },
350             'message_cb' => $message_cb,
351             'redirect_login' => '/',
352             'redirect_error' => '/',
353             'tags' => Tags::Output::Indent->new(
354             'preserved' => ['style'],
355             'xml' => 1,
356             ),
357             )->to_app;
358             my $builder = Plack::Builder->new;
359             $builder->add_middleware('Session');
360             my $app_with_session = $builder->wrap($app);
361             Plack::Runner->new->run($app_with_session);
362              
363             # Workflows:
364             # 1) Blank request.
365             # 2) Fill skim@skim.cz email and request.
366             # 3) Fill another email and request.
367              
368             # Output:
369             # HTTP::Server::PSGI: Accepting connections at http://0:5000/
370              
371             # > curl http://localhost:5000/
372             # <!DOCTYPE html>
373             # <html lang="en">
374             # <head>
375             # <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
376             # <meta name="generator" content="Plack::App::Login::Request" />
377             # <meta name="viewport" content="width=device-width, initial-scale=1.0" />
378             # <title>
379             # Login request page
380             # </title>
381             # <style type="text/css">
382             # * {
383             # box-sizing: border-box;
384             # margin: 0;
385             # padding: 0;
386             # }
387             # .container {
388             # display: flex;
389             # align-items: center;
390             # justify-content: center;
391             # height: 100vh;
392             # }
393             # .form-request {
394             # width: 300px;
395             # background-color: #f2f2f2;
396             # padding: 20px;
397             # border-radius: 5px;
398             # box-shadow: 0 0 10px rgba(0, 0, 0, 0.2);
399             # }
400             # .form-request .logo {
401             # height: 5em;
402             # width: 100%;
403             # }
404             # .form-request img {
405             # margin: auto;
406             # display: block;
407             # max-width: 100%;
408             # max-height: 5em;
409             # }
410             # .form-request fieldset {
411             # border: none;
412             # padding: 0;
413             # margin-bottom: 20px;
414             # }
415             # .form-request legend {
416             # font-weight: bold;
417             # margin-bottom: 10px;
418             # }
419             # .form-request p {
420             # margin: 0;
421             # padding: 10px 0;
422             # }
423             # .form-request label {
424             # display: block;
425             # font-weight: bold;
426             # margin-bottom: 5px;
427             # }
428             # .form-request input[type="email"] {
429             # width: 100%;
430             # padding: 8px;
431             # border: 1px solid #ccc;
432             # border-radius: 3px;
433             # }
434             # .form-request button[type="submit"] {
435             # width: 100%;
436             # padding: 10px;
437             # background-color: #4CAF50;
438             # color: #fff;
439             # border: none;
440             # border-radius: 3px;
441             # cursor: pointer;
442             # }
443             # .form-request button[type="submit"]:hover {
444             # background-color: #45a049;
445             # }
446             # .form-request .messages {
447             # text-align: center;
448             # }
449             # .error {
450             # color: red;
451             # }
452             # .info {
453             # color: blue;
454             # }
455             # </style>
456             # </head>
457             # <body>
458             # <div class="container">
459             # <div class="inner">
460             # <form class="form-request" method="post">
461             # <fieldset>
462             # <legend>
463             # Login request
464             # </legend>
465             # <p>
466             # <label for="email" />
467             # Email
468             # <input type="email" name="email" id="email" autofocus="autofocus"
469             # />
470             # </p>
471             # <p>
472             # <button type="submit" name="login_request" value="login_request">
473             # Request
474             # </button>
475             # </p>
476             # </fieldset>
477             # </form>
478             # </div>
479             # </div>
480             # </body>
481             # </html>
482              
483             # Output screenshot is in images/ directory.
484              
485             =begin html
486              
487             <a href="https://raw.githubusercontent.com/michal-josef-spacek/Plack-App-Login-Request/master/images/plack_app_login_request.png">
488             <img src="https://raw.githubusercontent.com/michal-josef-spacek/Plack-App-Login-Request/master/images/plack_app_login_request.png" alt="Web app example" width="300px" height="300px" />
489             </a>
490              
491             =end html
492              
493             =head1 DEPENDENCIES
494              
495             L<Plack::Component::Tags::HTM>,
496             L<Plack::Request>,
497             L<Plack::Response>,
498             L<Plack::Session>,
499             L<Plack::Util::Accessor>,
500             L<Tags::HTML::Container>,
501             L<Tags::HTML::Login::Request>.
502              
503             =head1 SEE ALSO
504              
505             =over
506              
507             =item L<Plack::App::Login>
508              
509             Plack login application.
510              
511             =item L<Plack::App::Login::Password>
512              
513             Plack login/password application.
514              
515             =item L<Plack::App::Register>
516              
517             Plack register application.
518              
519             =back
520              
521             =head1 REPOSITORY
522              
523             L<https://github.com/michal-josef-spacek/Plack-App-Login-Request>
524              
525             =head1 AUTHOR
526              
527             Michal Josef Špaček L<mailto:skim@cpan.org>
528              
529             L<http://skim.cz>
530              
531             =head1 LICENSE AND COPYRIGHT
532              
533             © 2024 Michal Josef Špaček
534              
535             BSD 2-Clause License
536              
537             =head1 VERSION
538              
539             0.03
540              
541             =cut