Magento’s Custom Variables Anywhere

Magento’s wiki states that: “CMS content cannot simply be translated in the same way as catalog content. Duplicate pages, blocks, banners and polls must be created for each language.”

The problem is that separate CMS pages would have different URL keys. This creates several complications:

1. Having separate pages with the same content isn’t advised. It’s not so much the false belief that Google frowns upon websites that contain duplicate content (it’s only dissallowed at a disproportional scale that would indicate this is used as a grey-hat SEO trick). It’s more a matter of directing more users to the same pages so as to promote their relevance in Google’s eyes.

2. Locale-dependant url codes make it hard to link to them in other places in the store. For example, a footer link to such a url means you’ll need a locale-based footer block too.

3. If the only difference between store views are those CMS pages, then what’s the point of having to create a separate sitemap.xml file for each locale?

To address these issues in my own store, I hacked Magento’s core to handle custom variables everywhere, and not just through its WYSIWYG editor. The code below is an unintrucive version that uses Magento’s event observers to react to a page rendering and scan for Magento’s custom variables syntax.

For those of you not interested in the tenchnical details, I created a packaged extension that supplies this functionality. It’s available to download for free on my professional website.

For those interested in the technicalities, you’ll notice below that I hook into the response object instead of parsing the individual blocks’ toHtml() methods. It seems surprising because individual blocks are cached in Magento CE, whereas whole pages are not, and it would have saved some parsing time if I were to hook into a method that only runs when there’s no cache available. The thing is, I tested it using Magento’s profiler and it simply doesn’t work as expected. If anyone has any insights as to why, I’d really appreciate a comment.

Either way, to get this working on your own store, create the XML file for the extension here:

ROOT/app/etc/modules/Namespace_Module.xml


<?xml version="1.0"?>
<config>
  <modules>
    <Namespace_Module>
      <active>true</active>
      <codePool>local</codePool>
    </Namespace_Module>
  </modules>
</config> 

Then create the XML file for your extension’s configurations, in which we’ll register our event observer, here:

ROOT/app/code/local/Namespace/Module/etc/config.xml


<?xml version="1.0"?>
<config>
  <modules>
    <Namespace_Module>
      <version>0.1.0</version>
    </Namespace_Module>
  </modules> 
  <global>
    <helpers>
      <variables>
        <class>Namespace_Module_Helper</class>
      </variables>
    </helpers>
    <events>
      <controller_front_send_response_before>
        <observers>
          <namespace_module>
            <type>singleton</type>
            <class>
              Namespace_Module_Helper_Observer
            </class>
            <method>parseCustomVars</method>
          </namespace_module>
        </observers>
      </controller_front_send_response_before>  
    </events>
  </global>
</config>

Last but not least, create the actual observer function inside the helper class, here:

ROOT/app/code/local/Namespace/Module/Helper/Observer.php


<?php
class Namespace_Module_Helper_Observer extends Mage_Core_Helper_Abstract
{
  public function parseCustomVars($observer)
  {
    $response = $observer->getEvent()->getFront()->getResponse();
    $html = $response->getBody();

    $callback = function($matches) {
      $var = Mage::getModel('core/variable');
      $var->setStoreId(Mage::app()->getStore()->getId());
      return $var->loadByCode($matches[1])->getValue('html');
    };

    if (!$this->isAdmin()) {
      $html = preg_replace_callback(
        "/{{customVar code=(.*)}}/U",
        $callback,
        $html
      );
    }
    $response->setBody($html);
    Mage::app()->setResponse($response);

    return $this;
  }

  public function isAdmin()
  {
    if(Mage::app()->getStore()->isAdmin())
    {
      return true;
    }

    if(Mage::getDesign()->getArea() == 'adminhtml')
    {
      return true;
    }

    return false;
  }
}

The isAdmin() function is copied from somewhere. I don’t remember where but I thank it’s author for the code.

This is pretty much it. With this code in place, you can type in {{customVar code=anything}} anywhere in the administration area – configuration menus, CMS page titles, category names, etc, and it’ll parse that into the corresponding value on the frontend.

Hope this helps someone. Please share and don’t forget to test this before deploying as it was only tested on the Magento CE ver. 1.7.0.0.

| Magento’s architecture requires the use of separate CMS pages, with separate URL keys for each language. In this post, I suggest a hack that enables using custom variables in the CMS’s title and content fields instead. Custom variables are translatable, and I’ve been successful at using them as a way of creating language-independent CMS pages.

Chrome is Omnimemoria

I’m in the midst of building myself a Magento website these days, and I ran into a peculiar situation. I was filling out a form in the admin panel of my local Magento installation on Chrome, when it suddenly gave me a drop-down menu with suggested items. The thing is, these suggestions were stuff I typed into a different Magento website I built more than a year ago, and on a different PC. The fact that it remembers data for so long and transfers it from one PC I use to another, can be seen as a feature… I understand that. What I can’t regard a feature is that it’s a different installation of Magento using a different URL, but it still remembered the values I typed into the same fields in the admin panel.

This is far more than just an annoyance. Both admin panels are password protected and Magento admin panels sometimes hold credit card information (though it’s not a best practice). It’s crucial that nobody unauthorized would have access to this data. I repeat that these installations were sitting on different locations with different URLs. Now imagine a company that manages several Magento websites. If a certain employee only has authorization on one of those websites, he can still view values typed into fields on other installations that run from that particular browser, as long as someone is signed in with his Google account. These are the kind of security loopholes people can use to steal sensitive information from companies they work for.

I’m moving to Firefox.

| Google Chrome remembers stuff I type into forms even when I’m not on the same URL. This is such an easy loophole I can’t imagine someone isn’t taking advantage of it already. I’m moving to Firefox.