File Coverage

blib/lib/Plack/App/ChangePassword.pm
Criterion Covered Total %
statement 51 88 57.9
branch 8 28 28.5
condition 1 17 5.8
subroutine 14 16 87.5
pod n/a
total 74 149 49.6


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