<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Magento Design and Development &#187; Branko Ajzele</title>
	<atom:link href="http://inchoo.net/author/branko/feed/" rel="self" type="application/rss+xml" />
	<link>http://inchoo.net</link>
	<description>Magento Design and Magento Development Professionals - Inchoo</description>
	<lastBuildDate>Mon, 06 Feb 2012 08:30:33 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.2.1</generator>
		<item>
		<title>Custom Magento Events: Customer First Order</title>
		<link>http://inchoo.net/ecommerce/magento/custom-magento-events-customer-first-order/</link>
		<comments>http://inchoo.net/ecommerce/magento/custom-magento-events-customer-first-order/#comments</comments>
		<pubDate>Sat, 28 Jan 2012 11:09:20 +0000</pubDate>
		<dc:creator>Branko Ajzele</dc:creator>
				<category><![CDATA[Events & Observers]]></category>
		<category><![CDATA[Magento]]></category>
		<category><![CDATA[custom event]]></category>
		<category><![CDATA[event]]></category>
		<category><![CDATA[observer]]></category>
		<category><![CDATA[order]]></category>

		<guid isPermaLink="false">http://inchoo.net/?p=12190</guid>
		<description><![CDATA[Often you will stumble upon a case where Magento lacks certain events that you can easily observe. Various business cases can sometimes truly stretch the boundaries of even the best shopping carts like Magento. Luckily, creating or more properly said &#8230;<p><a href="http://inchoo.net/ecommerce/magento/custom-magento-events-customer-first-order/">Read more</a><p>]]></description>
			<content:encoded><![CDATA[<p>Often you will stumble upon a case where Magento lacks certain events that you can easily observe. Various business cases can sometimes truly stretch the boundaries of even the best shopping carts like Magento. Luckily, creating or more properly said dispatching your own event in Magento is pretty straight forward task.<span id="more-12190"></span></p>
<p>Imagine the following business case, where your client, the merchant says: <em><strong>I need to give some reward points to Customer A who invited Customer B. These reward points will be assigned one time only and at the moment when Customer B creates it&#8217;s first order in the system</strong></em>.</p>
<p>Ideal scenario would be if you had the built in Magento event like &#8220;customer_first_order&#8221; or if you need more finer tuning to target the very state of the order, for example &#8220;customer_first_order_that_reached_state_complete&#8221;. Following along with the client requirement above, I will show you how you can easily make additional events with just some basic thinking invested into the whole process.</p>
<p>So where do we start? I will start from the built in &#8220;sales_order_save_after&#8221; event. Logically I need to do something after the order is created. From there I will do the logic that checks if this is customers first order or not. If you are new to Magento and you do a lookup/search on entire Magento installation code you will not find the expression <em>Mage::dispatchEvent(&#8216;sales_order_save_after&#8217;, array(&#8216;object&#8217;=>$this));</em> anywhere.</p>
<p>However you will find the defined values for <em>$_eventPrefix</em> and <em>$_eventObject</em> properties under the <em>Mage_Sales_Model_Order</em> class. Since <em>Mage_Sales_Model_Order</em> somewere down the line inherits from <em>Mage_Core_Model_Abstract</em> you can check it&#8217;s <em>_afterSave()</em> method and easily conclude that <em>sales_order_save_after</em> event comes from the expression <em>Mage::dispatchEvent($this->_eventPrefix.&#8217;_save_after&#8217;, $this->_getEventData());</em>.</p>
<p>The most important thing here for us is to &#8220;catch&#8221; the parameters that are passed to event. Function call <em>$this->_getEventData())</em> basically returns the array of <em>array(&#8216;data_object&#8217; => $this, $this->_eventObject => $this);</em>. As we mentioned previously <em>$_eventObject</em> property has the value of &#8220;<em>order</em>&#8221; set under the <em>Mage_Sales_Model_Order</em> class. What this means that all we need to do in our &#8220;<em>sales_order_save_after</em>&#8221; event observer in order to grab the order passed to the event is an expression like <em>$observer->getEvent()->Order();</em>.</p>
<p>From there on, we will start implementing the code for our specific client requirement. Before we do so, here is the actual code you need in order to have your <em>sales_order_save_after</em> event observer functional.</p>
<p><strong>config.xml</strong> from within your extension:</p>
<pre class="brush: xml; title: ; notranslate">
&lt;config&gt;
//...
	&lt;frontend&gt;
		&lt;events&gt;
            &lt;sales_order_save_after&gt;
                &lt;observers&gt;
                    &lt;customer_first_order&gt;
                        &lt;class&gt;myClassGroup/observer&lt;/class&gt;
                        &lt;method&gt;handleCustomerFirstOrder&lt;/method&gt;
                    &lt;/customer_first_order&gt;
                &lt;/observers&gt;
            &lt;/sales_order_save_after&gt;
		&lt;/events&gt;
	&lt;/frontend&gt;
//...
&lt;/config&gt;
</pre>
<p><strong>Observer.php</strong> from within your extension:</p>
<pre class="brush: php; title: ; notranslate">
&lt;?php

class MyCompany_MyExtension_Model_Observer
{
	public function handleCustomerFirstOrder($observer)
	{

	}
}
</pre>
<p>Now we need to add the necessary logic that implements client&#8217;s specific requirement, which transforms the above Observer.php into something like this:</p>
<pre class="brush: php; title: ; notranslate">
class MyCompany_MyExtension_Model_Observer
{
    private static $_handleCustomerFirstOrderCounter = 1;

	public function handleCustomerFirstOrder($observer)
	{
        $orders = Mage::getModel('sales/order')
                    -&gt;getCollection()
                    -&gt;addFieldToSelect('increment_id')
                    -&gt;addFieldToFilter('customer_id', array('eq' =&gt; $observer-&gt;getEvent()-&gt;getOrder()-&gt;getCustomerId()));

        //$orders-&gt;getSelect()-&gt;limit(2);

        //if ($orders-&gt;count() == 1) {
        if ($orders-&gt;getSize() == 1) {
            if (self::$_handleCustomerFirstOrderCounter &gt; 1) {
                return $this;
            }

            self::$_handleCustomerFirstOrderCounter++;

			Mage::dispatchEvent('customer_first_order', array('order' =&gt; $observer-&gt;getEvent()-&gt;getOrder()));
		}		

		return $this;
	}
}
</pre>
<p>Several things to explain here. </p>
<p>First the use of <em>$_handleCustomerFirstOrderCounter</em>. I have noticed that during the same page request on order after save action, depending on possible other existing event observers that might do order re-save action, etc. my code within <em>handleCustomerFirstOrder($observer)</em> method can get executed twice or more times, thus I used static variable in a form of counter to specifically allow it to execute only once.</p>
<p>Second, you will notice the limit I put on the colelction, <em>$orders->getSelect()->limit(2);</em>. Reason for this is the logic that says &#8220;check if this is customers first order&#8221;. In order to do that, I need to know if there are other order in the system for the same customer. Since there is no need to fetch/load or even query for all of the order&#8217;s of the same customers I merely said &#8220;tray to grab two orders from this customer&#8221; then with <em>if ($orders->count() == 1)</em> expression I merely said, if total count of grabbed orders is one then this is the first order by this customer. Now you will notice that I commented out the <em>$orders->getSelect()->limit(2);</em> and <em>if ($orders->count() == 1)</em> expressions at the end and went with the use of getSize() method on the collection. Looks nicer and does the same thing. Method getSize() comes from the Varien_Data_Collection_Db class and internally it actually calls the getSelectCountSql() method that specifically executes count on the database, <em>$countSelect->columns(&#8216;COUNT(*)&#8217;);</em> ,and returns just the count value. Thus making it even faster.</p>
<p>And finally, in place of implementing my reward points logic code directly there, I decided to trigger the <em>customer_first_order</em> event for this case. Reason is simply, maybe other developers in the system would like do do something else on this event, so why not dispatch it.</p>
<p>To conclude the client&#8217;s request I simply implement one more observer that handles the <em>customer_first_order</em> event and within that observer I give customer A it&#8217;s earned reward points.</p>
<p>Hopefully this article was helpful for some.</p>
]]></content:encoded>
			<wfw:commentRss>http://inchoo.net/ecommerce/magento/custom-magento-events-customer-first-order/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Geocoding Customer Addresses in Magento via Google Maps</title>
		<link>http://inchoo.net/ecommerce/magento/geocoding-customer-addresses-in-magento-via-google-maps/</link>
		<comments>http://inchoo.net/ecommerce/magento/geocoding-customer-addresses-in-magento-via-google-maps/#comments</comments>
		<pubDate>Wed, 14 Dec 2011 14:35:22 +0000</pubDate>
		<dc:creator>Branko Ajzele</dc:creator>
				<category><![CDATA[Extensions]]></category>
		<category><![CDATA[Magento]]></category>
		<category><![CDATA[api]]></category>
		<category><![CDATA[extension]]></category>
		<category><![CDATA[Google]]></category>
		<category><![CDATA[map]]></category>

		<guid isPermaLink="false">http://inchoo.net/?p=11874</guid>
		<description><![CDATA[Geocoding is the process of finding associated geographic coordinates (often expressed as latitude and longitude) from other geographic data, such as street addresses, or zip codes (postal codes)&#8230; so says Wikipedia. Geocoding an address is pretty simple if you are &#8230;<p><a href="http://inchoo.net/ecommerce/magento/geocoding-customer-addresses-in-magento-via-google-maps/">Read more</a><p>]]></description>
			<content:encoded><![CDATA[<p>Geocoding is the process of finding associated geographic coordinates (often expressed as latitude and longitude) from other geographic data, such as street addresses, or zip codes (postal codes)&#8230; so says Wikipedia. Geocoding an address is pretty simple if you are using Google Maps API. Since all we do here is Magento, let me show you how easily you can geocode customer address.<span id="more-11874"></span></p>
<p>We will start with adding the Gmap.php helper to our extension:</p>
<pre class="brush: php; title: ; notranslate">
&lt;?php
/**
 * @author Branko Ajzele &lt;ajzele@gmail.com&gt;
 */
class Inchoo_Extension_Helper_Gmap extends Mage_Core_Helper_Abstract
{
    const GOOGLE_MAPS_HOST = 'maps.google.com';
    const CONFIG_PATH_GOOGLE_MAPS_API_KEY = 'inchoo_google/maps/api_key';

    public function getGoogleMapsApiKey()
    {
        return Mage::getStoreConfig(self::CONFIG_PATH_GOOGLE_MAPS_API_KEY);
    }

    /**
     *
     * @param Mage_Customer_Model_Address $address
     * @param boolean $saveCoordinatesToAddress
     * @param boolean $forceLookup
     * @return array Returns the array of float values like {[0] =&gt; float(18.4438156) [1] =&gt; float(45.5013644)}
     */
    public function fetchAddressGeoCoordinates(Mage_Customer_Model_Address $address, $saveCoordinatesToAddress = true, $forceLookup = false)
    {
        /**
         * USAGE EXAMPLE

            $customer = Mage::getModel('customer/customer');
            $customer-&gt;setWebsiteId(Mage::app()-&gt;getWebsite()-&gt;getId());
            $customer-&gt;loadByEmail('some-email@domain.com');

            Mage::helper('inchoo/gmap')-&gt;fetchAddressGeoCoordinates($customer-&gt;getDefaultBillingAddress())

         */

        $coordinates = array();

        if ($address-&gt;getId() &amp;&amp; $address-&gt;getGmapCoordinatePointX() &amp;&amp; $address-&gt;getGmapCoordinatePointY() &amp;&amp; $forceLookup == false) {
            $coordinates = array($address-&gt;getGmapCoordinatePointX(), $address-&gt;getGmapCoordinatePointY());
        } else if (!$address-&gt;getGmapCoordinatePointX() &amp;&amp; !$address-&gt;getGmapCoordinatePointY())
            OR ($address-&gt;getId() &amp;&amp; $address-&gt;getGmapCoordinatePointX() &amp;&amp; $address-&gt;getGmapCoordinatePointY() &amp;&amp; $forceLookup == true)) {

            $lineAddress = $address-&gt;getStreet1(). ', '.$address-&gt;getPostcode().' '.$address-&gt;getCity().', '.$address-&gt;getCountry();

            $client = new Zend_Http_Client();
            $client-&gt;setUri('http://'.self::GOOGLE_MAPS_HOST.'/maps/geo');
            $client-&gt;setMethod(Zend_Http_Client::GET);
            $client-&gt;setParameterGet('output', 'json');
            $client-&gt;setParameterGet('key', $this-&gt;getGoogleMapsApiKey());
            $client-&gt;setParameterGet('q', $lineAddress);

            $response = $client-&gt;request();

            if ($response-&gt;isSuccessful() &amp;&amp; $response-&gt;getStatus() == 200) {
                $_response = json_decode($response-&gt;getBody());
                $_coordinates = @$_response-&gt;Placemark[0]-&gt;Point-&gt;coordinates;

                if (is_array($_coordinates) &amp;&amp; count($_coordinates) &gt;= 2) {

                    $coordinates = array_slice($_coordinates, 0, 2);

                    if ($saveCoordinatesToAddress) {
                        try {
                            $address-&gt;setGmapLng($coordinates[0]);
                            $address-&gt;setGmapLat($coordinates[1]);

                            $address-&gt;save();
                        } catch (Exception $e) {
                            Mage::logException($e);
                        }
                    }
                }
            }
        }

        return $coordinates;
    }
}
</pre>
<p>As you can see above in the code, function getGoogleMapsApiKey() pulls the API key info from my system configuration. I will not show you that part here. So for the sake of simplicity if you do not know how to code a configuration for your module you can just return a raw API key string here (surely this approach is then just for testing/playing not on production).</p>
<p>Since this helper references the two non-defualt-existing attributes on the address we will need to add those attributes. We do so trough install/upgrade scripts (you know, the files under app/code/{codePool}/Company/Extension/sql/extension_setup/ folder). I will not give you the full example of the file and it&#8217;s corresponding config.xml entry, as you should know how to do it yourself already, rather just the partial code that goes into the install/upgrade script in order to add the two missing customer address attributes:</p>
<pre class="brush: php; title: ; notranslate">
//Add attribute that will be used for Google Maps as a coordinate lng
$installer-&gt;addAttribute('customer_address', 'gmap_lng', array(
    'type'     =&gt; 'varchar',
    'input'    =&gt; 'hidden',
    'visible'  =&gt; false,
    'required' =&gt; false
));
//Add attribute that will be used for Google Maps as a coordinate lat
$installer-&gt;addAttribute('customer_address', 'gmap_lat', array(
    'type'     =&gt; 'varchar',
    'input'    =&gt; 'hidden',
    'visible'  =&gt; false,
    'required' =&gt; false
));
</pre>
<p>Next, we will add the observer to the proper _beforeSave() dispatched event of the Mage_Customer_Model_Address model class. Since Mage_Customer_Model_Address extends the Mage_Customer_Model_Address_Abstract which defines the &#8220;protected $_eventPrefix = &#8216;customer_address&#8217;;&#8221; we can easily conclude the all it takes is to create observer for &#8220;customer_address_save_before&#8221; event (Mage_Core_Model_Abstract::_beforeSave() &#8230; we have Mage::dispatchEvent($this-&gt;_eventPrefix.&#8217;_save_before&#8217;, $this-&gt;_getEventData());).</p>
<p>So we add something like this to the config.xml file of our extension:</p>
<pre class="brush: xml; title: ; notranslate">
&lt;global&gt;

    &lt;events&gt;
        &lt;customer_address_save_before&gt;
            &lt;observers&gt;
                &lt;inchoo_observer_customer_address_save_before&gt;
                    &lt;type&gt;singleton&lt;/type&gt;
                    &lt;class&gt;Inchoo_Extension_Model_Observer&lt;/class&gt;
                    &lt;method&gt;geocodeAddress&lt;/method&gt;
                &lt;/inchoo_observer_customer_address_save_before&gt;
            &lt;/observers&gt;
        &lt;/customer_address_save_before&gt;
    &lt;/events&gt;

&lt;/global&gt;
</pre>
<p>Then we create the observer Inchoo_Extension_Model_Observer with something like:</p>
<p>Within that observer we simply need to call something like:</p>
<pre class="brush: php; title: ; notranslate">
&lt;?php
class Inchoo_Extension_Model_Observer
{
	public function geocodeAddress()
	{
		Mage::helper('inchoo/gmap')
			-&gt;fetchAddressGeoCoordinates($observer-&gt;getEvent()-&gt;getCustomerAddress());

		return $this;
	}
}
</pre>
<p>Basically that&#8217;s it. Now each time an address is saved in Magento, there should be a lookup on Google Maps API in order to fetch the longitude and latitude coordinates. Please note, that all of this is just to fetch the coordinates and save them under the address. Showing the actual map somewhere on the frontend or backend requires a bit more input.</p>
<p>To show the Google Map on the frontend with the proper location marker shown on the map you need two things. First we need to add the script like shown below to the head part of the page:</p>
<pre class="brush: php; title: ; notranslate">
&lt;script type=&quot;text/javascript&quot; src=&quot;http://maps.googleapis.com/maps/api/js?key=&lt;?php echo Mage::helper('inchoo/gmap')-&gt;getGoogleMapsApiKey() ?&gt;&amp;sensor=false&quot;&gt;&lt;/script&gt;
</pre>
<p>Then finally we need to add something like:</p>
<pre class="brush: php; title: ; notranslate">

&lt;div id=&quot;address&lt;?php echo $_address-&gt;getId() ?&gt;-gmap&quot; style=&quot;width:480px; height:480px;&quot;&gt;&lt;/div&gt;

&lt;script type=&quot;text/javascript&quot;&gt;
//&lt;![CDATA[

	Event.observe(window, 'load', function() {

			  var myLatlng = new google.maps.LatLng(&lt;?php echo $_address-&gt;getGmapLat() ?&gt;,&lt;?php echo $_address-&gt;getGmapLng() ?&gt;);
			  var myOptions = {
				zoom: 4,
				center: myLatlng,
				mapTypeId: google.maps.MapTypeId.ROADMAP
			  }
			  var map = new google.maps.Map(document.getElementById(&quot;address&lt;?php echo $_address-&gt;getId() ?&gt;-gmap&quot;), myOptions);

			  var marker = new google.maps.Marker({
				  position: myLatlng,
				  map: map,
				  title:&quot;Address Location&quot;
			  });

	});
//]]&gt;
&lt;/script&gt;
</pre>
<p>And that&#8217;s it. Please note, the code above might not be fully copy paste, check the JavaScript when you implement it, and test if your customer address attributes are installed correctly. This article is not for Magento beginners so if you experience some difficulties implementing this approach, please consult the <a href="http://www.magentocommerce.com/knowledge-base">Magento Knowledge Base</a> first.</p>
<p><em>P.S. I was playing with this on Magento CE 1.6.1.0.</em></p>
<p>Hope this was helpful. Cheers.</p>
]]></content:encoded>
			<wfw:commentRss>http://inchoo.net/ecommerce/magento/geocoding-customer-addresses-in-magento-via-google-maps/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Reusing Magento&#8217;s calendar control</title>
		<link>http://inchoo.net/ecommerce/magento/reusing-magento-calendar-control/</link>
		<comments>http://inchoo.net/ecommerce/magento/reusing-magento-calendar-control/#comments</comments>
		<pubDate>Tue, 06 Dec 2011 09:13:58 +0000</pubDate>
		<dc:creator>Branko Ajzele</dc:creator>
				<category><![CDATA[Frontend]]></category>
		<category><![CDATA[Magento]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[block]]></category>
		<category><![CDATA[calendar]]></category>
		<category><![CDATA[control]]></category>
		<category><![CDATA[widget]]></category>

		<guid isPermaLink="false">http://inchoo.net/?p=11783</guid>
		<description><![CDATA[One of the cool things in Magento is that there is so many controls (widgets, blocks), etc. that you can reuse on the frontend or on the backend. One such control is the calendar control. Calendar control is mostly just &#8230;<p><a href="http://inchoo.net/ecommerce/magento/reusing-magento-calendar-control/">Read more</a><p>]]></description>
			<content:encoded><![CDATA[<p>One of the cool things in Magento is that there is so many controls (widgets, blocks), etc. that you can reuse on the frontend or on the backend. One such control is the calendar control. Calendar control is mostly just used on the backend, aka &#8220;adminhtml&#8221; area. You can easily figure that one out if you do a quick search for a &#8220;Calendar.setup&#8221; string on &#8220;app/design&#8221; folder, in which case you would get several search results but all from &#8220;app/design/adminhtml/&#8221; folder.<span id="more-11783"></span></p>
<p><a href="http://inchoo.net/wp-content/uploads/2011/12/Snap_2011.12.06_09h39m21s_001_.png"><img src="http://inchoo.net/wp-content/uploads/2011/12/Snap_2011.12.06_09h39m21s_001_-300x191.png" alt="" title="Snap_2011.12.06_09h39m21s_001_" width="300" height="191" class="alignnone size-thumbnail wp-image-11790" /></a></p>
<p>So the question is how to get the calendar (from image above) to show on the frontend area? Easily, all you need is to include some CSS, few JavaScript&#8217;s, image and you are done. To make things more flexible, I wrote a little block class that does this for you.</p>
<pre class="brush: php; title: ; notranslate">
class Inchoo_Module_Block_Calendar extends Mage_Core_Block_Template
{
    protected function _construct()
    {
        parent::_construct();
        $this-&gt;setTemplate('inchoo/calendar.phtml');
    }

    protected function _prepareLayout()
    {
        $this-&gt;_injectCalendarControlJsCSSInHTMLPageHead();

        return parent::_prepareLayout();
    }    

    private function _injectCalendarControlJsCSSInHTMLPageHead()
    {
        $this-&gt;getLayout()-&gt;getBlock('head')-&gt;append(
            $this-&gt;getLayout()-&gt;createBlock(
                'Mage_Core_Block_Html_Calendar',
                'html_calendar',
                array('template' =&gt; 'page/js/calendar.phtml')
            )
        );

        $this-&gt;getLayout()-&gt;getBlock('head')
                -&gt;addItem('js_css', 'calendar/calendar-win2k-1.css')
                -&gt;addJs('calendar/calendar.js')
                -&gt;addJs('calendar/calendar-setup.js');

        return $this;
    }
}
</pre>
<p>Please note that I placed this block class under the Inchoo/Module namespace, where you can easily make fully new YourCompany/Calendar extension and just put this in it, if you wish your entire extension just for calendar control.</p>
<p>Hopefully, _construct() method is clear to everyone in this case?! If not, then please skip this article entirely and jumo to the <a href="http://www.magentocommerce.com/knowledge-base/entry/magento-for-dev-part-4-magento-layouts-blocks-and-templates">Magento Wiki</a> <img src='http://inchoo.net/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </p>
<p>The core of the functionality here lies in the _injectCalendarControlJsCSSInHTMLPageHead() method which is called by _prepareLayout() method. If you study it closely you will see that _injectCalendarControlJsCSSInHTMLPageHead() adds the necessary CSS and JS to the head portion of the HTML page that gets rendered. </p>
<p>With this approach, all we need to do later in our XML layout files is just call our Calendar block like:</p>
<pre class="brush: xml; title: ; notranslate">
&lt;block type=&quot;inchoo/calendar&quot; name=&quot;inchoo.calendar&quot; /&gt;
</pre>
<p>And finally the content of your app/design/frontedn/SOMEPACKAGE/SOMETHEME/template/inchoo/calendar.phtml file should look like:</p>
<pre class="brush: jscript; title: ; notranslate">
&lt;span&gt;
    &lt;img style=&quot;&quot; title=&quot;Select Date&quot; id=&quot;date_select_trig&quot; class=&quot;v-middle&quot; alt=&quot;&quot; src=&quot;&lt;?php echo $this-&gt;getSkinUrl(&quot;images/calendar.gif&quot;);?&gt; &quot;/&gt;
    &lt;input type=&quot;text&quot; style=&quot;width: 120px;&quot; class=&quot;input-text&quot; value=&quot;&quot; id=&quot;selected_date&quot; name=&quot;selected_date&quot;/&gt;
    &lt;script type=&quot;text/javascript&quot;&gt;
    //&lt;![CDATA[
        Calendar.setup({
            inputField: &quot;selected_date&quot;,
            ifFormat: &quot;%m/%e/%Y %H:%M:%S&quot;,
            showsTime: true,
            button: &quot;date_select_trig&quot;,
            align: &quot;Bl&quot;,
            singleClick : true
        });
    //]]&gt;
    &lt;/script&gt;
&lt;/span&gt;
</pre>
<p>If all of the three files are in place then you should be able to see something like shown on image below.</p>
<p><a href="http://inchoo.net/wp-content/uploads/2011/12/Snap_2011.12.06_09h58m20s_002_.png"><img src="http://inchoo.net/wp-content/uploads/2011/12/Snap_2011.12.06_09h58m20s_002_-300x207.png" alt="" title="Snap_2011.12.06_09h58m20s_002_" width="300" height="207" class="alignnone size-thumbnail wp-image-11788" /></a></p>
<p>Additionally, please keep in mind that this is really basic approach. You could do a bit more abstraction to it so that XML layout block accepts few more parameters that would be passed to a Calendar.php block class, like show a bit different calendar, do a bit different format pickup, etc. so that you can truly reuse the same control in various ways.</p>
<p><em>P.S. If Magento is having trouble loading calendar/calendar.js, calendar/calendar-setup.js, calendar/calendar-win2k-1.css then try doing a seach on entire installation and moving them to your theme then modify the addItem, addJs in your Calendar.php block class.</em></p>
<p>Hope this was helpful.</p>
]]></content:encoded>
			<wfw:commentRss>http://inchoo.net/ecommerce/magento/reusing-magento-calendar-control/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Creating an EAV based model(s) in Magento</title>
		<link>http://inchoo.net/ecommerce/magento/creating-an-eav-based-models-in-magento/</link>
		<comments>http://inchoo.net/ecommerce/magento/creating-an-eav-based-models-in-magento/#comments</comments>
		<pubDate>Sun, 04 Dec 2011 12:58:14 +0000</pubDate>
		<dc:creator>Branko Ajzele</dc:creator>
				<category><![CDATA[Extensions]]></category>
		<category><![CDATA[Magento]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[eav]]></category>
		<category><![CDATA[extension]]></category>
		<category><![CDATA[model]]></category>

		<guid isPermaLink="false">http://inchoo.net/?p=11755</guid>
		<description><![CDATA[Magento database heavily utilizes an Entity-Attribute-Value (EAV) data model. However, the cost of flexibility is often complexity. The process of manipulating EAV data in Magento is often more difficult than manipulating flat relational tables. All Magento Models inherit from the &#8230;<p><a href="http://inchoo.net/ecommerce/magento/creating-an-eav-based-models-in-magento/">Read more</a><p>]]></description>
			<content:encoded><![CDATA[<p>Magento database heavily utilizes an Entity-Attribute-Value (EAV) data model. However, the cost of flexibility is often complexity. The process of manipulating EAV data in Magento is often more difficult than manipulating flat relational tables.</p>
<p>All Magento Models inherit from the Mage_Core_Model_Abstract. Difference between simple Model or an EAV Model is its Model Resource. </p>
<p>To build a model with proper collection object in Magento you need 4 things<span id="more-11755"></span>:</p>
<ul>
<li>model class</li>
<li>resource class</li>
<li>collection class</li>
<li>install script (the one found under mymodule_setup folder of your extension)</li>
</ul>
<style>
.gist-highlight .line {
	height:auto!important;
	float:none!important;
	float:none!important;
	background:none!important;
}
</style>
<p>To extend this little further there is one more file you would most likely need when building an EAV based model, the Setup.php which extends from Mage_Eav_Model_Entity_Setup. You would need to implement the getDefaultEntities() method in it as this is what&#8217;s called during the extension installation in Magento.</p>
<p>Imagine your module is called Phonebook and put into the /Inchoo namespace under the local code pool. Logically you would need a model class like User which represents single entry into the phonebook. With this in mind you already need a structure like:</p>
<ul>
<li>app/etc/modules/Inchoo_Phonebook.xml</li>
<li>app/code/local/Inchoo/Phonebook/etc/config.xml</li>
<li>app/code/local/Inchoo/Phonebook/Model/User.php</li>
<li>app/code/local/Inchoo/Phonebook/Model/Resource/User.php</li>
<li>app/code/local/Inchoo/Phonebook/Model/Resource/User/Collection.php</li>
<li>app/code/local/Inchoo/Phonebook/Model/Resource/Setup.php</li>
<li>app/code/local/Inchoo/Phonebook/sql/inchoo_phonebook_setup/install-1.0.0.0.php</li>
</ul>
<p>Now that we outlined the file structure, let&#8217;s get down to it and see what the content of files should look like in order for it to work.</p>
<p>app/etc/modules/Inchoo_Phonebook.xml<br />
<script src="https://gist.github.com/1430081.js?file=gistfile1.xml"></script></p>
<p>app/code/local/Inchoo/Phonebook/etc/config.xml<br />
<script src="https://gist.github.com/1430085.js?file=gistfile1.xml"></script></p>
<p>app/code/local/Inchoo/Phonebook/Model/User.php<br />
<script src="https://gist.github.com/1430086.js?file=gistfile1.aw"></script></p>
<p>app/code/local/Inchoo/Phonebook/Model/Resource/User.php<br />
<script src="https://gist.github.com/1430090.js?file=gistfile1.aw"></script></p>
<p>app/code/local/Inchoo/Phonebook/Model/Resource/User/Collection.php<br />
<script src="https://gist.github.com/1430091.js?file=gistfile1.aw"></script></p>
<p>app/code/local/Inchoo/Phonebook/Model/Resource/Setup.php<br />
<script src="https://gist.github.com/1430096.js?file=gistfile1.aw"></script></p>
<p>app/code/local/Inchoo/Phonebook/sql/inchoo_phonebook_setup/install-1.0.0.0.php<br />
<script src="https://gist.github.com/1430098.js?file=gistfile1.aw"></script></p>
<p>For each attribute type like &#8220;varchar&#8221;, &#8220;text&#8221;, &#8220;int&#8221;&#8230; you need to create a corresponding table like inchoo_phonebook_user_entity_varchar, inchoo_phonebook_user_entity_text, inchoo_phonebook_user_entity_int. Study the install-1.0.0.0.php and the config.xml file to see how to get that one done.</p>

<a href='http://inchoo.net/ecommerce/magento/creating-an-eav-based-models-in-magento/attachment/snap_2011-12-04_13h06m02s_001_/' title='Snap_2011.12.04_13h06m02s_001_'><img width="300" height="165" src="http://inchoo.net/wp-content/uploads/2011/12/Snap_2011.12.04_13h06m02s_001_-300x165.png" class="attachment-thumbnail" alt="Snap_2011.12.04_13h06m02s_001_" title="Snap_2011.12.04_13h06m02s_001_" /></a>
<a href='http://inchoo.net/ecommerce/magento/creating-an-eav-based-models-in-magento/attachment/snap_2011-12-04_13h07m00s_003_core-resource-blog-localhost-table/' title='Snap_2011.12.04_13h07m00s_003_core-resource -blog -localhost- - Table'><img width="300" height="235" src="http://inchoo.net/wp-content/uploads/2011/12/Snap_2011.12.04_13h07m00s_003_core-resource-blog-localhost-Table-300x235.png" class="attachment-thumbnail" alt="Snap_2011.12.04_13h07m00s_003_core-resource -blog -localhost- - Table" title="Snap_2011.12.04_13h07m00s_003_core-resource -blog -localhost- - Table" /></a>
<a href='http://inchoo.net/ecommerce/magento/creating-an-eav-based-models-in-magento/attachment/snap_2011-12-04_13h07m34s_004_-nibccapture-02f1dcaf-8f72-4627-b675-014a58d4f832/' title='Snap_2011.12.04_13h07m34s_004_-nibCCapture-02f1dcaf-8f72-4627-b675-014a58d4f832'><img width="300" height="176" src="http://inchoo.net/wp-content/uploads/2011/12/Snap_2011.12.04_13h07m34s_004_-nibCCapture-02f1dcaf-8f72-4627-b675-014a58d4f832-300x176.png" class="attachment-thumbnail" alt="Snap_2011.12.04_13h07m34s_004_-nibCCapture-02f1dcaf-8f72-4627-b675-014a58d4f832" title="Snap_2011.12.04_13h07m34s_004_-nibCCapture-02f1dcaf-8f72-4627-b675-014a58d4f832" /></a>
<a href='http://inchoo.net/ecommerce/magento/creating-an-eav-based-models-in-magento/attachment/snap_2011-12-04_13h08m10s_005_eav-entity-type-blog-localhost-table/' title='Snap_2011.12.04_13h08m10s_005_eav-entity-type -blog -localhost- - Table'><img width="300" height="157" src="http://inchoo.net/wp-content/uploads/2011/12/Snap_2011.12.04_13h08m10s_005_eav-entity-type-blog-localhost-Table-300x157.png" class="attachment-thumbnail" alt="Snap_2011.12.04_13h08m10s_005_eav-entity-type -blog -localhost- - Table" title="Snap_2011.12.04_13h08m10s_005_eav-entity-type -blog -localhost- - Table" /></a>
<a href='http://inchoo.net/ecommerce/magento/creating-an-eav-based-models-in-magento/attachment/snap_2011-12-04_13h11m03s_006_inchoo-phonebook-user-entity-blog-localhost-table/' title='Snap_2011.12.04_13h11m03s_006_inchoo-phonebook-user-entity -blog -localhost- - Table'><img width="300" height="199" src="http://inchoo.net/wp-content/uploads/2011/12/Snap_2011.12.04_13h11m03s_006_inchoo-phonebook-user-entity-blog-localhost-Table-300x199.png" class="attachment-thumbnail" alt="Snap_2011.12.04_13h11m03s_006_inchoo-phonebook-user-entity -blog -localhost- - Table" title="Snap_2011.12.04_13h11m03s_006_inchoo-phonebook-user-entity -blog -localhost- - Table" /></a>
<a href='http://inchoo.net/ecommerce/magento/creating-an-eav-based-models-in-magento/attachment/snap_2011-12-04_13h11m18s_007_inchoo-phonebook-user-entity-int-blog-localhost-table/' title='Snap_2011.12.04_13h11m18s_007_inchoo-phonebook-user-entity-int -blog -localhost- - Table'><img width="300" height="204" src="http://inchoo.net/wp-content/uploads/2011/12/Snap_2011.12.04_13h11m18s_007_inchoo-phonebook-user-entity-int-blog-localhost-Table-300x204.png" class="attachment-thumbnail" alt="Snap_2011.12.04_13h11m18s_007_inchoo-phonebook-user-entity-int -blog -localhost- - Table" title="Snap_2011.12.04_13h11m18s_007_inchoo-phonebook-user-entity-int -blog -localhost- - Table" /></a>
<a href='http://inchoo.net/ecommerce/magento/creating-an-eav-based-models-in-magento/attachment/snap_2011-12-04_13h11m32s_008_inchoo-phonebook-user-entity-text-blog-localhost-table/' title='Snap_2011.12.04_13h11m32s_008_inchoo-phonebook-user-entity-text -blog -localhost- - Table'><img width="300" height="215" src="http://inchoo.net/wp-content/uploads/2011/12/Snap_2011.12.04_13h11m32s_008_inchoo-phonebook-user-entity-text-blog-localhost-Table-300x215.png" class="attachment-thumbnail" alt="Snap_2011.12.04_13h11m32s_008_inchoo-phonebook-user-entity-text -blog -localhost- - Table" title="Snap_2011.12.04_13h11m32s_008_inchoo-phonebook-user-entity-text -blog -localhost- - Table" /></a>
<a href='http://inchoo.net/ecommerce/magento/creating-an-eav-based-models-in-magento/attachment/snap_2011-12-04_13h11m42s_009_inchoo-phonebook-user-entity-varchar-blog-localhost-table/' title='Snap_2011.12.04_13h11m42s_009_inchoo-phonebook-user-entity-varchar -blog -localhost- - Table'><img width="300" height="181" src="http://inchoo.net/wp-content/uploads/2011/12/Snap_2011.12.04_13h11m42s_009_inchoo-phonebook-user-entity-varchar-blog-localhost-Table-300x181.png" class="attachment-thumbnail" alt="Snap_2011.12.04_13h11m42s_009_inchoo-phonebook-user-entity-varchar -blog -localhost- - Table" title="Snap_2011.12.04_13h11m42s_009_inchoo-phonebook-user-entity-varchar -blog -localhost- - Table" /></a>
<a href='http://inchoo.net/ecommerce/magento/creating-an-eav-based-models-in-magento/attachment/model-4/' title='model'><img width="300" height="118" src="http://inchoo.net/wp-content/uploads/2011/12/model-e1323080904528-300x118.jpg" class="attachment-thumbnail" alt="model" title="model" /></a>

<p>Finally, you can test your model and it’s collection by running the following somewhere in your code:<br />
<script src="https://gist.github.com/1430101.js?file=gistfile1.aw"></script></p>
<p>And that&#8217;s it. Although simple, there is some typing to be done in order to get the EAV based model functional. Hope it helps someone. </p>
<p>Cheers.</p>
]]></content:encoded>
			<wfw:commentRss>http://inchoo.net/ecommerce/magento/creating-an-eav-based-models-in-magento/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>MagentoU “Fundamentals of Magento Development” impressions</title>
		<link>http://inchoo.net/ecommerce/magento/magentou-fundamentals-of-magento-development-impressions/</link>
		<comments>http://inchoo.net/ecommerce/magento/magentou-fundamentals-of-magento-development-impressions/#comments</comments>
		<pubDate>Wed, 02 Nov 2011 09:43:54 +0000</pubDate>
		<dc:creator>Branko Ajzele</dc:creator>
				<category><![CDATA[Magento]]></category>
		<category><![CDATA[Starting up]]></category>
		<category><![CDATA[course]]></category>
		<category><![CDATA[education]]></category>
		<category><![CDATA[magentiu]]></category>

		<guid isPermaLink="false">http://inchoo.net/?p=11198</guid>
		<description><![CDATA[Few days ago I was attending the MagentoU’s “Fundamentals of Magento Development” course in London. It’s a five day course covering Magento internals inside out (at least that’s the idea ). Price of the course at the moment is around &#8230;<p><a href="http://inchoo.net/ecommerce/magento/magentou-fundamentals-of-magento-development-impressions/">Read more</a><p>]]></description>
			<content:encoded><![CDATA[<p>Few days ago I was attending the MagentoU’s “Fundamentals of Magento Development” course in London. It’s a five day course covering Magento internals inside out (at least that’s the idea <img src='http://inchoo.net/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> ). Price of the course at the moment is around $3,850.00, which is not a small amount to pay for a five day education. Good thing is that we needed to pay only 50% of the cost as Magento Silver Partner. So is it worth the money?<span id="more-11198"></span></p>
<p>Course itself is targeted towards both developers who are new to Magento, as well as those developers who are experienced with Magento platform. It takes you trough the the process of Magento initialization, Rendering, Request Flow, Adminhtml, Database and EAV. The great part about it is the lecture-lab format, meaning you actually do the coding with the trainer.</p>
<p><a href="http://www.magentocommerce.com/services/certification-board" title="Magento Developer Certification Advisory Board">Ben Marks</a> from Blue Acorn was the trainer conducting the course. I can only say words of praise to his approach to lecturing. He was on top all of the tricky questions we threw at him. I knew Ben from before the course, as he was doing Magento for over three years now. His experience was an extra bonus on top of the training materials we got. Additionally I enjoyed his cheerful mood throughout entire course.</p>
<p>Company sent me to the course as we where focused on getting the first hand “big picture” about Magento developer education process. Meaning how it was organized, what it covers, and in general how much developers can learn in just a five day course. All in all, I would say it was a positive experience. Regardless of over 3 years of everyday Magento development its really refreshing to catch on a few tricks and share experience. I believe class was successful at completely clarifying  the fundamentals of the xml structure, control flow, and navigating through and making sense of the many files of Magento. Trainer, Ben, was really in depth with things, showing great understanding of a specific application areas.</p>
<p><a href="http://inchoo.net/wp-content/uploads/2011/11/IMAG0106.jpg"><img src="http://inchoo.net/wp-content/uploads/2011/11/IMAG0106-300x179.jpg" alt="" title="IMAG0106" width="300" height="179" class="alignnone size-thumbnail wp-image-11212" /></a> <a href="http://inchoo.net/wp-content/uploads/2011/11/IMAG0105.jpg"><img src="http://inchoo.net/wp-content/uploads/2011/11/IMAG0105-300x179.jpg" alt="" title="IMAG0105" width="300" height="179" class="alignnone size-thumbnail wp-image-11211" /></a></p>
<p><a href="http://inchoo.net/wp-content/uploads/2011/11/IMAG0104.jpg"><img src="http://inchoo.net/wp-content/uploads/2011/11/IMAG0104-300x179.jpg" alt="" title="IMAG0104" width="300" height="179" class="alignnone size-thumbnail wp-image-11210" /></a> <a href="http://inchoo.net/wp-content/uploads/2011/11/IMAG0103.jpg"><img src="http://inchoo.net/wp-content/uploads/2011/11/IMAG0103-300x179.jpg" alt="" title="IMAG0103" width="300" height="179" class="alignnone size-thumbnail wp-image-11209" /></a></p>
<p><a href="http://inchoo.net/wp-content/uploads/2011/11/IMAG0101.jpg"><img src="http://inchoo.net/wp-content/uploads/2011/11/IMAG0101-300x179.jpg" alt="" title="IMAG0101" width="300" height="179" class="alignnone size-thumbnail wp-image-11208" /></a> <a href="http://inchoo.net/wp-content/uploads/2011/11/IMAG0097.jpg"><img src="http://inchoo.net/wp-content/uploads/2011/11/IMAG0097-300x179.jpg" alt="" title="IMAG0097" width="300" height="179" class="alignnone size-thumbnail wp-image-11207" /></a></p>
<p>Although I must admit I am looking forward to the possible next level course, something like “Advanced Magento Developement” which might be more suited for us doing Magento development on a day to day basis. Course whose focus wound not be on the functional and structural but on “best practices, specific features (checkout, order handling, payment gateways, shipping gateways)” side of things.</p>
<p>As mentioned, the course is intended for new as well as those experienced to Magento development. I remind you, the thing about Magento is that it’s really huge, meaning there is so many things you need to know, which is why I would say this course hits the target when it comes to the “fundamentals”. I find it ideal for people who are experienced in PHP but are mostly just starting with Magento or have very little Magento experience, or are use to doing Magento development in a non Magento way of doing things. For those, I see a great value in it, as it will most certainly set you on the right track.</p>
<p>To conclude, if you recognized your self in what I wrote above and you can afford it, I suggest you take it.</p>
]]></content:encoded>
			<wfw:commentRss>http://inchoo.net/ecommerce/magento/magentou-fundamentals-of-magento-development-impressions/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>CheckItOut, alternative checkout for Magento</title>
		<link>http://inchoo.net/ecommerce/magento/magento-checkout/checkitout-alternative-checkout-for-magento/</link>
		<comments>http://inchoo.net/ecommerce/magento/magento-checkout/checkitout-alternative-checkout-for-magento/#comments</comments>
		<pubDate>Sat, 22 Oct 2011 07:32:54 +0000</pubDate>
		<dc:creator>Branko Ajzele</dc:creator>
				<category><![CDATA[Checkout]]></category>
		<category><![CDATA[checkout]]></category>
		<category><![CDATA[extension]]></category>
		<category><![CDATA[Magento]]></category>

		<guid isPermaLink="false">http://inchoo.net/?p=11080</guid>
		<description><![CDATA[If for some reason you are not a big fan of Magento’s default One Page Checkout concept / workflow, it might be worth checking out alternative. Usually these alternatives come in a form of so called &#8220;one step checkout&#8221; where &#8230;<p><a href="http://inchoo.net/ecommerce/magento/magento-checkout/checkitout-alternative-checkout-for-magento/">Read more</a><p>]]></description>
			<content:encoded><![CDATA[<p>If for some reason you are not a big fan of Magento’s default One Page Checkout concept / workflow, it might be worth checking out alternative. Usually these alternatives come in a form of so called &#8220;one step checkout&#8221; where main difference is that entire customer data / payment data / shipment data is displayed not just on one page, but in one &#8220;piece&#8221;. Why is this important? Well, it usually leads to a bit better user experience. Even a fraction better user experience can make a difference between new order and abandoned cart. No one likes abandoned carts <img src='http://inchoo.net/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> .<span id="more-11080"></span></p>
<p>Let me present you with one possible &#8220;one step checkout&#8221; like alternative, the <a href="http://www.ecomdev.org/shop/checkitout.html">CheckItOut!</a> Magento extension by <a href="http://www.ecomdev.org/shop/">EcomDev</a>.</p>
<p>There are several key things that make this extension stand up. Before everything, extension is simple. Simple in terms that it modifies standard Magento checkout in the places where it is really needed. Usually this is not something people who buy extension pay attention. But I cannot stress enough how important it is to use stable extension that do not poke around core Magento functionality a lot. The simpler, the better, as long as it do it’s job. Quick overview of the code show most fo the focus on custom checkout controller EcomDev_CheckItOut_OnepageController which in turn extends the default checkout controller Mage_Checkout_OnepageController. This is &#8220;turned on&#8221; by a router definition within app\code\community\EcomDev\CheckItOut\etc\config.xml file. Meaning CheckItOut inherits all of the default controller behaviour then simply implements it’s stuff in the proper controller method. This approach is relatively solid as it is straight forward and you know that at any point you simply disable module and your default checkout kicks in.</p>
<p>Code itself looks &#8220;rock solid&#8221; and it looks like they achieved a lot of functionality without a single class rewrite (except controller). Layout seems to be easy modifiable due to the nice CSS files that go along with it.</p>
<p>On top of that there is a quality developer manual that goes with it. We all love good documentation when it comes to Magento.</p>
<p>This is all nice and all, but what about real frontend features? </p>
<p>Given that the extension is based on the standard Magento checkout, all payment methods and additional Magento customizations are supported. Meaning, it should be fully compatible with features like: Gift cards, Customer balance, Reward points, etc.</p>
<p>Additionally extension functionality can be disabled for all websites or a particular website. Plus, you can return standard Magento checkout at any time by changing configuration field in the admin interface.</p>
<p>Seems like a nice product, especially since it costs only €249.00. I say only, because you should think twice before you actually pay anyone to code custom checkout just for your Magento store.</p>
<p>All in all, definitely worth checking out if you are in need for &#8220;one step checkout&#8221; like process.</p>
<p>Cheers.</p>
]]></content:encoded>
			<wfw:commentRss>http://inchoo.net/ecommerce/magento/magento-checkout/checkitout-alternative-checkout-for-magento/feed/</wfw:commentRss>
		<slash:comments>22</slash:comments>
		</item>
		<item>
		<title>Configuring Magento for development</title>
		<link>http://inchoo.net/ecommerce/magento/configuring-magento-for-development/</link>
		<comments>http://inchoo.net/ecommerce/magento/configuring-magento-for-development/#comments</comments>
		<pubDate>Thu, 20 Oct 2011 06:41:22 +0000</pubDate>
		<dc:creator>Branko Ajzele</dc:creator>
				<category><![CDATA[Magento]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[Starting up]]></category>
		<category><![CDATA[configuration]]></category>
		<category><![CDATA[development]]></category>

		<guid isPermaLink="false">http://inchoo.net/?p=11033</guid>
		<description><![CDATA[New to the Magento or just used to doing things one way? Here are few tips for configuring Magento for development, in case you overlooked them. Please note, the more proper title of this article would be something like: Configuring &#8230;<p><a href="http://inchoo.net/ecommerce/magento/configuring-magento-for-development/">Read more</a><p>]]></description>
			<content:encoded><![CDATA[<p>New to the Magento or just used to doing things one way? Here are few tips for configuring Magento for development, in case you overlooked them. Please note, the more proper title of this article would be something like: Configuring Magento for development on local machine (after installation config). Meaning the tips outlined here only apply after the Magento is already installed.<span id="more-11033"></span></p>
<p>Here are the steps that you should do in order to set your Magento more suited for development:</p>
<ul>
<li>System > Cache Management > Disable All</li>
<li>System > Configuration > Advanced > Developer > Log Settings > Enabled => Yes</li>
<li>System > Configuration > Web > Search Engine Optimization > Use Wbe Server Rewrites => Yes</li>
<li>System > Index Management > Reindex All</li>
<li>Open .htaccess and set: SetEnv MAGE_IS_DEVELOPER_MODE &#8220;true&#8221; at the end of the file</li>
<li>Open .htaccess and set: php_value display_errors On somewhere within &#60;IfModule mod_php5.c&#62;</li>
<li>Rename /errors/local.xml.sample to /errors/local.xml</li>
<li>Create one sample customer with full valid American address (for example use US/California, city Alamo with ZIP 94507), and one with full valid non American address (other country) to test payment and shipping gateways properly</li>
<li>Compensate for the possible lack of email server by doing something like explained in <a href="http://inchoo.net/ecommerce/magento/magento-l-e-s-s/">this article</a></li>
</ul>
<p>Hope it helps.</p>
<p>Cheers.</p>
]]></content:encoded>
			<wfw:commentRss>http://inchoo.net/ecommerce/magento/configuring-magento-for-development/feed/</wfw:commentRss>
		<slash:comments>7</slash:comments>
		</item>
		<item>
		<title>Magento Trusted Extensions</title>
		<link>http://inchoo.net/ecommerce/magento/trusted-extensions/</link>
		<comments>http://inchoo.net/ecommerce/magento/trusted-extensions/#comments</comments>
		<pubDate>Fri, 14 Oct 2011 16:21:18 +0000</pubDate>
		<dc:creator>Branko Ajzele</dc:creator>
				<category><![CDATA[Extensions]]></category>
		<category><![CDATA[Magento]]></category>
		<category><![CDATA[extension]]></category>
		<category><![CDATA[trusted]]></category>

		<guid isPermaLink="false">http://inchoo.net/?p=10968</guid>
		<description><![CDATA[About ten months ago I wrote an article titled Magento Extensions Quality Assurance Certification. If you read it, then you know it was a pure result of my imagination. In that article I spoke about (then) current state of various &#8230;<p><a href="http://inchoo.net/ecommerce/magento/trusted-extensions/">Read more</a><p>]]></description>
			<content:encoded><![CDATA[<p>About ten months ago I wrote an article titled <a href="http://inchoo.net/ecommerce/magento/magento-extensions-quality-assurance-certification/">Magento Extensions Quality Assurance Certification</a>. If you read it, then you know it was a pure result of my imagination. In that article I spoke about (then) current state of various extensions, their conflicts, etc. Finally concluding how nice it would be to have some sort of extension certification program. Certification program that would be in charge of checking several important parts of your extension inner working, etc. All with the final goal of classifying your extension as safe.<span id="more-10968"></span></p>
<p>As I said, this was around ten months ago. During that time folks at Magento wasted no time. Just recently they announced several interesting new things, one of which is <a href="https://www.magentocommerce.com/magento-connect/trusted_extension/">&#8220;<strong>Trusted Extension</strong>&#8221; program/process</a>. For the time being this only applies to the Magento GO platform, but as they announced it, it should come in play for Community Edition of Magento as well.</p>
<p>For all involved in Magento development it’s clear that such process of marking extension as &#8220;trusted&#8221; is all but simply. Extension reviewers really need to be up to the task when it comes to knowing Magento inside out. Magento folks included an extra step in this process, the legal one. Meaning, besides variety of technical tests your extension will need to pass, you as a developer will need to commit both from technical/support and legal side.</p>
<p>Technical tests are to include things like Code review, Security Checks, Magento database usage, interface and consistency, Remote calls to 3rd party systems, Memory usage, Performance impact and load impact, Overall behavior in multi-tenant SaaS environment.</p>
<p>From the legal stand, developers who are accepted to the process will sign on dedicated business and developer agreement with Magento.</p>
<p>At the end of the day Magento does not guarantee functional accuracy of specific extension behavior, which is pretty fare enough given that I do not see any mentions of this being a paid process.</p>
<p>All in all, I’m happy to see something happening in regards to &#8220;marking&#8221; the quality extensions. Time will show how much of actual benefit this will bring.</p>
<p>Combine this with all of the latest official Magento education materials/courses popping out and I’m sure where are in for a bright, more quality eCommerce business with this great platform.</p>
]]></content:encoded>
			<wfw:commentRss>http://inchoo.net/ecommerce/magento/trusted-extensions/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Magento certification, why bother?</title>
		<link>http://inchoo.net/ecommerce/magento/magento-certification-why-bother/</link>
		<comments>http://inchoo.net/ecommerce/magento/magento-certification-why-bother/#comments</comments>
		<pubDate>Mon, 10 Oct 2011 13:40:29 +0000</pubDate>
		<dc:creator>Branko Ajzele</dc:creator>
				<category><![CDATA[Magento]]></category>
		<category><![CDATA[Starting up]]></category>
		<category><![CDATA[certification]]></category>
		<category><![CDATA[exam]]></category>

		<guid isPermaLink="false">http://inchoo.net/?p=10879</guid>
		<description><![CDATA[Few days ago Magento announced their plans for Magento certification program. As they stated, initial round of certification will be a Beta exam, held on Innovate Developer Conference in San Francisco, October 12 and 13! Those who pass the exam &#8230;<p><a href="http://inchoo.net/ecommerce/magento/magento-certification-why-bother/">Read more</a><p>]]></description>
			<content:encoded><![CDATA[<p>Few days ago Magento announced their <a href="http://www.magentocommerce.com/blog/comments/innovate-magento-beta-certification-exam/">plans for Magento certification program</a>. As they stated, initial round of certification will be a Beta exam, held on Innovate Developer Conference in San Francisco, October 12 and 13! Those who pass the exam based on the Beta exam analysis, will actually earn the official Magento Developer certification. Final reward for those who pass the exam? <span id="more-10879"></span>They will receive Magento certificate and be able to use the official logo with their name and title. Clearly there is more to it than just a piece of paper, so let me share my thoughts and views with you.</p>
<p>The thing about the certification is that it’s not only about the piece of paper or a card issued by some authority verifying your knowledge and expertise. If you look at it more deeply, additional value lies in the individuals desire to actually go trough it. This shows determination and love towards an area of interest. It’s hard to imagine someone would go trough all the hassle of certification as specific as this one if he does not love his work. This goes for certification in general, not just Magento. This is something hiring managers will know how to recognise and value.</p>
<p>From a personal point of view, as a developer you gain recognition of your Magento skills. Which is not to be ignored, as Magento still is one of the most robust eCommerce platforms our there. This can help differentiate yourself from the competition, possibly increase your value on the market. Not to mention there is a good chance you gain some additional knowledge through preparation and training. Face it as much as you know Magento chances are you skipped a “chapter or two” in your everyday real job (I know I did).</p>
<p>On a company level, there is also a bonus of having some or all of your developers certified. This usually goes in favour of your overall quality.</p>
<p>I’m sure most of you in Magento business will agree that Magento certification was more than welcome and I would even say needed. With all their great products and services now available time is right for quality certification program. I’m only hoping that over the next few months, year or two, most of us in everyday Magento development will earn ourselves a certificate <img src='http://inchoo.net/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </p>
<p>So what’s it all about? <a href="http://www.magentocommerce.com/blog/comments/innovate-magento-beta-certification-exam/">Magento Developer Certification landing page</a> has a nice little PDF file called <a href="http://info.magento.com/rs/magentocommerce/images/Magento%20Certified%20Developer%20Exam%20Self-Assessment%20Checklist%20%28Beta%29.pdf">Certified Developer Exam: Self Assessment Checklist</a>. It outlines the certification program which comprises of two levels:</p>
<ul>
<li><strong>Magento Certified Developer (MCD)</strong></li>
<li><strong>Magento Certified Developer Plus (MCD Plus)</strong>, has an additional 20 questions</li>
</ul>
<p>Questions are based on Magento Community Edition Magento Enterprise Edition. Surprisingly there is a detailed outline of 10 specific areas that exam covers, all you need to do is to study it.</p>
<ol>
<li>Basics (5%)</li>
<li>Request Flow (7%)</li>
<li>Rendering (8%)</li>
<li>Working with Database in Magento (12%)</li>
<li>Entity-Attribute-Value (EAV) Model (10%)</li>
<li>Adminhtml (8%)</li>
<li>Catalog (10%)</li>
<li>Checkout (15%)</li>
<li>Sales and Customers (12%)</li>
<li>Advanced features (13%)</li>
</ol>
<p><a href="http://info.magento.com/rs/magentocommerce/images/Magento%20Certified%20Developer%20Exam%20Self-Assessment%20Checklist%20%28Beta%29.pdf">Check the document</a> for details. </p>
<p>In a nutshell that’s it for now. Grab your Magento installations, some coffee, and run trough outlined exam areas if you are interested in taking exam. I must say, it does not look like it’s going to be easy. Regardless of your experience, Magento is still a large platform, meaning lot of terrain to cover so get prepared:)</p>
<p>Cheers.</p>
]]></content:encoded>
			<wfw:commentRss>http://inchoo.net/ecommerce/magento/magento-certification-why-bother/feed/</wfw:commentRss>
		<slash:comments>8</slash:comments>
		</item>
		<item>
		<title>Creating your own web shop &#8211; Part 1: Where to start</title>
		<link>http://inchoo.net/ecommerce/creating-your-own-web-shop-part-1-where-to-start/</link>
		<comments>http://inchoo.net/ecommerce/creating-your-own-web-shop-part-1-where-to-start/#comments</comments>
		<pubDate>Sun, 07 Aug 2011 09:56:48 +0000</pubDate>
		<dc:creator>Branko Ajzele</dc:creator>
				<category><![CDATA[E-Commerce]]></category>
		<category><![CDATA[custom]]></category>
		<category><![CDATA[development]]></category>
		<category><![CDATA[Magento]]></category>
		<category><![CDATA[web shop]]></category>

		<guid isPermaLink="false">http://inchoo.net/?p=10576</guid>
		<description><![CDATA[Creating your own web shop can be a tedious task, both technically and time consuming. Not to mention there are tens of quality PHP based web shop systems out there available for free. One of the best if you ask &#8230;<p><a href="http://inchoo.net/ecommerce/creating-your-own-web-shop-part-1-where-to-start/">Read more</a><p>]]></description>
			<content:encoded><![CDATA[<p>Creating your own web shop can be a tedious task, both technically and time consuming. Not to mention there are tens of quality PHP based web shop systems out there available for free. One of the best if you ask me is Magento, which exceeds the term web shop system by even bigger term &#8220;platform&#8221;. However, regardless of how some much a given system/platform is feature rich there are always those special clients with special requests which exceed the possibilities of even the mightiest eCommerce systems/platforms out there.<span id="more-10576"></span></p>
<p>In my experience, usually these type of clients are either big enterprise level clients or &#8220;small first time running a web shop&#8221; client. Big enterprise level clients usually know very well what they want as they outgrown the features even the most feature rich platforms can give them out of the box.</p>
<p>Before I go any further, I would just like to clarify one thing. This article is by no means advice or suggestion you should consider building your own web shop solution over choosing Magento CE, PE, or EE or possibly some other professional/enterprise web shop platform. In my opinion doing so would only be justified in situations where desired feature list massively exceeds the features offered by any of Magento versions, or if your budget and delivery date are of no or little importance.</p>
<p>Seems like building your own web shop system these days comes down to (1) satisfying your personal needs as a developer (meaning you got some time on your hand and you wish to play with building something out of the scratch just so see if you can do it), (2) You really have a special feature list you need to implement in your own web shop while at the same time you do not need all or any of the features offered by powerful web shops that are already there. Everything else seems just like reinventing a wheel, because all it takes is to develop extensions/modules for existing systems/platforms and save yourself both money and time.</p>
<p>OK, enough for the introduction. Let&#8221;s get down to it. Where to start when creating your own shop? I would say there are five major topic that need to be outlined: </p>
<ul>
<li>Feature list (<i>in a broader term this should come with proper software requirements, as noted by <a href="http://inchoo.net/ecommerce/creating-your-own-web-shop-part-1-where-to-start/#comment-22233">beeplogic</a> below in comments</i>),</li>
<li>Development platform selection,</li>
<li>Overall budget</li>
<li>Available resources</li>
<li>Expected date of delivery</li>
</ul>
<p><strong>Feature list</strong> should cover a full list of requested features, like: Customer Single Sign On (Facebook, Twitter), One Step Checkout, Implementation of XYZ payment gateway, Product Review/Comments System, Multi-language, Multi-currency, Email Notifications System, Product types (standard, downloadable, bundle), etc.</p>
<p><strong>Development platform selection</strong> should evaluate possibly several different platforms that might be interested for our project. For example, usage of Zend Framework over Symfony over CodeIgniter over cakePHP etc., if we are talking about PHP platforms.</p>
<p><strong>Overall budget</strong> is pretty clear. Probably the most important thing here to note is that you should never plan your project on &#8220;how to spent entire budget&#8221; but more like &#8220;how to deliver all or most of the features by expected delivery date not exceeding the overall budget&#8221; concept. Sometimes clients need reality check, and they need to be forced to either prioritize or dismiss certain features if the budget is low.</p>
<p><strong>Available resources</strong> are all those people (developers, designers, project manager) in your team which you know will be working on a given project.</p>
<p><strong>Expected date of delivery</strong> is extremely important in the big picture as it represents the link between feature list, overall budget and available resources. For example, imagine someone gives you a budget of $500K, in order to deliver a system with 50 outlined features with a team of 7 experienced people working on it within 3 months. Let&#8221;s assume you take $120/hour for development services. It comes down to $120/hour * 8h/day * 5days/week * 4weeks/month = $ 19.200,00 per month per developer. Which is around $134.400,00 per month for entire 7 people team. In 3 months time this turns out to be $403.200,00 meaning you are not able to spent all of the budget available. Biggest mistake for a project and people on it you can do here is to throw in a few newbies just to fill in the gap for reaching the budget limit. This disrupts the workflow of the rest of the team for two reasons: (a) newbie developers need significant time to get to the level of senior developers thus not being nearly productive as other team members, (b) each team expansion requires extra management input or even change in management philosophy.</p>
<p>What I outlined above usually makes sense in business environment where entire team is working on a new system/platform. Things are significantly different if you alone are trying to create something in your own time (usually as a hobby). In that case we are usually focused on first two topics: (1) Feature list and (2) Development platform selection. Meaning we have no budged to look at, no expected delivery date to worry about.</p>
<p>In my next articles under this topic I will focus strictly on development platform selection and the actual development of web shop from ground up. Until then, I&#8221;ll give you a hint&#8230; Symfony + Zend Framework + Ext JS <img src='http://inchoo.net/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </p>
<p>Cheers.</p>
]]></content:encoded>
			<wfw:commentRss>http://inchoo.net/ecommerce/creating-your-own-web-shop-part-1-where-to-start/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>Magento&#8217;s &#8220;Quote/Order/Invoice&#8221; workflow</title>
		<link>http://inchoo.net/ecommerce/magento/magento-quote-order-invoice-workflow/</link>
		<comments>http://inchoo.net/ecommerce/magento/magento-quote-order-invoice-workflow/#comments</comments>
		<pubDate>Wed, 27 Jul 2011 08:06:06 +0000</pubDate>
		<dc:creator>Branko Ajzele</dc:creator>
				<category><![CDATA[Magento]]></category>
		<category><![CDATA[Orders]]></category>
		<category><![CDATA[Starting up]]></category>
		<category><![CDATA[invoice]]></category>
		<category><![CDATA[order]]></category>
		<category><![CDATA[quote]]></category>
		<category><![CDATA[workflow]]></category>

		<guid isPermaLink="false">http://inchoo.net/?p=10516</guid>
		<description><![CDATA[The purpose of this article is to shed some light onto the flow of things that happen behind the scene in the process of creating and later invoicing order in Magento. In order to properly code new functionality surrounding order &#8230;<p><a href="http://inchoo.net/ecommerce/magento/magento-quote-order-invoice-workflow/">Read more</a><p>]]></description>
			<content:encoded><![CDATA[<p>The purpose of this article is to shed some light onto the flow of things that happen behind the scene in the process of creating and later invoicing order in Magento. In order to properly code new functionality surrounding order creation process, one must have a solid understanding of that process otherwise you may impose some serious issues on to the system via custom coding around orders.<span id="more-10516"></span></p>
<p>If you would like to follow up, please set up your own test Magento store. In my example I am using Magento Community Edition, 1.5.</p>
<p>So where do we start. I suggest we start from empty sales tables, meaning we run the following query on database:</p>
<pre class="brush: sql; title: ; notranslate">
TRUNCATE `sales_bestsellers_aggregated_daily`;
TRUNCATE `sales_bestsellers_aggregated_monthly`;
TRUNCATE `sales_bestsellers_aggregated_yearly`;
TRUNCATE `sales_billing_agreement`;
TRUNCATE `sales_billing_agreement_order`;
TRUNCATE `sales_flat_creditmemo`;
TRUNCATE `sales_flat_creditmemo_comment`;
TRUNCATE `sales_flat_creditmemo_grid`;
TRUNCATE `sales_flat_creditmemo_item`;
TRUNCATE `sales_flat_invoice`;
TRUNCATE `sales_flat_invoice_comment`;
TRUNCATE `sales_flat_invoice_grid`;
TRUNCATE `sales_flat_invoice_item`;
TRUNCATE `sales_flat_order`;
TRUNCATE `sales_flat_order_address`;
TRUNCATE `sales_flat_order_grid`;
TRUNCATE `sales_flat_order_item`;
TRUNCATE `sales_flat_order_payment`;
TRUNCATE `sales_flat_order_status_history`;
TRUNCATE `sales_flat_quote`;
TRUNCATE `sales_flat_quote_address`;
TRUNCATE `sales_flat_quote_address_item`;
TRUNCATE `sales_flat_quote_item`;
TRUNCATE `sales_flat_quote_item_option`;
TRUNCATE `sales_flat_quote_payment`;
TRUNCATE `sales_flat_quote_shipping_rate`;
TRUNCATE `sales_flat_shipment`;
TRUNCATE `sales_flat_shipment_comment`;
TRUNCATE `sales_flat_shipment_grid`;
TRUNCATE `sales_flat_shipment_item`;
TRUNCATE `sales_flat_shipment_track`;
TRUNCATE `sales_invoiced_aggregated`;
TRUNCATE `sales_invoiced_aggregated_order`;
TRUNCATE `sales_order_aggregated_created`;
TRUNCATE `sales_order_tax`;
TRUNCATE `sales_payment_transaction`;
TRUNCATE `sales_recurring_profile`;
TRUNCATE `sales_recurring_profile_order`;
TRUNCATE `sales_refunded_aggregated`;
TRUNCATE `sales_refunded_aggregated_order`;
TRUNCATE `sales_shipping_aggregated`;
TRUNCATE `sales_shipping_aggregated_order`;
</pre>
<p>Now we will go to the frontend or our store and add 3 different items to our cart. You can check out the screenshot of the added items.</p>
<p>This simple action of adding 3 items made following entries into the tables:</p>
<pre class="brush: sql; title: ; notranslate">
-- ----------------------------
-- Records of sales_flat_quote
-- ----------------------------
INSERT INTO `sales_flat_quote` VALUES ('1', '1', '2011-07-26 11:20:34', '2011-07-26 11:21:23', null, '1', '0', '0', '3', '10.0000', '0', '1.0000', '1.0000', '1.0000', '1.0000', 'USD', 'USD', 'USD', 'USD', '6196.9900', '6196.9900', null, null, '3', '0', null, null, null, null, null, null, null, null, '1', '0', null, '127.0.0.1', null, null, null, null, '6196.9900', '6196.9900', '6196.9900', '6196.9900', null, '1', '0', null, null, '0');
</pre>
<pre class="brush: sql; title: ; notranslate">
-- ----------------------------
-- Records of sales_flat_quote_address
-- ----------------------------
INSERT INTO `sales_flat_quote_address` VALUES ('1', '1', '2011-07-26 11:20:34', '2011-07-26 11:21:23', null, '0', null, 'billing', null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, '0', '0', '0', null, null, '0.0000', '0.0000', '0.0000', '0.0000', '0.0000', '0.0000', '0.0000', '0.0000', '0.0000', '0.0000', '0.0000', '0.0000', '0.0000', '0.0000', '0.0000', null, 'a:0:{}', null, null, null, '0.0000', null, null, null, null, null, null, '0.0000', '0.0000');
INSERT INTO `sales_flat_quote_address` VALUES ('2', '1', '2011-07-26 11:20:34', '2011-07-26 11:21:23', null, '0', null, 'shipping', null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, '1', '0', '0', null, null, '29.0000', '6196.9900', '6196.9900', '0.0000', '0.0000', '0.0000', '0.0000', '0.0000', '0.0000', '0.0000', '0.0000', '0.0000', '0.0000', '6196.9900', '6196.9900', null, 'a:0:{}', null, '0.0000', '0.0000', '6196.9900', null, null, '0.0000', '0.0000', '0.0000', null, '0.0000', '0.0000');
</pre>
<pre class="brush: sql; title: ; notranslate">
-- ----------------------------
-- Records of sales_flat_quote_item
-- ----------------------------
INSERT INTO `sales_flat_quote_item` VALUES ('1', '1', '2011-07-26 11:20:34', '2011-07-26 11:20:34', '17', '1', null, '0', 'bb8100', 'BlackBerry 8100 Pearl', null, null, null, '0', '0', '0', '15.2000', '1.0000', '349.9900', '349.9900', null, '0.0000', '0.0000', '0.0000', '0.0000', '0.0000', '0.0000', '349.9900', '349.9900', '0.0000', '15.2000', 'simple', null, null, null, null, 'a:0:{}', '0.0000', '0.0000', '0.0000', null, '0.0000', '0.0000', '0.0000', '0.0000', null, '29.9900', '349.9900', '349.9900', '349.9900', '349.9900', null, null);
INSERT INTO `sales_flat_quote_item` VALUES ('2', '1', '2011-07-26 11:20:49', '2011-07-26 11:21:23', '166', '1', null, '0', 'HTC Touch Diamond', 'HTC Touch Diamond', null, null, null, '0', '0', '0', '0.3000', '6.0000', '750.0000', '750.0000', null, '0.0000', '0.0000', '0.0000', '0.0000', '0.0000', '0.0000', '4500.0000', '4500.0000', '0.0000', '1.8000', 'simple', null, null, null, null, 'a:0:{}', '0.0000', '0.0000', '0.0000', null, '0.0000', '0.0000', '0.0000', '0.0000', null, null, '750.0000', '750.0000', '4500.0000', '4500.0000', null, null);
INSERT INTO `sales_flat_quote_item` VALUES ('3', '1', '2011-07-26 11:21:12', '2011-07-26 11:21:12', '44', '1', null, '0', 'Rebel XT', 'Canon Digital Rebel XT 8MP Digital SLR Camera', null, null, null, '0', '0', '0', '4.0000', '3.0000', '449.0000', '449.0000', null, '0.0000', '0.0000', '0.0000', '0.0000', '0.0000', '0.0000', '1347.0000', '1347.0000', '0.0000', '12.0000', 'simple', null, null, null, null, 'a:0:{}', '0.0000', '0.0000', '0.0000', null, '0.0000', '0.0000', '0.0000', '0.0000', null, '200.0000', '449.0000', '449.0000', '1347.0000', '1347.0000', null, null);
</pre>
<pre class="brush: plain; title: ; notranslate">
-- ----------------------------
-- Records of sales_flat_quote_item_option
-- ----------------------------
INSERT INTO `sales_flat_quote_item_option` VALUES ('1', '1', '17', 'info_buyRequest', 'a:4:{s:4:\&quot;uenc\&quot;;s:124:\&quot;aHR0cDovL21hZ2VudG8tMS42LjAuMC1yYzIubG9jYWwvaW5kZXgucGhwL2VsZWN0cm9uaWNzL2NlbGwtcGhvbmVzL2JsYWNrYmVycnktODEwMC1wZWFybC5odG1s\&quot;;s:7:\&quot;product\&quot;;s:2:\&quot;17\&quot;;s:15:\&quot;related_product\&quot;;s:0:\&quot;\&quot;;s:3:\&quot;qty\&quot;;s:1:\&quot;1\&quot;;}');
INSERT INTO `sales_flat_quote_item_option` VALUES ('2', '2', '166', 'info_buyRequest', 'a:4:{s:4:\&quot;uenc\&quot;;s:120:\&quot;aHR0cDovL21hZ2VudG8tMS42LjAuMC1yYzIubG9jYWwvaW5kZXgucGhwL2VsZWN0cm9uaWNzL2NlbGwtcGhvbmVzL2h0Yy10b3VjaC1kaWFtb25kLmh0bWw,\&quot;;s:7:\&quot;product\&quot;;s:3:\&quot;166\&quot;;s:15:\&quot;related_product\&quot;;s:0:\&quot;\&quot;;s:3:\&quot;qty\&quot;;s:1:\&quot;6\&quot;;}');
INSERT INTO `sales_flat_quote_item_option` VALUES ('3', '3', '44', 'info_buyRequest', 'a:4:{s:4:\&quot;uenc\&quot;;s:224:\&quot;aHR0cDovL21hZ2VudG8tMS42LjAuMC1yYzIubG9jYWwvaW5kZXgucGhwL2VsZWN0cm9uaWNzL2NhbWVyYXMvZGlnaXRhbC1jYW1lcmFzL2Nhbm9uLWRpZ2l0YWwtcmViZWwteHQtOG1wLWRpZ2l0YWwtc2xyLWNhbWVyYS13aXRoLWVmLXMtMTgtNTVtbS1mMy01LTUtNi1sZW5zLWJsYWNrLmh0bWw,\&quot;;s:7:\&quot;product\&quot;;s:2:\&quot;44\&quot;;s:15:\&quot;related_product\&quot;;s:0:\&quot;\&quot;;s:3:\&quot;qty\&quot;;s:1:\&quot;3\&quot;;}');
</pre>
<p>Based on this simple example of 3 &#8220;simple&#8221; product types we have all those entires in the database. As you can see, all of the entries are done on only &#8220;sales_flat_quote_&#8221; prefixed tables, some of them, not all. Since we tested against very simple case.</p>
<p>Let us proceed now and do the checkout based on the items we have in the cart. In order to keep the process with minimum data in the system, I will go trough the checkout as &#8220;guest&#8221; user using &#8220;Flat Rate&#8221; shipping and &#8220;Check / Money order&#8221; payment.</p>
<p>Right before we click the &#8220;Place Order&#8221; button on our one page checkout, additional quote tables get filled. Below is the example that follows the previous state.</p>
<pre class="brush: sql; title: ; notranslate">
-- ----------------------------
-- Records of sales_flat_quote_address
-- ----------------------------
INSERT INTO `sales_flat_quote_address` VALUES ('1', '1', '2011-07-26 11:20:34', '2011-07-26 11:39:27', null, '1', null, 'billing', 'email_address@gmail.com', null, 'Branko', null, 'Ajzele', null, 'Inchoo', 'Sample address line1\nAddress line 2', 'Osijek', null, null, '31431', 'HR', '0038531888777', 'admin', '0', '0', '0', null, null, '0.0000', '0.0000', '0.0000', '0.0000', '0.0000', '0.0000', '0.0000', '0.0000', '0.0000', '0.0000', '0.0000', '0.0000', '0.0000', '0.0000', '0.0000', null, 'a:0:{}', null, null, null, '0.0000', null, null, null, null, null, null, '0.0000', '0.0000');
INSERT INTO `sales_flat_quote_address` VALUES ('2', '1', '2011-07-26 11:20:34', '2011-07-26 11:39:27', null, '0', null, 'shipping', 'email_address@gmail.com', null, 'Branko', null, 'Ajzele', null, 'Inchoo', 'Sample address line1\nAddress line 2', 'Osijek', null, null, '31431', 'HR', '0038531888777', 'admin', '1', '0', '0', 'flatrate_flatrate', 'Flat Rate - Fixed', '29.0000', '6196.9900', '6196.9900', '0.0000', '0.0000', '0.0000', '0.0000', '50.0000', '50.0000', '0.0000', '0.0000', '0.0000', '0.0000', '6246.9900', '6246.9900', null, 'a:0:{}', null, '0.0000', '0.0000', '6196.9900', null, null, '0.0000', '0.0000', '0.0000', null, '50.0000', '50.0000');
</pre>
<p>As you can see, our sales_flat_quote_address table got it’s records updated with new values. In previous (first) one we had no values for &#8220;email&#8221; or other columns, but now we do once we are passed the checkout steps all the way to final &#8220;Place Order&#8221; button.</p>
<pre class="brush: sql; title: ; notranslate">
-- ----------------------------
-- Records of sales_flat_quote_payment
-- ----------------------------
INSERT INTO `sales_flat_quote_payment` VALUES ('1', '1', '2011-07-26 11:29:36', '2011-07-26 11:39:27', 'checkmo', null, null, null, null, null, '0', '0', null, '0', '0', null, null, null, null, null, null, null, null, null, null);
</pre>
<pre class="brush: sql; title: ; notranslate">
-- ----------------------------
-- Records of sales_flat_quote_shipping_rate
-- ----------------------------
INSERT INTO `sales_flat_quote_shipping_rate` VALUES ('2', '2', '2011-07-26 11:39:01', '2011-07-26 11:39:27', 'flatrate', 'Flat Rate', 'flatrate_flatrate', 'flatrate', null, '50.0000', 'Fixed', null);
</pre>
<p>So basically, all the way prior to finally clicking the &#8220;Place Order&#8221; button we have our data stored in &#8220;quote&#8221; tables. Meaning, &#8220;Quote&#8221; model is &#8220;data storage&#8221; from which the &#8220;Order&#8221; model is actually created. Or by more fancy definition, &#8220;Quote&#8221; is a formal offer for products or services proposed at specific prices and related payment terms tied to a given customer.</p>
<p>After we hit the &#8220;Place Order&#8221; button we get our order created. In that regards to previously said about the quote, &#8220;Order&#8221; is a quote that has been accepted.</p>
<p>So after the order has been successfully created, there are several new entries in the &#8220;sales_flat_order_&#8221; prefixed tables. Let’s take a look at those.</p>
<pre class="brush: sql; title: ; notranslate">
-- ----------------------------
-- Records of sales_flat_order
-- ----------------------------
INSERT INTO `sales_flat_order` VALUES ('1', 'new', 'pending', null, 'b71db3', 'Flat Rate - Fixed', '0', '1', null, '0.0000', null, null, null, '6246.9900', '50.0000', null, null, null, '0.0000', null, '6196.9900', null, null, null, '0.0000', null, null, null, '1.0000', '1.0000', null, null, null, null, null, null, null, null, '0.0000', null, null, null, '6246.9900', '50.0000', null, null, null, '0.0000', null, '1.0000', '1.0000', '6196.9900', null, null, null, '0.0000', null, null, null, null, null, null, null, null, '10.0000', null, null, null, '1', '1', '1', '0', null, '1', null, null, null, null, null, '1', '2', null, null, null, null, '0.0000', '6196.9900', null, null, '0.0000', '6196.9900', null, '29.0000', null, '100000001', null, 'USD', 'email_address@gmail.com', 'Branko', 'Ajzele', null, null, null, null, null, null, null, 'USD', null, null, 'USD', null, null, null, null, null, '127.0.0.1', 'flatrate_flatrate', 'USD', 'Main Website\nMain Store\nEnglish', null, null, '2011-07-26 11:39:27', '2011-07-26 11:39:31', '3', null, null, null, null, null, null, null, null, null, null, null, '0.0000', '0.0000', '0.0000', '0.0000', null, null, null, null, '50.0000', '50.0000');
</pre>
<pre class="brush: sql; title: ; notranslate">
-- ----------------------------
-- Records of sales_flat_order_address
-- ----------------------------
INSERT INTO `sales_flat_order_address` VALUES ('1', '1', null, null, null, null, 'admin', null, '31431', 'Ajzele', 'Sample address line1\nAddress line 2', 'Osijek', 'email_address@gmail.com', '0038531888777', 'HR', 'Branko', 'billing', null, null, null, 'Inchoo', null, null, null);
INSERT INTO `sales_flat_order_address` VALUES ('2', '1', null, null, null, null, 'admin', null, '31431', 'Ajzele', 'Sample address line1\nAddress line 2', 'Osijek', 'email_address@gmail.com', '0038531888777', 'HR', 'Branko', 'shipping', null, null, null, 'Inchoo', null, null, null);
</pre>
<pre class="brush: sql; title: ; notranslate">
-- ----------------------------
-- Records of sales_flat_order_grid
-- ----------------------------
INSERT INTO `sales_flat_order_grid` VALUES ('1', 'pending', '1', 'Main Website\nMain Store\nEnglish', null, '6246.9900', null, '6246.9900', null, '100000001', 'USD', 'USD', 'Branko Ajzele', 'Branko Ajzele', '2011-07-26 11:39:27', '2011-07-26 11:39:31');
</pre>
<pre class="brush: sql; title: ; notranslate">
-- ----------------------------
-- Records of sales_flat_order_item
-- ----------------------------
INSERT INTO `sales_flat_order_item` VALUES ('1', '1', null, '1', '1', '2011-07-26 11:39:27', '2011-07-26 11:39:27', '17', 'simple', 'a:1:{s:15:\&quot;info_buyRequest\&quot;;a:4:{s:4:\&quot;uenc\&quot;;s:124:\&quot;aHR0cDovL21hZ2VudG8tMS42LjAuMC1yYzIubG9jYWwvaW5kZXgucGhwL2VsZWN0cm9uaWNzL2NlbGwtcGhvbmVzL2JsYWNrYmVycnktODEwMC1wZWFybC5odG1s\&quot;;s:7:\&quot;product\&quot;;s:2:\&quot;17\&quot;;s:15:\&quot;related_product\&quot;;s:0:\&quot;\&quot;;s:3:\&quot;qty\&quot;;s:1:\&quot;1\&quot;;}}', '15.2000', '0', 'bb8100', 'BlackBerry 8100 Pearl', null, null, null, '0', '0', '0', null, '0.0000', '0.0000', '1.0000', '0.0000', '0.0000', '29.9900', '349.9900', '349.9900', '349.9900', '349.9900', '0.0000', '0.0000', '0.0000', '0.0000', '0.0000', '0.0000', '0.0000', '0.0000', '0.0000', '0.0000', '0.0000', '0.0000', '349.9900', '349.9900', '0.0000', '0.0000', '15.2000', null, null, null, null, 'a:0:{}', '0.0000', '0.0000', '0.0000', '0.0000', '0.0000', '0.0000', '0.0000', '0.0000', null, null, null, '349.9900', '349.9900', '349.9900', '349.9900', null, null, null, null, null, null, '0', null, null, null);
INSERT INTO `sales_flat_order_item` VALUES ('2', '1', null, '2', '1', '2011-07-26 11:39:29', '2011-07-26 11:39:29', '166', 'simple', 'a:1:{s:15:\&quot;info_buyRequest\&quot;;a:4:{s:4:\&quot;uenc\&quot;;s:120:\&quot;aHR0cDovL21hZ2VudG8tMS42LjAuMC1yYzIubG9jYWwvaW5kZXgucGhwL2VsZWN0cm9uaWNzL2NlbGwtcGhvbmVzL2h0Yy10b3VjaC1kaWFtb25kLmh0bWw,\&quot;;s:7:\&quot;product\&quot;;s:3:\&quot;166\&quot;;s:15:\&quot;related_product\&quot;;s:0:\&quot;\&quot;;s:3:\&quot;qty\&quot;;s:1:\&quot;6\&quot;;}}', '0.3000', '0', 'HTC Touch Diamond', 'HTC Touch Diamond', null, null, null, '0', '0', '0', null, '0.0000', '0.0000', '6.0000', '0.0000', '0.0000', null, '750.0000', '750.0000', '750.0000', '750.0000', '0.0000', '0.0000', '0.0000', '0.0000', '0.0000', '0.0000', '0.0000', '0.0000', '0.0000', '0.0000', '0.0000', '0.0000', '4500.0000', '4500.0000', '0.0000', '0.0000', '1.8000', null, null, null, null, 'a:0:{}', '0.0000', '0.0000', '0.0000', '0.0000', '0.0000', '0.0000', '0.0000', '0.0000', null, null, null, '750.0000', '750.0000', '4500.0000', '4500.0000', null, null, null, null, null, null, '0', null, null, null);
INSERT INTO `sales_flat_order_item` VALUES ('3', '1', null, '3', '1', '2011-07-26 11:39:30', '2011-07-26 11:39:30', '44', 'simple', 'a:1:{s:15:\&quot;info_buyRequest\&quot;;a:4:{s:4:\&quot;uenc\&quot;;s:224:\&quot;aHR0cDovL21hZ2VudG8tMS42LjAuMC1yYzIubG9jYWwvaW5kZXgucGhwL2VsZWN0cm9uaWNzL2NhbWVyYXMvZGlnaXRhbC1jYW1lcmFzL2Nhbm9uLWRpZ2l0YWwtcmViZWwteHQtOG1wLWRpZ2l0YWwtc2xyLWNhbWVyYS13aXRoLWVmLXMtMTgtNTVtbS1mMy01LTUtNi1sZW5zLWJsYWNrLmh0bWw,\&quot;;s:7:\&quot;product\&quot;;s:2:\&quot;44\&quot;;s:15:\&quot;related_product\&quot;;s:0:\&quot;\&quot;;s:3:\&quot;qty\&quot;;s:1:\&quot;3\&quot;;}}', '4.0000', '0', 'Rebel XT', 'Canon Digital Rebel XT 8MP Digital SLR Camera', null, null, null, '0', '0', '0', null, '0.0000', '0.0000', '3.0000', '0.0000', '0.0000', '200.0000', '449.0000', '449.0000', '449.0000', '449.0000', '0.0000', '0.0000', '0.0000', '0.0000', '0.0000', '0.0000', '0.0000', '0.0000', '0.0000', '0.0000', '0.0000', '0.0000', '1347.0000', '1347.0000', '0.0000', '0.0000', '12.0000', null, null, null, null, 'a:0:{}', '0.0000', '0.0000', '0.0000', '0.0000', '0.0000', '0.0000', '0.0000', '0.0000', null, null, null, '449.0000', '449.0000', '1347.0000', '1347.0000', null, null, null, null, null, null, '0', null, null, null);
</pre>
<pre class="brush: sql; title: ; notranslate">
-- ----------------------------
-- Records of sales_flat_order_payment
-- ----------------------------
INSERT INTO `sales_flat_order_payment` VALUES ('1', '1', null, null, null, null, null, null, null, null, '50.0000', '50.0000', null, null, '6246.9900', null, null, null, '6246.9900', null, null, null, null, '0', '0', null, 'checkmo', null, null, null, null, null, null, null, null, null, null, null, '0', null, null, null, null, null, null, null, '0', null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null);
</pre>
<pre class="brush: sql; title: ; notranslate">
-- ----------------------------
-- Records of sales_flat_order_status_history
-- ----------------------------
INSERT INTO `sales_flat_order_status_history` VALUES ('1', '1', '1', '0', null, 'pending', '2011-07-26 11:39:31', 'order');
</pre>
<p>Now that we have our real order it is logical to make the final step and bill it / charge it. Usually if you are using PayPal or Authorize.net or some other fancy payment gateway this process of charging is done automatically. Once charged order changes it’s state from &#8220;new&#8221; or &#8220;pending&#8221; to &#8220;processing&#8221;. Which leads us to the next model in this workflow called &#8220;Invoice&#8221;. It’s easy to conclude that &#8220;Invoice&#8221; is by definition an order that has been billed. This &#8220;billed&#8221; data is then saved in invoice related/named tables. It’s easy to find those in Magento database as table names start with the &#8220;sales_flat_invoice&#8221; prefix.</p>
<p>Once we click the &#8220;Invoice&#8221; button on the Magento order view page in the admin, these invoice tables get populated with relevant data. Before I dump the SQL queries that filled my invoice table, its important to note that I invoiced entire order, meaning all items at once. General concept of Magento allows you to create several invoices for one order, meaning you can invoice partial quantities of ordered items or individual items only per single invoice.</p>
<pre class="brush: sql; title: ; notranslate">
-- ----------------------------
-- Records of sales_flat_invoice
-- ----------------------------
INSERT INTO `sales_flat_invoice` VALUES ('1', '1', '6246.9900', '0.0000', '0.0000', '0.0000', '1.0000', '0.0000', '0.0000', '1.0000', '6246.9900', '50.0000', '6196.9900', '6196.9900', '1.0000', '50.0000', '10.0000', '1.0000', '6196.9900', '6196.9900', '0.0000', '1', null, '1', null, '0', '2', '2', null, 'USD', null, 'USD', 'USD', 'USD', '100000001', '2011-07-26 12:09:56', '2011-07-26 12:09:56', null, null, null, null, null, null, null, '0.0000', '0.0000', '0.0000', null, '50.0000', '50.0000', null);
</pre>
<pre class="brush: sql; title: ; notranslate">
-- ----------------------------
-- Records of sales_flat_invoice_grid
-- ----------------------------
INSERT INTO `sales_flat_invoice_grid` VALUES ('1', '1', '6246.9900', '6246.9900', '1', '2', 'USD', 'USD', 'USD', 'USD', '100000001', '100000001', '2011-07-26 12:09:56', '2011-07-26 11:39:27', 'Branko Ajzele');
</pre>
<pre class="brush: sql; title: ; notranslate">
-- ----------------------------
-- Records of sales_flat_invoice_item
-- ----------------------------
INSERT INTO `sales_flat_invoice_item` VALUES ('1', '1', '349.9900', '0.0000', '0.0000', '0.0000', '0.0000', '349.9900', null, '349.9900', '0.0000', null, '0.0000', '349.9900', '0.0000', '0.0000', '349.9900', '1.0000', '0.0000', '29.9900', '0.0000', '349.9900', '349.9900', '349.9900', '17', '1', null, null, 'a:0:{}', 'bb8100', 'BlackBerry 8100 Pearl', null, '0.0000', '0.0000');
INSERT INTO `sales_flat_invoice_item` VALUES ('2', '1', '750.0000', '0.0000', '0.0000', '0.0000', '0.0000', '4500.0000', null, '4500.0000', '0.0000', null, '0.0000', '750.0000', '0.0000', '0.0000', '750.0000', '6.0000', '0.0000', null, '0.0000', '750.0000', '4500.0000', '4500.0000', '166', '2', null, null, 'a:0:{}', 'HTC Touch Diamond', 'HTC Touch Diamond', null, '0.0000', '0.0000');
INSERT INTO `sales_flat_invoice_item` VALUES ('3', '1', '449.0000', '0.0000', '0.0000', '0.0000', '0.0000', '1347.0000', null, '1347.0000', '0.0000', null, '0.0000', '449.0000', '0.0000', '0.0000', '449.0000', '3.0000', '0.0000', '200.0000', '0.0000', '449.0000', '1347.0000', '1347.0000', '44', '3', null, null, 'a:0:{}', 'Rebel XT', 'Canon Digital Rebel XT 8MP Digital SLR Camera', null, '0.0000', '0.0000');
</pre>
<p>Basically this circles the simplest story around the order creation process. Meaning there are 3 concepts/terms to remember here: Quote, Order, Invoice.</p>
<p>There is also a Shipment, but we will leave this for another time <img src='http://inchoo.net/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </p>
<p>As a developer you should have a pretty good understanding of &#8220;Quote, Order, Invoice&#8221; relation in Magento, especcially if you are doing any custom coding around it. Coding such as &#8220;custom order statuses&#8221;, &#8220;change of order states based on some info retrieved from external web service&#8221;, etc.</p>
<p>So now that all this &#8220;theory&#8221; is behind us, lets try to programmatically create order order and invoice it. Below is the code that does just that for the simplest possible case of using just &#8220;simple&#8221; product types.</p>
<pre class="brush: php; title: ; notranslate">
&lt;?php

require_once 'app/Mage.php';

Mage::app('default');

$store = Mage::app()-&gt;getStore('default');

$customer = Mage::getModel('customer/customer');
$customer-&gt;setStore($store);
$customer-&gt;loadByEmail('email_address@gmail.com');

$quote = Mage::getModel('sales/quote');
$quote-&gt;setStore($store);
$quote-&gt;assignCustomer($customer);

$product1 = Mage::getModel('catalog/product')-&gt;load(166); /* HTC Touch Diamond */
$buyInfo1 = array('qty' =&gt; 1);

$product2 = Mage::getModel('catalog/product')-&gt;load(18); /* Sony Ericsson W810i */
$buyInfo2 = array('qty' =&gt; 3);

$quote-&gt;addProduct($product1, new Varien_Object($buyInfo1));
$quote-&gt;addProduct($product2, new Varien_Object($buyInfo2));

$billingAddress = $quote-&gt;getBillingAddress()-&gt;addData($customer-&gt;getPrimaryBillingAddress());
$shippingAddress = $quote-&gt;getShippingAddress()-&gt;addData($customer-&gt;getPrimaryShippingAddress());

$shippingAddress-&gt;setCollectShippingRates(true)-&gt;collectShippingRates()
                -&gt;setShippingMethod('flatrate_flatrate')
                -&gt;setPaymentMethod('checkmo');

$quote-&gt;getPayment()-&gt;importData(array('method' =&gt; 'checkmo'));

$quote-&gt;collectTotals()-&gt;save();

$service = Mage::getModel('sales/service_quote', $quote);
$service-&gt;submitAll();
$order = $service-&gt;getOrder();

$invoice = Mage::getModel('sales/service_order', $order)-&gt;prepareInvoice();
$invoice-&gt;setRequestedCaptureCase(Mage_Sales_Model_Order_Invoice::CAPTURE_ONLINE);
$invoice-&gt;register();

$transaction = Mage::getModel('core/resource_transaction')
                    -&gt;addObject($invoice)
                    -&gt;addObject($invoice-&gt;getOrder());

$transaction-&gt;save();
</pre>
<p>I realize there are lot of things left unexplained in the code above, such as &#8220;where does the $service come from&#8221;, &#8220;why are we using $transaction resource for invoice creation&#8221;, etc. For example, if in the above code I where to code a order creation process for &#8220;grouped&#8221; or &#8220;configurable&#8221; product type, possibly for product with custom options, my code would be far more complex. Not to mention custom/new product types created by you or some other third party extension. There is no unique formula to cover all, especially custom product types. All I can say, is that in order to get a true understanding of the process in the background you really need to trace the code thoroughly for specific case.</p>
<p>Hope this article was, to some extent, useful.</p>
<p>Cheers.</p>
]]></content:encoded>
			<wfw:commentRss>http://inchoo.net/ecommerce/magento/magento-quote-order-invoice-workflow/feed/</wfw:commentRss>
		<slash:comments>13</slash:comments>
		</item>
		<item>
		<title>HowTo &#8211; &#8220;Google +1&#8243; extension for Magento</title>
		<link>http://inchoo.net/ecommerce/magento/howto-google-1-extension-for-magento/</link>
		<comments>http://inchoo.net/ecommerce/magento/howto-google-1-extension-for-magento/#comments</comments>
		<pubDate>Mon, 25 Jul 2011 19:26:31 +0000</pubDate>
		<dc:creator>Branko Ajzele</dc:creator>
				<category><![CDATA[Extensions]]></category>
		<category><![CDATA[Magento]]></category>
		<category><![CDATA[Marketing]]></category>
		<category><![CDATA[Google]]></category>
		<category><![CDATA[plus]]></category>

		<guid isPermaLink="false">http://inchoo.net/?p=10501</guid>
		<description><![CDATA[Embedding Google&#8217;s +1 button is pretty easy and straightforward. All it takes is to include 2 code snippets in your web page. One goes in the header of the page, and the other one goes to wherever where you want &#8230;<p><a href="http://inchoo.net/ecommerce/magento/howto-google-1-extension-for-magento/">Read more</a><p>]]></description>
			<content:encoded><![CDATA[<p>Embedding Google&#8217;s +1 button is pretty easy and straightforward. All it takes is to include 2 code snippets in your web page. One goes in the header of the page, and the other one goes to wherever where you want the +1 button to render. To do so all you need is to follow the official Google guideline outlined <a href="http://www.google.com/intl/en/webmasters/+1/button/index.html">here</a>.</p>
<p>With that in mind, let&#8217;s build us Magento extension, preferably the <a href="http://inchoo.net/ecommerce/magento/developing-extensions-that-react-unobtrusively-with-magento/">non-obtrusive</a> one.<span id="more-10501"></span></p>
<p>We will call our extension &#8220;GPlusOne&#8221; and place it under the &#8220;Inchoo&#8221; namespace. Let&#8217;s start by creating the /app/etc/modules/Inchoo_GPlusOne.xml file with the following content:</p>
<pre class="brush: php; title: ; notranslate">
&lt;?xml version=&quot;1.0&quot;?&gt;

&lt;config&gt;
    &lt;modules&gt;
        &lt;Inchoo_GPlusOne&gt;
            &lt;active&gt;true&lt;/active&gt;
            &lt;codePool&gt;community&lt;/codePool&gt;
        &lt;/Inchoo_GPlusOne&gt;
    &lt;/modules&gt;
&lt;/config&gt;
</pre>
<p>Now we need to create second file, app/code/community/Inchoo/GPlusOne/etc/<strong>config.xml</strong> with the following content:</p>
<pre class="brush: xml; title: ; notranslate">
&lt;?xml version=&quot;1.0&quot;?&gt;

&lt;config&gt;
    &lt;modules&gt;
        &lt;Inchoo_GPlusOne&gt;
            &lt;version&gt;1.0.0.0&lt;/version&gt;
        &lt;/Inchoo_GPlusOne&gt;
    &lt;/modules&gt;
    &lt;global&gt;
        &lt;blocks&gt;
            &lt;inchoo_gplusone&gt;
                &lt;class&gt;Inchoo_GPlusOne_Block&lt;/class&gt;
            &lt;/inchoo_gplusone&gt;
        &lt;/blocks&gt;
    &lt;/global&gt;

    &lt;frontend&gt;
        &lt;layout&gt;
            &lt;updates&gt;
                &lt;inchoo_gplusone&gt;
                    &lt;file&gt;inchoo/gplusone.xml&lt;/file&gt;
                &lt;/inchoo_gplusone&gt;
            &lt;/updates&gt;
        &lt;/layout&gt;
    &lt;/frontend&gt;
&lt;/config&gt;
</pre>
<p>As you can see we are declaring a new layout file here, called gplusone.xml which we will place under the app/design/frontend/default/default/layout/inchoo/<strong>gplusone.xml</strong>, with the following content:</p>
<pre class="brush: xml; title: ; notranslate">
&lt;?xml version=&quot;1.0&quot;?&gt;

&lt;layout version=&quot;1.0.0&quot;&gt;
    &lt;catalog_product_view&gt;
        &lt;reference name=&quot;head&quot;&gt;
            &lt;block type=&quot;core/template&quot; name=&quot;inchoo_gplusone_head&quot; template=&quot;inchoo/gplusone_head.phtml&quot; /&gt;
        &lt;/reference&gt;
        &lt;reference name=&quot;alert.urls&quot;&gt;
            &lt;block type=&quot;inchoo_gplusone/PlusButton&quot; name=&quot;inchoo_gplusone&quot; /&gt;
        &lt;/reference&gt;
    &lt;/catalog_product_view&gt;
&lt;/layout&gt;
</pre>
<p>Carefully observing the gplusone.xml layout update shows that my layout is to &#8220;kick in&#8221; only on the product view page. Since embedding the Google +1 Button reguires two scripts to be loaded on page, I&#8217;m splitting them into two view files, app/design/frontend/default/default/template/inchoo/<strong>gplusone_head.phtml</strong> and app/design/frontend/default/default/template/inchoo/<strong>gplusone_button.phtml</strong>.</p>
<p>File app/design/frontend/default/default/template/inchoo/gplusone_head.phtml is loaded into the head area of HTML via ‘reference name=&#8221;head&#8221;&#8216; layout update, having the following content:</p>
<pre class="brush: php; title: ; notranslate">
&lt;script type=&quot;text/javascript&quot; src=&quot;https://apis.google.com/js/plusone.js&quot;&gt;&lt;/script&gt;
</pre>
<p>While file app/design/frontend/default/default/template/inchoo/gplusone_button.phtml is loaded into the content area of the page, more specifically &#8220;alert.urls&#8221; block via ‘reference name=&#8221;alert.urls&#8221;. For more detailed understanding one should study the app/design/frontend/base/default/layout/catalog.xml file and its ‘<catalog_product_view translate="label">&#8216; section. Content of gplusone_button.phtml  is as follows:</p>
<pre class="brush: php; title: ; notranslate">
&lt;g:plusone &lt;?php if($size = $this-&gt;getSize()) { echo ' size=&quot;'.$size.'&quot;'; } ?&gt;&gt;&lt;/g:plusone&gt;
</pre>
<p>Which bring&#8217;s us to the final file needed to get this running, app/code/community/Inchoo/GPlusOne/Block/<strong>PlusButton.php</strong> with the following content:</p>
<pre class="brush: php; title: ; notranslate">
&lt;?php

class Inchoo_GPlusOne_Block_PlusButton extends Mage_Core_Block_Template
{
    /**
     * Constructor. Set template.
     */
    protected function _construct()
    {
        parent::_construct();
        $this-&gt;setTemplate('inchoo/gplusone_button.phtml');
    }

    public function getSize()
    {
        //Here we can implement the code to read the config values for sizes
        return '';
    }
}
</pre>
<p>If you look at the gplusone_button.phtml file you will see it has a call towards getSize() method from Inchoo_GPlusOne_Block_PlusButton class. At the moment, this method does nothing. It&#8217;s merely here for possible extension of this simple module. Given that Google +1 button supports various sizes (&#8220;small&#8221;, &#8220;medium&#8221;, &#8220;tall&#8221;, or no size definition for standard size) you can easily add some extra logic to this extension to have it read some config values that could be set from Magento admin.</p>
<p>The point of this article was not to give you a full blown extension ready to be used in instance, rather to show you the amount of effort needed to implement something as simple as Google +1 button in Magento the right way (not to say that this is the best possible way).</p>
<p>And here is the screenshot of the final result.</p>
<p><a href="http://inchoo.net/wp-content/uploads/2011/07/gplus.png"><img src="http://inchoo.net/wp-content/uploads/2011/07/gplus-300x150.png" alt="" title="gplus" width="300" height="150" class="alignnone size-thumbnail wp-image-10505" /></a></p>
<p>Hope someone finds it useful. </p>
<p>Cheers.</catalog_product_view></p>
]]></content:encoded>
			<wfw:commentRss>http://inchoo.net/ecommerce/magento/howto-google-1-extension-for-magento/feed/</wfw:commentRss>
		<slash:comments>15</slash:comments>
		</item>
		<item>
		<title>PHP to (Java) Android &#8211; Array vs HashMap</title>
		<link>http://inchoo.net/mobile-development/android-development/php-to-java-android-array-vs-hashmap/</link>
		<comments>http://inchoo.net/mobile-development/android-development/php-to-java-android-array-vs-hashmap/#comments</comments>
		<pubDate>Sat, 11 Jun 2011 01:21:51 +0000</pubDate>
		<dc:creator>Branko Ajzele</dc:creator>
				<category><![CDATA[Android development]]></category>
		<category><![CDATA[Mobile development]]></category>
		<category><![CDATA[Android]]></category>
		<category><![CDATA[arrays]]></category>

		<guid isPermaLink="false">http://inchoo.net/?p=10013</guid>
		<description><![CDATA[One of the coolest and most powerful things in PHP are arrays. Why? Simply put, you can place almost anything in them. Recently I was playing with the JSON API calling various API methods and passing parameters. For a moment &#8230;<p><a href="http://inchoo.net/mobile-development/android-development/php-to-java-android-array-vs-hashmap/">Read more</a><p>]]></description>
			<content:encoded><![CDATA[<p>One of the coolest and most powerful things in PHP are arrays. Why? Simply put, you can place almost anything in them. Recently I was playing with the JSON API calling various API methods and passing parameters. For a moment I got stuck on a pretty simple step, arrays.<span id="more-10013"></span></p>
<p>For example, imagine that in PHP you need to construct a JSON string like shown below:</p>
<p>HTTPPayloadLine: {&quot;id&quot;:148,&quot;method&quot;:&quot;customerRegistration&quot;,&quot;params&quot;:[&quot;v5unacb9rntvc10fh7k5l1ops3&quot;,{&quot;passwd&quot;:&quot;MyPassHere&quot;,&quot;email&quot;:&quot;devtest1@gmail.com&quot;,&quot;street&quot;:&quot;Sample Street 1&quot;,&quot;locale&quot;:&quot;en_US&quot;,&quot;lastname&quot;:&quot;LTest1&quot;,&quot;gender&quot;:&quot;0&quot;,&quot;firstname&quot;:&quot;FTest1&quot;}]}</p>
<p>More specifically the &#8220;params&#8221; part. In PHP I would most likely do something like:</p>
<pre class="brush: php; title: ; notranslate">
$params = array(
    &quot;v5unacb9rntvc10fh7k5l1ops3&quot;,
    array(
        &quot;passwd&quot; =&gt; &quot;MyPassHere&quot;,
        &quot;email&quot; =&gt; &quot;devtest1@gmail.com&quot;,
        &quot;street&quot; =&gt; &quot;Sample Street 1&quot;,
        &quot;locale&quot; =&gt; &quot;en_US&quot;,
        &quot;lastname&quot; =&gt; &quot;LTest1&quot;,
        &quot;gender&quot; =&gt; &quot;0&quot;,
        &quot;firstname&quot; =&gt; &quot;FTest1&quot;
    )
);
</pre>
<p>Then simply use json_encode($params) and I&#8217;m done.</p>
<p>Now, here is how I did it in my Java/Android example:</p>
<pre class="brush: java; title: ; notranslate">
HashMap&lt;string , Object&gt; customerMap = new HashMap&lt;/string&gt;&lt;string , Object&gt;();

customerMap.put(&quot;email&quot;, &quot;devtest1@gmail.com&quot;);
customerMap.put(&quot;passwd&quot;, &quot;MyPassHere&quot;);
customerMap.put(&quot;firstname&quot;, &quot;FTest1&quot;);
customerMap.put(&quot;lastname&quot;, &quot;LTest1&quot;);
customerMap.put(&quot;street&quot;, &quot;Sample Street 1&quot;);
customerMap.put(&quot;postcode&quot;, &quot;789991&quot;);
customerMap.put(&quot;city&quot;, &quot;City1&quot;);
customerMap.put(&quot;locale&quot;, &quot;en_US&quot;);
customerMap.put(&quot;gender&quot;, &quot;0&quot;);

JSONObject customerMapParams = new JSONObject();
Iterator&lt;entry &lt;String, Object&gt;&gt; itr = customerMap.entrySet().iterator();

while(itr.hasNext()) {
    Entry&lt;string , Object&gt; entry = itr.next();
    customerMapParams.put(entry.getKey(), entry.getValue());
}
</pre>
<p>Please note, this is just one way of doing things. Hope it helps someone.</p>
<p>Cheers.</p>
]]></content:encoded>
			<wfw:commentRss>http://inchoo.net/mobile-development/android-development/php-to-java-android-array-vs-hashmap/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Debugging Web Service API communication in Android</title>
		<link>http://inchoo.net/mobile-development/android-development/debugging-web-service-api-communication-in-android/</link>
		<comments>http://inchoo.net/mobile-development/android-development/debugging-web-service-api-communication-in-android/#comments</comments>
		<pubDate>Fri, 10 Jun 2011 08:08:31 +0000</pubDate>
		<dc:creator>Branko Ajzele</dc:creator>
				<category><![CDATA[Android development]]></category>
		<category><![CDATA[Mobile development]]></category>
		<category><![CDATA[Android]]></category>
		<category><![CDATA[api]]></category>
		<category><![CDATA[debugging]]></category>

		<guid isPermaLink="false">http://inchoo.net/?p=10021</guid>
		<description><![CDATA[Proper and fast debugging is a key to any application development. Unlike my great experience with PHP where I usually debug whatever I need in a breeze, I found my self against the wall when working with Android and web &#8230;<p><a href="http://inchoo.net/mobile-development/android-development/debugging-web-service-api-communication-in-android/">Read more</a><p>]]></description>
			<content:encoded><![CDATA[<p>Proper and fast debugging is a key to any application development. Unlike my great experience with PHP where I usually debug whatever I need in a breeze, I found my self against the wall when working with Android and web service API calls. Problem is simple, you cannot that easily dump entire $_SESSION array like in PHP and see it&#8217;s content. Surely there are other ways as Java language has full set of it&#8217;s own debugging ways.<span id="more-10021"></span> </p>
<p>Here I&#8217;ll give you some practical guide on how you can really, down to the great detail test your API calls in Android. Get ready because we are going down to the network level. First of all, I&#8217;m using Windows 7 machine here, but fear not, all that is demonstrated can be used on the Linux machine as well, just with different tools.</p>
<p>So, the general idea is to set your PC/Laptop WiFi connection to serve as a HotSpot on which you will connect trough your Android phone. This way all network traffic from your phone goes trough your computer. And then, all you need on your computer is software like Windows Network Monitor to monitor all your HTTP traffic and you can see all of the requests and responses in great details. For me, this approach was a savior as it enabled me more fast debugging of web service API calls trough my Android app.</p>
<p>So here it is in a few simple steps:</p>
<ol>
<li>Download and install Microsoft Network Monitor (or WireShark, which is also great tool).</li>
<li>Download and install <a href="www.connectify.me">Connectify</a>.</li>
<li>Using Connectify setup your PC to act as a HotSpot.</li>
<li>Connect your phone to the HotSpot on your PC.</li>
<li>Start your Microsoft Network Monitor (or WireShark, or some XYZ tool).</li>
<li>Start your application on Android phone and monitor the HTTP traffic.</li>
</ol>
<p>Additionally, in my case where I used MS Network Monitor I setup the filters like:</p>
<p><i><br />
HTTP AND ((Source == &#8220;192.168.2.101&#8243; AND Destination == &#8220;some-api.domain&#8221;) OR (Destination == &#8220;192.168.2.101&#8243; AND Source == &#8220;some-api.domain&#8221;))<br />
</i></p>
<p>This enabled me to specifically filter out the traffic between my Android app and the Webservice API I was calling.</p>
<p>Having an insight into the raw HTTP request response is far more informative then debugging individual Java objects like HttpPost, HttpResponse, HttpEntity (at least it was for me).</p>
<p>As an example, here is a sample of request:</p>
<p><i><br />
  Frame: Number = 283, Captured Frame Length = 199, MediaType = WiFi<br />
+ WiFi: [Unencrypted Data] .T&#8230;.., (I) RSSI = -40 dBm, Rate = 54.0 Mbps<br />
+ LLC: Unnumbered(U) Frame, Command Frame, SSAP = SNAP(Sub-Network Access Protocol), DSAP = SNAP(Sub-Network Access Protocol)<br />
+ Snap: EtherType = Internet IP (IPv4), OrgCode = XEROX CORPORATION<br />
+ Ipv4: Src = 192.168.2.101, Dest = 46.137.*.*, Next Protocol = TCP, Packet ID = 12297, Total IP Length = 135<br />
+ Tcp: Flags=&#8230;AP&#8230;, SrcPort=32886, DstPort=HTTP(80), PayloadLen=83, Seq=499775385 &#8211; 499775468, Ack=2775527642, Win=32044<br />
- Http: HTTP Payload, URL:<br />
  &#8211; payload: HttpContentType =<br />
     HTTPPayloadLine: {&#8220;id&#8221;:0,&#8221;method&#8221;:&#8221;getActiveCities&#8221;,&#8221;params&#8221;:["tlrg9507bl9tm6lu899vg90115","de_DE"]}<br />
</i> </p>
<p>And here is a sample of response captured from network monitor:</p>
<p><i><br />
  Frame: Number = 285, Captured Frame Length = 805, MediaType = WiFi<br />
+ WiFi: [Unencrypted Data] F&#8230;&#8230;, (I)<br />
+ LLC: Unnumbered(U) Frame, Command Frame, SSAP = SNAP(Sub-Network Access Protocol), DSAP = SNAP(Sub-Network Access Protocol)<br />
+ Snap: EtherType = Internet IP (IPv4), OrgCode = XEROX CORPORATION<br />
+ Ipv4: Src = 46.137.*.*, Dest = 192.168.2.101, Next Protocol = TCP, Packet ID = 14634, Total IP Length = 741<br />
+ Tcp: Flags=&#8230;AP&#8230;, SrcPort=HTTP(80), DstPort=32886, PayloadLen=689, Seq=2775527642 &#8211; 2775528331, Ack=499775468, Win=31<br />
- Http: Response, HTTP/1.1, Status: Ok, URL:<br />
    ProtocolVersion: HTTP/1.1<br />
    StatusCode: 200, Ok<br />
    Reason: OK<br />
    Cache-Control:  no-store, no-cache, must-revalidate, post-check=0, pre-check=0<br />
  + ContentType:  application/json<br />
    Date:  Fri, 10 Jun 2011 15:14:37 GMT<br />
    Expires:  Thu, 19 Nov 1981 08:52:00 GMT<br />
    Pragma:  no-cache<br />
    Server:  Apache<br />
    Set-Cookie:  PHPSESSID=tlrg9507bl9tm6lu899vg90115; path=/<br />
    ContentLength:  345<br />
    Connection:  keep-alive<br />
    HeaderEnd: CRLF<br />
  &#8211; payload: HttpContentType =  application/json<br />
     HTTPPayloadLine: {&#8220;result&#8221;:[{&#8220;cityId&#8221;:&#8221;239&#8243;,&#8221;name&#8221;:&#8221;Sofia&#8221;},{&#8220;cityId&#8221;:&#8221;266&#8243;,&#8221;name&#8221;:&#8221;Blagoevgrad&#8221;},{&#8220;cityId&#8221;:&#8221;296&#8243;,&#8221;name&#8221;:&#8221;Burgas&#8221;},{&#8220;cityId&#8221;:&#8221;294&#8243;,&#8221;name&#8221;:&#8221;Varna&#8221;},{&#8220;cityId&#8221;:&#8221;299&#8243;,&#8221;name&#8221;:&#8221;Veliko Tarnovo&#8221;},{&#8220;cityId&#8221;:&#8221;295&#8243;,&#8221;name&#8221;:&#8221;Plovdiv&#8221;},{&#8220;cityId&#8221;:&#8221;298&#8243;,&#8221;name<br />
</i></p>
<p>As you can see, in my case I was playing with JSON web service, calling it from Android app.</p>
<p>Hope this article will give you some ideas on debugging your Android apps.</p>
]]></content:encoded>
			<wfw:commentRss>http://inchoo.net/mobile-development/android-development/debugging-web-service-api-communication-in-android/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>PHP to Android &#8211; Simulate the $_SESSION behavior</title>
		<link>http://inchoo.net/mobile-development/android-development/php-to-android-simulate-the-_session-behavior/</link>
		<comments>http://inchoo.net/mobile-development/android-development/php-to-android-simulate-the-_session-behavior/#comments</comments>
		<pubDate>Tue, 07 Jun 2011 02:37:22 +0000</pubDate>
		<dc:creator>Branko Ajzele</dc:creator>
				<category><![CDATA[Android development]]></category>
		<category><![CDATA[Mobile development]]></category>
		<category><![CDATA[Android]]></category>

		<guid isPermaLink="false">http://inchoo.net/?p=10027</guid>
		<description><![CDATA[Poking around Google Android is really fun. I really like the platform, although I&#8217;m not that big of a Java fan. Anyhow, as a web developer my mind set is sometimes wired to certain architectural views of web development vs. &#8230;<p><a href="http://inchoo.net/mobile-development/android-development/php-to-android-simulate-the-_session-behavior/">Read more</a><p>]]></description>
			<content:encoded><![CDATA[<p>Poking around Google Android is really fun. I really like the platform, although I&#8217;m not that big of a Java fan. Anyhow, as a web developer my mind set is sometimes wired to certain architectural views of web development vs. android development. One of those things is concept of sessions. In Android development your app is not stateless like you would have in standard web development so there is no real need for &#8220;sessions&#8221;. However<span id="more-10027"></span>, sharing simple and small data amount across your Activities can turn out to be a real challenge. There are several ways to share data across activities in Android, two of them are Intents and Shared Preferences.</p>
<p>Personally I find both to be a little overhead for my case. </p>
<p>Here is a little class I wrote that might help you out in certain cases.</p>
<pre class="brush: php; title: ; notranslate">
package net.inchoo.demo.magentico;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.HashMap;

import android.os.Environment;
import android.util.Log;

public class Registry {

	private static String _MY_APP_TAG = &quot;APP_BY_AJZELE&quot;;

	private static File _storageDirectory = Environment.getExternalStorageDirectory();

	private static HashMap&lt;String, Object&gt; _registry = new HashMap&lt;String, Object&gt;();

	public static void set(String key, Object value) {
		_registry.put(key, value);
		persistRegistry();
	}

	public static Object get(String key) {
		initRegistry();
		return _registry.get(key);
	}

	@SuppressWarnings(&quot;unchecked&quot;)
	private static void initRegistry() {
        try {
			FileInputStream fis = new FileInputStream(_storageDirectory.getAbsolutePath() + &quot;/&quot; + _MY_APP_TAG + &quot;.registry&quot;);
			ObjectInputStream ois = new ObjectInputStream(fis);
			_registry = (HashMap&lt;String, Object&gt;) ois.readObject();
			ois.close();
		} catch (Exception e) {
			Log.i(_MY_APP_TAG, e.getMessage());
		}

	}

	private static void persistRegistry() {
        try {
            FileOutputStream fos = new FileOutputStream(_storageDirectory.getAbsolutePath() + &quot;/&quot; + _MY_APP_TAG + &quot;.registry&quot;);
            ObjectOutputStream oos = new ObjectOutputStream(fos);

            oos.writeObject(_registry);
			oos.close();
		} catch (Exception e) {
			Log.i(_MY_APP_TAG, e.getMessage());
		}
	}

	public static void clear() {
		_registry.clear();
		persistRegistry();
	}
}
</pre>
<p>I call it the <strong>Registry</strong>. It basically has two simple methods, get and set. Class behaves like registry/session saving it&#8217;s state into the file on the memory storage of your phone.</p>
]]></content:encoded>
			<wfw:commentRss>http://inchoo.net/mobile-development/android-development/php-to-android-simulate-the-_session-behavior/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>MagentoGO screenshot tour</title>
		<link>http://inchoo.net/ecommerce/magento/magentogo-screenshot-tour/</link>
		<comments>http://inchoo.net/ecommerce/magento/magentogo-screenshot-tour/#comments</comments>
		<pubDate>Wed, 02 Mar 2011 07:51:09 +0000</pubDate>
		<dc:creator>Branko Ajzele</dc:creator>
				<category><![CDATA[Magento]]></category>
		<category><![CDATA[Magento GO]]></category>
		<category><![CDATA[MagentoGO]]></category>

		<guid isPermaLink="false">http://inchoo.net/?p=8652</guid>
		<description><![CDATA[MagentoGO has been released few days ago. To be more precise on 28th of February 2011. For those interested here are few screenshots from its Admin panel. All in all, I&#8217;m pretty impressed with what they have done with it. &#8230;<p><a href="http://inchoo.net/ecommerce/magento/magentogo-screenshot-tour/">Read more</a><p>]]></description>
			<content:encoded><![CDATA[<p><a href="http://zfer.us/0J1ex">MagentoGO</a> has been released few days ago. To be more precise on 28th of February 2011. For those interested here are few screenshots from its Admin panel.<span id="more-8652"></span></p>
<p><object width="400" height="300"><param name="flashvars" value="offsite=true&#038;lang=en-us&#038;page_show_url=%2Fphotos%2Fajzele%2Fsets%2F72157626054113803%2Fshow%2F&#038;page_show_back_url=%2Fphotos%2Fajzele%2Fsets%2F72157626054113803%2F&#038;set_id=72157626054113803&#038;jump_to="></param><param name="movie" value="http://www.flickr.com/apps/slideshow/show.swf?v=71649"></param><param name="allowFullScreen" value="true"></param><embed type="application/x-shockwave-flash" src="http://www.flickr.com/apps/slideshow/show.swf?v=71649" allowFullScreen="true" flashvars="offsite=true&#038;lang=en-us&#038;page_show_url=%2Fphotos%2Fajzele%2Fsets%2F72157626054113803%2Fshow%2F&#038;page_show_back_url=%2Fphotos%2Fajzele%2Fsets%2F72157626054113803%2F&#038;set_id=72157626054113803&#038;jump_to=" width="400" height="300"></embed></object></p>
<p>All in all, I&#8217;m pretty impressed with what they have done with it. Will be even more impressed once the development stuff gets launched so we can dive into the extension development for MagentoGO platform. </p>
]]></content:encoded>
			<wfw:commentRss>http://inchoo.net/ecommerce/magento/magentogo-screenshot-tour/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>ZendFramework example on using Google URL Shortener service</title>
		<link>http://inchoo.net/tools-frameworks/zendframework-example-on-using-google-url-shortener-service/</link>
		<comments>http://inchoo.net/tools-frameworks/zendframework-example-on-using-google-url-shortener-service/#comments</comments>
		<pubDate>Sun, 27 Feb 2011 00:20:52 +0000</pubDate>
		<dc:creator>Branko Ajzele</dc:creator>
				<category><![CDATA[Tools & Frameworks]]></category>
		<category><![CDATA[Zend]]></category>
		<category><![CDATA[custom]]></category>
		<category><![CDATA[framework]]></category>
		<category><![CDATA[Google]]></category>

		<guid isPermaLink="false">http://inchoo.net/?p=10033</guid>
		<description><![CDATA[Seems like URL shorteners have gain extreme popularity with web services like Twitter. Not to mention the fact that long URLs are somewhat hard to pass along. They are also harder to verbalize in a conversation, and above all in &#8230;<p><a href="http://inchoo.net/tools-frameworks/zendframework-example-on-using-google-url-shortener-service/">Read more</a><p>]]></description>
			<content:encoded><![CDATA[<p>Seems like URL shorteners have gain extreme popularity with web services like Twitter. Not to mention the fact that long URLs are somewhat hard to pass along. They are also harder to verbalize in a conversation, and above all in some cases they are almost impossible to remember.</p>
<p>There are quite a large number of URL shorteners out there these days, mostly free. One of the freshest is Google’s URL shortener. This article is not about outlining the features that make this shortener better then the other ones (as I am sure some offer more features at the moment). This article is merely for the purpose of showing you how easy it is to use the Google URL Shortener service via your ZendFramework libraries.<span id="more-10033"></span></p>
<p>Basically all we need is the instance of Zend_Http_Client class and preferably the API key for Google URL Shortener. For API key, just follow the instructions outlined here.</p>
<p>Here are the few actions you can with the Google URL Shortener at the moment:</p>
<ul>
<li>Shorten a long URL</li>
<li>Expand a short URL</li>
<li>Look up a short URL&#8217;s analytics</li>
<li>Look up a user&#8217;s history</li>
</ul>
<p><strong><em>Example #1</em> &#8211; Shorten a long URL</strong></p>
<pre class="brush: php; title: ; notranslate">
$client = new Zend_Http_Client();

$client-&gt;setUri('https://www.googleapis.com/urlshortener/v1/url');
$client-&gt;setHeaders(Zend_Http_Client::CONTENT_TYPE, 'application/json');
$client-&gt;setMethod(Zend_Http_Client::POST);

$payload = json_encode(array(
    'longUrl' =&gt; 'http://activecodeline.net/',
    'key' =&gt; '___YOUR_API_KEY_HERE___'
));

$client-&gt;setRawData($payload);

$response = $client-&gt;request();
</pre>
<p>Below you can see the example of response for the above request.</p>
<pre class="brush: php; title: ; notranslate">
Zend_Debug::dump($response, '$response');

/**
$response object(Zend_Http_Response)#225 (5) {
  [&quot;version&quot;:protected] =&gt; string(3) &quot;1.1&quot;
  [&quot;code&quot;:protected] =&gt; int(200)
  [&quot;message&quot;:protected] =&gt; string(2) &quot;OK&quot;
  [&quot;headers&quot;:protected] =&gt; array(11) {
    [&quot;Cache-control&quot;] =&gt; string(46) &quot;no-cache, no-store, max-age=0, must-revalidate&quot;
    [&quot;Pragma&quot;] =&gt; string(8) &quot;no-cache&quot;
    [&quot;Expires&quot;] =&gt; string(29) &quot;Fri, 01 Jan 1990 00:00:00 GMT&quot;
    [&quot;Date&quot;] =&gt; string(29) &quot;Sat, 26 Feb 2011 08:06:45 GMT&quot;
    [&quot;Content-type&quot;] =&gt; string(31) &quot;application/json; charset=UTF-8&quot;
    [&quot;X-content-type-options&quot;] =&gt; string(7) &quot;nosniff&quot;
    [&quot;X-frame-options&quot;] =&gt; string(10) &quot;SAMEORIGIN&quot;
    [&quot;X-xss-protection&quot;] =&gt; string(13) &quot;1; mode=block&quot;
    [&quot;Server&quot;] =&gt; string(3) &quot;GSE&quot;
    [&quot;Connection&quot;] =&gt; string(5) &quot;close&quot;
  }
  [&quot;body&quot;:protected] =&gt; string(104) &quot;{
 &quot;kind&quot;: &quot;urlshortener#url&quot;,
 &quot;id&quot;: &quot;http://goo.gl/7rDDa&quot;,
 &quot;longUrl&quot;: &quot;http://activecodeline.net/&quot;
}
&quot;
}

*/
</pre>
<p><strong><em>Example #2</em> &#8211; Expand a short URL</strong></p>
<pre class="brush: php; title: ; notranslate">
$client = new Zend_Http_Client();

$client-&gt;setUri('https://www.googleapis.com/urlshortener/v1/url');
$client-&gt;setMethod(Zend_Http_Client::GET);
$client-&gt;setParameterGet('shortUrl', 'http://goo.gl/7rDDa');
$client-&gt;setParameterGet('key', '___YOUR_API_KEY_HERE___');

$client-&gt;setRawData($payload);

$response = $client-&gt;request();
</pre>
<p>Below you can see the example of response for the above request.</p>
<pre class="brush: php; title: ; notranslate">
Zend_Debug::dump($response, '$response');

/**

$response object(Zend_Http_Response)#225 (5) {
  [&quot;version&quot;:protected] =&gt; string(3) &quot;1.1&quot;
  [&quot;code&quot;:protected] =&gt; int(200)
  [&quot;message&quot;:protected] =&gt; string(2) &quot;OK&quot;
  [&quot;headers&quot;:protected] =&gt; array(11) {
    [&quot;Expires&quot;] =&gt; string(29) &quot;Sat, 26 Feb 2011 08:37:13 GMT&quot;
    [&quot;Date&quot;] =&gt; string(29) &quot;Sat, 26 Feb 2011 08:32:13 GMT&quot;
    [&quot;Content-type&quot;] =&gt; string(31) &quot;application/json; charset=UTF-8&quot;
    [&quot;X-content-type-options&quot;] =&gt; string(7) &quot;nosniff&quot;
    [&quot;X-frame-options&quot;] =&gt; string(10) &quot;SAMEORIGIN&quot;
    [&quot;X-xss-protection&quot;] =&gt; string(13) &quot;1; mode=block&quot;
    [&quot;Server&quot;] =&gt; string(3) &quot;GSE&quot;
    [&quot;Cache-control&quot;] =&gt; string(50) &quot;public, max-age=300, must-revalidate, no-transform&quot;
    [&quot;Age&quot;] =&gt; string(2) &quot;48&quot;
    [&quot;Connection&quot;] =&gt; string(5) &quot;close&quot;
  }
  [&quot;body&quot;:protected] =&gt; string(121) &quot;{
 &quot;kind&quot;: &quot;urlshortener#url&quot;,
 &quot;id&quot;: &quot;http://goo.gl/7rDDa&quot;,
 &quot;longUrl&quot;: &quot;http://activecodeline.net/&quot;,
 &quot;status&quot;: &quot;OK&quot;
}
&quot;
}

*/
</pre>
<p><strong><em>Example #3</em> &#8211; Look up a short URL&#8217;s analytics</strong></p>
<pre class="brush: php; title: ; notranslate">
$client = new Zend_Http_Client();

$client-&gt;setUri('https://www.googleapis.com/urlshortener/v1/url');
$client-&gt;setMethod(Zend_Http_Client::GET);
$client-&gt;setParameterGet('shortUrl', 'http://goo.gl/7rDDa');
$client-&gt;setParameterGet('projection', 'FULL');
$client-&gt;setParameterGet('key', '___YOUR_API_KEY_HERE___');

$client-&gt;setRawData($payload);

$response = $client-&gt;request();
</pre>
<p>Below you can see the example of response for the above request.</p>
<pre class="brush: php; title: ; notranslate">
Zend_Debug::dump($response, '$response');

/**

$response object(Zend_Http_Response)#225 (5) {
  [&quot;version&quot;:protected] =&gt; string(3) &quot;1.1&quot;
  [&quot;code&quot;:protected] =&gt; int(200)
  [&quot;message&quot;:protected] =&gt; string(2) &quot;OK&quot;
  [&quot;headers&quot;:protected] =&gt; array(10) {
    [&quot;Expires&quot;] =&gt; string(29) &quot;Sat, 26 Feb 2011 08:36:40 GMT&quot;
    [&quot;Date&quot;] =&gt; string(29) &quot;Sat, 26 Feb 2011 08:36:40 GMT&quot;
    [&quot;Cache-control&quot;] =&gt; string(48) &quot;public, max-age=0, must-revalidate, no-transform&quot;
    [&quot;Content-type&quot;] =&gt; string(31) &quot;application/json; charset=UTF-8&quot;
    [&quot;X-content-type-options&quot;] =&gt; string(7) &quot;nosniff&quot;
    [&quot;X-frame-options&quot;] =&gt; string(10) &quot;SAMEORIGIN&quot;
    [&quot;X-xss-protection&quot;] =&gt; string(13) &quot;1; mode=block&quot;
    [&quot;Server&quot;] =&gt; string(3) &quot;GSE&quot;
    [&quot;Connection&quot;] =&gt; string(5) &quot;close&quot;
  }
  [&quot;body&quot;:protected] =&gt; string(2042) &quot;{
 &quot;kind&quot;: &quot;urlshortener#url&quot;,
 &quot;id&quot;: &quot;http://goo.gl/7rDDa&quot;,
 &quot;longUrl&quot;: &quot;http://activecodeline.net/&quot;,
 &quot;status&quot;: &quot;OK&quot;,
 &quot;created&quot;: &quot;2011-02-26T08:06:45.845+00:00&quot;,
 &quot;analytics&quot;: {
  &quot;allTime&quot;: {
   &quot;shortUrlClicks&quot;: &quot;1&quot;,
   &quot;longUrlClicks&quot;: &quot;1&quot;,
   &quot;referrers&quot;: [
    {
     &quot;count&quot;: &quot;1&quot;,
     &quot;id&quot;: &quot;Unknown/empty&quot;
    }
   ],
   &quot;countries&quot;: [
    {
     &quot;count&quot;: &quot;1&quot;,
     &quot;id&quot;: &quot;HR&quot;
    }
   ],
   &quot;browsers&quot;: [
    {
     &quot;count&quot;: &quot;1&quot;,
     &quot;id&quot;: &quot;Chrome&quot;
    }
   ],
   &quot;platforms&quot;: [
    {
     &quot;count&quot;: &quot;1&quot;,
     &quot;id&quot;: &quot;Windows&quot;
    }
   ]
  },
  &quot;month&quot;: {
   &quot;shortUrlClicks&quot;: &quot;1&quot;,
   &quot;longUrlClicks&quot;: &quot;1&quot;,
   &quot;referrers&quot;: [
    {
     &quot;count&quot;: &quot;1&quot;,
     &quot;id&quot;: &quot;Unknown/empty&quot;
    }
   ],
   &quot;countries&quot;: [
    {
     &quot;count&quot;: &quot;1&quot;,
     &quot;id&quot;: &quot;HR&quot;
    }
   ],
   &quot;browsers&quot;: [
    {
     &quot;count&quot;: &quot;1&quot;,
     &quot;id&quot;: &quot;Chrome&quot;
    }
   ],
   &quot;platforms&quot;: [
    {
     &quot;count&quot;: &quot;1&quot;,
     &quot;id&quot;: &quot;Windows&quot;
    }
   ]
  },
  &quot;week&quot;: {
   &quot;shortUrlClicks&quot;: &quot;1&quot;,
   &quot;longUrlClicks&quot;: &quot;1&quot;,
   &quot;referrers&quot;: [
    {
     &quot;count&quot;: &quot;1&quot;,
     &quot;id&quot;: &quot;Unknown/empty&quot;
    }
   ],
   &quot;countries&quot;: [
    {
     &quot;count&quot;: &quot;1&quot;,
     &quot;id&quot;: &quot;HR&quot;
    }
   ],
   &quot;browsers&quot;: [
    {
     &quot;count&quot;: &quot;1&quot;,
     &quot;id&quot;: &quot;Chrome&quot;
    }
   ],
   &quot;platforms&quot;: [
    {
     &quot;count&quot;: &quot;1&quot;,
     &quot;id&quot;: &quot;Windows&quot;
    }
   ]
  },
  &quot;day&quot;: {
   &quot;shortUrlClicks&quot;: &quot;1&quot;,
   &quot;longUrlClicks&quot;: &quot;1&quot;,
   &quot;referrers&quot;: [
    {
     &quot;count&quot;: &quot;1&quot;,
     &quot;id&quot;: &quot;Unknown/empty&quot;
    }
   ],
   &quot;countries&quot;: [
    {
     &quot;count&quot;: &quot;1&quot;,
     &quot;id&quot;: &quot;HR&quot;
    }
   ],
   &quot;browsers&quot;: [
    {
     &quot;count&quot;: &quot;1&quot;,
     &quot;id&quot;: &quot;Chrome&quot;
    }
   ],
   &quot;platforms&quot;: [
    {
     &quot;count&quot;: &quot;1&quot;,
     &quot;id&quot;: &quot;Windows&quot;
    }
   ]
  },
  &quot;twoHours&quot;: {
   &quot;shortUrlClicks&quot;: &quot;1&quot;,
   &quot;longUrlClicks&quot;: &quot;1&quot;,
   &quot;referrers&quot;: [
    {
     &quot;count&quot;: &quot;1&quot;,
     &quot;id&quot;: &quot;Unknown/empty&quot;
    }
   ],
   &quot;countries&quot;: [
    {
     &quot;count&quot;: &quot;1&quot;,
     &quot;id&quot;: &quot;HR&quot;
    }
   ],
   &quot;browsers&quot;: [
    {
     &quot;count&quot;: &quot;1&quot;,
     &quot;id&quot;: &quot;Chrome&quot;
    }
   ],
   &quot;platforms&quot;: [
    {
     &quot;count&quot;: &quot;1&quot;,
     &quot;id&quot;: &quot;Windows&quot;
    }
   ]
  }
 }
}
&quot;
}

*/
</pre>
<p>The above 3 examples are pretty easy, and straight forward. Last example, &#8220;Look up a user&#8217;s history&#8221;, requires a bit more finesse as it requires your/user authentication in order to get the proper response. There are several ways you can authenticate (OAuth, ClientLogin). OAuth is the preferred mechanism for logging in and performing actions on behalf of a Google user. </p>
<p>If Oauth is not feasible, you can use ClientLogin. With this mechanism, you give google.com an email and password in exchange for an access token.</p>
<p>In this last example &#8220;Look up a user&#8217;s history&#8221; I will show you how to do it with ClientLogin approach as it is somewhat simpler. However, this does not mean that ClientLogin approach will suite your needs, in which case you should really study the Oauth implementation.</p>
<p><strong><em>Example #4</em> &#8211; Look up a user&#8217;s history</strong></p>
<pre class="brush: php; title: ; notranslate">
$clientLogin = new Zend_Http_Client();

$clientLogin-&gt;setUri('https://www.google.com/accounts/ClientLogin');
$clientLogin-&gt;setMethod(Zend_Http_Client::POST);
$clientLogin-&gt;setHeaders(Zend_Http_Client::CONTENT_TYPE, 'application/x-www-form-urlencoded');
$clientLogin-&gt;setParameterPost('accountType', 'HOSTED_OR_GOOGLE');
$clientLogin-&gt;setParameterPost('Email', '_YOUR_GOOGLE_EMAIL_HERE_');
$clientLogin-&gt;setParameterPost('Passwd', '_YOUR_GOOGLE_PASS_HERE_');
$clientLogin-&gt;setParameterPost('service', 'urlshortener');
$clientLogin-&gt;setParameterPost('source', 'myname-testapp-1.0.1');

$responseLogin = $clientLogin-&gt;request();
$responseLoginBody = $responseLogin-&gt;getBody();

$loginTokens = explode(&quot;\n&quot;, $responseLoginBody);
$authToken = '';

foreach ($loginTokens as $r) {
    $_r = explode(&quot;=&quot;, $r);
    if (count($_r) &gt; 1) {
        if ($_r[0] == 'Auth') {
            $authToken = $_r[1];
        }
    }
}

if (!empty($authToken)) {
    $clientHistory = new Zend_Http_Client();

    $clientHistory-&gt;setUri('https://www.googleapis.com/urlshortener/v1/url/history');
    $clientHistory-&gt;setMethod(Zend_Http_Client::GET);
    $clientHistory-&gt;setHeaders(Zend_Http_Client::CONTENT_TYPE, 'application/json');
    $clientHistory-&gt;setHeaders('Authorization', 'GoogleLogin auth='.$authToken);
    $clientHistory-&gt;setParameterGet('key', '___YOUR_API_KEY_HERE___');

    $responseHistory = $clientHistory-&gt;request();
    $responseHistoryBody = $responseHistory-&gt;getBody();
    //Zend_Debug::dump($responseHistoryBody, '$responseHistoryBody');
    //Zend_Debug::dump($responseHistory, '$responseHistory');
}
</pre>
<p>Below you can see the example of response for the above request.</p>
<pre class="brush: php; title: ; notranslate">
Zend_Debug::dump($responseHistory, '$responseHistory');

/**

$responseHistory object(Zend_Http_Response)#237 (5) {
  [&quot;version&quot;:protected] =&gt; string(3) &quot;1.1&quot;
  [&quot;code&quot;:protected] =&gt; int(200)
  [&quot;message&quot;:protected] =&gt; string(2) &quot;OK&quot;
  [&quot;headers&quot;:protected] =&gt; array(10) {
    [&quot;Expires&quot;] =&gt; string(29) &quot;Sat, 26 Feb 2011 10:20:34 GMT&quot;
    [&quot;Date&quot;] =&gt; string(29) &quot;Sat, 26 Feb 2011 10:20:34 GMT&quot;
    [&quot;Cache-control&quot;] =&gt; string(49) &quot;private, max-age=0, must-revalidate, no-transform&quot;
    [&quot;Content-type&quot;] =&gt; string(31) &quot;application/json; charset=UTF-8&quot;
    [&quot;X-content-type-options&quot;] =&gt; string(7) &quot;nosniff&quot;
    [&quot;X-frame-options&quot;] =&gt; string(10) &quot;SAMEORIGIN&quot;
    [&quot;X-xss-protection&quot;] =&gt; string(13) &quot;1; mode=block&quot;
    [&quot;Server&quot;] =&gt; string(3) &quot;GSE&quot;
    [&quot;Connection&quot;] =&gt; string(5) &quot;close&quot;
  }
  [&quot;body&quot;:protected] =&gt; string(2943) &quot;{
 &quot;kind&quot;: &quot;urlshortener#urlHistory&quot;,
 &quot;totalItems&quot;: 13,
 &quot;itemsPerPage&quot;: 30,
 &quot;items&quot;: [
  {
   &quot;kind&quot;: &quot;urlshortener#url&quot;,
   &quot;id&quot;: &quot;http://goo.gl/IkFNS&quot;,
   &quot;longUrl&quot;: &quot;http://www.jetbrains.com/phpstorm/whatsnew/index.html&quot;,
   &quot;status&quot;: &quot;OK&quot;,
   &quot;created&quot;: &quot;2011-02-25T09:29:03.995+00:00&quot;
  },
  {
   &quot;kind&quot;: &quot;urlshortener#url&quot;,
   &quot;id&quot;: &quot;http://goo.gl/NpdS9&quot;,
   &quot;longUrl&quot;: &quot;http://www.eclipse.org/egit/?gclid\u003dCPfd-5L_oqcCFZMK3wodkC6_BA&quot;,
   &quot;status&quot;: &quot;OK&quot;,
   &quot;created&quot;: &quot;2011-02-25T09:28:28.545+00:00&quot;
  },
  {
   &quot;kind&quot;: &quot;urlshortener#url&quot;,
   &quot;id&quot;: &quot;http://goo.gl/RiVJH&quot;,
   &quot;longUrl&quot;: &quot;http://www.flickr.com/photos/ajzele/sets/72157626133406912/detail/&quot;,
   &quot;status&quot;: &quot;OK&quot;,
   &quot;created&quot;: &quot;2011-02-25T08:46:47.203+00:00&quot;
  },

*/
</pre>
<p>That’s it. Examples above cover the most of what you can do with the Google URL Shortener API at the moment. Hope you find the article useful.</p>
]]></content:encoded>
			<wfw:commentRss>http://inchoo.net/tools-frameworks/zendframework-example-on-using-google-url-shortener-service/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>How you could serve static content files from your Magento extension folder</title>
		<link>http://inchoo.net/ecommerce/magento/how-you-could-serve-static-content-files-from-your-magento-extension-folder/</link>
		<comments>http://inchoo.net/ecommerce/magento/how-you-could-serve-static-content-files-from-your-magento-extension-folder/#comments</comments>
		<pubDate>Thu, 24 Feb 2011 10:25:25 +0000</pubDate>
		<dc:creator>Branko Ajzele</dc:creator>
				<category><![CDATA[Extensions]]></category>
		<category><![CDATA[Magento]]></category>
		<category><![CDATA[extension]]></category>
		<category><![CDATA[unobtrusive]]></category>

		<guid isPermaLink="false">http://inchoo.net/?p=8512</guid>
		<description><![CDATA[As a part of my personal ongoing &#8220;unobtrusive Magento extensions&#8221; campaign. I will show you another &#8220;hack&#8221;/approach you can apply in order to squeeze your static files under the main extension folder. When I say static, I am mainly referring &#8230;<p><a href="http://inchoo.net/ecommerce/magento/how-you-could-serve-static-content-files-from-your-magento-extension-folder/">Read more</a><p>]]></description>
			<content:encoded><![CDATA[<p>As a part of my personal ongoing &#8220;unobtrusive Magento extensions&#8221; campaign. I will show you another &#8220;hack&#8221;/approach you can apply in order to squeeze your static files under the main extension folder.</p>
<p>When I say static, I am mainly referring to images, CSS and JavaScript in this case.</p>
<p>For example, imagine you are coding an extension called &#8220;<strong>Sociable</strong>&#8220;, which will display several links to various web services like Twitter, GoogleBuzz, etc. Links that you can click and the publish a short message about a product/category on that web service. Something like on the image shown below. Logical question is, where do you store your images?<span id="more-8512"></span></p>
<p>Usually images are stored within the root <strong>/media/</strong> folder. So it might seem perfectly fit to store them under the <strong>/media/mycompany/mymodule/</strong> folder.</p>
<p>Similar to that, CSS files are usually stored under the <strong>/skin/frontend/default/default/css/</strong>, possibly in a sub-folder like<strong> /skin/frontend/default/default/css/mycompany/mymodule/</strong>.</p>
<p>Now, this is not a bad approach, possibly just little &#8220;distributed&#8221; in terms of spreading your extension across several root folders, making it a bit harder for manual &#8220;cleanup&#8221; or maintenance, etc. This is the way how most of extensions are build these days.</p>
<p>Now I will show you another possible approach (not necessarily the better one) but more &#8220;centralized&#8221; in terms of keeping everything within one folder. </p>
<p>Basically it comes down to storing all your public static files under the extension folders like:</p>
<ul>
<li>/app/code/community/Inchoo/Sociable/public/js</li>
<li>/app/code/community/Inchoo/Sociable/public/css</li>
<li>/app/code/community/Inchoo/Sociable/public/image</li>
</ul>
<p>And adding few lines of code into a custom controller like <b>/app/code/community/Inchoo/Sociable/controllers/StaticController.php</b> that will basically be acting as static file server. Don’t worry, its actually pretty simple.</p>
<p>Let’s start with our <b>/app/code/community/Inchoo/Sociable/etc/config.xml</b> file. Within it we will first add the router definition like shown below.</p>
<pre class="brush: xml; title: ; notranslate">
&lt;frontend&gt;
	&lt;routers&gt;
		&lt;inchoo_sociable&gt;
			&lt;use&gt;standard&lt;/use&gt;
			&lt;args&gt;
				&lt;module&gt;Inchoo_Sociable&lt;/module&gt;
				&lt;frontName&gt;inchoo_sociable&lt;/frontName&gt;
			&lt;/args&gt;
		&lt;/inchoo_sociable&gt;
	&lt;/routers&gt;
&lt;/frontend&gt;
</pre>
<p>Now we will add some basic security check/handling to our files we will be serving torugh controller action. We can put this logic into the /app/code/community/Inchoo/Sociable/Helper/Data.php file, like shown below.</p>
<pre class="brush: php; title: ; notranslate">
class Inchoo_Sociable_Helper_Data extends Mage_Core_Helper_Abstract
{
    const DIR_IMAGE = 'image';
    const DIR_CSS = 'css';
    const DIR_JS = 'js';

    /**
     * @param string 'icons' or 'css' or 'js'
     * @return string
     */
    public function getExtPubDir($type)
    {
        return __DIR__.DS.'..'.DS.DS.'public'.DS.$type;
    }

    public function getAllowedFiles($dir)
    {
        $results = array();
        $handler = opendir($this-&gt;getExtPubDir($dir));

        /* Might be improved later via cache, not to list entire folder on each request. */
        while ($file = readdir($handler)) {
            if ($file != &quot;.&quot; &amp;&amp; $file != &quot;..&quot;) {
                $results[] = $file;
            }
        }

        closedir($handler);

        return $results;
    }
}
</pre>
<p>Finally we will create controller <b>/app/code/community/Inchoo/Sociable/controllers/StaticController.php</b> that will be used to serve our static files directly from our extension folder (code shown below).</p>
<pre class="brush: php; title: ; notranslate">
class Inchoo_Sociable_StaticController extends Mage_Core_Controller_Front_Action
{
    public function getAction()
    {
        $type = $this-&gt;getRequest()-&gt;getParam('t');

        switch ($type) {
            case 'css':
                return $this-&gt;_css();
                break;
            case 'image':
                return $this-&gt;_image();
                break;
            case 'js':
                return $this-&gt;_js();
                break;
            default:
                $this-&gt;getResponse()-&gt;setHeader('HTTP/1.1','404 Not Found');
                $this-&gt;getResponse()-&gt;setHeader('Status','404 File not found');
                break;
        }
    }

    private function _css()
    {
        $file = $this-&gt;getRequest()-&gt;getParam('f');
        $type = $this-&gt;getRequest()-&gt;getParam('t');

        $helper = Mage::helper('inchoo_sociable');

        if (in_array($file, $helper-&gt;getAllowedFiles($type))) {
            $this-&gt;getResponse()-&gt;setHeader('Content-Type', 'text/css');
            $this-&gt;getResponse()-&gt;setBody(file_get_contents($helper-&gt;getExtPubDir($helper::DIR_CSS).DS.$file));
        } else {
            $this-&gt;getResponse()-&gt;setHeader('HTTP/1.1','404 Not Found');
            $this-&gt;getResponse()-&gt;setHeader('Status','404 File not found');
        }
    }

    private function _image()
    {
        $file = $this-&gt;getRequest()-&gt;getParam('f');
        $type = $this-&gt;getRequest()-&gt;getParam('t');

        $helper = Mage::helper('inchoo_sociable');

        if (in_array($file, $helper-&gt;getAllowedFiles($type))) {
            $icon = new Varien_Image($helper-&gt;getExtPubDir($helper::DIR_IMAGE).DS.$file);
            $this-&gt;getResponse()-&gt;setHeader('Content-Type', $icon-&gt;getMimeType());
            $this-&gt;getResponse()-&gt;setBody($icon-&gt;display());
        } else {
            $this-&gt;getResponse()-&gt;setHeader('HTTP/1.1','404 Not Found');
            $this-&gt;getResponse()-&gt;setHeader('Status','404 File not found');
        }
    }

    private function _js()
    {
        $file = $this-&gt;getRequest()-&gt;getParam('f');
        $type = $this-&gt;getRequest()-&gt;getParam('t');

        $helper = Mage::helper('inchoo_sociable');

        if (in_array($file, $helper-&gt;getAllowedFiles($type))) {
            $this-&gt;getResponse()-&gt;setHeader('Content-Type', 'application/javascript');
            $this-&gt;getResponse()-&gt;setBody(file_get_contents($helper-&gt;getExtPubDir($helper::DIR_JS).DS.$file));
        } else {
            $this-&gt;getResponse()-&gt;setHeader('HTTP/1.1','404 Not Found');
            $this-&gt;getResponse()-&gt;setHeader('Status','404 File not found');
        }
    }
}
</pre>
<p>Basically that’s all we need. Now if we run the url like <b>http://{{unsecure_base_url}}/index.php/inchoo_sociable/static/get/t/js/f/jquery.js</b> we would get jquery script served. Same goes for url like <b>http://{{unsecure_base_url}}/index.php/inchoo_sociable/static/get/t/image/f/delicious.png</b> which would give us the image.</p>
<p>So now, you can use write HTML like <b>img src=&#8221;http://{{unsecure_base_url}}/index.php/inchoo_sociable/static/get/t/image/f/delicious.png&#8221;</b> in your extension block that would then pull the image from your extension folder.</p>
<p>Once again, this approach is not &#8220;official Magento way&#8221; if there is a such thing. Point of this article is to merely show how you can do it if you wish to keep your static content in one extension folder, and not other, folder like /media, /skin, /js, etc. Like shown below on the image.</p>
<p><a href="http://inchoo.net/wp-content/uploads/2011/02/screen324234.png"><img src="http://inchoo.net/wp-content/uploads/2011/02/screen324234-177x300.png" alt="" title="screen324234" width="177" height="300" class="alignnone size-thumbnail wp-image-8517" /></a></p>
<blockquote><p>P.S. I am not actually using jQuery in Magento (would never mix Prototype and jQuery). Image above is merely for demo purpose.</p></blockquote>
<p>Surely there are better ways to handle this &#8220;serve static content files from your Magento extension folder&#8221; concept. You be the judge. The important thing is to know what the code does and you might do it differently in a given situation.</p>
<p>Hope someone finds it useful.</p>
<p>Cheers.</p>
]]></content:encoded>
			<wfw:commentRss>http://inchoo.net/ecommerce/magento/how-you-could-serve-static-content-files-from-your-magento-extension-folder/feed/</wfw:commentRss>
		<slash:comments>11</slash:comments>
		</item>
		<item>
		<title>How you could build your Magento extensions without view files</title>
		<link>http://inchoo.net/ecommerce/magento/how-you-could-build-your-magento-extensions-without-view-files/</link>
		<comments>http://inchoo.net/ecommerce/magento/how-you-could-build-your-magento-extensions-without-view-files/#comments</comments>
		<pubDate>Tue, 22 Feb 2011 15:14:56 +0000</pubDate>
		<dc:creator>Branko Ajzele</dc:creator>
				<category><![CDATA[Extensions]]></category>
		<category><![CDATA[Frontend]]></category>
		<category><![CDATA[Magento]]></category>
		<category><![CDATA[extension]]></category>
		<category><![CDATA[unobtrusive]]></category>

		<guid isPermaLink="false">http://inchoo.net/?p=8497</guid>
		<description><![CDATA[The main idea behind this article is something I previously wrote, making your extensions more distributable, less error prone, less depended, less intrusive. Such extensions are then more “safer” to be fetched and installed via Magento Connect. When I say &#8230;<p><a href="http://inchoo.net/ecommerce/magento/how-you-could-build-your-magento-extensions-without-view-files/">Read more</a><p>]]></description>
			<content:encoded><![CDATA[<p>The main idea behind this article is something I previously wrote, making your extensions more distributable, less error prone, less depended, less intrusive. Such extensions are then more “safer” to be fetched and installed via Magento Connect. When I say safer I am thinking in terms of your extension not breaking someone live store, etc.</p>
<p>I would dare to say that one of the most annoying and dangerous areas of extension development are the theme view files. For example, imagine an extension that adds some “color switcher” like functionality on the product view page.<span id="more-8497"></span></p>
<p>Usually extension developer will create his alternative of <strong>/app/design/frontend/default/default/template/catalog/product/view.phtml</strong> or <strong>/app/design/frontend/default/default/template/catalog/product/view/media.phtml</strong> file placing it under the default theme. </p>
<p>This approach is bad for at least two reasons:<br />
- what if some other extension already does the same thing<br />
- what if theme developer deletes it and places its own code in it</p>
<p>Personally I like my extensions code centralized. When I say centralized, I am referring to keeping all of my extension code under the one folder, the /app/code/community/ folder, depending on the name of the company and module namespace.</p>
<p>Let me give you a simple example of simple extension that demonstrates powerful concept of “adding things unobtrusively”. My extension is called “Selectify“ and it will be located under the “Inchoo” company folder.</p>
<p><strong>/app/code/community/Inchoo/Selectify/</strong></p>
<p>Extension will be turning simple quantity input fields from product view page into dropdown/select fields, allowing selections of 1, 2, 3 quantity to be selected. Why would anyone want to do that? Most likely it would not, but lets just imagine we need to do it.</p>
<p>Easiest approach would be simply to open/create appropriate <strong>/app/design/frontend/default/default/template/catalog/product/view/addtocart.phtml</strong> file and just replace the input text field element to select element.</p>
<p>The problem with that approach is that we are developing extension, not modifying the theme. We do not care on the look &#038; feel of the theme or theme name or, etc. So, in the spirit of the “unobtrusively” we will not apply this approach.</p>
<p>We will apply following logic to the approach:</p>
<ul>
<li>Find the file that needs to be modified /app/design/frontend/default/default/template/catalog/product/view/addtocart.phtml</li>
<li>Think it trough how we can change it without touching it (obviously via some simple JavaScript in this case)</li>
<li>Apply the change by hooking into some appropriate event</li>
</ul>
<p>Now, the interesting part is the “hooking into some appropriate event”. Each child of Mage_Core_Block_Abstract class triggers an event that looks like this:</p>
<pre class="brush: php; title: ; notranslate">
Mage::dispatchEvent('core_block_abstract_to_html_after', array('block' =&gt; $this, 'transport' =&gt; self::$_transportObject));
</pre>
<p>What this means is that simply by observing that event you can change the raw HTML output of it, as the “transport” holds the HTML content of the given block. So basically, all you need is an event observer targeted specifically at some block by it’s name. Reminds me on the good old WordPress stuff, LOL.</p>
<p><strong>Step 1: Declare observer in config.xml file</strong></p>
<pre class="brush: php; title: ; notranslate">
&lt;frontend&gt;
	&lt;events&gt;
		&lt;core_block_abstract_to_html_after&gt;
			&lt;observers&gt;
				&lt;inchoo_selectify&gt;
					&lt;class&gt;inchoo_selectify/observer&lt;/class&gt;
					&lt;method&gt;convertQtyInputToSelect&lt;/method&gt;
				&lt;/inchoo_selectify&gt;
			&lt;/observers&gt;
		&lt;/core_block_abstract_to_html_after&gt;
	&lt;/events&gt;
&lt;/frontend&gt;
</pre>
<p><strong>Step 2: Write a block class that will hold some essential logic or HTML code itself</strong></p>
<pre class="brush: php; title: ; notranslate">
class Inchoo_Selectify_Block_QtyInputToSelect extends Mage_Core_Block_Text
{
    protected $_nameInLayout = 'selectify.qty_input_to_select';
    protected $_alias = 'qty_input_to_select';

    public function setPassingTransport($transport)
    {
        $this-&gt;setData('text', $transport.$this-&gt;_generateQtyInputToSelectHtml());
    }

    private function _generateQtyInputToSelectHtml()
    {
        return '
            &lt;script type=&quot;text/javascript&quot;&gt;
            //&lt;![CDATA[

            document.observe(&quot;dom:loaded&quot;, function() {
                $(&quot;qty&quot;).replace(\'&lt;select class=&quot;input-text qty&quot; title=&quot;Qty&quot; value=&quot;1&quot; id=&quot;qty&quot; name=&quot;qty&quot;&gt;&lt;option selected=&quot;selected&quot; value=&quot;1&quot;&gt;1&lt;/option&gt;&lt;option value=&quot;2&quot;&gt;2&lt;/option&gt;&lt;option value=&quot;3&quot;&gt;3&lt;/option&gt;&lt;/select&gt;\');
            });

            //]]&gt;
            &lt;/script&gt;
        ';
    }
}
</pre>
<p><strong>Step 3: Handle the actual event by some observer code</strong></p>
<pre class="brush: php; title: ; notranslate">
class Inchoo_Selectify_Model_Observer
{
    const MODULE_NAME = 'Inchoo_Selectify';

    public function convertQtyInputToSelect($observer = NULL)
    {
        if (!$observer) {
            return;
        }

        if ('product.info.addtocart' == $observer-&gt;getEvent()-&gt;getBlock()-&gt;getNameInLayout()) {

            if (!Mage::getStoreConfig('advanced/modules_disable_output/'.self::MODULE_NAME)) {

                $transport = $observer-&gt;getEvent()-&gt;getTransport();

                $block = new Inchoo_Selectify_Block_QtyInputToSelect();
                $block-&gt;setPassingTransport($transport['html']);
                $block-&gt;toHtml();
            }
        }

        return $this;
    }
}
</pre>
<p>And that’s it. You will notice that I am using <strong>Mage::getStoreConfig(&#8216;advanced/modules_disable_output/&#8217;.self::MODULE_NAME)</strong> to check if the module’s output is actually enabled/disabled. If “disabled” then extension has no effect on the frontend, meaning it does not do anything in this case.</p>
<p>Also notice the <strong>setPassingTransport</strong> method. It’s a custom method I wrote on the <strong>Inchoo_Selectify_Block_QtyInputToSelect</strong> block class just to abstract some code into it. </p>
<p>And finally take a look at the <strong>_generateQtyInputToSelectHtml </strong>method. I dumped a chunk of JavaScript code in it. This JavaScript actually handles the change of “input text” field into the “select” field with a bit of help from PrototpyJS. Which implies Prototype must be loaded on page for this to work.</p>
<p>Now, the above example might not be the perfect example of clean abstracted code. Especially because of the fact that I have a pure JavaScript in my PHP class. Obviously more proper place for this would be into the view file. However, I deliberately refused to go with the standard view file from theme folder as I want to keep my extension out of the theme folders.</p>
<p>BEFORE<br />
<a href="http://inchoo.net/wp-content/uploads/2011/02/selectify_before.png"><img src="http://inchoo.net/wp-content/uploads/2011/02/selectify_before-300x264.png" alt="" title="selectify_before" width="300" height="264" class="alignnone size-thumbnail wp-image-8499" /></a></p>
<p>AFTER<br />
<a href="http://inchoo.net/wp-content/uploads/2011/02/selectify_after.png"><img src="http://inchoo.net/wp-content/uploads/2011/02/selectify_after-300x247.png" alt="" title="selectify_after" width="300" height="247" class="alignnone size-thumbnail wp-image-8498" /></a></p>
<p>I did not took additional time to check how does this reflect to block caching or similar, but I for one fancy the approach for given (not all) frontend situations.</p>
<p>Please keep in mind that entire article speaks on extensions that are suppose to be super easy to distribute and remove. Leaving no negative impact on the custom themes, etc.</p>
<p>Feel free to share your ideas or other approaches you might have for keeping your stuff out of the theme folders.</p>
]]></content:encoded>
			<wfw:commentRss>http://inchoo.net/ecommerce/magento/how-you-could-build-your-magento-extensions-without-view-files/feed/</wfw:commentRss>
		<slash:comments>16</slash:comments>
		</item>
		<item>
		<title>Logging user/customer actions in Magento</title>
		<link>http://inchoo.net/ecommerce/magento/logging-user-customer-actions-in-magento/</link>
		<comments>http://inchoo.net/ecommerce/magento/logging-user-customer-actions-in-magento/#comments</comments>
		<pubDate>Fri, 18 Feb 2011 08:52:05 +0000</pubDate>
		<dc:creator>Branko Ajzele</dc:creator>
				<category><![CDATA[Magento]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[action]]></category>
		<category><![CDATA[customer]]></category>
		<category><![CDATA[log]]></category>
		<category><![CDATA[user]]></category>

		<guid isPermaLink="false">http://inchoo.net/?p=8423</guid>
		<description><![CDATA[Recently I wrote a Magento extension called ActionLogger, inspired (thematically) by the &#8220;Logging of Administrator Actions&#8221; feature available in Magento Enterprise. ActionLogger is pretty simple extension, working on pretty simple but powerful concept of Magento controllers predispatch action. All you &#8230;<p><a href="http://inchoo.net/ecommerce/magento/logging-user-customer-actions-in-magento/">Read more</a><p>]]></description>
			<content:encoded><![CDATA[<p>Recently I wrote a Magento extension called <em><strong>ActionLogger</strong></em>, inspired (thematically) by the &#8220;<em>Logging of Administrator Actions</em>&#8221; feature available in <em>Magento Enterprise</em>. <em><strong>ActionLogger</strong></em> is pretty simple extension, working on pretty simple but powerful concept of Magento controllers predispatch action.</p>
<p>All you need to do is to create event observer/observers for &#8220;<strong>controller_action_predispatch</strong>&#8221; event. This event is fired on every controller action call.<span id="more-8423"></span></p>
<p>For example, if your store is located on url http://magento-1501.net url, and you go to url like http://magento-1501.net/electronics/cell-phones this will call action &#8220;<em>view</em>&#8221; from controller &#8220;<em>category</em>&#8221; from module &#8220;<em>Mage_Catalog</em>&#8220;. Simple logic/tracing would point this to be app\code\core\Mage\Catalog\controllers\CategoryController.php file and its viewAction().</p>
<p>Adding an event observer like the one shown below will trigger your code upon calling such url.</p>
<pre class="brush: php; title: ; notranslate">
&lt;frontend&gt;
	&lt;events&gt;
		&lt;controller_action_predispatch&gt;
			&lt;observers&gt;
				&lt;activecodeline_actionlogger_controller_action_predispatch&gt;
					&lt;class&gt;activecodeline_actionlogger/observer&lt;/class&gt;
					&lt;method&gt;hookToFrontendControllerActionPredispatch&lt;/method&gt;
				&lt;/activecodeline_actionlogger_controller_action_predispatch&gt;
			&lt;/observers&gt;
		&lt;/controller_action_predispatch&gt;
	&lt;/events&gt;
&lt;/frontend&gt;
</pre>
<p>Same goes for the admin section, for example if you open a link like http://magento-1501.net/index.php/admin/sales_order/index/key/9c13e78eecdc3d5805f6426be9f56254/. This one would call action &#8220;<em>index</em>&#8221; from controller &#8220;<em>sales_order</em>&#8221; from module &#8220;<em>Mage_Adminhtml</em>&#8220;. Simple logic/tracing would point this to be app\code\core\Mage\Adminhtml\controllers\Sales\OrderController.php file and its indexAction().</p>
<p>Adding an event observer like the one shown below will trigger your code upon calling such url.</p>
<pre class="brush: php; title: ; notranslate">
&lt;adminhtml&gt;
	&lt;events&gt;
		&lt;controller_action_predispatch&gt;
			&lt;observers&gt;
				&lt;activecodeline_actionlogger_controller_action_predispatch&gt;
					&lt;class&gt;activecodeline_actionlogger/observer&lt;/class&gt;
					&lt;method&gt;hookToAdminhtmlControllerActionPredispatch&lt;/method&gt;
				&lt;/activecodeline_actionlogger_controller_action_predispatch&gt;
			&lt;/observers&gt;
		&lt;/controller_action_predispatch&gt;
	&lt;/events&gt;
&lt;/adminhtml&gt;
</pre>
<p>All this is fine &#038; etc., but what’s the point? We already know this, you might say. </p>
<p>The point is, the concept is very simple but enables you to add powerful logging mechanism on top of it. All you need to do is to add few lines of code that would record given action, controller, current user/customer, possibly entire parameters passed to controller, etc. </p>
<p>Then you can store it either in database or in some file.</p>
<p>For example, in ActionLogger extension I created a special models, called Admin and Frontend that handle writing logged values to database from observer.</p>
<pre class="brush: php; title: ; notranslate">
public function hookToFrontendControllerActionPredispatch($observer = null)
{
	if (!Mage::helper('activecodeline_actionlogger')-&gt;_canLogFrontendActions()) {
		return;
	}

	$log = Mage::getModel('activecodeline_actionlogger/frontend');

	$log-&gt;setActionName(Mage::app()-&gt;getRequest()-&gt;getActionName());
	$log-&gt;setControllerName(Mage::app()-&gt;getRequest()-&gt;getControllerName());

	if (Mage::helper('activecodeline_actionlogger')-&gt;_canLogRequestParams()) {
		if($params = Mage::app()-&gt;getRequest()-&gt;getParams()) {
			$log-&gt;setParams(Mage::helper('core')-&gt;encrypt(serialize($params)));
		}
	}

	$log-&gt;setClientIp(Mage::app()-&gt;getRequest()-&gt;getClientIp());
	$log-&gt;setControllerModule(Mage::app()-&gt;getRequest()-&gt;getControllerModule());

	if ($customer = Mage::getSingleton('customer/session')-&gt;getCustomer()) {
		$log-&gt;setCustomerId($customer-&gt;getId());
	} else {
		$log-&gt;setCustomerId(0);
	}

	try {
		$log-&gt;save();
	} catch (Exception $e) {
		Mage::log('file: '.__FILE__.', line: '.__LINE__, 'msg: '.$e-&gt;getMessage());
	}
}
</pre>
<p>On top of that you can create a nice grid in Magento for listing these. Combine this with built in Magento roles, and you can have your grid shown only to certain role.</p>
<p><a href="http://inchoo.net/wp-content/uploads/2011/02/ActionLogger_2.png"><img src="http://inchoo.net/wp-content/uploads/2011/02/ActionLogger_2-300x197.png" alt="" title="ActionLogger_2" width="300" height="197" class="alignnone size-thumbnail wp-image-8435" /></a>  <a href="http://inchoo.net/wp-content/uploads/2011/02/ActionLogger4.png"><img src="http://inchoo.net/wp-content/uploads/2011/02/ActionLogger4-300x202.png" alt="" title="ActionLogger4" width="300" height="202" class="alignnone size-thumbnail wp-image-8436" /></a>  <a href="http://inchoo.net/wp-content/uploads/2011/02/ActionLogger2.png"><img src="http://inchoo.net/wp-content/uploads/2011/02/ActionLogger2-300x198.png" alt="" title="ActionLogger2" width="300" height="198" class="alignnone size-thumbnail wp-image-8437" /></a>  <a href="http://inchoo.net/wp-content/uploads/2011/02/ActionLogger3.png"><img src="http://inchoo.net/wp-content/uploads/2011/02/ActionLogger3-300x202.png" alt="" title="ActionLogger3" width="300" height="202" class="alignnone size-thumbnail wp-image-8439" /></a></p>
<p>Concept like this can be used for development/debugging purposes and possibly for some user/customer behaviour profiling. Although &#8220;profiling&#8221; can mostly be done via Google Analytics these days, there are still admin part of sites that are not suppose to get analyzed by Google.</p>
<p>Thinking further down the road, one can build truly dynamic user specific menus based on the logged data statistics, etc.</p>
<p>Hope I got you thinking.</p>
<blockquote><p>P.S. Unlike my usual way of packing up the code into the zip archive, this time I decided to actually submit extension to Magento connect. <del datetime="2011-02-22T19:40:36+00:00">This was done yesterday and is waiting to be approved <img src='http://inchoo.net/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> . Once approved, I’ll post a link to extension for those who are interested.</del> Extension is now live at Magento Connect <a href="http://www.magentocommerce.com/magento-connect/branko.ajzele/extension/5829/actionlogger/">here</a>.</p></blockquote>
<p>Cheers.</p>
]]></content:encoded>
			<wfw:commentRss>http://inchoo.net/ecommerce/magento/logging-user-customer-actions-in-magento/feed/</wfw:commentRss>
		<slash:comments>11</slash:comments>
		</item>
	</channel>
</rss>

