Add Security Question/Answer for Forgotten Password – magento

To ensure high security and CPNI Compliance of your magento shop, if customer forget password, then required to provide security answer credentials in order to retrieve a new password. This protects customer information and ensures that the original creator of the account will always have a way of getting their account back (if their account had been hacked/stolen).

In this tutorial, I’m going to explain how to add those two fields to the registration page and require them to be provided on the forgot password page – where they the information will be validated.

[For best practice create new module as usual but I am referring core file name here.]
Please open the following files, as we will be working with them:
**/app/code/core/Mage/Customer/controllers/AccountController.php** – This controls all of the functions that we need to edit.
**/app/design/frontend/default/yourtheme/template/forgotpassword.phtml** – This is the template file for the forgot password page.
**/app/design/frontend/default/yourtheme/template/customer/form/register.phtml** – this is the template file for the register page.
**/app/design/frontend/default/yourtheme/template/customer/form/edit.phtml** – this is the template file for the customer dashboard page.

Alright, first things first. We will start by adding the Security Question and Answer fields to the registration page. We will also have to create a new attribute for the two fields.

At the top of register.phtml, add the following code:

<?php
$setup = new Mage_Eav_Model_Entity_Setup('core_setup');
// Set up the Security Question Attribute
$AttrCode = 'squestion';
$settings = array (
'position' => 1,
'is_required'=> 1
);
// Set up the Security Answer Attribute
$AttrCode2 = 'sanswer';
$settings2 = array (
'position' => 1,
'is_required'=> 1
);
// Adds both attributes
$setup->addAttribute('1', $AttrCode, $settings);
$setup->addAttribute('1', $AttrCode2, $settings2);
?>

Now, navigate your browser to  http://www.yoursite.com/customer/account/create/
By navigating to this page, you have executed the php code we just added to the register.phtml file. Your attributes are now added to database. Please remove or comment out the code we previously added to the register.phtml file (it is no longer needed).

In register.phtml, add the following block of code:

<ul>
        <li>
                <div class="input-box">
                    <label for="squestion"><?php echo $this->__('Security Question') ?> <span class="required">*</span></label><br />
                    <select id="squestion" name="squestion" title="<?php echo $this->__('Security Question') ?>" class="required-entry validate-select">
            <option value="What is the name of your dog?">What is the name of your dog?</option>
                        <option value="What is the name of your favorite teacher?">What is the name of your favorite teacher?</option>
            <option value="What is your maiden name?">What is your maiden name?</option>
            <option value="In what city were you born?">In what city were you born?</option>
            <option value="What is your favorite food?">What is your favorite food?</option>
                    </select>
                </div>
                <div class="input-box">
                    <label for="sanswer"><?php echo $this->__('Security Answer') ?> <span class="required">*</span></label><br />
                    <input type="text" name="sanswer" id="sanswer" value="<?php echo $this->htmlEscape($this->getFormData()->getSecurityAnswer()) ?>" title="<?php echo $this->__('Security Answer') ?>" class="required-entry input-text" />
                </div>
            </li>
        </ul>

Now, in your forgotpassword.phtml file, find this block of code (around line 25):

<fieldset>
                <legend><?php echo $this->__('Retrieve your password here') ?></legend>
        <p><?php echo $this->__('Please enter your email below and we will send you a new password.') ?></p>
        <ul class="form-list">
            <li>
                <div class="input-box"><label for="email_address"><?php echo $this->__('Email Address') ?> <span class="required">*</span></label><br/>
                <input name="email" alt="email" type="text" id="email_address" class="required-entry validate-email input-text" value="<?php echo $this->htmlEscape($this->getEmailValue()) ?>" /></div></li>
        </ul>
    <div class="button-set">
        <p class="required"><?php echo $this->__('* Required Fields') ?></p>
        <a href="<?php echo $this->helper('customer')->getLoginUrl() ?>" class="left">&laquo; <?php echo $this->__('Back to Login') ?></a>
        <button type="submit" class="form-button right"><span><?php echo $this->__('Submit') ?></span></button>
    </div>
        </fieldset>

