<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/rss2full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><rss 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/" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" version="2.0">

<channel>
	<title>mandagreen.com</title>
	
	<link>http://mandagreen.com</link>
	<description>Refresh your vision</description>
	<lastBuildDate>Sun, 06 May 2012 18:33:43 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.2</generator>
		<atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/rss+xml" href="http://feeds.feedburner.com/mandagreen" /><feedburner:info uri="mandagreen" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><item>
		<title>When JOINs fail to work</title>
		<link>http://feedproxy.google.com/~r/mandagreen/~3/81Fvacx4Pac/</link>
		<comments>http://mandagreen.com/when-joins-fail-to-work/#comments</comments>
		<pubDate>Sun, 06 May 2012 18:33:43 +0000</pubDate>
		<dc:creator>Cristi</dc:creator>
				<category><![CDATA[MySQL]]></category>
		<category><![CDATA[mysql]]></category>
		<category><![CDATA[performance]]></category>

		<guid isPermaLink="false">http://mandagreen.com/?p=266</guid>
		<description><![CDATA[Any developer, junior or senior, should know and understand the basic rules of database normalization since they are the foundation of relational DBMS. Edgar Codd introduced the concept in 1970 and since then, most of the architectures have been following his rules. However, for high-traffic websites, sometime de-normalized is better than normalized, resulting in faster [...]]]></description>
			<content:encoded><![CDATA[<p>Any developer, junior or senior, should know and understand the basic rules of <a href="http://en.wikipedia.org/wiki/Database_normalization">database normalization</a> since they are the foundation of relational DBMS. Edgar Codd introduced the concept in 1970 and since then, most of the architectures have been following his rules. However, for high-traffic websites, sometime de-normalized is better than normalized, resulting in faster queries, less swap space and less storage space. </p>
<p>Although this post is not exactly about normalization, another common recommendation is to use a join rather than multiple single queries. In other words, rather than doing &#8220;select * from a where condition1&#8243; then &#8220;select * from b where condition2&#8243;, if a and b are in a relationship, it&#8217;s better to usually run a query like &#8220;select * from a inner join b on a.id = b.aid where conditions&#8221;. A classic example is when using categories for a certain object, say movies. Let&#8217;s say each movie can have a category and you want to display a list of all movies and the category that movie is in. Some developers might write the following php code:</p>
<pre class="brush: php; title: ; notranslate">
connect_to_database();
$rs = mysql_query('select * from movies');
while( $row = mysql_fetch_array($rs) ) {
  $rs2 = mysql_query('select * from categories where category_id = ' . $row['category_id'];
  $row2 = mysql_fetch_array($rs2);
  $category = $row2['category_name'];
  echo $row['name'] . &quot;&lt;br /&gt;Category: &quot; . $category . &quot;&lt;br /&gt;&lt;br /&gt;&quot;;
}
</pre>
<p>but there&#8217;s a simpler and (most of the time) better approach:</p>
<pre class="brush: php; title: ; notranslate">
connect_to_database();
$rs = mysql_query('select * from movies m inner join categories c on c.category_id = m.category_id');
while( $row = mysql_fetch_array($rs) ) {
  echo $row['name'] . &quot;&lt;br /&gt;Category: &quot; . $row['category_name'] . &quot;&lt;br /&gt;&lt;br /&gt;&quot;;
}
</pre>
<p>The first approach generates 1 + N queries, where N is the number of movies. If N is a big number, things might take a while&#8230; The second approach only takes 1 query, leaving the hard part to the internal RDBMS engine. Well, sometimes, in certain scenarios, the first version is the right choice. It&#8217;s THE solution.  </p>
<p>Let&#8217;s assume there are 4+1 tables &#8211; the main one, call it <code>objects</code>, one for <code>tags</code>, one for <code>categories</code>, another one called <code>hits</code> which will track the number of times a certain object has been seen by a user, and the last one for storing the n-to-n relationship between tags and objects, call it >code>object_tags</code>. The ERD is rather simple:</p>
<p><img src="http://mandagreen.com/download/db-denormalized.png" alt="" title="Database ERD" width="537" height="126" class="alignnone size-full wp-image-337" /></p>
<p>Also, let's assume we want to show a list of objects, with all their tags, their current category and the number of hits it has. The results will be paginated, only showing 20 items per page. Simple, right? Anyone would tend to write something like this for the first page:</p>
<pre class="brush: sql; title: ; notranslate">
SELECT * FROM objects o
INNER JOIN categories c ON c.category_id = o.category_id
INNER JOIN object_tags ot ON ot.object_id = o.object_id
INNER JOIN tags t ON t.tag_id = ot.tag_id
INNER JOIN hits h ON h.object_id = o.object_id
LIMIT 20
</pre>
<p>This was what I wrote too, more or less. However, after spending many hours trying to figure out why that page was loading in more than 100 seconds, whenever it was loading at all, I've learned that sometimes it's better to break the rules and not follow any recommendations. So I tried the following code, and the results were staggering - less than 1s loading time:</p>
<pre class="brush: php; title: ; notranslate">
$rs = mysql_query('select * from objects limit 20');
$categories = $tags = array(); //used for locally caching categories and tags
while( $row = mysql_fetch_array($rs) ) {
  if( !isset($categories[$row['category_id']]) {
    $rsCat = mysql_query('SELECT category_name FROM categories WHERE category_id = ' . $row['category_id'] . ' LIMIT 1';
    $rowCat = mysql_fetch_array($rsCat);
    $categories[$row['category_id']] = $rowCat['category_name'];
    unset($rowCat, $rsCat);
  }

  $objectTags = $toLoad = array();
  $rsObjTags = mysql_query('SELECT tag_id FROM object_tags WHERE object_id = ' . $row['object_id']);
  while( $rowObjTags = mysql_fetch_array($rsObjTags) ) {
    if( isset($tags[$rowObjTags['tag_id']]) ) {
      $objectTags[$rowObjTags['tag_id']][] = $tags[$rowObjTags['tag_id']];
    }
    else {
      $toLoad[] = $rowObjTags['tag_id'];
    }
  }
  unset($rsObjTags , $rowObjTags);

  if( count($toLoad) ) {
    $rsTags = 'SELECT tag_id, tag FROM tags WHERE tag_id IN (' . implode(',', $toLoad) . ') LIMIT ' . count($toLoad);
    while( $rowTags  = mysql_fetch_array($rsTags) ) {
      $tags[$rowTags['tag_id']] = $rowTags['tag'];
      $objectTags[$rowTags['tag_id']][] = $rowTags['tag'];
    }
  }
  unset($toLoad, $rsTags, $rowTags);

  $rsHits = 'SELECT today_hits FROM hits WHERE object_id = ' . $row['object_id'] . ' LIMIT 1';
  $hits = mysql_fetch_array($rsHits);

  //ready to display the data
  echo $row['object_name'] . ' is in category ' . $categories[$row['category_id']] . '&lt;br /&gt;';
  echo 'Tags: ' . implode(', ', $objectTags) . '&lt;br/&gt;';
  echo 'Hits today: ' . $hits['today_hits'] . '&lt;hr /&gt;';
}
</pre>
<p><em>I hope there aren't too many mistakes in the code, I wrote everything in here without actually testing it. </em></p>
<p>Seems like a lot of work and totally not optimized for something quite straight-forward. However, let's assume once more that the tables above have the following number of records:<br />
objects - 50,000<br />
categories - 500<br />
tags - 100,000<br />
objects_tags - 250,000<br />
hits - 20,000,000</p>
<p>In this case, 4 joins would result in a huge dataset, requiring a lot of memory, and eventually ending up on the disk. Also, the LIMIT doesn't help at all, since the dataset is truncated at the end, after scanning all the necessary rows. However, in the unoptimized version we do benefit from the LIMIT, by only getting a fixed set (20 rows) from 50k rows. The rest of the queries are acceptable because they are all primary key based, they are using "LIMIT" as well (3 out of 4), and because their number is rather small - somewhere between 45 and 80 queries.</p>
<p>Hope this helps someone some day. I'd like to know your thoughts, what would have you done? Any other quick fixes for this problem?</p>
<img src="http://feeds.feedburner.com/~r/mandagreen/~4/81Fvacx4Pac" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://mandagreen.com/when-joins-fail-to-work/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://mandagreen.com/when-joins-fail-to-work/</feedburner:origLink></item>
		<item>
		<title>Hardening OpenX</title>
		<link>http://feedproxy.google.com/~r/mandagreen/~3/od3yMf1ROeE/</link>
		<comments>http://mandagreen.com/hardening-openx/#comments</comments>
		<pubDate>Wed, 18 Apr 2012 17:34:16 +0000</pubDate>
		<dc:creator>Cristi</dc:creator>
				<category><![CDATA[Security]]></category>
		<category><![CDATA[openx]]></category>
		<category><![CDATA[security]]></category>

		<guid isPermaLink="false">http://mandagreen.com/?p=277</guid>
		<description><![CDATA[There are times when your website or application might get hacked. The usual reasons include weak passwords, outdated software or poor code (especially on validating user input). The same apply to OpenX &#8211; your ad server can be hacked due to an outdated version of OpenX, weak passwords, insecure server settings or a combinations of [...]]]></description>
			<content:encoded><![CDATA[<p>There are times when your website or application might get hacked. The usual reasons include weak passwords, outdated software or poor code (especially on validating user input). The same apply to <a href="http://www.openx.com/">OpenX</a> &#8211; your ad server can be hacked due to an outdated version of OpenX, weak passwords, insecure server settings or a combinations of the above. Most common hacks add an iframe or script to the banner page distributing malware (a virus, trojan horse, or similar). This can get your website (the one that uses your infected openx server) to be blocked on all major browsers and most of the search engines.</p>
<p>To decrease the chances of having your openx hijacked, it&#8217;s recommended to follow these quick and easy steps. You&#8217;ll notice that some of them apply to any web application, like using strong passwords (at least 6 chars, using both uppercase and lowercase letters, digits and special chars).</p>
<p><strong>Things to do with your OpenX installation</strong><br />
1. Always update the application and plugins to the latest versions. This ensures your ad server has the latest patches and security fixes, but it also makes hacking a bit more difficult. You can check if you have the latest version by going to Configuration / Product Updates in the OpenX administration interface.</p>
<p>2. If your administrator username is admin or administrator (or anything similar), change it to something less common, like your first name or last name. This cannot be done from the openx admin interface, but it can be done directly in mysql (using phpmyadmin for instance) &#8211; the table you are looking for is <code>[table_prefix]_users</code></p>
<p>3. If there are other users who manage the inventory, create separate accounts for them and make them advertisers, not admins. Keep the admin account safe and do not share it with anyone. </p>
<p>4. If you only server banners on your website(s), you can remove all users from Advertisers and Websites. As a general rule, when you create a new advertiser, do not also create a username. If you don&#8217;t offer OpenX access to advertisers, there&#8217;s really no point in adding new users. </p>
<p>5. Always use the logout link to log out, instead of just closing the tab or window. This prevents session hijacking or keeping an active session on a public computer. </p>
<p>6. Disable all plugins that you don&#8217;t use</p>
<p><strong>Things to do on the server side</strong><br />
1. Setup the openx database with its own username and use a really strong password. Make sure the user is only allowed to connect from the server that runs openx (where your web server is located) &#8211; this is usually the same machine, so <code>localhost</code> will be just fine. The password should be really strong. You can use an online password generator tool, like this one: http://www.pctools.com/guides/password/</p>
<p>2. Add an extra layer of authentication, using http basic auth for example. Follow the instruction in the links below:</p>
<p>http://httpd.apache.org/docs/2.0/howto/auth.html</p>
<p>http://httpd.apache.org/docs/2.0/programs/htpasswd.html</p>
<p>3. Change the database structure of the banner&#8217;s table so it doesn&#8217;t allow append &#038; prepend codes anymore. You can do this by changing the two fields in the banners table ([table_prefix]_banners) from varchar(255) to varchar(0) (through phpmyadmin or the cli):<br />
<code>alter table openx_banners change append append varchar(0);<br />
alter table openx_banners change prepend prepend varchar(0);</code><br />
Make sure to replace &#8220;openx&#8221; with the table prefix you use. </p>
<p>4. Set the proper permissions to all files &#038; folders &#8211; all files should <strong>not</strong> be writable by the server by default, except the <code>var/</code>, <code>plugins/</code> and <code>www/images/</code> folders.<br />
Your configuration file, located in <code>var/</code>, should <strong>not</strong> be writable:<br />
<code>chmod 0444 [domain].conf.php</code></p>
<p>5. Remove all installation files</p>
<p><strong>What if I am already infected?</strong><br />
It&#8217;s important to find out all infected files or database entries and then figure out how they got there. Do not remove anything befire understanding how you got infected in the first place. You can disable the openx server temporarily (by adding this line in an .htaccess file in the folder www/delivery: <code>deny from all</code>).<br />
Once you know where the security breach is, fix it, harden the server, change passwords (just to be sure), clean up the infected data and remove the deny all line from the .htaccess file.</p>
<p>We can also help you cleaning, securing and upgrading your openx installation(s) &#8211; <a href="http://mandagreen.com/contact">contact us</a>.</p>
<img src="http://feeds.feedburner.com/~r/mandagreen/~4/od3yMf1ROeE" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://mandagreen.com/hardening-openx/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://mandagreen.com/hardening-openx/</feedburner:origLink></item>
		<item>
		<title>Installing Memcache Server for PHP on Windows</title>
		<link>http://feedproxy.google.com/~r/mandagreen/~3/HZk0m2s-_WI/</link>
		<comments>http://mandagreen.com/installing-memcached-server-php-windows/#comments</comments>
		<pubDate>Wed, 15 Feb 2012 07:00:12 +0000</pubDate>
		<dc:creator>Cristi</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[memcached]]></category>
		<category><![CDATA[performance]]></category>

		<guid isPermaLink="false">http://mandagreen.com/?p=301</guid>
		<description><![CDATA[Memcached is a free open source, high-performance, distributed memory object caching system. It is currently used by a lot of websites, including Flickr, Twitter, Youtube, Digg and WordPress. I&#8217;ve been using memcached on a few production servers, but never thought it could come in handy on a Windows development machine &#8211; in fact I didn&#8217;t [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://memcached.org/">Memcached</a> is a <q>free open source, high-performance, distributed memory object caching system</q>. It is currently used by a lot of websites, including Flickr, Twitter, Youtube, Digg and WordPress.</p>
<p>I&#8217;ve been using memcached on a few production servers, but never thought it could come in handy on a Windows development machine &#8211; in fact I didn&#8217;t even thought it was available on Windows. So, a few days ago, while working on <a href="http://mandagreen.com/services/magento/">yet another Magento project</a>, I ran a Google search for &#8220;memcache windows&#8221; and it turned out there are a few Win32 ports of the original version. Cool! It can be used on Windows, now all I need is to find the right PHP extension.</p>
<p>Here&#8217;s a step-by-step tutorial on how to get memcached running in PHP on a Windows box. There&#8217;s are a bunch of really good tutorials out there, but I think another could only be helpful.</p>
<p>1. Go to <a href="http://splinedancer.com/memcached-win32/">http://splinedancer.com/memcached-win32/</a> and download memcached. I&#8217;ve used the 09.03.2008 binaries, memcached 1.2.4.<br />
You can also use v1.2.6 that you can download from <a href="http://code.jellycan.com/memcached/">http://code.jellycan.com/memcached/</a> &#8211; I&#8217;ve used this one to update from the previous 1.2.4 and I can confirm that it works.</p>
<p>2. Unzip the downloaded file to any folder (i.e. C:/memcached). I&#8217;ve saved in the same place where I have Apache, MySQL and PHP.</p>
<p>3a. If you&#8217;re on Windows Vista, navigate to your memcached folder, right click on memcached.exe and click Properties, then click the Compatibility tab. In here, check the “Run this program as an administrator” checkbox.</p>
<p>3. Install the service by running <code>memcached.exe -d install</code> from the command line. (you should be in the folder where you unzipped the file(s), ie: c:/memcached)</p>
<p>By default, memcached has 64M available for caching and runs on port 11211. If you need to change any of these you can do it by editing the Registry &#8211; open <code>regedit</code> then search for <code>HKEY_LOCAL_MACHINE/SYSTEM/CurrentControlSet/Services/memcached Server</code>. Change the <code>ImagePath</code> entry from <code>"C:\memcached\memcached.exe" -d runservice</code> to <code>"C:\memcached\memcached.exe" -d runservice -m 256 -p 11222"</code>. This gives you 256 megs of memory and change the port to 11222 (on a development machine you probably don&#8217;t need to change the port, but who knows). <a href="http://linux.die.net/man/1/memcached">See the full list of memcached options</a> &#8211; not sure which ones apply to the Windows version too.</p>
<p>4. Now, that the service is installed and configured, let&#8217;s start it: <code>memcached.exe -d start</code><br />
To test that memcached is running you can try telnet&#8217;ing &#8211; <code>telnet localhost 11211</code> (use the port you configured the service to listen to). If you can connect on that port, everything is fine.</p>
<p>5. Memcached is installed, but we need to use in PHP, so we need an extension. Here&#8217;s a list of sites where you can download the dll from:<br />
<a href="http://downloads.php.net/pierre/">http://downloads.php.net/pierre/</a><br />
<a href="http://www.pureformsolutions.com/pureform.wordpress.com/2008/06/17/php_memcache.dll">http://www.pureformsolutions.com/pureform.wordpress.com/2008/06/17/php_memcache.dll</a><br />
<a href="http://kromann.info/download.php?strFolder=php5_1-Release_TS&amp;strIndex=PHP5_1">http://kromann.info/download.php?strFolder=php5_1-Release_TS&amp;strIndex=PHP5_1</a><br />
<a href="shikii.net/blog/downloads/php_memcache-cvs-20090703-5.3-VC6-x86.zip">shikii.net/blog/downloads/php_memcache-cvs-20090703-5.3-VC6-x86.zip</a></p>
<p>I had to install a VC6 dll, so I&#8217;ve downloaded the last one. Unzip it and put it in your php extensions folder. If you don&#8217;t know where this folder is, try this command: <code>php -i | find "extension_dir"</code>. Add the dll in there, then open php.ini (to find it, you can run <code>php -i | find "Loaded Config"</code>) and at the end add something like this:</p>
<pre class="brush: plain; title: ; notranslate">
[PHP_MEMCACHE]
extension=php_memcache.dll
</pre>
<p>Now restart Apache.</p>
<p>6. To test that memcached can now be used in PHP, use the following snippet:</p>
<pre class="brush: php; title: ; notranslate">
$memcache = new Memcache;
if( !$memcache-&gt;connect('localhost', 11211)) { //change 11211 with the port your memcached is configured to listen to
  die('Could not connect!');
}

echo '
';
print_r($memcache-&gt;getStats());
</pre>
<p>If you see an array with a bunch of data in it, you&#8217;ve done it. If not, something went wrong at some point. Make sure memcached is running, then make sure you&#8217;ve got the correct php settings and extension.</p>
<p>As I mentioned at the beginning of the post, installing memcached was something that just hit me while working on a Magento project &#8211; for those familiar with Magento, you know how slow it can be sometimes, especially when you need to constantly refresh the pages to check html/js/css changes. So, I thought memcached would help speed up the parts on which I wasn&#8217;t working on &#8211; everything except layouts and html block cache. And so it did, development seems MUCH easier now &#8211; but all about this in another post, soon to come.</p>
<p>Many thanks for helping me figure this out and (hopefully) speeding up development to:<br />
<a href="http://splinedancer.com/memcached-win32/">http://splinedancer.com/memcached-win32/</a><br />
<a href="http://pureform.wordpress.com/2008/01/10/installing-memcache-on-windows-for-php/">http://pureform.wordpress.com/2008/01/10/installing-memcache-on-windows-for-php/</a><br />
<a href="http://shikii.net/blog/installing-memcached-for-php-5-3-on-windows-7/">http://shikii.net/blog/installing-memcached-for-php-5-3-on-windows-7/</a></p>
<img src="http://feeds.feedburner.com/~r/mandagreen/~4/HZk0m2s-_WI" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://mandagreen.com/installing-memcached-server-php-windows/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://mandagreen.com/installing-memcached-server-php-windows/</feedburner:origLink></item>
		<item>
		<title>Workaround for attribute store labels not being displayed in Magento</title>
		<link>http://feedproxy.google.com/~r/mandagreen/~3/C_3XWSp6dJM/</link>
		<comments>http://mandagreen.com/workaround-attribute-store-labels-not-displayed-magento/#comments</comments>
		<pubDate>Sun, 12 Feb 2012 16:10:45 +0000</pubDate>
		<dc:creator>Cristi</dc:creator>
				<category><![CDATA[Magento]]></category>
		<category><![CDATA[PHP]]></category>

		<guid isPermaLink="false">http://mandagreen.com/?p=275</guid>
		<description><![CDATA[Here&#8217;s a quick workaround for a rather strange issue &#8211; sometimes, calling $attribute->getStoreLabel() returns null, although everything looks fine in the admin and in the database. It might be something that is slipping my mind, or it might be a bug, not really sure. However, I had to deal with it and come up with [...]]]></description>
			<content:encoded><![CDATA[<p>Here&#8217;s a quick workaround for a rather strange issue &#8211; sometimes, calling <code>$attribute->getStoreLabel()</code> returns <code>null</code>, although everything looks fine in the admin and in the database. It might be something that is slipping my mind, or it might be a bug, not really sure. However, I had to deal with it and come up with a fix, so I&#8217;d thought I&#8217;d share.</p>
<pre class="brush: php; title: ; notranslate">
$_aIds = array();
foreach( $prod-&gt;getAttributes() as $attribute ) {
	//need the attribute_id which is missing from the attribute object
	if( !isset($_aIds[$attribute-&gt;getAttributeCode()]) ) {
		$_aIds[$attribute-&gt;getAttributeCode()] = $attribute-&gt;getIdByCode('catalog_product', $attribute-&gt;getAttributeCode());
	}

	//if there's not store_label try to set it
	if( !$attribute-&gt;getStoreLabel() ) {
		$labels = $attribute-&gt;getResource()-&gt;getStoreLabelsByAttributeId( $_aIds[$attribute-&gt;getAttributeCode()] );
		if( array_key_exists(Mage::app()-&gt;getStore()-&gt;getStoreId(), $labels)) {
			$attribute-&gt;setStoreLabel( $labels[Mage::app()-&gt;getStore()-&gt;getStoreId()] );
		}
		else {
			$attribute-&gt;setStoreLabel( $attribute-&gt;getFrontendLabel() );
		}
	}
}
// end workaround
</pre>
<p><em>Applies to Magento 1.5.x</em></p>
<img src="http://feeds.feedburner.com/~r/mandagreen/~4/C_3XWSp6dJM" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://mandagreen.com/workaround-attribute-store-labels-not-displayed-magento/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://mandagreen.com/workaround-attribute-store-labels-not-displayed-magento/</feedburner:origLink></item>
		<item>
		<title>Prototype 1.6, Event.stop and IE9 – Quick patch</title>
		<link>http://feedproxy.google.com/~r/mandagreen/~3/qyyCBQlhWCc/</link>
		<comments>http://mandagreen.com/prototype-1-6-event-stop-ie9-quick-patch/#comments</comments>
		<pubDate>Wed, 30 Nov 2011 15:22:56 +0000</pubDate>
		<dc:creator>Cristi</dc:creator>
				<category><![CDATA[Javascript]]></category>
		<category><![CDATA[js]]></category>
		<category><![CDATA[patch]]></category>
		<category><![CDATA[prototype]]></category>

		<guid isPermaLink="false">http://mandagreen.com/?p=290</guid>
		<description><![CDATA[For those of you using Prototype 1.6 (or Magento prior 1.6), you might wonder why event.stop() or Event.stop(event) doesn&#8217;t work in Internet Explorer 9. Here&#8217;s a quick explanation: IE 9 makes major changes to the event system. We had to rewrite the event code in 1.7 to support it. You can either (a) upgrade to [...]]]></description>
			<content:encoded><![CDATA[<p>For those of you using Prototype 1.6 (or Magento prior 1.6), you might wonder why event.stop() or Event.stop(event) doesn&#8217;t work in Internet Explorer 9. Here&#8217;s a quick explanation:</p>
<blockquote><p>IE 9 makes major changes to the event system. We had to rewrite the<br />
event code in 1.7 to support it. You can either (a) upgrade to 1.7;<br />
(b) force your site into compatibility mode [1]. </p></blockquote>
<p>So it&#8217;s been fixed in 1.7, but what if I can&#8217;t upgrade to 1.7 and don&#8217;t want to render my website in compatibility mode? Well, after a couple of hours trying to sort this out, here&#8217;s quick (and dirty) patch for Prototype 1.6.0.3. <a href="http://mandagreen.com/downloads/prototype.diff_.zip" title="Download - ">Download Prototype 1.6.0.3 Event.stop IE9 fix (448 bytes)</a></p>
<p>Also, for further reading, here are two external links that you might find useful for this issue.</p>
<ul>
<li><a href="https://prototype.lighthouseapp.com/projects/8886-prototype/tickets/1264">https://prototype.lighthouseapp.com/projects/8886-prototype/tickets/1264</a></li>
<li><a href="http://groups.google.com/group/prototype-scriptaculous/browse_thread/thread/475eddbc3decfd23/31af63a49ac77b8a">http://groups.google.com/group/prototype-scriptaculous/browse_thread/thread/475eddbc3decfd23/31af63a49ac77b8a</a></li>
</ul>
<p><strong>Note/Disclaimer: </strong> This has only been tested on Prototype 1.6.0.3. Use it at your own risk.</p>
<img src="http://feeds.feedburner.com/~r/mandagreen/~4/qyyCBQlhWCc" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://mandagreen.com/prototype-1-6-event-stop-ie9-quick-patch/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		<feedburner:origLink>http://mandagreen.com/prototype-1-6-event-stop-ie9-quick-patch/</feedburner:origLink></item>
		<item>
		<title>Strange Prototype Class.create behavior</title>
		<link>http://feedproxy.google.com/~r/mandagreen/~3/3a4qHOMIXjY/</link>
		<comments>http://mandagreen.com/strange-prototype-class-create-behavior/#comments</comments>
		<pubDate>Tue, 01 Nov 2011 15:59:39 +0000</pubDate>
		<dc:creator>Cristi</dc:creator>
				<category><![CDATA[Javascript]]></category>
		<category><![CDATA[js]]></category>
		<category><![CDATA[prototype]]></category>

		<guid isPermaLink="false">http://mandagreen.com/?p=268</guid>
		<description><![CDATA[A few more months and it would&#8217;ve been a year since the last post. Worry not, this blog hasn&#8217;t died &#8211; and just to make sure I don&#8217;t forget what I want to write, I&#8217;ve already prepare a few articles. So stay tuned! But enough with the intro and let&#8217;s see what this article is [...]]]></description>
			<content:encoded><![CDATA[<p>A few more months and it would&#8217;ve been a year since the last post. Worry not, this blog hasn&#8217;t died &#8211; and just to make sure I don&#8217;t forget what I want to write, I&#8217;ve already prepare a few articles. So stay tuned!</p>
<p>But enough with the intro and let&#8217;s see what this article is about: Prototype (<a href="http://www.prototypejs.org/">http://www.prototypejs.org/</a>), not really my favorite javascript framework, but since Magento relies on it, I need to be able to use it and write proper code (I don&#8217;t believe in mixing frameworks either).</p>
<p>The scenario is rather simple &#8211; create a &#8220;class&#8221; called Stuff and then instantiate two objects of that class. </p>
<pre class="brush: jscript; title: ; notranslate">
var Stuff = Class.create({
	data: {},
	x: null,
	y: [],

	initialize: function() {}
});

var a = new Stuff();
var b = new Stuff();
</pre>
<p>So far so good, because the code does nothing, but let&#8217;s add the following:</p>
<pre class="brush: jscript; title: ; notranslate">
a.data.x = 1;
a.x = 5;
a.y.push(3);
</pre>
<p>What would you expect b to contain? From my (lack of) knowledge, I would say b.data is an empty object, b.x is null and b.y is an empty list/array. At least that&#8217;s how it goes in other programming languages, such as PHP. Well, this doesn&#8217;t happen here. If you do <code>console.log(b)</code> you&#8217;ll get:</p>
<pre class="brush: plain; title: ; notranslate">
data	Object { x=1}
x	null
y	[3]
</pre>
<p>In other words, object and array attributes have the same reference, or point to the same object. I would&#8217;ve said that the variables a and b are pointing to the same object, if a.x was the same as b.x, but b.x is still null, while a.x is 5. </p>
<p>Bug, feature or normal behavior, this can still be very annoying &#8211; I spent about an hour trying to figure out why two objects of the same &#8220;class&#8221; had the same settings when they were clearly configured differently. Hope this comes in handy at some point.</p>
<p>Tested on prototype v1.6.0.3 &#038; v1.6.1. </p>
<img src="http://feeds.feedburner.com/~r/mandagreen/~4/3a4qHOMIXjY" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://mandagreen.com/strange-prototype-class-create-behavior/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		<feedburner:origLink>http://mandagreen.com/strange-prototype-class-create-behavior/</feedburner:origLink></item>
		<item>
		<title>Patching the Magento USPS module</title>
		<link>http://feedproxy.google.com/~r/mandagreen/~3/1sp6uniAXXE/</link>
		<comments>http://mandagreen.com/patching-the-magento-usps-module/#comments</comments>
		<pubDate>Thu, 20 Jan 2011 10:30:52 +0000</pubDate>
		<dc:creator>Cristi</dc:creator>
				<category><![CDATA[Magento]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[patch]]></category>

		<guid isPermaLink="false">http://mandagreen.com/?p=253</guid>
		<description><![CDATA[Since their last update on Jan 3rd, 2011 USPS has stopped working on all of my Magento distro&#8217;s. The reason for that is a change in USPS&#8217; response XML values. Magento has its part of blame here, first for not patching it immediately (which again shows how much they care about the CE users) and [...]]]></description>
			<content:encoded><![CDATA[<p>Since their last update on Jan 3rd, 2011 USPS has stopped working on all of my Magento distro&#8217;s. The reason for that is a change in USPS&#8217; response XML values. Magento has its part of blame here, first for not patching it immediately (which again shows how much they care about the CE users) and second for not coding it properly in the first place &#8211; they&#8217;re using the method name (i.e. Express Mail) to see if that method is allowed from the admin, instead of using the CLASSID attribute. </p>
<p>This issue has been reported on magentocommerce.com <a href="http://www.magentocommerce.com/bug-tracking/issue?issue=10631">bug tracking system</a> and it&#8217;s also discussed in the <a href="http://www.magentocommerce.com/boards/viewthread/215963/">forum</a>. USPS makes it clear that they&#8217;ll discontinue all API versions prior RateV4 ad IntlRateV2:<br />
<em>&#8220;All Rate Calculator API integrators are encouraged to migrate to the latest API versions (RateV4, IntlRateV2):<br />
RateV4 and IntlRateV2 will be the only Rate Calculator API versions to offer the full range of new products and functionality<br />
Rate, RateV2, RateV3 and IntlRate will be retired in May 2011, requiring all integrators to migrate to the latest versions&#8221;</em></p>
<p>As explained on the forums, the fix is quite simple, as USPS only added the &amp;lt;sup&amp;gt;&amp;reg;&amp;lt;/sup&amp;gt; which is the encoded (twice <img src='http://mandagreen.com/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' />  ) version of the registered sign in a sup tag. Magento values are defined without those so when the code tries to match them it fails. All you have to do is clean that string and you&#8217;re good to go. I don&#8217;t like to modify core files so the fix involves copying <code>app/code/core/Mage/Usa/Model/Shipping/Carrier/Usps.php</code> to <code>app/code/local/Mage/Usa/Model/Shipping/Carrier/</code> and working on the local version of Usps.php. Strip the special chars:</p>
<pre class="brush: php; title: ; notranslate">
$mailService = str_replace('&amp;reg;', '', strip_tags(htmlspecialchars_decode(htmlspecialchars_decode((string)$postage-&gt;MailService))));
</pre>
<p>and </p>
<pre class="brush: php; title: ; notranslate">
$svcDescription = str_replace('&amp;reg;', '', strip_tags(htmlspecialchars_decode(htmlspecialchars_decode((string)$service-&gt;SvcDescription))));
</pre>
<p>&nbsp;</p>
<p>Here&#8217;s the archive with the patch. Just download it and unzip it in the root of your Magento distribution. <a href="http://mandagreen.com/downloads/usps-fix-mage1420.zip" title="Download - ">Download USPS patch for Magento 1.4.2.0 (8.59 kB)</a></p>
<p>&nbsp;</p>
<p><strong>Note:</strong> This patch applies to Magento CE 1.4.2.0. For all other versions you will have to do it manually as explained above.</p>
<p>&nbsp;</p>
<img src="http://feeds.feedburner.com/~r/mandagreen/~4/1sp6uniAXXE" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://mandagreen.com/patching-the-magento-usps-module/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
		<feedburner:origLink>http://mandagreen.com/patching-the-magento-usps-module/</feedburner:origLink></item>
		<item>
		<title>Anatomy of a Magento extension</title>
		<link>http://feedproxy.google.com/~r/mandagreen/~3/1EoifWh5hMM/</link>
		<comments>http://mandagreen.com/anatomy-of-a-magento-extension/#comments</comments>
		<pubDate>Wed, 05 Jan 2011 12:35:54 +0000</pubDate>
		<dc:creator>Cristi</dc:creator>
				<category><![CDATA[Magento]]></category>
		<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://mandagreen.com/?p=229</guid>
		<description><![CDATA[Magento Commerce has been on the OSS &#8220;market&#8221; for a while now and I see more and more developers, designers and of course store owners migrating their ecommerce sites to Magento or installing it for their new stores. I&#8217;ve been coding on Magento since its beta 0.6 version, actually my first integration was based on [...]]]></description>
			<content:encoded><![CDATA[<p>Magento Commerce has been on the OSS &#8220;market&#8221; for a while now and I see more and more developers, designers and of course store owners migrating their ecommerce sites to Magento or installing it for their new stores. I&#8217;ve been coding on Magento since its beta 0.6 version, actually my first integration was based on this beta. I&#8217;ve been advised not to use the beta and they were right. However, I had my reasons for recommending it, using it and somehow advocating for that fresh, promising open source piece of software. In the meanwhile, it turned out I was right &#8211; it&#8217;s one of the most complete, scalable &#038; feature-rich free ecommerce solution. Not the easiest to integrate or use, not the smoothest or lightweight, but it&#8217;s come a long way and it still have room for improvements. </p>
<p>One of the things I liked from the start (as a developer) was the ability to extend it in a very simple and loosely coupled way (unlike other solutions). Adding the OOP and design patterns made it perfect for my way of thinking. And I think this is the way to go for any major application, even if it might seem cluttered or with a steep learning curve at a first glance. I remember one of the first problems I faced was module creation &#8211; I was adding all my code in the core/Mage folder, I was overwriting a lot of the core code although I knew it was bad and so on. However, once I understood how to create a module, things became clearer and much simpler. So here&#8217;s a beginner&#8217;s guide to creating a new module. Although it&#8217;s meant for beginners, you should know the basic folder layout (skins, library, js, apps).<br />
<Br /></p>
<p>All Magento code resides in <code>app/code</code> and the <code>lib</code> folder. If you check <code>app/code</code> you&#8217;ll notice three subfolders: <code>community</code>, <code>core</code>, <code>local</code>. You should be adding your stuff in local or community (usually only when it is a community contribution). So let&#8217;s create our first module. I&#8217;ll use the namespace mandagreen and the module will be called HelloWorld. For this I need to create the following folders under <code>app/code/local</code>:<br />
<strong>Mandagreen/HelloWorld</strong></p>
<p>To keep things simple, in this example we&#8217;ll be creating one Block, one Helper and one Model &#8211; no controllers &#038; no Resource Models. Here&#8217;s where each of these will reside:<br />
blocks &#8211; <code>app/code/local/Mandagreen/HelloWorld/Block</code><br />
helpers &#8211; <code>app/code/local/Mandagreen/HelloWorld/Helper</code><br />
models &#8211; <code>app/code/local/Mandagreen/HelloWorld/Model</code></p>
<p>Create these three folders and then also create a new <code>etc</code> folder. The structure will then look like this:</p>
<pre class="brush: plain; title: ; notranslate">
    app/code/local/Mandagreen/HelloWorld/Block
    app/code/local/Mandagreen/HelloWorld/etc
    app/code/local/Mandagreen/HelloWorld/Helper
    app/code/local/Mandagreen/HelloWorld/Model
</pre>
<p>The <code>etc</code> folder holds configuration information &#8211; in this case we&#8217;ll focus only on the module configuration file, which is <code>config.xml</code>. Let&#8217;s create it and start adding some xml into it:</p>
<pre class="brush: xml; title: ; notranslate">
&lt;?xml version=&quot;1.0&quot;?&gt;
&lt;config&gt;
	&lt;modules&gt;
		&lt;Mandagreen_HelloWorld&gt;
			&lt;version&gt;0.0.1&lt;/version&gt;
		&lt;/Mandagreen_HelloWorld&gt;
	&lt;/modules&gt;

	&lt;global&gt;
		&lt;models&gt;
			&lt;helloworld&gt;
				&lt;class&gt;Mandagreen_HelloWorld_Model&lt;/class&gt;
			&lt;/helloworld&gt;
		&lt;/models&gt;

		&lt;blocks&gt;
			&lt;helloworld&gt;
				&lt;class&gt;Mandagreen_HelloWorld_Block&lt;/class&gt;
			&lt;/helloworld&gt;
		&lt;/blocks&gt;

		&lt;helpers&gt;
			&lt;helloworld&gt;
				&lt;class&gt;Mandagreen_HelloWorld_Helper&lt;/class&gt;
			&lt;/helloworld&gt;
		&lt;/helpers&gt;
	&lt;/global&gt;

	&lt;frontend&gt;
		&lt;layout&gt;
			&lt;updates&gt;
				&lt;helloworld&gt;
					&lt;file&gt;mg_helloworld.xml&lt;/file&gt;
				&lt;/helloworld&gt;
			&lt;/updates&gt;
		&lt;/layout&gt;

		&lt;translate&gt;
			&lt;modules&gt;
				&lt;Mandagreen_HelloWorld&gt;
					&lt;files&gt;
						&lt;default&gt;Mandagreen_HelloWorld.csv&lt;/default&gt;
					&lt;/files&gt;
				&lt;/Mandagreen_HelloWorld&gt;
			&lt;/modules&gt;
		&lt;/translate&gt;
	&lt;/frontend&gt;
&lt;/config&gt;
</pre>
<p>That&#8217;s a lot, isn&#8217;t it? <img src='http://mandagreen.com/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' />  No worries, I&#8217;ll explain. Under the main node (<code>config</code>), youll usually need the global node. The <code>modules</code> node is being used to hold mode info about the current module, maybe dependencies and so on. The <code>frontend</code> node is usually used for defining layout handlers and translation files, but they can do more than that. In this example, we&#8217;re gonna have our own layout file called mg_helloworld.xml &#8211; you can use any name on it (as long as it&#8217;s unique), but I prefer to namespace it as well. Also, I&#8217;ve defined a translation file <code>Mandagreen_HelloWorld.csv</code> which needs to be created in <code>app/locale/en_US</code> (or whatever your default locale is). </p>
<p>The <code>global</code> node defines all the classes you&#8217;ll be using, in this case helpers, models and blocks. You can see that they are defined the same way, but in different locations: &lt;helpers /&gt;, &lt;models /&gt; and &lt;blocks /&gt;. Let&#8217;s use the helpers node as an example.</p>
<pre class="brush: xml; title: ; notranslate">
		&lt;helpers&gt;
			&lt;helloworld&gt;
				&lt;class&gt;Mandagreen_HelloWorld_Helper&lt;/class&gt;
			&lt;/helloworld&gt;
		&lt;/helpers&gt;
</pre>
<p>The <code>helloworld</code> node defines a handle and you should be careful to always use unique names. If you&#8217;re not sure if a certain name exist, namespace it (mghelloworld for example). This handle will be used to instantiate models, call helpers or use blocks using the Magento factory. For example, for models, you&#8217;ll be using <code>Mage::getModel('helloworld/modelname');</code> instead of <code>new Mandagreen_HelloWorld_Model_Modelname();</code>. Same with helpers, inside a block template &#8211; <code>$this->helper('helloworld')->someMethod();</code>.<br />
The other node, <code>class</code> is being used to define the namespace of the class name. In the example above, the name of the Modelname class has to being with <code>Mandagreen_HelloWorld_Helper</code>. A few examples might help:</p>
<ul>
<li><code>Mandagreen_HelloWorld_Model_Salute</code>, will be found in <code>Mandagreen/HelloWorld/Model/Salute.php</code> and can be accessed via <code>Mage::getModel('helloworld/salute')</code></li>
<li><code>Mandagreen_HelloWorld_Model_Salute_Hi</code>, will be found in <code>Mandagreen/HelloWorld/Model/Salute/Hi.php</code> and can be accessed via <code>Mage::getModel('helloworld/salute_hi')</code></li>
<li><code>Mandagreen_HelloWorld_Helper_Help</code>, will be found in <code>Mandagreen/HelloWorld/Model/Help.php</code> and can be accessed via <code>Mage::helper('helloworld/help')</code></li>
</ul>
<p>Now that we explained the config file, let&#8217;s move on to actually creating the <strong>classes </strong>and code. I&#8217;ll use the example above and create the following files:</p>
<p><strong>Mandagreen/HelloWorld/Block/Standard.php</strong></p>
<pre class="brush: php; title: ; notranslate">
&lt;?php

class Mandagreen_HelloWorld_Block_Standard extends Mage_Core_Block_Template {
    function getSomething() {
          return Mage::getModel('helloworld/salute')-&gt;getName();
    }
}
</pre>
<p><strong>Mandagreen/HelloWorld/Helper/Help.php</strong></p>
<pre class="brush: php; title: ; notranslate">
&lt;?php

class Mandagreen_HelloWorld_Helper_Help extends Mage_Core_Helper_Abstract {
    function shouldSayHi() {
          return true;
    }
}
</pre>
<p><strong>Mandagreen/HelloWorld/Model/Salute.php</strong></p>
<pre class="brush: php; title: ; notranslate">
&lt;?php

class Mandagreen_HelloWorld_Model_Salute extends Mage_Core_Model_Abstract { //or Varien_Object or none
    function getName() {
          //do some heavy logic here
          return 'John';
    }
}
</pre>
<p>One more file is required for the translations to work with this module &#8211; a &#8220;default&#8221; Data helper, defined in <strong>Mandagreen/HelloWorld/Helper/Data.php</strong> like this:</p>
<pre class="brush: php; title: ; notranslate">
class Mandagreen_HelloWorld_Helper_Data extends Mage_Core_Helper_Abstract {}
</pre>
<p>We have the classes but we also need to use them in a template, so let&#8217;s create <code>standard.phtml</code> in <code>app/design/frontend/default/default/template/helloworld</code> (create all additional folders if needed). Also, create <code>mg_helloworld.xml</code> under <code>app/design/frontend/default/default/layout</code>.</p>
<p>For the template, things are very easy:</p>
<pre class="brush: php; title: ; notranslate">
&lt;div style=&quot;background: red; padding: 20px;&quot;&gt;
	&lt;?php if( $this-&gt;helper('helloworld/help')-&gt;shouldSayHi() ): ?&gt;
	Hello &lt;?php echo $this-&gt;getSomething(); ?&gt;
	&lt;?php else: ?&gt;
	Can't say anything...
	&lt;?php endif; ?&gt;
&lt;/div&gt;
</pre>
<p>while for the layout we&#8217;ll use this approach:</p>
<pre class="brush: xml; title: ; notranslate">
&lt;?xml version=&quot;1.0&quot;?&gt;
&lt;layout version=&quot;0.1.0&quot;&gt;
    &lt;default&gt;
        &lt;reference name=&quot;content&quot;&gt;
            &lt;block type=&quot;helloworld/standard&quot; name=&quot;helloworld&quot; template=&quot;helloworld/standard.phtml&quot; after=&quot;-&quot; /&gt;
        &lt;/reference&gt;
    &lt;/default&gt;
&lt;/layout&gt;
</pre>
<p>This should display &#8220;Hello John&#8221; at the end of the content area on most of the pages, including the homepage. But wait, before you try that you&#8217;ll have to enable the module in <code>app/etc/modules</code>. Create a file called <code>Mandagreen_HelloWorld.xml</code> with the following code:</p>
<pre class="brush: xml; title: ; notranslate">
&lt;?xml version=&quot;1.0&quot;?&gt;
&lt;config&gt;
    &lt;modules&gt;
        &lt;Mandagreen_HelloWorld&gt;
            &lt;active&gt;true&lt;/active&gt;
            &lt;codePool&gt;local&lt;/codePool&gt;
        &lt;/Mandagreen_HelloWorld&gt;
    &lt;/modules&gt;
&lt;/config&gt;
</pre>
<p>Clear all magento cache and refresh the page &#8211; your first module, up and running! And here&#8217;s the archive for this tutorial &#8211; <a href="http://mandagreen.com/downloads/helloworld.zip" title="Download - ">Download Hello World Magento Module (2.99 kB)</a></p>
<p>Further/recommended reads:</p>
<ul>
<li><a href="http://www.php.net/manual/en/language.namespaces.rationale.php">Namespaces Rationale</a></li>
<li><a href="http://www.magentocommerce.com/wiki/5_-_modules_and_development/0_-_module_development_in_magento/custom_module_with_custom_database_table">Custom Module with Custom Database Table</a></li>
<li><a href="http://unirgy.com/wiki/uscaffold">uScaffold extension by Unirgy</a></li>
</ul>
<img src="http://feeds.feedburner.com/~r/mandagreen/~4/1EoifWh5hMM" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://mandagreen.com/anatomy-of-a-magento-extension/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
		<feedburner:origLink>http://mandagreen.com/anatomy-of-a-magento-extension/</feedburner:origLink></item>
		<item>
		<title>Adding Customer Comments as Order Status Comments using Magento &amp; OneStepCheckout</title>
		<link>http://feedproxy.google.com/~r/mandagreen/~3/R3_-5HMWByU/</link>
		<comments>http://mandagreen.com/customer-comments-as-order-status-comments-magento-onestepcheckout/#comments</comments>
		<pubDate>Sun, 24 Oct 2010 10:08:13 +0000</pubDate>
		<dc:creator>Cristi</dc:creator>
				<category><![CDATA[Magento]]></category>
		<category><![CDATA[onestepcheckout]]></category>

		<guid isPermaLink="false">http://mandagreen.com/?p=218</guid>
		<description><![CDATA[A few days ago I got an email from someone asking me about adding the default customer comments in OneStepCheckout (<a href="http://www.onestepcheckout.com/">www.onestepcheckout.com</a>) as regular order comments. I thought I'd share this quick &#038; simple hack with everyone, so here's what you have to d]]></description>
			<content:encoded><![CDATA[<p>A few days ago I got an email from someone asking me about adding the default customer comments in OneStepCheckout (<a href="http://www.onestepcheckout.com/">www.onestepcheckout.com</a>) as regular order comments. I thought I&#8217;d share this quick &#038; simple hack with everyone, so here&#8217;s what you have to do:<br />
Open app/code/local/Idev/OneStepCheckout/Helper/Data.php, and after </p>
<pre class="brush: php; title: ; notranslate">$observer-&gt;getEvent()-&gt;getOrder()-&gt;setOnestepcheckoutCustomercomment($orderComment);</pre>
<p>add this line:<br />
<del>$observer->getEvent()->getOrder()->setState( Mage_Sales_Model_Order::STATE_NEW, true, $orderComment, false );</del></p>
<pre class="brush: php; title: ; notranslate">$observer-&gt;getEvent()-&gt;getOrder()-&gt;setState(
    $observer-&gt;getEvent()-&gt;getOrder()-&gt;getStatus(),
    true,
    $orderComment,
    false
);</pre>
<p>This will add the comments on the regular comments/statuses thread, as well as a customer comment. If you want to disable customer comments, just comment the original code. </p>
<p>Actually, this approach should also work with the out-of-the-box Magento one step checkout. Add an input on the last step, create a listener for
<pre class="brush: plain; title: ; notranslate">checkout_type_onepage_save_order</pre>
<p> and use the same piece of code:</p>
<pre class="brush: php; title: ; notranslate">$observer-&gt;getEvent()-&gt;getOrder()-&gt;setState( $observer-&gt;getEvent()-&gt;getOrder()-&gt;getStatus(), true, $this-&gt;_getRequest()-&gt;getPost('order_comments'), false );</pre>
<p><ins><strong>Note: </strong>Tested on Magento 1.4.1.0 &#038; 1.4.1.1</ins></p>
<img src="http://feeds.feedburner.com/~r/mandagreen/~4/R3_-5HMWByU" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://mandagreen.com/customer-comments-as-order-status-comments-magento-onestepcheckout/feed/</wfw:commentRss>
		<slash:comments>8</slash:comments>
		<feedburner:origLink>http://mandagreen.com/customer-comments-as-order-status-comments-magento-onestepcheckout/</feedburner:origLink></item>
		<item>
		<title>Introducing Magento CSS &amp; JS Minifier</title>
		<link>http://feedproxy.google.com/~r/mandagreen/~3/vjkiIG0abDg/</link>
		<comments>http://mandagreen.com/introducing-magento-css-js-minifier/#comments</comments>
		<pubDate>Wed, 15 Sep 2010 11:24:28 +0000</pubDate>
		<dc:creator>Cristi</dc:creator>
				<category><![CDATA[Magento]]></category>
		<category><![CDATA[CSS]]></category>
		<category><![CDATA[js]]></category>
		<category><![CDATA[performance]]></category>

		<guid isPermaLink="false">http://mandagreen.com/?p=196</guid>
		<description><![CDATA[As the title says, I&#8217;m glad to announce my first public Magento extension (not yet added in the Connect repository). During my 3 years experience with Magento, I&#8217;ve worked on a lot of custom extensions, improvements &#38; fixes, but most of them were client-specific, plus they weren&#8217;t designed to have a backend interface (with a [...]]]></description>
			<content:encoded><![CDATA[<p>As the title says, I&#8217;m glad to announce my first public Magento extension (not yet added in the Connect repository). During my 3 years experience with Magento, I&#8217;ve worked on a lot of custom extensions, improvements &amp; fixes, but most of them were client-specific, plus they weren&#8217;t designed to have a backend interface (with a few exceptions). This one, however, is entirely configurable from the Admin and it&#8217;s both simple and effective.</p>
<p>&nbsp;</p>
<h2>What it does</h2>
<p>This quick optimizer parses all javascript &amp; css files included on a page and removes all unnecessary characters. The most simple step is to remove spaces, tabs and new lines &#8211; but there&#8217;s more than just that. Of course, for small files compression is insignificant, but when you work with almost 600KB and around 30 requests, you can save a lot. Here&#8217;s a quick math on one of my Magento installs:<br />
<a class="thickbox" href="http://mandagreen.com/download/minifier.png"><img class="alignright size-thumbnail wp-image-199" style="border: 1px solid #d0d0d0; margin-top: 5px;" title="minifier" src="http://mandagreen.com/download/minifier-150x150.png" alt="" width="150" height="150" /></a></p>
<p><strong>Javascript</strong> &#8211; 26 requests, 479 KB<br />
<strong>CSS</strong> &#8211; 4 requests, 102 KB</p>
<p><strong>With Magento&#8217;s default merging enabled:</strong><br />
<strong>Javascript</strong> &#8211; 1 request, 360 KB (not sure why this is smaller then the 26 summed up, but nvm)<br />
<strong>CSS</strong> &#8211; 1 request, 108.2 KB (same for this one too, but again, nvm)</p>
<p>We&#8217;ve already saved 28 requests, which means less overhead &#8211; quicker download times for user, less stress on the server.</p>
<p><strong>With the Minifier enabled:</strong><br />
<strong>Javascript</strong> is 255 KB, which means almost 47% compression<br />
<strong>CSS</strong> is 92 KB, which means almost 10% compression</p>
<p>&nbsp;</p>
<h2>Advantages</h2>
<ul>
<li>Faster loading times</li>
<li>Less stress on the server and on bandwith</li>
<li>Improving <a href="http://code.google.com/speed/page-speed/" target="_blank">PageSpeed</a> score with 4-8 points</li>
<li>Checking 4 to-do&#8217;s from the <a href="http://code.google.com/speed/page-speed/docs/rules_intro.html" target="_blank">Web Performance Best Practices Guide</a></li>
</ul>
<p>&nbsp;</p>
<h2>Disatvantages</h2>
<ul>
<li>Need to write javascripts very careful, adding a semicolon after almost everything</li>
<li>Have to rewrite of Mage_Core_Model file</li>
<li>Have to override two Magento methods</li>
<li>Additional processing time (insignificant in my opinion)</li>
</ul>
<p>&nbsp;</p>
<h2>Credits</h2>
<p>This plugin wouldn&#8217;t exist if it weren&#8217;t for these two outstanding PHP projects:<br />
Joe Scylla&#8217;s <a href="http://code.google.com/p/cssmin/" target="_blank">CssMin</a><br />
Ryan Grove&#8217;s <a href="http://wonko.com/post/a_faster_jsmin_library_for_php" target="_blank">JsMin</a><br />
Big thanks to both of them.</p>
<p>&nbsp;</p>
<h2>Download &amp; Install</h2>
<p><a class="thickbox" href="http://mandagreen.com/download/minifier-screenshot.png"><img class="alignright size-thumbnail wp-image-206" style="border: 1px solid #d0d0d0; margin-top: 5px;" title="minifier-screenshot" src="http://mandagreen.com/download/minifier-screenshot-150x150.png" alt="" width="150" height="150" /></a><br />
First, <a href="http://mandagreen.com/downloads/oxygen_minifier.zip" title="Download - Magento Minifier takes the merged javascript and css files and minifies them, removing white spaces, new lines and other chars, resulting in a smaller file. Compression is usually around 20% for javascripts and 15-25% for css.">Download Minifier for Magento (10.54 kB)</a>, then unzip it and copy the app/ folder to your Magento root folder.</p>
<p>Logout from the admin if you&#8217;re already logged in, then login. Go to Cache Management, click on the &#8220;Fulsh cache storage&#8221; button, then go to Configuration &gt; Developer and enable all the options, as shown in the attached screenshot. Go back to Cache Management and this time click on the &#8220;Flush Javascript/CSS cache&#8221;.</p>
<p>Go to your store frontend and behold, you&#8217;re now using compressed js&#8217;s and css&#8217;s.</p>
<p>Works on Magento 1.4+</p>
<p style="clear: both;">
<img src="http://feeds.feedburner.com/~r/mandagreen/~4/vjkiIG0abDg" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://mandagreen.com/introducing-magento-css-js-minifier/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		<feedburner:origLink>http://mandagreen.com/introducing-magento-css-js-minifier/</feedburner:origLink></item>
	</channel>
</rss>

