Saturday, November 21, 2009

How to reset the passwords for all the customers (for Andrew)

This is a response for: http://www.magentocommerce.com/boards/viewthread/67065/
Hi Andrew You can create a new php file.
Let's call it customer.php and place it on the same level as index.php file af Magento. Put this code in the file.
error_reporting(E_ALL | E_STRICT);
$mageFilename = 'app/Mage.php';
if (!file_exists($mageFilename)) {
if (is_dir('downloader')) {
header("Location: downloader");
} else {
echo $mageFilename." was not found";
}
exit;
}
require_once $mageFilename;
Varien_Profiler::enable();
Mage::setIsDeveloperMode(true);
ini_set('display_errors', 1);
umask(0);
Mage::app('default');
$passwordLength = 10;
$customers = Mage::getModel('customer/customer')->getCollection();
foreach ($customers as $customer){
    //[Update 2012-04-19: thanks Michael]
    $customer->load($customer->getId());
    //[/Update 2012-04-19]
    $customer->setPassword($customer->generatePassword($passwordLength))->save();
    $customer->sendNewAccountEmail();
}
?>


then just call the page in your browser http://mydomain.com/customer.php After you finish delete the customer.php file from your server to avoid running it again.

36 comments:

  1. Can you limit the selection to users without passwords? I imported users without passwords and I want to assign them to those users, but not users with passwords.

    ReplyDelete
  2. Yes you can. Just replace this:

    $customers = Mage::getModel('customer/customer')->getCollection();
    with this
    $customers = Mage::getModel('customer/customer')->getCollection()->addAttributeToFilter(array(array('attribute'=>'password_hash', 'eq'=>'')));

    ReplyDelete
  3. I'm wondering if you can import customer login and hashed password, I believe Magento uses MD5 encryption I still can't find the code needed to correctly import customers. Also what if the customer is in a different customer group?

    ReplyDelete
  4. For the customer group I have a solution.
    In the script above inside the foreach you can add before calling $customer->save(), this:
    $customer->setGroupId('YOUR GROUP ID HERE').

    For your first problem, importing the hashed passwords I don't have a solution.
    You can try instead of setPassword to use setPasswordHash, but I'm not sure it will work. I don't know (yet) how the password salt works in Magento.

    ReplyDelete
  5. I had one small problem with this, if you leave the template as is, using the magento calls for urls, i.e. the call thats in the new customer email template that links them to login, (/customer/login) then it links relative to where the script was origionally called. so my login link inside of my email was mydomain.com/my_resetscript.php/customer/login

    We have a database of 3400 customers, and after about 100 users clicked it immediately (I forgot to remove the script right away) it sent out a total of somewhere near 60,000+ emails, and froze our server. It also upset a lot of customers, so if you are going to do this, take precautions. This is just a warning.

    ReplyDelete
  6. Hello. I get this error when using the code on Magento 1.4.1.1. Any ideas would be appreciated.

    Fatal error: Uncaught exception 'Mage_Core_Model_Store_Exception' in /home/magento/app/code/core/Mage/Core/Model/App.php:1247 Stack trace: #0 /home/magento/app/code/core/Mage/Core/Model/App.php(760): Mage_Core_Model_App->throwStoreException() #1 /home/magento/app/Mage.php(322): Mage_Core_Model_App->getStore(NULL) #2 /home/magento/app/Mage.php(334): Mage::getStoreConfig('web/url/use_sto...', NULL) #3 /home/magento/app/code/core/Mage/Core/Controller/Request/Http.php(204): Mage::getStoreConfigFlag('web/url/use_sto...') #4 /home/magento/app/code/core/Mage/Core/Controller/Request/Http.php(156): Mage_Core_Controller_Request_Http->_canBeStoreCodeInUrl() #5 /home/magento/app/code/core/Mage/Core/Model/App.php(379): Mage_Core_Controller_Request_Http->setPathInfo() #6 /home/magento/app/code/core/Mage/Core/Model/App.php(262): Mage_Core_Model_App->_initRequest() #7 /home/magento/app/Mage.php(570): Mage_Core_Model_App->init('default', 'store', Array) #8 /home/magento/gen_pass.php(17): Mage::app('default') #9 {main} thrown in /home/magento/app/code/core/Mage/Core/Model/App.php on line 1247

    ReplyDelete
  7. Hello Aydin. I never got this error before, but I think there is a problem with your store code. You don't have a store with the code 'default'. If this is the case replace 'default' with one of your store codes in 'Mage::app('default');'.

    ReplyDelete
  8. Hello tzyganu,

    Yes, changing the store code resolved the problem. Thanks for your help and the great script.

    ReplyDelete
  9. Hi, great post but just wondering would this would work on a very large store?
    Our store has over 13000 customers that we have just migrated from creLoaded.
    Will this work for so many or will it just error or is there a way to process the emails in batches to ensure success?

    ReplyDelete
  10. Hello Ryan.
    In theory it should work for any number of customers. But the time needed to finish is going to be long.
    My recommendation is to split them in batches.
    In order to add a filter to the collection replace this
    $customers = Mage::getModel('customer/customer')->getCollection();
    with this
    $customers = Mage::getModel('customer/customer')->getCollection()->addAttributeToFilter('entity_id', array('gte'=>$startId))->addAttributeToFilter('entity_id',array('lt'=>$endId));
    'gte' means greater then or equal
    'lt' means less then.
    You can addapt the script to receive 2 parameters $startId and $endId and run it from the command line on separate threads.

    Cheers,
    Marius.

    ReplyDelete
  11. This script works really well. The only problem I have is that the customer’s name does not display properly in the email. I’ve tried several different variations of the customer name variable in my transactional template (and yes, the template is enabled and configured). The default is {{htmlescape var=$customer.name}}. When I send individual password generation emails from the Magento backend they display the customer name just fine using the default {{htmlescape var=$customer.name}}.

    Do I need to pass an argument to sendNewAccountEmail()?

    Thanks for any help on this!

    ReplyDelete
    Replies
    1. Hello Michael.
      What do you mean by 'customer’s name does not display properly'. Give me an example.
      This shouldn't happen, because the script uses the same methods as the 'create account' process.
      Then again I wrote this 2 and half years ago, maybe something has changed. :).

      Marius.

      Delete
    2. Marius, thank you for the quick reply.

      What I mean is that sendNewAccountEmail() sends the New account email template. My transactional template for this email (which is based on the default and was only changed to include my store logo) has a subject = "Welcome, {{var customer.name}}!" and a first line of "Dear {{htmlescape var=$customer.name}},"

      This works fine when users register and displays the full customer name. However, when I use your script those two variables both are blank. So the subejct is "Welcome, !" and the first line says "Dear ,".

      I hope this clarified my question and thank you again for your diligence!

      Delete
    3. Marius, just wondering if you saw my reply clarifying your question...

      Delete
    4. Yes I saw it.
      Sorry for answering with a delay. Please take into consideration the timezone difference. I'm on GMT + 2.

      It seams that not all the attributes for the customer are loaded when looping through the collection.
      The solution is to add this line: $customer->load($customer->getId()); as the first line inside the foreach loop. I updated the post to fit your needs.

      Cheers,
      Marius.

      Delete
  12. Marius, no worries about the timing. I know Magento devs are all over the world. I only got concerned because your first reply was so quick!

    Thank you for taking the time to go back over this after so long. Magnificent job, it now works perfectly!

    Again, I really appreciate your diligence! Thank you again!

    ReplyDelete
    Replies
    1. Yeah...my fist response was quick because you caught me just before "shutdown".
      Glad I could help.
      Marius.

      Delete
  13. I'm trying to use your script to send passwords in batches. I just added $customers = Mage::getModel('customer/customer')->getCollection()->addAttributeToFilter('entity_id', array(1=>$startId))->addAttributeToFilter('entity_id',array(50=>$endId)); to the script. I'm getting this error Fatal error: Uncaught exception 'Exception' with message 'Notice: Undefined variable: startId in /var/www/clients/client1/web1/web/customer.php on line 19' in /var/www/clients/client1/web1/web/app/code/core/Mage/Core/functions.php:245Stack trace: #0 /var/www/clients/client1/web1/web/customer.php(19): mageCoreErrorHandler(8, 'Undefined varia...', '/var/www/client...', 19, Array) #1 {main} thrown in /var/www/clients/client1/web1/web/app/code/core/Mage/Core/functions.php on line 245

    ReplyDelete
    Replies
    1. try it like this:
      $startId = 1;
      $endId = 50;
      customers = Mage::getModel('customer/customer')->getCollection()->addAttributeToFilter('entity_id', array('gte'=>$startId))->addAttributeToFilter('entity_id',array('lt'=>$endId));

      Delete
  14. I had to uncomment the line Mage::setIsDeveloperMode(true); to get it to work. I would otherwise throw a fatal error. (version 1.6.2)

    ReplyDelete
  15. Thank you for the post. I would like to use this but I have a little issue. Is there a way this will reset the password of the customers who have not reset their password or never logged in magento. We migrated customers from Oscommerce and many customers have already reset their passwords or are new customers. I want to reset only users who have not logged into magento yet. Appreciate the help. Thanks

    ReplyDelete
    Replies
    1. Hello Magentist.
      I'm not sure this will work but you can try it.
      There is a field in the customer table (customer_entity) called 'updated_at'. This is the last date and hour any customer data was updated (captain obvious).
      You can try to reset the password only for customers that have this field lower that a specific date (date of the migration + 5 minutes for example).
      I think you can do that by replacing in the script this:
      $customers = Mage::getModel('customer/customer')->getCollection();

      with this:
      $somedate = YOUR DATE VALUE HERE (for example: 2012-11-11 12:35:00)
      $customers = Mage::getModel('customer/customer')->getCollection()
      ->addAttributeToFilter('updated_at', array('gte'=>$somedate));

      Don't try it directly on live. Try it first on a backup db and don't send out emails.

      Let me know if it works.

      If in your previous website (Oscommerce) you didn't have this field it's even easier. The updated_at field should be blank (0000-00-00 00:00:00) or will have the same value for all the customers (the date of the migration). But this depends on the way the migration was done.

      Cheers.
      Marius.

      Delete
  16. Thank you Marius, you are awesome. I will try it out and let you know if it worked.

    ReplyDelete
  17. Thanks tzyganu!
    I can confirm that still working for magento 1.7.0.2

    But don't work your patch for only send to customers without passwords, the second comment in this post.

    $customers = Mage::getModel('customer/customer')->getCollection();
    with this
    $customers = Mage::getModel('customer/customer')->getCollection()->addAttributeToFilter(array(array('attribute'=>'password_hash', 'eq'=>'')));

    When I change the file, is not sending any e-mail how if all customers have a password.

    I was imported the customer using the default magento import tool.

    Can you help me with this?
    thanks!

    ReplyDelete
  18. hey,

    When i run the code i get this fatal error.

    Fatal error: Uncaught exception 'Exception' with message 'Warning: simplexml_load_string() [function.simplexml-load-string]: Entity: line 8: parser error : StartTag: invalid element name in /home/senorehe/public_html/lib/Varien/Simplexml/Config.php on line 510' in /home/senorehe/public_html/app/code/core/Mage/Core/functions.php:245 Stack trace: #0 [internal function]: mageCoreErrorHandler(2, 'simplexml_load_...', '/home/senorehe/...', 510, Array) #1 /home/senorehe/public_html/lib/Varien/Simplexml/Config.php(510): simplexml_load_string('loadFile('/home/senorehe/...') #4 /home/senorehe/public_html/app/code/core/Mage/Core/Model/Config.php(315): Mage_Core_Model_Config->_loadDeclaredModules() # in /home/senorehe/public_html/app/code/core/Mage/Core/functions.php on line 245

    any chance you know why?

    ReplyDelete
    Replies
    1. Most probably this has nothing do to with the code in the post. I think that one of the files in app/etc/modules is not properly formatted.

      Delete
  19. if i want to send reset password mail to all customer what changes have i to made for it ??

    ReplyDelete
  20. how do i send a custom transactional email i created using the template "New Account" to the affected group users instead of the default email?

    ReplyDelete
  21. Any updates need to be made to this for 1.9?

    ReplyDelete
    Replies
    1. I haven't tested this on 1.9 but it should work without any issues. The internal API did not change regarding to customer passwords.

      Delete
  22. Hello, i have installed the script with 777 privilegies, but i have error 404. What im doing wrong?

    ReplyDelete
  23. This comment has been removed by the author.

    ReplyDelete
  24. This comment has been removed by the author.

    ReplyDelete
  25. For anybody who arrives here and needs to email only customers who don't have a password and/or have never been sent an email with this code.

    First a quick note, for whatever reason, you are unable to check whether the password_hash is null or ''. I tried so many variations and am pretty sure it just fails to filter this for some reason. You can however, check if the password_hash already exists. Secondly, my php and Magento skills are nonexistent, so feel free to make this code more efficient.

    To use this code, you must create a custom attribute for customers called initemail and set it as a boolean (Yes/No) with default as No.

    getCollection()

    //Check for accounts
    ->addAttributeToFilter(array(array('attribute'=>'password_hash', 'neq'=>'')));


    foreach ($customers as $customer){
    $customer->load($customer->getId());
    //Update custom field to true
    $customer->setinitemail(1)->save();
    };


    //Email based on custom field
    $customers1 = Mage::getModel('customer/customer')->getCollection()

    ->addAttributeToFilter(
    array(
    array('attribute'=>'initemail','0'),
    array('attribute'=>'initemail',array('null'=>true))
    ));

    //Original code + custom field update
    foreach ($customers1 as $cust){
    //[Update 2012-04-19: thanks Michael]
    $cust->load($cust->getId());
    //[/Update 2012-04-19]
    $cust->setPassword($cust->generatePassword($passwordLength))->save();
    $cust->sendNewAccountEmail();
    $cust->setinitemail(1)->save();
    }

    ?>

    ReplyDelete