Store and serve Magento media files using Amazon S3

At the moment, the extension is experimental, so be very careful

One of the interesting items on the Magento 1.5.1.0 Release Notes is the “Alternative image storage” feature. This allows large stores to do away with sharing the media directory between multiple web servers.

In Magento Community edition, Filesystem and Database are the only two options for media storage, so I decided write an Magento extension to store media files using Amazon Simple Storage Service (Amazon S3).

The extension is installed as a Magento module without hacking Magento’s core. It works with Magento Community 1.5.1.0 and uses the  Zend_Service_Amazon_S3 class from Zend Framework.

Code available at: https://github.com/sstoiana/magneto-s3

README and INSTALL notes: https://github.com/sstoiana/magneto-s3#readme

I will say it again: this is not suitable for production use yet. Some internal functions are not yet implemented, and there’s tons of logging done. I’m not responsible for any data loss you might experience.

Some notes:

  • Unfortunately, due to the way the feature is implemented in Magento, I had to replace the Database storage option in the config, so it will not be available after installing the S3 extension.
  • Switching from Filesystem storage to S3 should work just fine, but the reverse is known to have issues (i.e. it’s incomplete)
  • In order to serve files from S3 or CloudFront directly, you should change the Base Media URL in Magento’s admin; otherwise, the media urls will be served from your domain, using the s3get.php script, like this: http://mydomain.com/s3get.php/media/wysiwig/stuff.png

Magento Admin Configuration Option Search Extension

UPDATE: The Magneto Admin Search extension is now available on Magento Connect.

With over 950 options in 176 tabs grouped in 34 configuration sections, it’s easy to get lost in Magento’s configuration menu. Magneto_Adminsearch to the rescue!

Magneto_Adminsearch is a small search box in the top left that you can use to find your way around when searching for obscure Magento settings like “Allow Symlinks”, “Display Demo Store Notice” or “Notify for Quantity Below”. I dare you to tell me *exactly* where these options are from the top of your head.

From the release notes:

Magento Admin Configuration Search widget

This extensions allows quick access to configuration options in Magento’s admin panel

INSTALLATION

Via Modman

Via Magento Connect

Magento Connect extension package is available here: http://www.magentocommerce.com/magento-connect/sstoiana/extension/6715/magnetoadminsearch

FEATURES

  • Search configuration items and sections in Magento’s Configuration admin page
  • Works with translated admin options (you can search for “Wochenende” in German instead of “Weekend Days” in English)

Source code available at https://github.com/sstoiana/magneto-adminsearch.

Screenshots below:

“NOTE Gem::Specification#default_executable= is deprecated with no replacement.”

On Mac OS X 10.6.7 you may get the following messages after running gem update --system:

Gem::Specification#default_executable= called from /Library/Ruby/Gems/1.8/specifications/json_pure-1.4.6.gemspec:10.
NOTE: Gem::Specification#default_executable= is deprecated with no replacement. It will be removed on or after 2011-10-01.
Gem::Specification#default_executable= called from /Library/Ruby/Gems/1.8/specifications/rake-0.8.7.gemspec:10.
NOTE: Gem::Specification#default_executable= is deprecated with no replacement. It will be removed on or after 2011-10-01.
Gem::Specification#default_executable= called from /Library/Ruby/Gems/1.8/specifications/ruby-debug-ide-0.4.5.gemspec:11.
NOTE: Gem::Specification#default_executable= is deprecated with no replacement. It will be removed on or after 2011-10-01.
Gem::Specification#default_executable= called from /Library/Ruby/Gems/1.8/specifications/rubyforge-2.0.4.gemspec:10.
NOTE: Gem::Specification#default_executable= is deprecated with no replacement. It will be removed on or after 2011-10-01.
Gem::Specification#default_executable= called from /Library/Ruby/Gems/1.8/specifications/rubygems-update-1.8.1.gemspec:11.

The fix is to simply run

gem pristine --all

If that doesn’t work, you can use sudo.

Doctrine Many to Many Relationships and Aggregation Inheritance

Suppose you want to do Many to Many relationship between an Event and a Image table using Doctrine; Each Event has a “main” Image list and a “other” list. Here’s the schema.yml:

Image:
  columns:
    id: { type: integer(4), fixed: false, unsigned: false, primary: true, autoincrement: true }
    name: { type: string(), fixed: false, unsigned: false, primary: false, notnull: true, autoincrement: false }
  relations:
    Events:       {class: Event, local: image_id, foreign: event_id, refClass: EventImages }
    EventsMain:   {class: Event, local: image_id, foreign: event_id, refClass: EventImagesMain }
    EventsOther:  {class: Event, local: image_id, foreign: event_id, refClass: EventImagesOther }

