AIAIO: Our Blog

AIAIO: Our Blog

The pulse of Alexander Interactive

Archive for the ‘Technology’ Category

Extending a Magento Controller

We’re ajaxing part of the Magento shopping cart so we need to modify/extend some of the cart controller functionality. Sometimes when modifying controller’s you have to worry about updating the routes. For this, we don’t need to, we still want all the urls to be used the same way.

app/code/local/Ai/Checkout/etc/config.xml:

<config>
    <modules>
        <Ai_Checkout>
             <version>0.0.1</version>
        </Ai_Checkout>
    </modules>
...
    <frontend>
        <routers>
            <checkout>
                <use>standard</use>
                <args>
                    <module>Ai_Checkout</module>
                    <frontName>checkout</frontName>
                </args>
            </checkout>
        </routers>
    </frontend>
</config>

app/code/local/Ai/Checkout/controllers/CartController.php:

require_once Mage::getModuleDir('controllers', 'Mage_Checkout') . DS . 'CartController.php';

class Ai_Checkout_CartController extends Mage_Checkout_CartController
{
   public function updatePostAction()
    {
    	Mage::log("NEW CONTROLLER", null, 'tim.log');
        try {
Technology

Moving Gmail Gadgets to the Right Side

I started using Remember the Milk recently but didn’t want the gmail gadget to be so far down on the left hand side of my screen. There is no built in way to move gadgets to the right hand side with the exception of chat (labels used to do this but was removed in favor of drag in drop back in late 2009).

If you don’t have anything in the right hand column, enable Right-Side Chat from Gmail Labs. We are going to add in some custom css to gmail so install either Stylist for Chrome or Stylish for Firefox.

Add the following style:

div.TZ:nth-child(8) {
    position:absolute !important;
    right:0px;
    top:165px;
    width:164px;
}

In chrome you can also restrict the domain to mail.google.com. For me, the Remember the Milk gadget was the 8th child. Play with this until it looks right for you. You may also have to play with the “top” element depending on how much room your chat gadget takes up

Technology

Chrome’s Autofill and honeypot fields

Ai developers typically add a hidden “honeypot” field to forms to avoid spam bots. Spam bots usually fill out all of the fields on a form, including the honeypot field. When the honeypot field is filled out and the form is submitted, the form information is not captured.

While doing some cross-browser form testing for several sites I noticed that in the Chrome browser form information wasn’t always being captured.

Upon further investigation I learned that Chrome’s Autofill feature is causing this problem. Chrome’s Autofill fills out the honeypot field and makes the form think that it has been filled out by a spam bot. Since this may result in a significant amount of legitimate form submissions not reaching their intended target, it is important to test all forms with Chrome’s Autofill in mind. Developers need to provide a solution that successfully curbs malicious spam bots without preventing legitimate form submissions.

Technology

Want to trace the call stack in Magento?

This has helped me immensely in situations like “Where is this getting called from??!?”

Create a helper like so:

class Timbroder_Stack_Helper_Callstack extends Mage_Core_Helper_Abstract
{
	private function get_callstack($delim="\n") {
	  $dt = debug_backtrace();
	  $cs = '';
	  foreach ($dt as $t) {
	    $cs .= $t['file'] . ' line ' . $t['line'] . ' calls ' . $t['function'] . "()" . $delim;
	  }

	  return $cs;
	}

	public function toLog() {
		Mage::log($this->get_callstack());
	}

	public function toFirePhp() {
		$stack = $this->get_callstack();
		foreach (explode("\n", $stack) as $line) {
			Mage::helper('firephp')->send($line);
		}
	}
}

That can be called from anywhere:

Mage::helper('stack/callstack')->toFirePhp();
Mage::helper('stack/callstack')->toLog();

I’ve also wrapped this into a module that you can drop right into your project. Details here

Example output:

.../app/code/community/Timbroder/Stack/Helper/Callstack.php line 16 calls get_callstack()
.../app/design/frontend/mongoose/default/template/catalog/cms/bikes_bmx.phtml line 12 calls toLog()
.../app/design/frontend/mongoose/default/template/catalog/cms/bikes.phtml line 21 calls require_once()
.../app/code/core/Mage/Core/Block/Template.php line 212 calls include()
.../app/code/core/Mage/Core/Block/Template.php line 239 calls fetchView()
.../app/code/core/Mage/Core/Block/Template.php line 253 calls renderView()
.../app/code/core/Mage/Core/Block/Abstract.php line 668 calls _toHtml()
.../app/code/core/Mage/Core/Model/Email/Template/Filter.php line 190 calls toHtml()
.../lib/Varien/Filter/Template.php line 134 calls call_user_func()
.../app/code/core/Mage/Core/Model/Email/Template/Filter.php line 501 calls filter()
.../app/code/core/Mage/Cms/Block/Page.php line 100 calls filter()
.../app/code/core/Mage/Core/Block/Abstract.php line 668 calls _toHtml()
.../app/code/core/Mage/Core/Block/Abstract.php line 513 calls toHtml()
.../app/code/core/Mage/Core/Block/Abstract.php line 460 calls _getChildHtml()
.../app/code/local/Mage/Page/Block/Html/Wrapper.php line 52 calls getChildHtml()
.../app/code/core/Mage/Core/Block/Abstract.php line 668 calls _toHtml()
.../app/code/core/Mage/Core/Block/Text/List.php line 43 calls toHtml()
.../app/code/core/Mage/Core/Block/Abstract.php line 668 calls _toHtml()
.../app/code/core/Mage/Core/Block/Abstract.php line 513 calls toHtml()
.../app/code/core/Mage/Core/Block/Abstract.php line 464 calls _getChildHtml()
.../app/design/frontend/mongoose/default/template/page/1column.phtml line 55 calls getChildHtml()
.../app/code/core/Mage/Core/Block/Template.php line 212 calls include()
.../app/code/core/Mage/Core/Block/Template.php line 239 calls fetchView()
.../app/code/core/Mage/Core/Block/Template.php line 253 calls renderView()
.../app/code/core/Mage/Core/Block/Abstract.php line 668 calls _toHtml()
.../app/code/core/Mage/Core/Model/Layout.php line 529 calls toHtml()
.../app/code/local/Mage/Core/Controller/Varien/Action.php line 389 calls getOutput()
.../app/code/core/Mage/Cms/Helper/Page.php line 130 calls renderLayout()
.../app/code/core/Mage/Cms/Helper/Page.php line 52 calls _renderPage()
.../app/code/core/Mage/Cms/controllers/PageController.php line 45 calls renderPage()
.../app/code/local/Mage/Core/Controller/Varien/Action.php line 418 calls viewAction()
.../app/code/core/Mage/Core/Controller/Varien/Router/Standard.php line 254 calls dispatch()
.../app/code/core/Mage/Core/Controller/Varien/Front.php line 177 calls match()
.../app/code/core/Mage/Core/Model/App.php line 304 calls dispatch()
.../app/Mage.php line 598 calls run()
.../index.php line 155 calls run()

Thanks to nextide for some of the code

Technology

Chromium + instant = predictive page loading

We’ve been recently sending in our predictions for the next 15 years as a follow up to yesterday’s blog post by Alex.  I was going to send in “Google will predict the page you want to go to and automatically load it”  However, to my surprise, since I had enabled --enable-match-preview as a parameter of my Chromium startup, I started experiencing just that.  When I type in “j” it automatically loads Jira in the background (see image).  I didn’t tell it to, it just figured it out based on my history.  With Google instant saving 2-3 seconds per search and Chromium bypassing search completely, we’ll have more time to make awesome websites!

Technology

Using widgets outside of the CMS in Magento

Magento ships with widget functionality that lets you build out data models and then reuse them on product and CMS pages. If you want to use these in a custom template however, you are out of luck. This can be done by extending the Widget Collection class.

Create the following directory structure: app/code/local/Mage/Widget/Model/Myswql4/Widget/Instance

Copy app/code/core/Mage/Widget/Model/Myswql4/Widget/Instance/Collection.php into your new directory

The Mage_Widget_Model_Mysql4_Widget_Instance_Collection comes with a store filter but thats about it. To be more usefull we are going to add a type filter, a title filter, and a sorter.

class Mage_Widget_Model_Mysql4_Widget_Instance_Collection extends Mage_Core_Model_Mysql4_Collection_Abstract
{
    /**
     * Constructor
     */
    protected function _construct()
    {
        parent::_construct();
        $this->_init('widget/widget_instance');
    }

    /**
     * Filter by store ids
     *
     * @param array|integer $storeIds
     * @param boolean $withDefaultStore if TRUE also filter by store id '0'
     * @return Mage_Widget_Model_Mysql4_Widget_Instance_Collection
     */
    public function addStoreFilter($storeIds = array(), $withDefaultStore = true)
    {
        if (!is_array($storeIds)) {
            $storeIds = array($storeIds);
        }
        if ($withDefaultStore && !in_array(0, $storeIds)) {
            array_unshift($storeIds, 0);
        }
        $select = $this->getSelect();
        foreach ($storeIds as $storeId) {
            $select->orWhere('FIND_IN_SET(?, `store_ids`)', $storeId);
        }
        return $this;
    }

    public function addTypeFilter($type) {
    	$this->getSelect()->where('type=?', $type);
    	return $this;
    }

    public function addTitleFilter($type) {
    	$this->getSelect()->where('title=?', $type);
    	return $this;
    }

    public function addAttributeToSort($attribute, $dir='asc') {
    	$this->getSelect()->order("{$attribute} {$dir}");
    	return $this;
    }
}

Now we should be able to query any widgets from any template in our system:

< ?php
$wids = Mage::getModel('widget/widget_instance')
	->getCollection()
	->addTypeFilter('masswidget/list')
	->addAttributeToSort('title', 'asc')
	->load();

foreach ($wids as $wid):
	$params = $wid->getWidgetParameters();
	echo $params['custom_param'];
	echo $wid->gettitle();
endforeach;
?>
Technology

How to fix your gadgets

Ever feel like you’re doing the same silly things to fix things the same things over and over? Ever read forums where 10 people enthusiasticly post the same solution that you’re already trying to find an alternative for? Well, no need to post on forums any more, your answers are all here.

How to fix:

  • Windows XP - reinstall
  • Macs – delete preferences, fix disk permissions (really?)
  • Windows Vista – install Windows 7 (really!)
  • Linux – you’re already capable of helping yourself. Move along.
  • iphone – you’re holding it wrong
  • blackberry – delete service books, wipe data
  • android – root your phone, install a community ROM, then complain to Cyanogen

Credit:You’re holding it wrong“  courtesy of Engadget, my favorite blog.

Funny Stuff

Improving the Performance of a Local Magento Install

Magento is great, but it needs a good amount of hardware behind it.  Developing locally can get slow and cumbersome unless your environment is tweaked properly.  Here are a few tips for boosting Magento performance without impacting the rest of your development environment.  Please keep in mind that the memory allocations work well for my machine (dual core, 4 gigs of ram).

Database
Install innoDB.  Magento can use the in-memory buffer pool to cache table indexes and data.

Configure my.ini:
innodb_buffer_pool_size  = 64M
innodb_thread_concurrency = 4 (or 8 if you have dual core)
query_cache_size = 64M
query_cache_limit  = 2M

apache
enable mod_expires in httpd.conf

php
in php.ini enable:
realpath_cache_size = 16k
realpath_cache_ttl = 120

Install the eAccelerator binaries for php.  APC is a better solution but is less compatible with windows.  If you need to compile these, click here for instructions. Then configure it:
extension=eaccelerator.dll
eaccelerator.shm_size=64
eaccelerator.cache_dir=C:\PHP\tmp
eaccelerator.enable=1
eaccelerator.optimizer=1
eaccelerator.check_mtime=1
eaccelerator.shm_max=0

Install memcached.
add the following lines inside the config of epp/etc/local.xml
<cache>
    <backend>memcached</backend>
    <memcached>
        <servers>
            <server>
                <host><![CDATA[localhost]]></host>
                <port><![CDATA[11211]]></port>
                <persistent><![CDATA[1]]></persistent>
            </server>
        </servers>
        <compression><![CDATA[0]]></compression>
        <cache_dir><![CDATA[]]></cache_dir>
        <hashed_directory_level><![CDATA[]]></hashed_directory_level>
        <hashed_directory_umask><![CDATA[]]></hashed_directory_umask>
        <file_name_prefix><![CDATA[]]></file_name_prefix>
    </memcached>
</cache>

Admin Backend

  • Keep the indexes up to date (System > index management)
  • Compile Mage classes (System > tools > Complilation)
  • Enable all cachine (System > Cache Management)
Technology

Optimizing for speed

Interesting point from Scott Porad on optimizing page load times. Hint: it’s not about the images; it’s about the http calls.

In other words, the frequency of visits as a factor in reducing empty cache visits is counteracted by the frequency that a site’s content is updated. Of course, this makes sense because unless a site updates it’s content frequently users don’t have a reason to return frequently.

The bottom line: reducing HTTP requests continues to be most important for improving site performance.

Scott found a research report that notes 20% of users have no cache, making local caching a moot point. (I know this first-hand; on my creaky old Windows box at Clarins, I set my own cache to zero, because it minimized the internal RAM and hard drive needs and sped up page rendering.) More important, especially in this era of Ajax, is to minimize server requests, which create the bottlenecks.

Considering Google’s new inclusion of site speed in PageRank this is going to be a key performance metric in 2010, and one to monitor regularly.

Technology

HTML5, round 4: Other tags, accessibility

While HTML5 has a nice amount of new tags for semantic markup as well as media-based tags such as <video>, <audio>, or the JavaScript-based <canvas> tag, there are a handful of other new elements that are still worthy of mention:

  • <command> is used to define a button, such as a radio button, or check box. It must be used inside of a <menu> element.
  • <article> is used to define an article from an external source – news site, blog, forum, etc.
  • <datalist> defines a list of options, such as a drop-down menu. This tag is used in conjunction with the <input> tag.
  • <dialog> is used to define a dialog or conversation.
  • <embed> defines external content or plugins. This element has been used in previous HTML versions but is now re-introduced as an HTML5 standard.
  • <figure> is used to represent flow content, separate from the primary content of a document. Additionally, the <figcaption> element is used within <figure> in place of the older <legend> element.
  • <meter> is used to define a scalar measurement within a known range. For example, think of this as a representation of a “2 out of 10″ score.
  • <time> represents time on a 24-hour clock, or a date in the proleptic Gregorian calendar.
  • <source> is used in conjunction with the new media elements, <audio> and <video>, to define multiple source files.
  • <ruby> defines a ruby annotation – Chinese notes or characters. <rt> is a child of <ruby> and is used to define the explanation of the ruby annotation. <rp> is used for browsers that do not support ruby annotations.
  • <mark> represents a run of text in a document that has been marked or highlighted due to its relevance in another context. This new element is similar to the existing <strong> and <em> elements, though it is only used in terms of relevance and not importance or emphasis.
  • <progress> defines the completion amount of a task. This could be used to display the download progress of an object.
  • <keygen> represents a key pair generator control. When the control’s form is submitted, the private key is stored in the local keystore, and the public key is packaged and sent to the server.
  • <output> defines the result of a calculation, such as output written from a script.
  • <details> is used to show details about a document, or parts of a document. A user can obtain information or controls from this element. <summary> is a child element to show the summary/control of the <details> element. Think of this as an expanding area where the summary is a cickable link to “See more information.”
  • <hgroup> is used to group a section of headings (<h1><h6>) when there are multiple levels of headings (primary heading, subhead, tagline).

The HTML5 standards draft is not 100% finalized as of the publish date of this post but is close to completion. The current draft of the proposed new standards can be viewed at http://dev.w3.org/html5/spec/Overview.html.

HTML5 will be a great thing, once supported enough across the spectrum of browsers. Until then, only some of the new elements can be utilized in common practice. Internet Explorer will certainly not understand how to render CSS on a majority of the new elements, but there is a means to force IE to read and accept the elements by means of JavaScript so CSS styles can be applied.

Something as simple as document.createElement(‘section’); will tell IE to render the <section> tag. While this method has been tested, and does work for a majority of the new HTML5 elements, there is still the issue of accessibility. Given that JavaScript must be used for IE to render the elements, it cannot be relied upon as a common practice in HTML development. If a user does not have JavaScript enabled on their browser, it will completely break a web page using this method. It is therefore safe to say that we will not see most of the new HTML5 tags used in general development until we at least see the death of Internet Explorer 6 and possibly IE7 and IE8.

Technology