View | Details | Raw Unified | Return to bug 8753
Collapse All | Expand All

(-)a/C4/Members.pm (-1 / +91 lines)
Lines 42-47 use Koha::Borrower::Debarments qw(IsDebarred); Link Here
42
use Text::Unaccent qw( unac_string );
42
use Text::Unaccent qw( unac_string );
43
use Koha::AuthUtils qw(hash_password);
43
use Koha::AuthUtils qw(hash_password);
44
44
45
## Password recovery
46
use CGI;
47
use Encode qw(encode decode);
48
use MIME::QuotedPrint qw(encode_qp);
49
use Mail::Sendmail;
50
45
our ($VERSION,@ISA,@EXPORT,@EXPORT_OK,$debug);
51
our ($VERSION,@ISA,@EXPORT,@EXPORT_OK,$debug);
46
52
47
BEGIN {
53
BEGIN {
Lines 112-118 BEGIN { Link Here
112
    push @EXPORT, qw(
118
    push @EXPORT, qw(
113
        &ModMember
119
        &ModMember
114
        &changepassword
120
        &changepassword
115
         &ModPrivacy
121
        &ModPrivacy
122
    );
123
124
    # Password recovery
125
    push @EXPORT, qw(
126
        &SendPasswordRecoveryEmail
116
    );
127
    );
117
128
118
    #Delete data
129
    #Delete data
Lines 2241-2246 sub ModPrivacy { Link Here
2241
                      privacy        => $privacy );
2252
                      privacy        => $privacy );
2242
}
2253
}
2243
2254
2255
=head2 SendPasswordRecoveryEmail
2256
2257
  SendPasswordRecoveryEmail( $borrowernumber, $email, $query );
2258
2259
Sends email to user using template file opac-send-password-recovery.tt.
2260
The mail contains a link to the script for password reset, with a unique key stored
2261
in the database for validation
2262
2263
$query is the CGI object, required to determine if http or https must be used
2264
2265
=cut
2266
2267
sub SendPasswordRecoveryEmail {
2268
    my $borrowernumber = shift;
2269
    my $email = shift;
2270
    my $query = shift;
2271
    my $dbh = C4::Context->dbh;
2272
    my $username = GetMemberDetails($borrowernumber)->{userid};
2273
2274
    #generate UUID
2275
    my @chars = ("A".."Z", "a".."z", "0".."9");
2276
    my $uuid_str;
2277
    $uuid_str .= $chars[rand @chars] for 1..32;
2278
2279
    #insert into database
2280
    my $expirydate = DateTime->now(time_zone => C4::Context->tz())->add( days => 2 );
2281
    my $sth = $dbh->prepare( 'INSERT INTO borrower_password_recovery VALUES (?, ?, ?)');
2282
    $sth->execute($borrowernumber, $uuid_str, $expirydate->ymd());
2283
2284
    my $userEmail = ( $email ) ? $email : GetFirstValidEmailAddress($borrowernumber);
2285
    #define to/from emails
2286
    my $kohaEmail = C4::Context->preference( 'KohaAdminEmailAddress' );
2287
2288
    #create link
2289
    my $protocol = $query->https() ? "https://" : "http://";
2290
    my $uuidLink = $protocol . C4::Context->preference( 'OPACBaseURL' ) . "/cgi-bin/koha/opac-password-recovery.pl?uniqueKey=$uuid_str";
2291
2292
    #build email content
2293
    my ( $template2, $borrower_number, $cookie ) = C4::Auth::get_template_and_user(
2294
    {
2295
        template_name   => "opac-send-password-recovery.tmpl",
2296
        query           => $query,
2297
        type            => "opac",
2298
        authnotrequired => 1,
2299
    }
2300
    );
2301
    $template2->param(
2302
        uuidLink         => $uuidLink,
2303
        username         => $username,
2304
    );
2305
2306
    # Getting template result and
2307
    my $template_res = $template2->output();
2308
2309
    #Mail attributes
2310
    my %mail = (
2311
        To  => $userEmail,
2312
        From  => $kohaEmail,
2313
        'Content-Type' => 'text/html; charset=utf-8'
2314
    );
2315
2316
    #getting mail properties
2317
    if ( $template_res =~ /<SUBJECT>(.*)<END_SUBJECT>/s ) { $mail{'subject'} = decode( 'utf-8', $1 ); }
2318
    if ( $template_res =~ /<MESSAGE>\n(.*)\n<END_MESSAGE>/s ) { $mail{'body'} = decode( 'utf-8', $1 ); }
2319
    #send mail
2320
    if ( sendmail %mail )
2321
    {
2322
        # if it works....
2323
        return 1;
2324
    }
2325
    else
2326
    {
2327
        # if it doesnt work....
2328
        warn "Error sending mail: $Mail::Sendmail::error \n";
2329
        return 0;
2330
    }
2331
}
2332
2333
2244
=head2 AddMessage
2334
=head2 AddMessage
2245
2335
2246
  AddMessage( $borrowernumber, $message_type, $message, $branchcode );
2336
  AddMessage( $borrowernumber, $message_type, $message, $branchcode );