EventImages:
  columns:
    image_id: { type: integer(4), primary:true }
    event_id: { type: integer(4), primary:true }
    type: { type: enum, values: ["main", "other"], default: "main", notnull: true, primary:true }
  relations:
    Images : {local: image_id, foreign: id}

EventImagesMain:
  inheritance:
    extends: EventImages
    type: column_aggregation
    keyField: type
    keyValue: main

EventImagesOther:
  inheritance:
    extends: EventImages
    type: column_aggregation
    keyField: type
    keyValue: other

Event:
  columns:
    id: { type: integer(4), fixed: false, unsigned: false, primary: true, autoincrement: true }
    name: { type: string(), fixed: false, unsigned: false, primary: false, notnull: true, autoincrement: false }
  relations:
    Images:      { class: Image, local: event_id, foreign: image_id, refClass: EventImages }
    OtherImages: { class: Image, local: event_id, foreign: image_id, refClass: EventImagesOther }
    MainImages:  { class: Image, local: event_id, foreign: image_id, refClass: EventImagesMain }

In order for methods like $image->getEventsMain() and $event->getMainImages() to work, you need to define the relations for all of the tables involved (Image, EventImages, Event).

Knowing this beforehand would have saved me around five hours of tinkering with the schema.

Hope this helps.

Modify Magento theme layout without copying XML files

When you’re ever in need to do small changes to a Magento theme’s layout relative to it’s base layout, you have two options:

- copy the base theme’s layout xml (e.g.page.xml) to your theme’s layout folder and do the changes in the copied file

- create a local.xml file and write the layout update xml code in it

<layout version="0.1.0">
    <default>
        <!-- setup to use sprites -->
        <reference name="head">
            <action method="removeItem"><type>skin_css</type><name>css/styles.css</name></action>
            <action method="addCss" before="-"><stylesheet>css/styles-sprite.css</stylesheet></action>
        </reference>
    </default>
</layout>

iPhone DTMF woes

Unfortunately, every other day is equal to another iPhone horror story for me. Developing an Asterisk IVR (Interactive Voice Response) system means interpreting DTMF tones from a wide range of devices. Now, if one of them happens to be an iPhone then all hell breaks loose.

While testing, we weren’t exactly sure why the keypad input from one of our phones was always garbled. After a few minutes of testing and testing, we figured it must be a phone problem. And it was. The phone in question is (of course) an Apple iPhone 3G. *My* iPhone 3G.

Digging through the apple support forums, this is what came up: the iPhone only knows how to send “long” DTMF tones. Because phone line quality is lousy by default, line noise and interruptions cause these long tones to sometimes (almost always) get broken into two or more “short” tones. Hence, the keypad input nightmare begins.

Empirically we discovered that turning off the 3G service on the iPhone alleviates the problem, but it’s not guaranteed to work, and it’s not documented anywhere.

Did I mention I have a love-hate relationship with my iPhone?

Redirect Non-www Domain to www for Better SEO

Search engines consider http://domain.com and http://www.domain.com different websites. As a result, you are effectively splitting the potential benefit of valuable link popularity, as well as risking to be penalized by Google for duplicate content.

Using a permanent redirect you can effectively consolidate all of your link popularity to a single URL.
This Search Engine Optimization will serve to increase your website’s chances of obtaining and maintaining top rankings.

First of all, you need to decide which version you intend to keep: the “www” or “non-www” URL for your website. Depending on which version you intend to keep, you need to use the following code in your Apache configuration files:

  1. Redirect domain.com to www.domain.com

    RewriteEngine On
    RewriteBase /
    RewriteCond %{HTTP_HOST} !^www.domain.com$ [NC]
    RewriteRule ^(.*)$ http://www.domain.com/$1 [L,R=301]

  2. Redirect www.domain.com to domain.com

    RewriteEngine On
    RewriteBase /
    RewriteCond %{HTTP_HOST} !^domain.com$ [NC]
    RewriteRule ^(.*)$ http://domain.com/$1 [L,R=301]

Some hosting providers are more permissive than others. If you have access to your httpd.conf file, you can insert the code there. If not, you can always use the .htaccess file in your site’s document root. See the post
How to Disable Directory Listing/Browsing in Apache Web Server for more details.

Also, you need to ensure that your hosting provider has the Apache Rewrite Module turned on; this is a definite requirement for this fix to work. You can enable the Rewrite Module using the following line in the configuration files:

LoadModule rewrite_module modules/mod_rewrite.so