Hi Andrei,
It happens everyday.
The steps customers take are as follows:
1. Click on 'Forgot Password' (takes you for the Password Recovery page)
2. Enter email address and click on 'Recover' button.
3. Password Recovery email is sent using the Customer.PasswordRecovery Message Template
We have updated the standard template to suit our clients Brand. The template contains the following Html snippet
<p>Hi %Customer.FirstName%,</p>
<p>To change your password, please <a href="%Customer.PasswordRecoveryURL%">click here</a>.<br />If this link does not work, please reply to this email to let us know and we will reset it for you.</p>
4. Customer received the email. This is when the bug shows. The %Customer.PasswordRecoveryURL% token is not set. In some instances it has the full Url, and when the customer clicks the link, it goes to the Change Password page which redirects to the homepage.
So the bug is 2 fold and it's really weird cos when we test it, it works fine for us.
Here is the PasswordRecoverySend method from CustomerController
[HttpPost, ActionName("PasswordRecovery")]
[FormValueRequired("send-email")]
public ActionResult PasswordRecoverySend(PasswordRecoveryModel model)
{
if (ModelState.IsValid)
{
var customer = _customerService.GetCustomerByEmail(model.Email);
if (customer != null && customer.Active && !customer.Deleted)
{
var passwordRecoveryToken = Guid.NewGuid();
_genericAttributeService.SaveAttribute(customer, SystemCustomerAttributeNames.PasswordRecoveryToken, passwordRecoveryToken.ToString());
_workflowMessageService.SendCustomerPasswordRecoveryMessage(customer, _workContext.WorkingLanguage.Id);
model.Result = _localizationService.GetResource("Account.PasswordRecovery.EmailHasBeenSent");
}
else
model.Result = _localizationService.GetResource("Account.PasswordRecovery.EmailNotFound");
return View(model);
}
//If we got this far, something failed, redisplay form
return View(model);
}
The following snippet is from the MessageTokenProvider class
try
{
//changed to add in Company Name if exists
tokens.Add(new Token("Customer.FullName", customer.GetFullName()));
tokens.Add(new Token("Customer.FirstName", customer.GetAttribute<string>(SystemCustomerAttributeNames.FirstName)));
tokens.Add(new Token("Customer.LastName", customer.GetAttribute<string>(SystemCustomerAttributeNames.LastName)));
tokens.Add(new Token("Customer.VatNumber", customer.GetAttribute<string>(SystemCustomerAttributeNames.VatNumber)));
tokens.Add(new Token("Customer.VatNumberStatus", ((VatNumberStatus)customer.GetAttribute<int>(SystemCustomerAttributeNames.VatNumberStatusId)).ToString()));
tokens.Add(new Token("Customer.PhoneNumber", customer.GetAttribute<string>(SystemCustomerAttributeNames.Phone)));
//address
tokens.Add(new Token("Customer.Address1", customer.BillingAddress.Address1));
tokens.Add(new Token("Customer.Address2", customer.BillingAddress.Address2));
tokens.Add(new Token("Customer.City", customer.BillingAddress.City));
tokens.Add(new Token("Customer.ZipPostalCode", customer.BillingAddress.ZipPostalCode));
string companyName = "";
if (string.IsNullOrEmpty(customer.GetAttribute<string>(SystemCustomerAttributeNames.Company)))
companyName = customer.GetFullName();
else
companyName = customer.GetAttribute<string>(SystemCustomerAttributeNames.Company);
tokens.Add(new Token("Customer.Company", companyName));
//note: we do not use SEO friendly URLS because we can get errors caused by having .(dot) in the URL (from the email address)
//TODO add a method for getting URL (use routing because it handles all SEO friendly URLs)
string passwordRecoveryUrl = string.Format("{0}passwordrecovery/confirm?token={1}&email={2}", GetStoreUrl(), customer.GetAttribute<string>(SystemCustomerAttributeNames.PasswordRecoveryToken), HttpUtility.UrlEncode(customer.Email));
string accountActivationUrl = string.Format("{0}customer/activation?token={1}&email={2}", GetStoreUrl(), customer.GetAttribute<string>(SystemCustomerAttributeNames.AccountActivationToken), HttpUtility.UrlEncode(customer.Email));
var wishlistUrl = string.Format("{0}wishlist/{1}", GetStoreUrl(), customer.CustomerGuid);
tokens.Add(new Token("Customer.PasswordRecoveryURL", passwordRecoveryUrl, true));
tokens.Add(new Token("Customer.AccountActivationURL", accountActivationUrl, true));
tokens.Add(new Token("Wishlist.URLForCustomer", wishlistUrl, true));
}
catch
{
}
I have two clients on Nopcommerce and only receiving complaints from 1 client. We have made extensive modifications to various processes, but have not touched the Password Recovery code in any way, except for the Message Template and we have added in a new notification that emails the customer when their Password has changed. The new email template only happens once they can load the Change Password page though.
I'm not sure if because it in a try catch block, the code could be breaking before it reaches formatting the Password Recovery Url?
I'm going to try remove the Recovery Url code out of the try catch block and see what happens. I can ask my client to get their customer to test it for us and see what happens.
Kind Regards,
Orion