Replace this block of code with this:

<fieldset>
                <legend><?php echo $this->__('Retrieve your password here') ?></legend>
        <p><?php echo $this->__('Please enter your email below and we will send you a new password.') ?></p>
        <ul class="form-list">
            <li>
                <div class="input-box"><label for="email_address"><?php echo $this->__('Email Address') ?> <span class="required">*</span></label><br/>
                <input name="email" alt="email" type="text" id="email_address" class="required-entry validate-email input-text" value="<?php echo $this->htmlEscape($this->getEmailValue()) ?>" /></div>
        </li>
        <li>
        <div class="input-box">
        <?
        if(strlen($this->htmlEscape($this->getSquestionValue()))>0){
        ?>
        <label for="squestion"><?php echo $this->__('Security Question') ?> <span class="required">*</span></label><br />
        <input name="squestion" alt="squestion" type="text" id="squestion" class="required-entry input-text" style="border:0; background:none; font-weight:bold;" value="<?php echo $this->htmlEscape($this->getSquestionValue()) ?>" readonly /></div>
                <? } else { ?>
        <label for="squestion"><?php echo $this->__('Security Question') ?> <span class="required">*</span></label><br />
                    <select id="squestion" name="squestion" title="<?php echo $this->__('Security Question') ?>" class="required-entry validate-select">
            <option value="What is the name of your dog?">What is the name of your dog?</option>
                        <option value="What is the name of your favorite teacher?">What is the name of your favorite teacher?</option>
            <option value="What is your maiden name?">What is your maiden name?</option>
            <option value="In what city were you born?">In what city were you born?</option>
            <option value="What is your favorite food?">What is your favorite food?</option>
                    </select>
        </div>
        <? } ?>
        </li>
        <li>
        <div class="input-box">
        <label for="sanswer"><?php echo $this->__('Security Answer') ?> <span class="required">*</span></label><br />
        <input name="sanswer" alt="sanswer" type="text" id="sanswer" class="required-entry input-text" /></div>
        </div>
        </li>
        </ul>
    <div class="button-set">
        <p class="required"><?php echo $this->__('* Required Fields') ?></p>
        <a href="<?php echo $this->helper('customer')->getLoginUrl() ?>" class="left">&laquo; <?php echo $this->__('Back to Login') ?></a>
        <button type="submit" class="form-button right"><span><?php echo $this->__('Submit') ?></span></button>
    </div>
        </fieldset>

Alright, your template files are finished. Now we need to edit the AccountController.php code.
Find this block of code:

public function loginAction()
    {
        if ($this->_getSession()->isLoggedIn()) {
            $this->_redirect('*/*/');
            return;
        }
        $this->getResponse()->setHeader('Login-Required', 'true');
        $this->loadLayout();
        $this->_initLayoutMessages('customer/session');
        $this->_initLayoutMessages('catalog/session');
        $this->renderLayout();
    }

Add this line of code immediately before “$this->getResponse()->setHeader(‘Login-Required’, ‘true’);”

if(!$this->_getSession()->getUsername()){$this->_getSession()->unsForgottenEmail();}

Now, find this block of code within the **loginPostAction** function:

if (!$session->login($login['username'], $login['password'])) {
                    $session->addError($this->__('Invalid login or password'));
                    $session->setUsername($login['username']);
                }

Replace it with this:

if (!$session->login($login['username'], $login['password'])) {
                    $session->addError($this->__('Invalid login or password'));
                    $session->setUsername($login['username']);
                    $session->setForgottenEmail($login['username']);
                }

Now, find this block of code within the **createPostAction** function:

$customer = Mage::getModel('customer/customer')
                ->setFirstname($this->getRequest()->getPost('firstname'))
                ->setLastname($this->getRequest()->getPost('lastname'))
                ->setEmail($this->getRequest()->getPost('email'))
                ->setPassword($this->getRequest()->getPost('password'))
                ->setConfirmation($this->getRequest()->getPost('confirmation'))
                ->setId(null);

