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

(-)a/installer/data/mysql/en/mandatory/sample_notices.sql (+3 lines)
Lines 142-144 Thank you. Link Here
142
142
143
Your library.'
143
Your library.'
144
);
144
);
145
146
INSERT INTO `letter` VALUES ('members','PASSWORD_RESET','','Online password reset',1,'Koha password recovery','<html>\r\n<p>This email has been sent in response to your password recovery request for the account <strong><< borrowers.userid>></strong>.\r\n</p>\r\n<p>\r\nYou can now create your new password using the following link:\r\n<br/><a href=\"<<passwordreseturl>>\"><<passwordreseturl>></a>\r\n</p>\r\n<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>\r\n<p>Thank you.</p>\r\n</html>\r\n','email');
147
(-)a/installer/data/mysql/kohastructure.sql (+11 lines)
Lines 3422-3427 CREATE TABLE IF NOT EXISTS `misc_files` ( -- miscellaneous files attached to rec Link Here
3422
  KEY `record_id` (`record_id`)
3422
  KEY `record_id` (`record_id`)
3423
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
3423
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
3424
3424
3425
-- Table structure for table 'borrower_password_recovery'
3426
-- this stores the unique ID sent by email to the patron, for future validation
3427
--
3428
3429
CREATE TABLE IF NOT EXISTS borrower_password_recovery (
3430
  borrowernumber int(11) NOT NULL,
3431
  uuid varchar(128) NOT NULL,
3432
  valid_until timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
3433
  KEY borrowernumber (borrowernumber)
3434
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
3435
3425
/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
3436
/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
3426
/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
3437
/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
3427
/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
3438
/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
(-)a/installer/data/mysql/sysprefs.sql (+1 lines)
Lines 270-275 INSERT INTO systempreferences ( `variable`, `value`, `options`, `explanation`, ` Link Here
270
('opacreadinghistory','1','','If ON, enables display of Patron Circulation History in OPAC','YesNo'),
270
('opacreadinghistory','1','','If ON, enables display of Patron Circulation History in OPAC','YesNo'),
271
('OpacRenewalAllowed','0',NULL,'If ON, users can renew their issues directly from their OPAC account','YesNo'),
271
('OpacRenewalAllowed','0',NULL,'If ON, users can renew their issues directly from their OPAC account','YesNo'),
272
('OpacRenewalBranch','checkoutbranch','itemhomebranch|patronhomebranch|checkoutbranch|null','Choose how the branch for an OPAC renewal is recorded in statistics','Choice'),
272
('OpacRenewalBranch','checkoutbranch','itemhomebranch|patronhomebranch|checkoutbranch|null','Choose how the branch for an OPAC renewal is recorded in statistics','Choice'),
273
('OpacResetPassword','1','','Shows the \'Forgot your password?\' link in the OPAC','YesNo'),
273
('OPACResultsSidebar','','70|10','Define HTML to be included on the search results page, underneath the facets sidebar','Textarea'),
274
('OPACResultsSidebar','','70|10','Define HTML to be included on the search results page, underneath the facets sidebar','Textarea'),
274
('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'),
275
('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'),
275
('OpacSeparateHoldings','0',NULL,'Separate current branch holdings from other holdings (OPAC)','YesNo'),
276
('OpacSeparateHoldings','0',NULL,'Separate current branch holdings from other holdings (OPAC)','YesNo'),
(-)a/installer/data/mysql/updatedatabase.pl (+18 lines)
Lines 8560-8565 if ( CheckVersion($DBversion) ) { Link Here
8560
    SetVersion($DBversion);
8560
    SetVersion($DBversion);
8561
}
8561
}
8562
8562
8563
$DBversion = "3.17.00.XXX";
8564
if ( CheckVersion($DBversion) ) {
8565
    $dbh->do("INSERT INTO systempreferences (variable,value,options,explanation,type) VALUES ('OpacResetPassword',  '1','','Shows the ''Forgot your password?'' link in the OPAC','YesNo')");
8566
    $dbh->do(q{
8567
        CREATE TABLE IF NOT EXISTS borrower_password_recovery (
8568
          borrowernumber int(11) NOT NULL,
8569
          uuid varchar(128) NOT NULL,
8570
          valid_until timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
8571
          KEY borrowernumber (borrowernumber)
8572
          ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
8573
    });
8574
    $dbh->do(q{
8575
        INSERT INTO `letter` (module, code, branchcode, name, is_html, title, content, message_transport_type) VALUES ('members','PASSWORD_RESET','','Online password reset',1,'Koha password recovery','<html>\r\n<p>This email has been sent in response to your password recovery request for the account <strong><< borrowers.userid>></strong>.\r\n</p>\r\n<p>\r\nYou can now create your new password using the following link:\r\n<br/><a href=\"<<passwordreseturl>>\"><<passwordreseturl>></a>\r\n</p>\r\n<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>\r\n<p>Thank you.</p>\r\n</html>\r\n','email')
8576
        });
8577
    print "Upgrade to $DBversion done (Bug 8753: Add forgot password link to OPAC)\n";
8578
    SetVersion ($DBversion);
8579
}
8580
8563
=head1 FUNCTIONS
8581
=head1 FUNCTIONS
8564
8582
8565
=head2 TableExists($table)
8583
=head2 TableExists($table)
(-)a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/opac.pref (+8 lines)
Lines 338-343 OPAC: Link Here
338
                  no: "Don't allow"
338
                  no: "Don't allow"
339
            - patrons to change their own password on the OPAC. Note that this must be off to use LDAP authentication.
339
            - patrons to change their own password on the OPAC. Note that this must be off to use LDAP authentication.
340
        -
340
        -
341
            - "The user "
342
            - pref: OpacResetPassword
343
              default: 1
344
              choices:
345
                  yes: "can reset"
346
                  no: "can not reset"
347
            - " their password on OPAC."
348
        -
341
            - pref: OPACPatronDetails
349
            - pref: OPACPatronDetails
342
              choices:
350
              choices:
343
                  yes: Allow
351
                  yes: Allow
(-)a/koha-tmpl/opac-tmpl/bootstrap/en/includes/masthead.inc (+4 lines)
Lines 283-288 Link Here
283
                        <label for="mpassword">Password:</label><input type="password" id="mpassword" name="password" />
283
                        <label for="mpassword">Password:</label><input type="password" id="mpassword" name="password" />
284
                    [% 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 %]
284
                    [% 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 %]
285
                    </fieldset>
285
                    </fieldset>
286
                    [% IF Koha.Preference('OpacPasswordChange') && Koha.Preference('OpacResetPassword') %]
287
                         <p><a href="/cgi-bin/koha/opac-password-recovery.pl">Forgot your password?</  a></p>
288
                    [% END %]
286
            </div>
289
            </div>
287
            <div class="modal-footer">
290
            <div class="modal-footer">
288
                <input type="submit" class="btn btn-primary" value="Log in" />
291
                <input type="submit" class="btn btn-primary" value="Log in" />
Lines 290-292 Link Here
290
            </div>
293
            </div>
291
        </form> <!-- /#auth -->
294
        </form> <!-- /#auth -->
292
    </div>  <!-- /#modalAuth  -->
295
    </div>  <!-- /#modalAuth  -->
296
(-)a/koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-auth.tt (+3 lines)
Lines 94-99 Link Here
94
                                    <input type="text"  size="25" id="userid"  name="userid" />
94
                                    <input type="text"  size="25" id="userid"  name="userid" />
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
                                [% IF Koha.Preference('OpacPasswordChange') && Koha.Preference('OpacResetPassword') %]
98
                                    <p><a href="/cgi-bin/koha/opac-password-recovery.pl">Forgot your password?</a></p>
99
                                [% END %]
97
100
98
                                <input type="submit" value="Log in" class="btn" />
101
                                <input type="submit" value="Log in" class="btn" />
99
                                <div id="nologininstructions">
102
                                <div id="nologininstructions">
(-)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/opac/opac-password-recovery.pl (-1 / +231 lines)
Line 0 Link Here
0
- 
1
#!/usr/bin/perl
2
3
use strict;
4
use Modern::Perl;
5
use CGI;
6
7
use C4::Auth;
8
use C4::Koha;
9
use C4::Members qw(changepassword GetMember GetMemberDetails );
10
use C4::Output;
11
use C4::Context;
12
use Koha::AuthUtils qw(hash_password);
13
14
my $query = new CGI;
15
16
my ( $template, $dummy, $cookie ) = get_template_and_user(
17
    {
18
        template_name   => "opac-password-recovery.tmpl",
19
        query           => $query,
20
        type            => "opac",
21
        authnotrequired => 1,
22
        debug           => 1,
23
    }
24
);
25
26
my $email          = $query->param('email') // q{};
27
my $password       = $query->param('password');
28
my $repeatPassword = $query->param('repeatPassword');
29
my $minPassLength  = C4::Context->preference('minPasswordLength');
30
my $id             = $query->param('id');
31
my $uniqueKey      = $query->param('uniqueKey');
32
my $username       = $query->param('username');
33
my $borrower_number;
34
35
#errors
36
my $hasError;
37
38
#email form error
39
my $errNoEmailFound;
40
my $errAlreadyStartRecovery;
41
42
#new password form error
43
my $errLinkNotValid;
44
my $errPassNotMatch;
45
my $errPassTooShort;
46
47
my $dbh = C4::Context->dbh;
48
49
if ( $query->param('sendEmail') || $query->param('resendEmail') ) {
50
    #send mail + confirmation
51
52
    #try with the main email
53
    $email ||= ''; # avoid undef
54
    my $borrower_infos = GetMember( email => $email );
55
    $borrower_infos = GetMember( emailpro => $email ) unless $borrower_infos;
56
    $borrower_infos = GetMember( B_email => $email ) unless $borrower_infos;
57
    if($borrower_infos) {
58
        $borrower_number = $borrower_infos->{'borrowernumber'};
59
    }
60
61
    if ( !$email || !$borrower_number ) {
62
        $hasError        = 1;
63
        $errNoEmailFound = 1;
64
    }
65
    elsif ( !$query->param('resendEmail') ) {
66
        my $sth = $dbh->prepare(
67
"SELECT borrowernumber FROM borrower_password_recovery WHERE NOW() < valid_until AND borrowernumber = ?"
68
        );
69
        $sth->execute($borrower_number);
70
        if ( my $already = $sth->fetchrow ) {
71
            $hasError                = 1;
72
            $errAlreadyStartRecovery = 1;
73
        }
74
    }
75
76
    if ($hasError) {
77
        $template->param(
78
            hasError                => 1,
79
            errNoEmailFound         => $errNoEmailFound,
80
            errAlreadyStartRecovery => $errAlreadyStartRecovery,
81
            password_recovery       => 1,
82
            email                   => HTML::Entities::encode($email),
83
        );
84
    }
85
    elsif ( SendPasswordRecoveryEmail( $borrower_infos, $email, $query, $query->param('resendEmail') ) ) {#generate uuid and send recovery email
86
        $template->param(
87
            mail_sent => 1,
88
            email     => $email
89
        );
90
    }
91
    else {# if it doesnt work....
92
        $template->param(
93
            password_recovery => 1,
94
            sendmailError     => 1
95
        );
96
    }
97
}
98
elsif ( $query->param('passwordReset') ) {
99
    #new password form
100
    #check if the link is still valid
101
    my $sth = $dbh->prepare(
102
        "SELECT borrower_password_recovery.borrowernumber, userid
103
                              FROM borrower_password_recovery, borrowers
104
                              WHERE borrowers.borrowernumber = borrower_password_recovery.borrowernumber
105
                              AND NOW() < valid_until
106
                              AND uuid = ?"
107
    );
108
    $sth->execute($uniqueKey);
109
    ( $borrower_number, $username ) = $sth->fetchrow;
110
111
    #validate password length & match
112
    if (   ($borrower_number)
113
        && ( $password eq $repeatPassword )
114
        && ( length($password) >= $minPassLength ) )
115
    {  #apply changes
116
        changepassword( $username, $borrower_number, hash_password($password) );
117
118
        #remove entry
119
        my $sth = $dbh->prepare("DELETE FROM borrower_password_recovery WHERE uuid = ? or NOW() > valid_until");
120
        $sth->execute($uniqueKey);
121
122
        $template->param(
123
            password_reset_done => 1,
124
            username            => $username
125
        );
126
    }
127
    else { #errors
128
        if ( !$borrower_number ) { #parameters not valid
129
            $errLinkNotValid = 1;
130
        }
131
        elsif ( $password ne $repeatPassword ) { #passwords does not match
132
            $errPassNotMatch = 1;
133
        }
134
        elsif ( length($password) < $minPassLength ) { #password too short
135
            $errPassTooShort = 1;
136
        }
137
        $template->param(
138
            new_password    => 1,
139
            minPassLength   => $minPassLength,
140
            email           => $email,
141
            uniqueKey       => $uniqueKey,
142
            errLinkNotValid => $errLinkNotValid,
143
            errPassNotMatch => $errPassNotMatch,
144
            errPassTooShort => $errPassTooShort,
145
            hasError        => 1
146
        );
147
    }
148
}
149
elsif ($uniqueKey) {  #reset password form
150
    #check if the link is valid
151
    my $sth = $dbh->prepare(
152
        "SELECT borrower_password_recovery.borrowernumber, userid
153
                              FROM borrower_password_recovery, borrowers
154
                              WHERE borrowers.borrowernumber = borrower_password_recovery.borrowernumber
155
                              AND NOW() < valid_until
156
                              AND uuid = ?"
157
    );
158
    $sth->execute($uniqueKey);
159
    ( $borrower_number, $username ) = $sth->fetchrow;
160
    if ( !$borrower_number ) {
161
        $errLinkNotValid = 1;
162
    }
163
warn "INLIBRO username $username";
164
    $template->param(
165
        new_password    => 1,
166
        minPassLength   => $minPassLength,
167
        email           => $email,
168
        uniqueKey       => $uniqueKey,
169
        username        => $username,
170
        errLinkNotValid => $errLinkNotValid
171
    );
172
}
173
else { #password recovery form (to send email)
174
    $template->param( password_recovery => 1 );
175
}
176
177
output_html_with_http_headers $query, $cookie, $template->output;
178
179
#
180
# It creates an email using the templates and send it to the user, using the specified email
181
#
182
sub SendPasswordRecoveryEmail {
183
    my $borrower = shift; # from GetMember
184
    my $userEmail = shift; #to_address (the one specified in the request)
185
    my $query = shift; #only required to determine if 'http' or 'https'
186
    my $update = shift;
187
    
188
    my $dbh = C4::Context->dbh;
189
190
    # generate UUID
191
    my @chars = ("A".."Z", "a".."z", "0".."9");
192
    my $uuid_str;
193
    $uuid_str .= $chars[rand @chars] for 1..32;
194
195
    # insert into database
196
    my $expirydate = DateTime->now(time_zone => C4::Context->tz())->add( days => 2 );
197
    if($update){
198
       my $sth = $dbh->prepare( 'UPDATE borrower_password_recovery set uuid=?, valid_until=? where borrowernumber=? ');
199
       $sth->execute($uuid_str, $expirydate->datetime(), $borrower->{'borrowernumber'});
200
    } else {
201
       my $sth = $dbh->prepare( 'INSERT INTO borrower_password_recovery VALUES (?, ?, ?)');
202
       $sth->execute($borrower->{'borrowernumber'}, $uuid_str, $expirydate->datetime());
203
    }
204
205
    # create link
206
    my $protocol = $query->https() ? "https://" : "http://";
207
    my $uuidLink = $protocol . C4::Context->preference( 'OPACBaseURL' ) . "/cgi-bin/koha/opac-password-recovery.pl?uniqueKey=$uuid_str";
208
209
    # prepare the email
210
    my $letter = C4::Letters::GetPreparedLetter (
211
        module => 'members',
212
        letter_code => 'PASSWORD_RESET',
213
        branchcode => $borrower->{branchcode},
214
        tables => {borrowers => $borrower},
215
        substitute => {passwordreseturl => $uuidLink},
216
    );  
217
218
    # define to/from emails
219
    my $kohaEmail = C4::Context->preference( 'KohaAdminEmailAddress' ); # from
220
221
    C4::Letters::EnqueueLetter( {
222
         letter => $letter,
223
         borrowernumber => $borrower->{'borrowernumber'},
224
         to_address => $userEmail,
225
         from_address => $kohaEmail,
226
         message_transport_type => 'email',
227
    } );     
228
229
    return 1;
230
}
231

Return to bug 8753