(-)a/installer/data/mysql/kohastructure.sql (+11 lines)
Lines 3393-3398 CREATE TABLE IF NOT EXISTS marc_modification_template_actions ( Link Here
3393
  CONSTRAINT `mmta_ibfk_1` FOREIGN KEY (`template_id`) REFERENCES `marc_modification_templates` (`template_id`) ON DELETE CASCADE ON UPDATE CASCADE
3393
  CONSTRAINT `mmta_ibfk_1` FOREIGN KEY (`template_id`) REFERENCES `marc_modification_templates` (`template_id`) ON DELETE CASCADE ON UPDATE CASCADE
3394
) ENGINE=InnoDB  DEFAULT CHARSET=utf8;
3394
) ENGINE=InnoDB  DEFAULT CHARSET=utf8;
3395
3395
3396
-- Table structure for table 'borrower_password_recovery'
3397
-- this stores the unique ID sent by email to the patron, for future validation
3398
--
3399
3400
CREATE TABLE IF NOT EXISTS borrower_password_recovery (
3401
  borrowernumber int(11) NOT NULL,
3402
  uuid varchar(128) NOT NULL,
3403
  valid_until timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
3404
  KEY borrowernumber (borrowernumber)
3405
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
3406
3396
/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
3407
/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
3397
/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
3408
/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
3398
/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
3409
/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
(-)a/installer/data/mysql/sysprefs.sql (+1 lines)
Lines 256-261 INSERT INTO systempreferences ( `variable`, `value`, `options`, `explanation`, ` Link Here
256
('opacreadinghistory','1','','If ON, enables display of Patron Circulation History in OPAC','YesNo'),
256
('opacreadinghistory','1','','If ON, enables display of Patron Circulation History in OPAC','YesNo'),
257
('OpacRenewalAllowed','0',NULL,'If ON, users can renew their issues directly from their OPAC account','YesNo'),
257
('OpacRenewalAllowed','0',NULL,'If ON, users can renew their issues directly from their OPAC account','YesNo'),
258
('OpacRenewalBranch','checkoutbranch','itemhomebranch|patronhomebranch|checkoutbranch|null','Choose how the branch for an OPAC renewal is recorded in statistics','Choice'),
258
('OpacRenewalBranch','checkoutbranch','itemhomebranch|patronhomebranch|checkoutbranch|null','Choose how the branch for an OPAC renewal is recorded in statistics','Choice'),
259
('OpacResetPassword','1','','Shows the \'Forgot your password?\' link in the OPAC','YesNo'),
259
('OPACResultsSidebar','','70|10','Define HTML to be included on the search results page, underneath the facets sidebar','Textarea'),
260
('OPACResultsSidebar','','70|10','Define HTML to be included on the search results page, underneath the facets sidebar','Textarea'),
260
('OPACSearchForTitleIn','<li><a  href=\"http://worldcat.org/search?q={TITLE}\" target=\"_blank\">Other Libraries (WorldCat)</a></li>\n<li><a href=\"http://www.scholar.google.com/scholar?q={TITLE}\" target=\"_blank\">Other Databases (Google Scholar)</a></li>\n<li><a href=\"http://www.bookfinder.com/search/?author={AUTHOR}&amp;title={TITLE}&amp;st=xl&amp;ac=qr\" target=\"_blank\">Online Stores (Bookfinder.com)</a></li>\n<li><a href=\"http://openlibrary.org/search/?author=({AUTHOR})&title=({TITLE})\" target=\"_blank\">Open Library (openlibrary.org)</a></li>','70|10','Enter the HTML that will appear in the \'Search for this title in\' box on the detail page in the OPAC.  Enter {TITLE}, {AUTHOR}, or {ISBN} in place of their respective variables in the URL. Leave blank to disable \'More Searches\' menu.','Textarea'),
261
('OPACSearchForTitleIn','<li><a  href=\"http://worldcat.org/search?q={TITLE}\" target=\"_blank\">Other Libraries (WorldCat)</a></li>\n<li><a href=\"http://www.scholar.google.com/scholar?q={TITLE}\" target=\"_blank\">Other Databases (Google Scholar)</a></li>\n<li><a href=\"http://www.bookfinder.com/search/?author={AUTHOR}&amp;title={TITLE}&amp;st=xl&amp;ac=qr\" target=\"_blank\">Online Stores (Bookfinder.com)</a></li>\n<li><a href=\"http://openlibrary.org/search/?author=({AUTHOR})&title=({TITLE})\" target=\"_blank\">Open Library (openlibrary.org)</a></li>','70|10','Enter the HTML that will appear in the \'Search for this title in\' box on the detail page in the OPAC.  Enter {TITLE}, {AUTHOR}, or {ISBN} in place of their respective variables in the URL. Leave blank to disable \'More Searches\' menu.','Textarea'),
261
('OpacSeparateHoldings','0',NULL,'Separate current branch holdings from other holdings (OPAC)','YesNo'),
262
('OpacSeparateHoldings','0',NULL,'Separate current branch holdings from other holdings (OPAC)','YesNo'),
(-)a/installer/data/mysql/updatedatabase.pl (+15 lines)
Lines 7945-7950 if (CheckVersion($DBversion)) { Link Here
7945
    SetVersion($DBversion);
7945
    SetVersion($DBversion);
7946
}
7946
}
7947
7947
7948
$DBversion = "3.15.00.XXX";
7949
if ( CheckVersion($DBversion) ) {
7950
    $dbh->do("INSERT INTO systempreferences (variable,value,options,explanation,type) VALUES ('OpacResetPassword','1','','Shows the ''Forgot your password?'' link in the OPAC','YesNo')");
7951
    $dbh->do(q{
7952
        CREATE TABLE IF NOT EXISTS borrower_password_recovery (
7953
          borrowernumber int(11) NOT NULL,
7954
          uuid varchar(128) NOT NULL,
7955
          valid_until timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
7956
          KEY borrowernumber (borrowernumber)
7957
          ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
7958
    });
7959
    print "Upgrade to $DBversion done (Bug 8753: Add forgot password link to OPAC)\n";
7960
    SetVersion ($DBversion);
7961
}
7962
7948
=head1 FUNCTIONS
7963
=head1 FUNCTIONS
7949
7964
7950
=head2 TableExists($table)
7965
=head2 TableExists($table)
(-)a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/opac.pref (+8 lines)
Lines 557-562 OPAC: Link Here
557
                  track: "Track"
557
                  track: "Track"
558
                  no: "Don't track"
558
                  no: "Don't track"
559
            - links that patrons click on
559
            - links that patrons click on
560
        -
561
            - "The user "
562
            - pref: OpacResetPassword
563
              default: 1
564
              choices:
565
                  yes: "can reset"
566
                  no: "can not reset"
567
            - " their password on OPAC."
560
568
561
    Shelf Browser:
569
    Shelf Browser:
562
        -
570
        -
(-)a/koha-tmpl/opac-tmpl/bootstrap/en/includes/masthead.inc (-1 / +4 lines)
Lines 278-287 Link Here
278
                        <label for="mpassword">Password:</label><input type="password" id="mpassword" name="password" />
278
                        <label for="mpassword">Password:</label><input type="password" id="mpassword" name="password" />
279
                    [% IF PatronSelfRegistration && PatronSelfRegistrationDefaultCategory %]<div id="mpatronregistration"><p>Don't have an account? <a href="/cgi-bin/koha/opac-memberentry.pl">Register here.</a></p></div>[% END %]
279
                    [% IF PatronSelfRegistration && PatronSelfRegistrationDefaultCategory %]<div id="mpatronregistration"><p>Don't have an account? <a href="/cgi-bin/koha/opac-memberentry.pl">Register here.</a></p></div>[% END %]
280
                    </fieldset>
280
                    </fieldset>
281
                    [% IF Koha.Preference('OpacPasswordChange') && Koha.Preference('OpacResetPassword') %]
282
                         <p><a href="/cgi-bin/koha/opac-password-recovery.pl">Forgot your password?</  a></p>
283
                    [% END %]
281
            </div>
284
            </div>
282
            <div class="modal-footer">
285
            <div class="modal-footer">
283
                <input type="submit" class="btn btn-primary" value="Log in" />
286
                <input type="submit" class="btn btn-primary" value="Log in" />
284
                <a href="#" data-dismiss="modal" aria-hidden="true" class="cancel">Cancel</a>
287
                <a href="#" data-dismiss="modal" aria-hidden="true" class="cancel">Cancel</a>
285
            </div>
288
            </div>
286
        </form> <!-- /#auth -->
289
        </form> <!-- /#auth -->
287
    </div>  <!-- /#modalAuth  -->
290
    </div>  <!-- /#modalAuth  -->
(-)a/koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-auth.tt (+4 lines)
Lines 95-100 Link Here
95
                                    <label for="password">Password</label><input type="password"  size="25" id="password"  name="password" />
95
                                    <label for="password">Password</label><input type="password"  size="25" id="password"  name="password" />
96
                                </fieldset>
96
                                </fieldset>
97
97
98
                                [% IF Koha.Preference('OpacPasswordChange') && Koha.Preference('OpacResetPassword') %]
99
                                    <p><a href="/cgi-bin/koha/opac-password-recovery.pl">Forgot your password?</a></p>
100
                                [% END %]
101
98
                                <input type="submit" value="Log in" class="btn" />
102
                                <input type="submit" value="Log in" class="btn" />
99
                                <div id="nologininstructions">
103
                                <div id="nologininstructions">
100
                                    <h5>Don't have a password yet?</h5><p> If you don't have a password yet, stop by the circulation desk the next time you're in the library. We'll happily set one up for you.</p>
104
                                    <h5>Don't have a password yet?</h5><p> If you don't have a password yet, stop by the circulation desk the next time you're in the library. We'll happily set one up for you.</p>
(-)a/koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-main.tt (+3 lines)
Lines 78-83 Link Here
78
                                    </fieldset>
78
                                    </fieldset>
79
                                    [% IF PatronSelfRegistration && PatronSelfRegistrationDefaultCategory %]<div id="patronregistration"><p>Don't have an account? <a href="/cgi-bin/koha/opac-memberentry.pl">Register here.</a></p></div>[% END %]
79
                                    [% IF PatronSelfRegistration && PatronSelfRegistrationDefaultCategory %]<div id="patronregistration"><p>Don't have an account? <a href="/cgi-bin/koha/opac-memberentry.pl">Register here.</a></p></div>[% END %]
80
                                    </fieldset>
80
                                    </fieldset>
81
                                    [% IF Koha.Preference('OpacPasswordChange') && Koha.Preference('OpacResetPassword') %]
82
                                        <p><a href="/cgi-bin/koha/opac-password-recovery.pl">Forgot your password?</a></p>
83
                                    [% END %]
81
                                </form>
84
                                </form>
82
                            </div> <!-- /#login -->
85
                            </div> <!-- /#login -->
83
                        [% END # /casAuthentication %]
86
                        [% END # /casAuthentication %]
(-)a/koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-password-recovery.tt (+132 lines)
Line 0 Link Here
1
[% USE Koha %]
2
[% INCLUDE 'doc-head-open.inc' %]
3
[% IF (LibraryNameTitle) %][% LibraryNameTitle %][% ELSE %]Koha online[% END %] catalog &rsaquo;
4
[% INCLUDE 'doc-head-close.inc' %]
5
[% BLOCK cssinclude %][% END %]
6
[% BLOCK jsinclude %]
7
<script type="text/javascript" language="javascript">
8
   $(function() {
9
        $("#CheckAll").click(function(){
10
                $("[name=deleteRequest]").attr('checked', true);
11
                return false;
12
            });
13
14
        $("#CheckNone").click(function(){
15
                $("[name=deleteRequest]").attr('checked', false);
16
                return false;
17
            });
18
19
        $("select#type").change(function() {
20
            $("fieldset#serial, fieldset#book, fieldset#chapter").hide()
21
            $("fieldset#" + $(this).val() ).show();
22
        });
23
   });
24
</script>
25
[% END %]
26
</head>
27
<body>
28
29
<div id="doc3" class="yui-t1">
30
    <div id="bd">
31
[% INCLUDE 'masthead.inc' %]
32
        <div id="yui-main">
33
            <div class="yui-b">
34
                <div class="yui-g">
35
                    <div class="illrequest">
36
[% IF (!Koha.Preference('OpacResetPassword')) %]
37
    <div class="dialog alert">You can't reset your password.</div>
38
[% ELSIF (password_recovery) %]
39
    [% IF (hasError) %]
40
        <span class="TxtErreur">
41
        [% IF (sendmailError) %]
42
            An error has occured while sending you the password recovery link.
43
            <br/>Please try again later.
44
        [% ELSIF (errNoEmailFound) %]
45
            No account was found with the email address "<strong>[% email %]</strong>"
46
            <br/>Check if you typed it correctly.
47
        [% ELSIF (errTooManyEmailFound) %]
48
            More than one account has been found for the email address: "<strong>[% email %]</strong>"
49
            <br/>Try to use an alternative email if you have one.
50
        [% ELSIF (errAlreadyStartRecovery) %]
51
            The process of password recovery has already started for this account ("<strong>[% email %]</strong>")
52
            <br/>Check your emails; you should receive the link to reset your password.
53
            <br/>If you did not receive it, <a href="/cgi-bin/koha/opac-password-recovery.pl?resendEmail=true&email=[% email %]">click here to get a new password recovery link</a>
54
        [% END %]
55
        <br/><br/>Please contact the staff if you need further assistance.
56
        </span>
57
    [% END %]
58
        <div id="password-recovery" class="container">
59
            <form action="/cgi-bin/koha/opac-password-recovery.pl" method="post">
60
                <input type="hidden" name="koha_login_context" value="opac" />
61
                <fieldset class="brief">
62
                    <legend>Password recovery form:</legend>
63
                        <p>To reset your password, enter your email address.
64
                        <br/>A link to reset your password will be sent at this address.</p>
65
                        <ol>
66
                            <li><label for="email">Email:</label><input type="text" id="email" size="40" name="email" value="[% email %]" /></li>
67
                        </ol>
68
                    <fieldset class="action">
69
                 <input type="submit" value="Submit" class="submit" name="sendEmail" />
70
                    </fieldset>
71
                 </fieldset>
72
            </form>
73
        </div>
74
75
[% ELSIF (new_password) %]
76
    [% IF (errLinkNotValid) %]
77
        <span class="TxtErreur"><h6>
78
        We could not authenticate you as the account owner.
79
        <br/>Be sure to use the link you received in your email.
80
        </h6></span>
81
    [% ELSE %]
82
        [% IF (hasError) %]
83
            <span class="TxtErreur">
84
            [% IF (errPassNotMatch) %]
85
                The passwords entered does not match.
86
                <br/>Please try again.
87
            [% ELSIF (errPassTooShort) %]
88
                The password is too short.
89
                <br/>The password must contain at least [% minPassLength %] characters.
90
            [% END %]
91
            </span>
92
        [% END %]
93
            <div id="password-recovery" class="container">
94
                <form action="/cgi-bin/koha/opac-password-recovery.pl" method="post">
95
                    <input type="hidden" name="koha_login_context" value="opac" />
96
                    <fieldset class="brief">
97
                        <legend>Password recovery form:</legend>
98
                            <p class="light">The password must contain at least [% minPassLength %] characters.</p>
99
                            <ol>
100
                                <li><label for="password">New password:</label><input type="password" id="password" size="40" name="password" /></li>
101
                                <li><label for="repeatPassword">Confirm new password:</label><input type="password" id="repeatPassword" size="40" name="repeatPassword" /></li>
102
                            </ol>
103
                        <fieldset class="action">
104
                        <input type="hidden" name="username" value="[% username %]" />
105
                        <input type="hidden" name="uniqueKey" value="[% uniqueKey %]" />
106
                        <input type="submit" value="Submit" class="submit" name="passwordReset" />
107
                        </fieldset>
108
                     </fieldset>
109
                </form>
110
            </div>
111
    [% END %]
112
[% ELSIF (mail_sent) %]
113
    <p>A mail has been sent to "[% email %]".
114
    <br/>It contains a link to create a new password.
115
    <br/>This link will be valid for 2 days starting now.</p>
116
    <br/><a href="/cgi-bin/koha/opac-main.pl"">Click here to return to the main page.</a>
117
[% ELSIF (password_reset_done) %]
118
    <p>The password has been changed for user "[% username %]".
119
    <br/>You can now login using <a href="/cgi-bin/koha/opac-user.pl">this form</a>.</p>
120
[% END %]
121
                    </div>
122
                </div>
123
            </div>
124
        </div>
125
        <div class="yui-b">
126
            <div class="container">
127
                [% INCLUDE 'usermenu.inc' %]
128
            </div>
129
        </div>
130
    </div>
131
[% INCLUDE 'opac-bottom.inc' %]
132
</div>
(-)a/koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-send-password-recovery.tt (+19 lines)
Line 0 Link Here
1
[% USE Koha %]
2
[% INCLUDE 'doc-head-open.inc' %]
3
[% IF (LibraryNameTitle) %][% LibraryNameTitle %][% ELSE %]Koha online[% END %] catalog &rsaquo;
4
[% INCLUDE 'doc-head-close.inc' %]
5
[% BLOCK cssinclude %][% END %]
6
7
<SUBJECT>[% IF ( LibraryNameTitle ) %][% LibraryNameTitle %][% ELSE %]Koha Online[% END %] - Password recovery<END_SUBJECT>
8
9
<MESSAGE>
10
<html>
11
<p>This email has been sent in response to your password recovery request for the account <strong>[% username %]</strong>.</p>
12
<p>
13
You can now create your new password using the following link:
14
<br/><a href="[% uuidLink %]">[% uuidLink %]</a>
15
</p>
16
<p>This link will be valid for 2 days from this email's reception, then you must reapply if you do not change your password.</p>
17
<p>Thank you.</p>
18
</html>
19
<END_MESSAGE>
(-)a/koha-tmpl/opac-tmpl/prog/en/modules/opac-auth.tt (+3 lines)
Lines 85-90 please choose against which one you would like to authenticate: </p> Link Here
85
</ol></fieldset>
85
</ol></fieldset>
86
86
87
<input type="submit" value="Log In" class="submit" />
87
<input type="submit" value="Log In" class="submit" />
88
[% IF Koha.Preference('OpacPasswordChange') && Koha.Preference('OpacResetPassword') %]
89
    <p><a href="/cgi-bin/koha/opac-password-recovery.pl">Forgot your password?</a></p>
90
[% END %]
88
<div id="nologininstructions">
91
<div id="nologininstructions">
89
    <h5>Don't have a password yet?</h5><p> If you don't have a password yet, stop by the circulation desk the next time you're in the library. We'll happily set one up for you.</p>
92
    <h5>Don't have a password yet?</h5><p> If you don't have a password yet, stop by the circulation desk the next time you're in the library. We'll happily set one up for you.</p>
90
    <h5>Don't have a library card?</h5><p> If you don't have a library card, stop by your local library to sign up[% IF PatronSelfRegistration && PatronSelfRegistrationDefaultCategory %]<span id="registrationinstructions"> or  <a href="/cgi-bin/koha/opac-memberentry.pl">register here</a></span>[% END %].  </p>
93
    <h5>Don't have a library card?</h5><p> If you don't have a library card, stop by your local library to sign up[% IF PatronSelfRegistration && PatronSelfRegistrationDefaultCategory %]<span id="registrationinstructions"> or  <a href="/cgi-bin/koha/opac-memberentry.pl">register here</a></span>[% END %].  </p>
(-)a/koha-tmpl/opac-tmpl/prog/en/modules/opac-main.tt (-1 / +5 lines)
Lines 60-66 Link Here
60
60
61
        [% IF PatronSelfRegistration && PatronSelfRegistrationDefaultCategory %]<div id="patronregistration">Don't have an account? <a href="/cgi-bin/koha/opac-memberentry.pl">Register here.</a></div>[% END %]
61
        [% IF PatronSelfRegistration && PatronSelfRegistrationDefaultCategory %]<div id="patronregistration">Don't have an account? <a href="/cgi-bin/koha/opac-memberentry.pl">Register here.</a></div>[% END %]
62
62
63
	 </fieldset></fieldset>
63
     </fieldset>
64
     [% IF Koha.Preference('OpacPasswordChange') && Koha.Preference('OpacResetPassword') %]
65
             <p><a href="/cgi-bin/koha/opac-password-recovery.pl">Forgot your password?</a></p>
66
     [% END %]
67
     </fieldset>
64
	</form>
68
	</form>
65
	</div>
69
	</div>
66
    [% END %]
70
    [% END %]
(-)a/koha-tmpl/opac-tmpl/prog/en/modules/opac-password-recovery.tt (+130 lines)
Line 0 Link Here
1
[% USE Koha %]
2
[% INCLUDE 'doc-head-open.inc' %]
3
[% IF (LibraryNameTitle) %][% LibraryNameTitle %][% ELSE %]Koha online[% END %] catalog &rsaquo;
4
[% INCLUDE 'doc-head-close.inc' %]
5
6
<script type="text/javascript" language="javascript">
7
   $(function() {
8
        $("#CheckAll").click(function(){
9
                $("[name=deleteRequest]").attr('checked', true);
10
                return false;
11
            });
12
13
        $("#CheckNone").click(function(){
14
                $("[name=deleteRequest]").attr('checked', false);
15
                return false;
16
            });
17
18
        $("select#type").change(function() {
19
            $("fieldset#serial, fieldset#book, fieldset#chapter").hide()
20
            $("fieldset#" + $(this).val() ).show();
21
        });
22
   });
23
</script>
24
</head>
25
<body>
26
27
<div id="doc3" class="yui-t1">
28
    <div id="bd">
29
[% INCLUDE 'masthead.inc' %]
30
        <div id="yui-main">
31
            <div class="yui-b">
32
                <div class="yui-g">
33
                    <div class="illrequest">
34
[% IF (!Koha.Preference('OpacResetPassword')) %]
35
    <div class="dialog alert">You can't reset your password.</div>
36
[% ELSIF (password_recovery) %]
37
    [% IF (hasError) %]
38
        <span class="TxtErreur">
39
        [% IF (sendmailError) %]
40
            An error has occured while sending you the password recovery link.
41
            <br/>Please try again later.
42
        [% ELSIF (errNoEmailFound) %]
43
            No account was found with the email address "<strong>[% email %]</strong>"
44
            <br/>Check if you typed it correctly.
45
        [% ELSIF (errTooManyEmailFound) %]
46
            More than one account has been found for the email address: "<strong>[% email %]</strong>"
47
            <br/>Try to use your alternative email if you have one.
48
        [% ELSIF (errAlreadyStartRecovery) %]
49
            The process of password recovery has already started for this account ("<strong>[% email %]</strong>")
50
            <br/>Check your emails; you should receive the link to reset your password.
51
            <br/>If you didn't receive it, <a href="/cgi-bin/koha/opac-password-recovery.pl?resendEmail=true&email=[% email %]">click here to get a new password recovery link</a>
52
        [% END %]
53
        <br/><br/>Please contact the staff if you need further assistance.
54
        </span>
55
    [% END %]
56
        <div id="password-recovery" class="container">
57
            <form action="/cgi-bin/koha/opac-password-recovery.pl" method="post">
58
                <input type="hidden" name="koha_login_context" value="opac" />
59
                <fieldset class="brief">
60
                    <legend>Password recovery form:</legend>
61
                        <p>To reset your password, enter your email address.
62
                        <br/>A link to reset your password will be sent at this address.</p>
63
                        <ol>
64
                            <li><label for="email">Email:</label><input type="text" id="email" size="40" name="email" value="[% email %]" /></li>
65
                        </ol>
66
                    <fieldset class="action">
67
                 <input type="submit" value="Submit" class="submit" name="sendEmail" />
68
                    </fieldset>
69
                 </fieldset>
70
            </form>
71
        </div>
72
73
[% ELSIF (new_password) %]
74
    [% IF (errLinkNotValid) %]
75
        <span class="TxtErreur"><h6>
76
        We could not authenticate you as the account owner.
77
        <br/>Be sure to use the link you received in your email.
78
        </h6></span>
79
    [% ELSE %]
80
        [% IF (hasError) %]
81
            <span class="TxtErreur">
82
            [% IF (errPassNotMatch) %]
83
                The passwords entered does not match.
84
                <br/>Please try again.
85
            [% ELSIF (errPassTooShort) %]
86
                The password is too short.
87
                <br/>The password must contain at least [% minPassLength %] characters.
88
            [% END %]
89
            </span>
90
        [% END %]
91
            <div id="password-recovery" class="container">
92
                <form action="/cgi-bin/koha/opac-password-recovery.pl" method="post">
93
                    <input type="hidden" name="koha_login_context" value="opac" />
94
                    <fieldset class="brief">
95
                        <legend>Password recovery form:</legend>
96
                            <p class="light">The password must contain at least [% minPassLength %] characters.</p>
97
                            <ol>
98
                                <li><label for="password">New password:</label><input type="password" id="password" size="40" name="password" /></li>
99
                                <li><label for="repeatPassword">Confirm new password:</label><input type="password" id="repeatPassword" size="40" name="repeatPassword" /></li>
100
                            </ol>
101
                        <fieldset class="action">
102
                        <input type="hidden" name="username" value="[% username %]" />
103
                        <input type="hidden" name="uniqueKey" value="[% uniqueKey %]" />
104
                        <input type="submit" value="Submit" class="submit" name="passwordReset" />
105
                        </fieldset>
106
                     </fieldset>
107
                </form>
108
            </div>
109
    [% END %]
110
[% ELSIF (mail_sent) %]
111
    <p>A mail has been sent to "[% email %]".
112
    <br/>It contains a link to create a new password.
113
    <br/>This link will be valid for 2 days starting now.</p>
114
    <br/><a href="/cgi-bin/koha/opac-main.pl"">Click here to return to the main page.</a>
115
[% ELSIF (password_reset_done) %]
116
    <p>The password has been changed for user "[% username %]".
117
    <br/>You can now login using <a href="/cgi-bin/koha/opac-user.pl">this form</a>.</p>
118
[% END %]
119
                    </div>
120
                </div>
121
            </div>
122
        </div>
123
        <div class="yui-b">
124
            <div class="container">
125
                [% INCLUDE 'usermenu.inc' %]
126
            </div>
127
        </div>
128
    </div>
129
[% INCLUDE 'opac-bottom.inc' %]
130
</div>
(-)a/koha-tmpl/opac-tmpl/prog/en/modules/opac-send-password-recovery.tt (+17 lines)
Line 0 Link Here
1
[% USE Koha %]
2
[% INCLUDE 'doc-head-open.inc' %]
3
[% IF (LibraryNameTitle) %][% LibraryNameTitle %][% ELSE %]Koha online[% END %] catalog &rsaquo;
4
[% INCLUDE 'doc-head-close.inc' %]
5
<SUBJECT>[% IF ( LibraryNameTitle ) %][% LibraryNameTitle %][% ELSE %]Koha Online[% END %] - Password recovery<END_SUBJECT>
6
7
<MESSAGE>
8
<html>
9
<p>This email has been sent in response to your password recovery request for the account <strong>[% username %]</strong>.</p>
10
<p>
11
You can now create your new password using the following link:
12
<br/><a href="[% uuidLink %]">[% uuidLink %]</a>
13
</p>
14
<p>This link will be valid for 2 days from this email's reception, then you must reapply if you do not change your password.</p>
15
<p>Thank you.</p>
16
</html>
17
<END_MESSAGE>
(-)a/opac/opac-password-recovery.pl (-1 / +203 lines)
Line 0 Link Here
0
- 
1
#!/usr/bin/perl
2
3
use strict;
4
use Modern::Perl;
5
6
use CGI;
7
use Mail::Sendmail;
8
use HTML::Entities;
9
10
use C4::Auth;
11
use C4::Koha;
12
use C4::Members qw(changepassword GetMember GetMemberDetails SendPasswordRecoveryEmail);
13
use C4::Output;
14
use C4::Context;
15
use Koha::AuthUtils qw(hash_password);
16
17
my $query = new CGI;
18
19
my ( $template, $borrowernumber, $cookie ) = get_template_and_user(
20
    {
21
        template_name   => "opac-password-recovery.tmpl",
22
        query           => $query,
23
        type            => "opac",
24
        authnotrequired => 1,
25
        debug           => 1,
26
    }
27
);
28
29
my $email          = $query->param('email');
30
my $password       = $query->param('password');
31
my $repeatPassword = $query->param('repeatPassword');
32
my $minPassLength  = C4::Context->preference('minPasswordLength');
33
my $id             = $query->param('id');
34
my $uniqueKey      = $query->param('uniqueKey');
35
my $username       = $query->param('username');
36
my $borrower_number;
37
38
#errors
39
my $hasError;
40
41
#email form error
42
my $errNoEmailFound;
43
my $errAlreadyStartRecovery;
44
45
#new password form error
46
my $errLinkNotValid;
47
my $errPassNotMatch;
48
my $errPassTooShort;
49
50
my $dbh = C4::Context->dbh;
51
52
if ( $query->param('sendEmail') || $query->param('resendEmail') ) {
53
    #send mail + confirmation
54
55
    #try with the main email
56
    my $borrower_number;
57
    my %borrower_infos = GetMember( email => $email );
58
    if (%borrower_infos) {
59
        $borrower_number = GetMember( email => $email )->{'borrowernumber'};
60
        $username = GetMemberDetails($borrower_number)->{'userid'};
61
    }
62
    else {
63
        #try with the secondary email
64
        %borrower_infos = GetMember( emailpro => $email );
65
        if (%borrower_infos) {
66
            $borrower_number = GetMember( emailpro => $email )->{'borrowernumber'};
67
            $username = GetMemberDetails($borrower_number)->{'userid'};
68
        }
69
        else {
70
            #try when the other contact email
71
            %borrower_infos = GetMember( B_email => $email );
72
            if (%borrower_infos) {
73
                $borrower_number = GetMember( B_email => $email )->{'borrowernumber'};
74
                $username = GetMemberDetails($borrower_number)->{'userid'};
75
            }
76
        }
77
    }
78
79
    if ( !$email || !$username || !$borrower_number ) {
80
        $hasError        = 1;
81
        $errNoEmailFound = 1;
82
    }
83
    elsif ( !$query->param('resendEmail') ) {
84
        my $sth = $dbh->prepare(
85
"SELECT borrowernumber FROM borrower_password_recovery WHERE NOW() < valid_until AND borrowernumber = ?"
86
        );
87
        $sth->execute($borrower_number);
88
        if ( my $already = $sth->fetchrow ) {
89
            $hasError                = 1;
90
            $errAlreadyStartRecovery = 1;
91
        }
92
    }
93
94
    if ($hasError) {
95
        $template->param(
96
            hasError                => 1,
97
            errNoEmailFound         => $errNoEmailFound,
98
            errAlreadyStartRecovery => $errAlreadyStartRecovery,
99
            password_recovery       => 1,
100
            email                   => HTML::Entities::encode($email),
101
        );
102
    }
103
    else {
104
        #generate uuid and send recovery email
105
        if ( SendPasswordRecoveryEmail( $borrower_number, $email, $query ) ) {
106
            # if it works....
107
            $template->param(
108
                mail_sent => 1,
109
                email     => $email
110
            );
111
        }
112
        else {
113
            # if it doesnt work....
114
            $template->param(
115
                password_recovery => 1,
116
                sendmailError     => 1
117
            );
118
        }
119
    }
120
}
121
elsif ( $query->param('passwordReset') ) {
122
    #new password form
123
    #check if the link is still valid
124
    my $sth = $dbh->prepare(
125
        "SELECT borrower_password_recovery.borrowernumber, userid
126
                              FROM borrower_password_recovery, borrowers
127
                              WHERE borrowers.borrowernumber = borrower_password_recovery.borrowernumber
128
                              AND NOW() < valid_until
129
                              AND uuid = ?"
130
    );
131
    $sth->execute($uniqueKey);
132
    ( $borrower_number, $username ) = $sth->fetchrow;
133
134
    #validate password length & match
135
    if (   ($borrower_number)
136
        && ( $password eq $repeatPassword )
137
        && ( length($password) >= $minPassLength ) )
138
    {  #apply changes
139
        changepassword( $username, $borrower_number, hash_password($password) );
140
141
        #remove entry
142
        my $sth = $dbh->prepare(
143
            "DELETE FROM borrower_password_recovery
144
                                  WHERE uuid = ?
145
                                  ORDER BY valid_until DESC LIMIT 1"
146
        );
147
        $sth->execute($uniqueKey);
148
149
        $template->param(
150
            password_reset_done => 1,
151
            username            => $username
152
        );
153
    }
154
    else { #errors
155
        if ( !$borrower_number ) { #parameters not valid
156
            $errLinkNotValid = 1;
157
        }
158
        elsif ( $password ne $repeatPassword ) { #passwords does not match
159
            $errPassNotMatch = 1;
160
        }
161
        elsif ( length($password) < $minPassLength ) { #password too short
162
            $errPassTooShort = 1;
163
        }
164
        $template->param(
165
            new_password    => 1,
166
            minPassLength   => $minPassLength,
167
            email           => $email,
168
            uniqueKey       => $uniqueKey,
169
            errLinkNotValid => $errLinkNotValid,
170
            errPassNotMatch => $errPassNotMatch,
171
            errPassTooShort => $errPassTooShort,
172
            hasError        => 1
173
        );
174
    }
175
}
176
elsif ($uniqueKey) {  #reset password form
177
    #check if the link is valid
178
    my $sth = $dbh->prepare(
179
        "SELECT borrower_password_recovery.borrowernumber, userid
180
                              FROM borrower_password_recovery, borrowers
181
                              WHERE borrowers.borrowernumber = borrower_password_recovery.borrowernumber
182
                              AND NOW() < valid_until
183
                              AND uuid = ?"
184
    );
185
    $sth->execute($uniqueKey);
186
    ( $borrower_number, $username ) = $sth->fetchrow;
187
    if ( !$borrower_number ) {
188
        $errLinkNotValid = 1;
189
    }
190
    $template->param(
191
        new_password    => 1,
192
        minPassLength   => $minPassLength,
193
        email           => $email,
194
        uniqueKey       => $uniqueKey,
195
        username        => $username,
196
        errLinkNotValid => $errLinkNotValid
197
    );
198
}
199
else { #password recovery form (to send email)
200
    $template->param( password_recovery => 1 );
201
}
202
203
output_html_with_http_headers $query, $cookie, $template->output;

Return to bug 8753