Add these lines immediately after the line containing setEmail:

->setSquestion($this->getRequest()->getPost('squestion'))
                ->setSanswer($this->getRequest()->getPost('sanswer'))

Now we need to edit the forgotPasswordAction() function. So find this block of code:

public function forgotPasswordAction()
    {
        $this->loadLayout();

        $this->getLayout()->getBlock('forgotPassword')->setEmailValue(
            $this->_getSession()->getForgottenEmail()
        );
        $this->_getSession()->unsForgottenEmail();

        $this->_initLayoutMessages('customer/session');
        $this->renderLayout();
    }

And replace it with this:

public function forgotPasswordAction()
    {
        $this->loadLayout();

        $this->getLayout()->getBlock('forgotPassword')->setEmailValue(
            $this->_getSession()->getForgottenEmail()
        );

        if((strlen($this->_getSession()->getForgottenEmail())>0)){
            $customer = Mage::getModel('customer/customer')
                    ->setWebsiteId(Mage::app()->getStore()->getWebsiteId())
                    ->loadByEmail($this->_getSession()->getForgottenEmail());
            //$this->_getSession()->unsForgottenEmail();
            if ($customer->getId()) {
                $this->getLayout()->getBlock('forgotPassword')->setSquestionValue(
                    $customer->getSquestion()
                );
            } else {
                $this->_getSession()->addError($this->__('This email address was not found in our records'));
            }
        }

        $this->_initLayoutMessages('customer/session');
        $this->renderLayout();
    }

Alright, finally we can edit the forgotPasswordPostAction() function. Find this large block of code:

public function forgotPasswordAction()
    {
        $this->loadLayout();

        $this->getLayout()->getBlock('forgotPassword')->setEmailValue(
            $this->_getSession()->getForgottenEmail()
        );
        $this->_getSession()->unsForgottenEmail();

        $this->_initLayoutMessages('customer/session');
        $this->renderLayout();
    }

    /**
     * Forgot customer password action
     */
    public function forgotPasswordPostAction()
    {
        $email = $this->getRequest()->getPost('email');
        if ($email) {
            if (!Zend_Validate::is($email, 'EmailAddress')) {
                $this->_getSession()->setForgottenEmail($email);
                $this->_getSession()->addError($this->__('Invalid email address'));
                $this->getResponse()->setRedirect(Mage::getUrl('*/*/forgotpassword'));
                return;
            }
            $customer = Mage::getModel('customer/customer')
                ->setWebsiteId(Mage::app()->getStore()->getWebsiteId())
                ->loadByEmail($email);

            if ($customer->getId()) {
                try {
                    $newPassword = $customer->generatePassword();
                    $customer->changePassword($newPassword, false);
                    $customer->sendPasswordReminderEmail();

                    $this->_getSession()->addSuccess($this->__('A new password was sent'));

                    $this->getResponse()->setRedirect(Mage::getUrl('*/*'));
                    return;
                }
                catch (Exception $e){
                    $this->_getSession()->addError($e->getMessage());
                }
            }
            else {
                $this->_getSession()->addError($this->__('This email address was not found in our records'));
                $this->_getSession()->setForgottenEmail($email);
            }
        } else {
            $this->_getSession()->addError($this->__('Please enter your email.'));
            $this->getResponse()->setRedirect(Mage::getUrl('*/*/forgotpassword'));
            return;
        }

        $this->getResponse()->setRedirect(Mage::getUrl('*/*/forgotpassword'));
    }

And replace it with this:

