2

For background reference SEE: Magento: How do I get observers to work in an external script?

I wanted to ask what the preferred method to 'replicate' a frontend controller's action from an external script is. I am creating an external SSO login for Magento EE 1.12.

My code exists as the following in a php file. You can test it by creating test.php and replacing my user (185) with whatever your user ID is. Navigate to the page and then again. You will notice you are logged in and out, however in admin it will not show you as being online. Read on...

<?php

    umask(0);
    require_once 'app/Mage.php';

    Mage::app("default");

    // Set the session to frontend according to many suggestions
    Mage::getSingleton("core/session", array("name" => "frontend"));

    // Alan Storm suggestion to load specific area in Magento (frontend)
    Mage::app()->loadArea(Mage_Core_Model_App_Area::AREA_FRONTEND);

    // Load My Specific User
    $user = Mage::getModel('customer/customer')->load(185)->setWebsiteId(Mage::app()->getStore()->getWebsiteId());

    // Get the session object
    $session = Mage::getSingleton('customer/session');

    // Make it look good for debugging
    echo "<PRE>";

    // Toggle the customer logged in / logged out upon page load
    if ($session->isLoggedIn())
    {
        try
        {
            $session->session->setCustomer($user);
            echo "LOGGED IN<br>";
            var_dump($session->getCustomer()->getIsJustConfirmed());
        } catch (Mage_Core_Exception $e) {
            $message = $e->getMessage();
            var_dump($message);
        }

    } else {
        $session->logout();
    }

    var_dump($session->getCustomer());
    echo $session->isLoggedIn() ? $user->getName().' is online!' : 'not logged in';

?>

This code logs in the user, however none of the Mage_Log, Mage_Persistent, or any other module without a controller that relies on the controller_action_predispatch and controller_action_postdispatch event attached to the frontend area in config.xml will ever fire.

Mage_Log is a perfect example of this situation where it watches customer_login and fires the bindCustomerLogin() function (since I'm using Alan Storm's suggestion above) but the controller dispatch does not fire, resulting in failure of the module to work properly.

How can these other modules ever possibly be triggered from an external script (or a global observer watching the controller_front_init_routers event)?

EDIT: SOLUTION Here is the final results from the suggestions by benmarks above... I am emulating the Mage_Customer controller. The script below demonstrates how to perform a COMPLETE magento login. It is not extensively tested, but it does show the user as being logged in in the backend. It is the most complete solution i've seen to date.

public function autoMageLogin() {
                // Include the controller itself so the object can be used
                include ('/var/www/app/code/core/Mage/Customer/controllers/AccountController.php');

                // Configure Magento to think its using the frontend
                Mage::getSingleton("core/session", array("name" => "frontend"));
                Mage::getConfig()->init();
                Mage::getConfig()->loadEventObservers('frontend');
                Mage::app()->addEventArea('frontend');
                Mage::app()->loadArea(Mage_Core_Model_App_Area::AREA_FRONTEND);

                // Prepare the request as if it were coming from the specific
                // controller (I've chosed Mage_Customer as my chosen controller
                // to 'emulate' in php code
                $request = Mage::app()->getRequest();
                $request->setRouteName('customer');
                $request->setControllerModule('Mage_Customer');
                $request->setRoutingInfo('');
                $request->setModuleName('customer');
                $request->setControllerName('account');
                $request->setModuleKey('module');
                $request->setControllerKey('account');
                $request->setActionName('loginPost');
                $request->setActionKey('action');


                $response = Mage::app()->getResponse();

                // Instantiate a new AccountController object using the modified request
                // and the modified response (see the include() above)
                $accountControl = new Mage_Customer_AccountController($request, $response);

                // Dispatch events associated to the controller
                Mage::dispatchEvent('controller_action_predispatch', array('controller_action' => $accountControl));
                Mage::dispatchEvent('controller_action_predispatch_customer', array('controller_action' => $accountControl));
                Mage::dispatchEvent('controller_action_predispatch_customer_account_loginPost', array('controller_action' => $accountControl));


                // Load the current user
                $user = Mage::getModel('customer/customer')->load(185)->setWebsiteId(Mage::app()->getStore()->getWebsiteId());

                // Grab the current session
                $session = Mage::getSingleton('customer/session');

                // From this point forward, emulate the controller actions    
                if (!$session->isLoggedIn()){
                        try{

                                $session->setCustomerAsLoggedIn($user);
                                $session->renewSession();
                                echo "LOGGED IN<br>";
                        } catch (Mage_Core_Exception $e) {
                                $message = $e->getMessage();
                                var_dump($message);
                        }

                } else {
                        echo "LOGGED OUT<br>";

                        $session->logout();
                }


                    // Now fire the post controller action events
                    Mage::dispatchEvent('controller_action_postdispatch_customer_account_loginPost', array('controller_action'=>$accountControl));
                    Mage::dispatchEvent('controller_action_postdispatch_customer', array('controller_action'=>$accountControl));
                    Mage::dispatchEvent('controller_action_postdispatch', array('controller_action'=>$accountControl));


                // log to the screen and be done
                var_dump($session->getCustomer());
                echo $session->isLoggedIn() ? $session->getCustomer()->getName().' is online!' : 'not logged in';

                die();

        }
Community
  • 1
  • 1
CarComp
  • 1,929
  • 1
  • 21
  • 47
  • 1
    Hi, thank you for sharing, I only have a question: why do you set the website for the user after loading him? Being loaded by id, doesn't he already have his own website already set? Am I missing somethig? Thank you again – Alessandro Ronchi Aug 28 '14 at 04:39

2 Answers2

3

You will need to manually dispatch the events with the original params e.g.

Mage::dispatchEvent(
    'controller_action_predispatch',
    array('controller_action',Mage::app()->getRequest()
);

See Mage_Core_Controller_Varien_Action::preDispatch() (link) for more info. Note that the pre- and post-dispatch methods dispatch dynamic events based on routename params which may or may not be a concern.

benmarks
  • 23,384
  • 1
  • 62
  • 84
  • 1
    I did think about doing this. I logged the events in preDispatch once, even ob_get_clean() $this, although I never thought that it would be possible to replicate these objects, as complex as they were (most Mage objects are). Do you happen to know of a way to determine which events are fired upon specific controller actions (without logging them and just blindly trying to figure it out? I get the feeling the objects are going to be overly dynamic... – CarComp Jan 07 '13 at 21:56
  • 1
    Again, it's a matter of what you are trying to accomplish. If only one observer is doing something that you need, then it's sufficient to just make sure that the one method is fired. So, you can drop a logging call into `Mage_Core_Model_App::dispatchEvent()` to get an idea of the events and observers involved in any execution scope about which you may care. Sorry it isn't more straightforward, but that's the nature of the beast. – benmarks Jan 07 '13 at 22:53
  • 1
    It is turning out that the dynamic events are indeed needing to be fired. – CarComp Jan 08 '13 at 15:48
  • 1
    Added solution to my original question using your answer. – CarComp Jan 08 '13 at 19:27
0

If you rewrite the external script as a custom controller and action then all the events will fire naturally. However, you must have a reason for making it external in the first place.

clockworkgeek
  • 37,650
  • 9
  • 89
  • 127
  • The reason to make the script external was to implement ADFS / SSO for the site so that sign on for sites across all of our projects could be combined. Also I thought about using an Observer but honestly those run in global area as well. – CarComp Jan 07 '13 at 21:59