Magento2: Reset Password Email Issue on Multi Store from Admin

Created on 21 Jul 2016  路  22Comments  路  Source: magento/magento2

Steps to reproduce

  1. Login to Magento Backend
  2. Create Multi Store with Two Different Stores
  3. Go to Magento Store Frontend in both the Stores
  4. Register Customer on both the Stores from Frontend
  5. Login to Magento Backend if not loggedin already
  6. go to Customers -> All Customers
  7. Find the Customer Id which you have Registered on the Store 2
  8. Click on Edit Customer
  9. Click on the Reset Password Link on the Top

    Expected result

  10. The Customer Registered on the Second Store Should Receive the Mail from the Second Store with the Reset Password Link for the Second Store

    Actual result

  11. The Customer Regsitered on the Second Store gets the Email from the Default Store or the First Store

    Solution:

    Inside the Customer AccountManagement.php Model(vendor/magento/module-customer/Model)

Inside sendPasswordReminderEmail function change
Replace:

 public function sendPasswordReminderEmail($customer)
    {
       $storeId = $this->storeManager->getStore()->getId();


    if (!$storeId) {
        $storeId = $this->getWebsiteStoreId($customer);
    }

    $customerEmailData = $this->getFullCustomerObject($customer);

    $this->sendEmailTemplate(
        $customer,
        self::XML_PATH_REMIND_EMAIL_TEMPLATE,
        self::XML_PATH_FORGOT_EMAIL_IDENTITY,
        ['customer' => $customerEmailData, 'store' => $this->storeManager->getStore($storeId)],
        $storeId
    );

    return $this;
}

with:

 public function sendPasswordReminderEmail($customer)
    {  
        // Need to Get Store Id from the Customer Account
        $storeId = $customer->getStoreId();


    if (!$storeId) {
        $storeId = $this->getWebsiteStoreId($customer);
    }

    $customerEmailData = $this->getFullCustomerObject($customer);

    $this->sendEmailTemplate(
        $customer,
        self::XML_PATH_REMIND_EMAIL_TEMPLATE,
        self::XML_PATH_FORGOT_EMAIL_IDENTITY,
        ['customer' => $customerEmailData, 'store' => $this->storeManager->getStore($storeId)],
        $storeId
    );

    return $this;
}
Fixed in 2.2.x Fixed in 2.3.x Clear Description Confirmed Format is not valid Ready for Work Reproduced on 2.1.x Reproduced on 2.2.x Reproduced on 2.3.x bug report good first issue

Most helpful comment