public function forgotPasswordPostAction()
    {
        $email = $this->getRequest()->getPost('email');
        $squestion = $this->getRequest()->getPost('squestion');
        $sanswer = $this->getRequest()->getPost('sanswer');
        if ($email) {
            if (!Zend_Validate::is($email, 'EmailAddress')) {
                $this->_getSession()->setForgottenEmail($email);
                $this->_getSession()->addError($this->__('Invalid email address'));
                $this->getResponse()->setRedirect(Mage::getUrl('*/*/forgotpassword'));
                return;
            }
            $customer = Mage::getModel('customer/customer')
                ->setWebsiteId(Mage::app()->getStore()->getWebsiteId())
                ->loadByEmail($email);

            if ($customer->getId()) {
                if(($customer->getSquestion()==$squestion)&&($customer->getSanswer()==$sanswer)) {
                    try {
                        $newPassword = $customer->generatePassword();
                        $customer->changePassword($newPassword, false);
                        $customer->sendPasswordReminderEmail();

                        $this->_getSession()->addSuccess($this->__('A new password was sent'));

                        $this->getResponse()->setRedirect(Mage::getUrl('*/*'));
                        return;
                    }
                    catch (Exception $e){
                        $this->_getSession()->addError($e->getMessage());
                    }
                } else {
                    $this->_getSession()->addError($this->__('Your security Q&A credentials were incorrect.'));
                    $this->_getSession()->setForgottenEmail($email);
                }
            }
            else {
                $this->_getSession()->addError($this->__('This email address was not found in our records'));
                $this->_getSession()->setForgottenEmail($email);
            }
        } else {
            $this->_getSession()->addError($this->__('Please enter your email.'));
            $this->getResponse()->setRedirect(Mage::getUrl('*/*/forgotpassword'));
            return;
        }

        $this->getResponse()->setRedirect(Mage::getUrl('*/*/forgotpassword'));
    }

If you navigate to your account login page and the login fails, you will be redirected to the same page telling you that your password/username was incorrect. Now, click on the Forgot Password link that should already be there. You will notice that the username and security question will automatically be filled in for the username you attempted to login to. From here, put in your security answer and your password will be sent to you. If you type in the wrong security answer, you will get a notice saying your security credentials were incorrect.

Add extra field(s) in registration page

Dear Tow…, this post is basically for new guys. Few guys asked me how to add extra field(s) in registration process. So, here I will try to give few technical tricks.

1. Add new fields in config xml (app\code\core\Mage\Customer\etc\config.xml). If you create new module then you can add this xml block in module config xml file. Create new module is the best practice and recommended :)

<?xml version="1.0"?>
<fieldsets>
   <customer_account>
     <prefix><create>1</create><update>1</update><name>1</name></prefix>
     <firstname><create>1</create><update>1</update><name>1</name></firstname>
     <middlename><create>1</create><update>1</update><name>1</name></middlename>
     <lastname><create>1</create><update>1</update><name>1</name></lastname>
     <suffix><create>1</create><update>1</update><name>1</name></suffix>
     <email><create>1</create><update>1</update></email>
     <password><create>1</create></password>
     <confirmation><create>1</create></confirmation>
     <dob><create>1</create><update>1</update></dob>
     <taxvat><create>1</create><update>1</update></taxvat>
     <gender><create>1</create><update>1</update></gender>

     /* newly added field(s)*/
     <accounttype><create>1</create><update>1</update><name>1</name></accounttype>
     <companyname><create>1</create><update>1</update><name>1</name></companyname>
  </customer_account>
</fieldsets>

2. Add these extra field(s) in your respective theme registration.phtml (app\design\frontend\…\…\template\customer\form\registration.phtml). Keep the field name same as you mentioned in config.xml

3. To save extra data in database with customer registration process you have to add these extra field(s) as entity attribute in eav_attribute table. Here is sql script.

insert into `magento`.`eav_attribute` (`entity_type_id`, `attribute_code`, `attribute_model`, `backend_model`, `backend_type`, `backend_table`, `frontend_model`, `frontend_input`, frontend_label`, `frontend_class`, `source_model`, `is_required`, `is_user_defined`, `default_value`, `is_unique`, `note`)
values(1, 'accounttype', '', '', 'varchar', '', '', 'select', 'Account Type', '', '', 0, 0, '', 0,''),
        (1, 'companyname', '', '', 'varchar', '', '', 'text', 'Company Name', '', '', 0, 0, '', 0,'');

That’s all. Try and let me know if you face any complexity.

Like this you can add extra field(s) in any magento pages as well.

Add option(s) to quote item object at order processing step

Hi guys, how are you? Hope all of you are doing well. I have been backed after long time and have decided to share few of my recent experiences.

For a recent project I had to create a custom order processing module in magento. I got help from a post of magento forum ( http://www.magentocommerce.com/boards/viewthread/28426/). That was nice and very helpful for me. And it saved my time as well. So my heartiest thanks to all of the guys who have been contributed in that forum post.

But I faced problem to add option(s) with order item. I found a solution to add option(s) to quote object. But using this you can order only single item at a time. Below is the code snap.

$option = array('options'=>array(
              "option_id1" => 'option_value1',
              "option_id2" => 'option_value2'
             ));

$request = new Varien_Object();
 $request->setData($option);
$quoteObj->addProduct($productObj,$request);

To order multiple items at a time you have to add option(s) with quote item object. Here is the code snap for your help.

$quoteObj=Mage::getModel('sales/quote')->assignCustomer($customerObj);
$productModel=Mage::getModel('catalog/product');

foreach($products as $product) {
	$productObj = $productModel->load($productId);
	$quoteItem = Mage::getModel('sales/quote_item')->setProduct($productObj);

	$quoteItem->addOption(new Varien_Object(
                   	array(
                      		'product' => $quoteItem->getProduct(),
                       		'code' => 'option_ids',
                       		'value' => $option_id  // 45,46,55
               	         )
               	));

	$quoteItem->addOption(new Varien_Object(
                    	array(
                       		'product' => $quoteItem->getProduct(),
                       		'code' => 'option_'.$option_id,   //45
                       		'value' => $option_value          // ‘White’
             	         )
   		));

        $quoteItem->addOption(new Varien_Object(
                    	array(
                       		'product' => $quoteItem->getProduct(),
                       		'code' => 'option_'.$option_id,   //46
                       		'value' => $option_value         // ‘Large’
               	        )
   		));

	$quoteItem->setQuote($quoteObj);
	$quoteItem->setQty($Quantity);
	$quoteObj->addItem($quoteItem);
}
$quoteObj->collectTotals();
$quoteObj->save();

This code snap has been worked so far. For more details and any kind of helps contact with me.

Product Tier Price add/update in Magento

Magento has strong features to set Tier price. If you are interested to set multiple prices of a product for combination of customer group and product quantities, you can set by Magento Tier price option.

We can handle tier price in many ways. Here I am describing basic logic to add/update tier price in modular way. At first define all tier prices as an array. Ofcourse mind one thing about quantity. Quantity format must be four decimal precision. Like 100.0000. For this you can use below formatting.

$qty = number_format($qty, 4, ‘.’, ”);

//Define tier price as an array
$tierPrices[] = array(
		     'website_id'  => 0,
		     'cust_group'  => $customer_group_id,
		     'price_qty'   => $qty,
		     'price'       => $price 
		   );

Ex.
$tierPrices[0] = array(
		     'website_id'  => 0,
		     'cust_group'  => 1,
		     'price_qty'   => 100.0000,
		     'price'       => 12.0000 
		   );

$tierPrices[1] = array(
		     'website_id'  => 0,
		     'cust_group'  => 2,
		     'price_qty'   => 100.0000,
		     'price'       => 10.0000 
		   );


//get productid for corresponding SKU.
$productid = Mage::getModel('catalog/product')
                  ->getIdBySku($sku);

// Initiate product model 
$product = Mage::getModel('catalog/product');

// Load specific product whose tier price want to update
$product ->load($productid);

// take existing tier prices of that product
$existingTierPrice = $product->tier_price;
		
// Marge existing and new tier prices to update
$tierPrices=array_merge($existingTierPrice,$newTierPrices);
		
// Assign all tier prices to product's tier_price object
$product->tier_price = $tierPrices;

// Save you product with all tier prices
$product->save(); 

Also Magento has web service API support to handle tier price. If you are expert in web service work then you can do the same thing using Magento API. To handle tier price with api call do something like below.

$proxy = new SoapClient('http://127.0.0.1/magento/api/soap/?wsdl');
$sessionId = $proxy->login('apiUser', 'apiKey');
 
// Get tier prices
$tierPrices = $proxy->call($sessionId, 'product_tier_price.info', 'Sku');
 
// Add new
$tierPrices[] = array(
    'website'           => 'all',
    'customer_group_id' => 'all',
    'qty'               => 68,
    'price'             => 18.20
);
 
// Update tier prices
$proxy->call($sessionId, 'product_tier_price.update', array('Sku', $tierPrices));

Magento API / web service work

Magento has strong web service features. I can say Magento’s web service is one step ahead than others e-commerce. Magneto has soap, v2_soap(soap 2) and xmlrpc adapter facilities. Using magento web services you can synchronize customer, categories, products, orders etc data with existing stores. Here I am going to describe magento’s web service deals step by step.

Setup API: At first create api user and api key from Magento’s admin. Follow below steps.
1. Create api user from admin->system->web services-> Users->Add New User->User Info. See picture.

Api user create with api key

Api user create with api key

2. Create role to set permission on api method under admin->system->web services-> Roles->Add New Role.
3. Define “Role Resources” for created role. See picture.

Set api permission on role

Set api permission on role

4. Set “User Role” from admin->system->web services-> Users->choose user->User Role.

Call API:
API user and key is created. Now we can call api methods in two ways.
1. Soap call
2. V2_Soap call

Here I am going to discuss both ways.

Soap call:
Magento wiki and help center have described this method. So who are beginner in web service work they can call magento api using this soap method. You get all api documentations in Magento site. Here is an example.


//create soap object
$proxy = new SoapClient('http://127.0.0.1/magento/api/soap/?wsdl');

// create authorized session id using api user name and api key
// $sessionId = $proxy->login('apiUser', 'apiKey');
$sessionId = $proxy->login('m4u_admin', '12345678');

 // Get customer info for customer id = 1
$customerinfo = $proxy->call($sessionId, 'customer.info', 1);

print_r($customerinfo);


V2_Soap call:

V2_soap call is for advance user. Who are expert in web service work they can easily use this method to call any magento api method from remote place. Whose magento knowledge is zero they also can use this soap method to work with magento web service. But about this method you will not get any api documentation in Magento wiki. But I think it will easy for you.

Here I am going to describe a-z about v2_soap method as any beginner also can use this soap calling method.


//create soap object
$proxy = new SoapClient('http://127.0.0.1/magento/api/v2_soap/?wsdl');

// create authorized session id using api user name and api key
// $sessionId = $proxy->login('apiUser', 'apiKey');
$sessionId = $proxy->login('m4u_admin', '12345678');

 // Get customer info for customer id = 1
$customerinfo = $proxy->customerCustomerInfo($sessionId,1);

print_r($customerinfo);

Which method you need to call?

At first decide which method you need to call of api. Suppose you want to retrieve customer info from Magento store. So you should call customer related api method to get customer info. But you donot know which method exactly you need. To view all available methods in your magento api please run http://127.0.0.1/magento/api/v2_soap/?wsdl. Now pick appropriate api method from wsdl operation list. Suppose I pick “customerCustomerInfo” method to get customer info.

<operation name="customerCustomerInfo">
   <documentation>Retrieve customer data</documentation> 
   <input message="typens:customerCustomerInfoRequest"/>
   <output message="typens:customerCustomerInfoResponse"/>
</operation>

What is request parameter?

From wsdl you get a clear concept about api request/input parameter. For “customerCustomerInfo” operation input message is “customerCustomerInfoRequest”. So you will get all input/request parameter info (name and type) to call customerCustomerInfo method in message “customerCustomerInfoRequest”. customerCustomerInfo has three request parameter such sessionid(string), customerid(int) and atribuates(Array).

<message name="customerCustomerInfoRequest"> 
    <part name="sessionId" type="xsd:string"/>
    <part name="customerId" type="xsd:int"/>
    <part name="attributes" type="typens:ArrayOfString"/>
</message>   

So we can request customerCustomerInfo method as below.

// Get customer info for customer id = 1 and attribute is optional
$attribute = array(‘firstname’, ‘lastname’, ‘email’, ‘store_id’);
$customerinfo = $proxy->call($sessionId, ‘customer.info’, 1, $attribute);

see picture for details-

API v2_soap wsdl request and response message.

API v2_soap wsdl request and response message.

What is return/response data?

From wsdl you also get idea about api response/return/output. For “customerCustomerInfo” operation output message is “customerCustomerInfoResponse”. So you will get output/result info (name and type) of customerCustomerInfo method in message “customerCustomerInfoResponse”. customerCustomerInfo will return customerCustomerEntity object as data. Please see what the format of customerCustomerEntity object in wsdl.

<message name="customerCustomerInfoResponse">
    <part name="customerInfo" type="typens:customerCustomerEntity"/>
</message>

That’s it!!

Update Magento product / inventory from external source.

Magento is newly coming great e-commerce. So if you wish you can move your existing store in Magento or if you interested then you can create another new store in Magento. In this case you may want to synchronize your existing inventory with new magento store. Here I am going to explain how we can update magento product/inventory from outside of Magento admin. We can use corn job to process inventory synchronization in automatic way. Magento also have corn job feature.

In two ways we can update magento’s inventory.
1. By creating new module or
2. Using product API/web service.

Here I will discuss update through module system.

Update inventory creating new module:

At first create new module in local pool to handle inventory update work. And create all of your necessary files. Then follow below steps to update your inventory.

Load product: load product using product id. If you have product sku then retrieve product id for corresponding sku.

// retrieve product id using sku
 $product_id = Mage::getModel('catalog/product')
                    ->getIdBySku($sku);

// call product model and create product object
$product    = Mage::getModel('catalog/product');
// Load product using product id
 $product ->load($product_id);

Set updated inventory data: set all of your updated data in product object.

// get product's general info such price, status, description
$productInfoData = $product->getData();

// update general info using new data
$productInfoData['price'] = 11;
$productInfoData['description'] = 'Testing product update';
$productInfoData['status'] = 1;

// then set product's general info to update
$product->setData($productInfoData);

// get product's stock data such quantity, in_stock etc
$stockData = $product->getStockData();

// update stock data using new data
$stockData['qty'] = 356;
$stockData['is_in_stock'] = 1;

// then set product's stock data to update
$product->setStockData($stockData);

// call save() method to save your product with updated data
$product->save();

Basically our product update process is completed but in this situation updated product will not be save. Magento want origData to save product object. Without admin session magento cannot create origData for product. This is a magento security issue. So if you want to update magento product outside of admin or without creating admin session then you need to override Mage_Catalog_Model_Product class.

Configure new module and create app/local/M4U/catalog/etc/c onfig.xml file and write below code.

<?xml version="1.0"?>
<config> 
    <global>
	<modules>
           <m4u_catalog>
               <version>0.1.0</version>
           </m4u_catalog>
        </modules>
	<models>
	  <catalog>
            <rewrite>
              <product>M4U_Catalog_Model_Product</product>
            </rewrite>
          </catalog>
       </models> 
   </global>	
</config>

Override Mage_Catalog_Model_Product class and write setOrigData method as below.

class M4U_Catalog_Model_Product extends Mage_Catalog_Model_Product
{
    /**
     * Set original loaded data if needed
     * @param string $key
     * @param mixed $data
     * @return Varien_Object
     */
    public function setOrigData($key=null, $data=null)
    {
       return Mage_Catalog_Model_Abstract::setOrigData($key, $data);
    }
}

If you expert in Magento module system or in override system only then you can follow this process to update magento inventory otherwise you should proceed with api. For alternative solution, you can see “update product through api / web service” post.

Have any query or question? Feel free to post.

Magento CMS controller override / cms page controller override in magento

Whoops !! what Magento’s bad ? Ofcourse documentation. Bullshit documentation !!. Magento have features but without documentation !!!!

Magento cms module has some nice functionality. Using Magento’s 404 event handlers of cms module you can integrate 3rd Party CMS Content such Expression Engine, Drupal, wordpress, joomla etc within Magento.
Magento is robust and have some great nice features but still their documentation is very poor. In Magento forum and wiki they just demonstrate “how to override a controller?” as a general theory. But how to override exceptional cms and api controller is not clear. Even Magento did not mention anything about exceptionality of cms and api module. Cms module calls internally. So we cannot override cms module’s controller using as usual magento controller override process. Here I described how to overrite cms index and page controller.

Target : Override Cms module’s Index (Mage_Cms_IndexController) controller and Page (Mage_Cms_IndexController) controller.

Hint: IndexController is responsible for displaying errors (404) & home page and PageController is responsible for displaying pages like ‘about-us’, keep this in mind as you may want to rewrite which controller.

Ok, let’s start—

Firsty, it’s wise to name your module differently to avoid name collisions. Lets call my module M4U_Mycms for that case.

<?xml version="1.0"?>
<config>
    <modules>
        <M4U_Mycms>
            <active>true</active>
            <codePool>local</codePool>
        </M4U_Mycms>
    </modules>
</config>

Now create config xml for new module in app/code/local/M4U/Mycms/etc/config.xml. Here as example I mention xml to override norouteAction method of IndexController.

<?xml version="1.0"?>
<config>

    <modules>
        <M4U_Mycms>
            <version>0.1.0</version>
        </ M4U_Mycms>
    </modules>
   
    <frontend>
        <routers>
            <mycms>
                <use>standard</use>
                <args>
                    <module> M4U_Mycms</module>
                    <frontName>mycms</frontName>
                </args>
            </mycms>
        </routers>
</frontend>	
	
<global>
     <routers>
            <cms>
                <rewrite>
                    <index>
                        <to>M4U_Mycms/index</to>
                        <override_actions>true</override_actions>
                        <actions>
                           <noroute><to> M4U_Mycms/index/noroute</to></noroute>
                        </actions>
                    </index>
                </rewrite>
            </cms>
        </routers>

       </global>
</config>

N.B: Please take care about tag. It will let you create additional NEW actions that will be rewritten to new controller.

Now create new index controller in app/code/local/M4U/Mycms/controllers/IndexController.php and update noRouteAction method.


require_once "Mage/Cms/controllers/IndexController.php";
class M4U_Mycms_IndexController extends Mage_Cms_IndexController
{
    public function noRouteAction($coreRoute = null)
    {
        //modify this method as you need
        
        exit('Yes, I can successfully override cms/index noRoute method ');
		
        //$this->getResponse()->setHeader('HTTP/1.1','404 Not Found');
        //$this->getResponse()->setHeader('Status','404 File not found');
       // $pageId = Mage::getStoreConfig('web/default/cms_no_route');
       // if (!Mage::helper('cms/page')->renderPage($this, $pageId)) {
       //     $this->_forward('defaultNoRoute');
       // }
    }
}

Done !!

When any requested class/method is not found in system then Magento throw “404 Not Found” error using noRouteAction method. So we can test this method calling any undefined page url like http://127.0.0.1/magento/index.php/undefinedpagename. Here, undefinedpagename is not a defined page in magento.

ok, now run http://127.0.0.1/magento/index.php/undefinedpagename and if you get result
“Yes, I can successfully override cms/index noRoute method” in browser then noRouteAction method override is success.

Here i just describe noRouteAction method modification system. Like this way you can modify all of the indexController and pageController method.

You will get source code in right side box widget. Download code and enjoy your work.

if need any help let me know.