Because of all the use of private scope, I had to write a 163 line plugin to change this single line :(

All 22 comments

Solution:

Inside the Customer AccountManagement.php Model(vendor/magento/module-customer/Model)

Inside sendPasswordReminderEmail function change
Replace:
public function sendPasswordReminderEmail($customer)
{
$storeId = $this->storeManager->getStore()->getId();

if (!$storeId) {
    $storeId = $this->getWebsiteStoreId($customer);
}

$customerEmailData = $this->getFullCustomerObject($customer);

$this->sendEmailTemplate(
    $customer,
    self::XML_PATH_REMIND_EMAIL_TEMPLATE,
    self::XML_PATH_FORGOT_EMAIL_IDENTITY,
    ['customer' => $customerEmailData, 'store' => $this->storeManager->getStore($storeId)],
    $storeId
);

return $this;

}
with:

public function sendPasswordReminderEmail($customer)
{

// Need to Get Store Id from the Customer Account
$storeId = $customer->getStoreId();

if (!$storeId) {
    $storeId = $this->getWebsiteStoreId($customer);
}

$customerEmailData = $this->getFullCustomerObject($customer);

$this->sendEmailTemplate(
    $customer,
    self::XML_PATH_REMIND_EMAIL_TEMPLATE,
    self::XML_PATH_FORGOT_EMAIL_IDENTITY,
    ['customer' => $customerEmailData, 'store' => $this->storeManager->getStore($storeId)],
    $storeId
);

return $this;

}

Can confirm this is still an issue, seems like an oversight honestly. We have to overwrite these via a plugin or by editing code in order to get around this issue.

The check for $storeId seems useless and only serves to break the email sent from the backend.

If a customer can only be associated to one website, what's the point of getting the currently viewed website?

On the frontend, the website stored on the customer will be the same website the user is looking at 100% of the time as they cannot sing into any other website. The only thing this code does is prevent the password reset from working in admin:

   // given the customer is associated to website id 2....
   // on the frontend, the user is viewing website of id 2. 2 is returned
   //    this is the correct website - 2, all good
   // on the backend, this is 1 - the default website.
   $storeId = $this->storeManager->getStore()->getId();
   // this will always exist, there seems so scenario where $storeId will be falsy
   // on the frontend this doesn't matter, $storeId already has 2, email will be sent correctly
   // on the backend $storeId will be 1, which is the wrong store view. it is truthy, now the correct
   //   store view will never get retrieved
   if (!$storeId) {
       $storeId = $this->getWebsiteStoreId($customer);
   }
   // then the email is sent. in the backend it will be sent from store id 1 regardless of the website
   // associated to the customer

Surely just getting the website associated to the customer account is always going to be right thing to do? In which case literally just use

 $storeId = $this->getWebsiteStoreId($customer);

and nothing else

Why is this not fixed yet!?

This Bug also exists in Version 2.1.3, but above named method sendPasswordReminderEmail() of AccountManagement.php is not used anymore (marked as deprecated). The "faulty" code has moved to method passwordReminder() of customers EmailNotification.php model:

public function passwordReminder(CustomerInterface $customer)
{
    $storeId = $this->storeManager->getStore()->getId();
    if (!$storeId) {
        $storeId = $this->getWebsiteStoreId($customer);
    }

    $customerEmailData = $this->getFullCustomerObject($customer);

    $this->sendEmailTemplate(
        $customer,
        self::XML_PATH_REMIND_EMAIL_TEMPLATE,
        self::XML_PATH_FORGOT_EMAIL_IDENTITY,
        ['customer' => $customerEmailData, 'store' => $this->storeManager->getStore($storeId)],
        $storeId
    );
}

@ngashokkumar Thanks for your issue. The internal ticket was created MAGETWO-70043

In the version 2.1.7-2017-05-30-02-18-42, there is still a problem with multi store and resetting passwords.

This bug is still in 2.1.7. Also other mails send from the backend have a similar issue. The fix mentioned above is already applied but doesnt make any sense, as far as i can tell, since the store id is only used to get the email identity identifier (e.g. "general", "support", "sales").

I think the issue is in the \Magento\Framework\Mail\Template\TransportBuilder::setFrom() function called in \Magento\Customer\Model\EmailNotification::sendEmailTemplate(). The email identity is resolved via the identifier and the setFrom() function doesnt pass the scopeID to the senderResolver so the scope will always be null on function initialization. Giving that the senderResolver uses the current Scope if none given would explain why emails from the backend are always send via the default store identities.

As a hotfix i changed \Magento\Framework\Mail\Template\TransportBuilder::setFrom()

public function setFrom($from)
    {
        $result = $this->_senderResolver->resolve($from);
        $this->message->setFrom($result['email'], $result['name']);
        return $this;
    }

to

public function setFrom($from, $scopeId = null)
    {
        $result = $this->_senderResolver->resolve($from, $scopeId);
        $this->message->setFrom($result['email'], $result['name']);
        return $this;
    }

Then you can add the store id to the setFrom Method (e.g. \Magento\Customer\Model\EmailNotification::sendEmailTemplate())

private function sendEmailTemplate(
        $customer,
        $template,
        $sender,
        $templateParams = [],
        $storeId = null,
        $email = null
    ) {
        $templateId = $this->getTemplateId($template, 'store', $storeId);
        if ($email === null) {
            $email = $customer->getEmail();
        }
        $transport = $this->transportBuilder->setTemplateIdentifier($templateId)
            ->setTemplateOptions(['area' => 'frontend', 'store' => $storeId])
            ->setTemplateVars($templateParams)
            ->setFrom($this->scopeConfig->getValue($sender, 'store', $storeId), $storeId)
            ->addTo($email, $this->customerViewHelper->getCustomerName($customer))
            ->getTransport();
        $transport->sendMessage();
    }

Seems to work in my case for all customer emails. Notice that this has to be done to all emails send via the backend. As an alternative you can resolve the desired sender email directly and pass the value to the setFrom method instead of using the email identity identifier.

I've found that when resetting a customer password via admin, the method used in 2.1.7 is passwordReminder in module-customer/Model/EmailNotification.php. This still has the $store_id set as follow:

$storeId = $this->storeManager->getStore()->getId();

Replacing this with:

$storeId = $customer->getStoreId();

should fix the issue of the links being generated for the wrong store.

Because of all the use of private scope, I had to write a 163 line plugin to change this single line :(

@krutkoroma79911 This problem was already solved in Magento 2.0.8, but it never made it to the 2.1 branch.
https://github.com/magento/magento2/blob/2.0.8/lib/internal/Magento/Framework/Mail/Template/TransportBuilder.php#L182

@ngashokkumar, thank you for your report.
We've created internal ticket(s) MAGETWO-70043 to track progress on the issue.

I'm working on this. Will create backport for 2.1-develop and 2.2-develop branch.

Hello ,
Same issue with my multistore i have 2 different store normal and trade when i have reset password from admin for reset wholesaler customer password link are not created for trade site i have follow up all your above steps no result pls somebody help me to solve this issue.

@maderlock your code is working 2.1.7 perfectly thanks for your updated code .
vendor/magento/module-customer/Model/EmailNotification.php

Working function -

public function passwordReminder(CustomerInterface $customer)
{
$storeId = $customer->getStoreId();
if (!$storeId) {
$storeId = $this->getWebsiteStoreId($customer);
}

    $customerEmailData = $this->getFullCustomerObject($customer);

    $this->sendEmailTemplate(
        $customer,
        self::XML_PATH_REMIND_EMAIL_TEMPLATE,
        self::XML_PATH_FORGOT_EMAIL_IDENTITY,
        ['customer' => $customerEmailData, 'store' => $this->storeManager->getStore($storeId)],
        $storeId
    );
}

@joshdavenport

Surely just getting the website associated to the customer account is always going to be right thing to do?

Not always. If you have a website with different store views in different languages, and the customer tends to use a specific store view with a specific language, then you might prefer to use the language of the store view the customer is logged into.

I've run into this scenario in practice in a Magento 1 application, in which this issue is also present. Customers who browse the site in Italian or Hungarian are getting English language password reset emails, because the website's language is English.

Interesting point @ToonSpinISAAC though wouldn't that be a problem with this bug present or not as customers don't have a record of which store view they registered in (or last opted into via language switcher or whatever), only the store. I guess this issue would have to be dealt with some other way?

This is causing issues for me too in a case where I have a retail and wholesale site, with wholesalers sending password resets to customers on the retail store.

Due to all the private declarations, it's a nightmare to override, I've basically had to copy the entire file for now.

Will this be updated in later versions?

Imagine2018

@rodrigowebjump thank you for joining. Please accept team invitation here and self-assign the issue.

Hi @ngashokkumar. Thank you for your report.
The issue has been fixed in magento/magento2#14800 by @rodrigowebjump in 2.2-develop branch
Related commit(s):

The fix will be available with the upcoming 2.2.5 release.

Hi @ngashokkumar. Thank you for your report.
The issue has been fixed in magento/magento2#15095 by @rogyar in 2.3-develop branch
Related commit(s):

The fix will be available with the upcoming 2.3.0 release.

30794 was born from this.

Was this page helpful?
0 / 5 - 0 ratings