Posted on Leave a comment

Automatically clean product texts in Magento

Magento Logo

So someone copied text from a PDF document and pasted it into the Magento product description (or short description) text field. Unfortunately, the copied text included non-printable control characters in the Unicode range from \x00—\x1F and \x80-\x9f (e.g. DEL, NUL, VT, etc.) thus breaking other extensions from functioning properly, or prohibiting your data export to work at all. 

Clean product texts extensions to the rescue

The Clean product texts extension helps you to automatically clean your product texts (description and short description) by removing unwanted (invisible) control characters that might cause problems with other extensions or when exporting products. Oftentimes, unwanted characters are copied into product descriptions when copying texts from PDF documents. Since these control characters are not displayed by browsers they cannot be removed manually from Magento’s editor. This extension will check your product texts and automatically remove unwanted characters. Thus, your employees can still copy texts from e.g. PDFs directly into Magento – this extension will take care of removing unwanted characters when saving products.

Simulate Product Cleaning

Furthermore, you can also simulate the process of cleaning product texts by activating the corresponding option in the configuration under General / Simulate.

Log File

In addition this extension enabled you to logs messages to a custom log file that can be specified in the configuration options to your log directory under General / Log File. So you have the option to easily track events handled by this extension and check for possible problems. Feel free to check out the Clean product texts extension on Magento Connect.

Posted on Leave a comment

Avoid recursive calls in Magento observer classes

Magento Logo

Since I was recently asked how to avoid recursive calls in Magento observer classes when for instance calling save() in an xxx_save_after event observer: Simply use an internal processing flag.

class My_Products_Model_Observer {

  /** @var boolean flag to signal successful processing of current model entity */
  private $_isProcessed = false;

  /**
   * Handle catalog_product_save_after event.
   * @param Varien_Event_Observer $observer
   */
  public function catalog_product_save_after(Varien_Event_Observer $observer) {
    try {
      $product = $observer->getEvent()->getProduct();

      // do some processing...

      if(!$_isProcessed) {
        $this->_isProcessed = true; // avoid recursion
        $product->save();
      }
    } catch (Exception $e) {
      Mage::log($e->getMessage()); // do some logging
    }
  }
}

That’s it – Sometimes things can be very simple 😉

Posted on Leave a comment

Fixing Magento error:14077410:SSL routines:SSL23_GET_SERVER_HELLO:sslv3 alert handshake failure

Magento Logo

This is just a quick fix post in case you are experiencing the error

error:14077410:SSL routines:SSL23_GET_SERVER_HELLO:sslv3 alert handshake failure

Magento SSLv3 Connect Error Check file downloader/lib/Mage/HTTP/Client/Curl.php for a proper secure transportation protocol supported by Magento Connect (SSLv3 vs. TLSv1):

$this->curlOption(CURLOPT_URL, $uri);
$this->curlOption(CURLOPT_SSL_VERIFYPEER, FALSE);
$this->curlOption(CURLOPT_SSL_VERIFYHOST, 2);

Solution

In order to overcome this error simply add the cUrl option TLSV1:

$this->curlOption(CURLOPT_SSL_CIPHER_LIST, 'TLSv1');

Background info

Magento Connect (finally) canceled support for SSLv3. Thus, when Magento downloader tries to communicate with Magento Connect server it fails due to incompatible security protocols. You can easily fix this error by specifying TLSv1 as alternative security protocol.

Posted on Leave a comment

Managing Magento Invoice and Billing Ids

Magento Logo

In case you need to change the invoice and billing IDs in Magento only a few things need to be done. First, make sure you understand the logic how these IDs are assembled so you don’t mess with existing numbers 😉

Magento Invoice and Billing ID Schema

Have a look at the eav_entity_type table and search for the entity_type_codes

  • order
  • invoice
  • creditmemo
  • shipment

Magento Entity Type IDs Note the corresponding entity_type_id column which will be used by the eav_entity_store table: Magento Order ID and Invoice ID Based on the triplet

  • store_id
  • entity_type_id
  • increment_prefix

you can set unique billing and invoice IDs for your stores.

Advanced settings

In addition to the increment prefix you can also set advanced features such as

  • increment_pad_length
  • increment_pad_char

Magento advanced billing and invoice ID settings Thus, in case you want to set custom padding characters or change the default ID length simply update to corresponding value in the table eav_entity_type.

Changing Billing or Invoice IDs

In case you want to change your billing or invoice IDs make sure that your new ID schema does not conflict with an existing one (and as always make a backup of your database). To update the IDs alter the corresponding increment_prefix and generate all artefacts at least once so that Magento sets the initial IDs for your new ID schema.

Posted on 13 Comments

Automatically copy images from parent configurable product to variants in Magento

Magento Logo

By default Magento does not copy images from parent configurable products to their child variants, i.e. associated products. So you need to manually set images for child products once they are associated with the parent configurable product, which can be rather time consuming and (let’s face it) annoying if you deal with a broader range of product variants. So, wouldn’t it be nice if Magento automatically copied images from parent products to their child product variants? Yes! Luckily, this can be achieved with the following extension.

Extension to the rescue

The Magento extension called Automatic Image assignment for configurable product variants takes care of automatically assigning required image types from parent configurable products to their associated child product variants based on the base image from the parent product once it is saved (hint: see shell script for bulk updates below). Thus, once you save the parent configurable product it checks its child products for a custom list of required image types you can set via the extension configuration. Possible required image types include

  • Base image
  • Small image
  • Thumbnail

So, you are able to set individual image types to be copied to child products based on the base image from the parent configurable product. For instance, selecting small image and thumbnail as required image types will copy the base image from the parent product and set them for the child products in case these image types do not yet exist.

Simulate Image Assignment

Since your products are your treasure the extension offers the options to simulate the process of automatically copying and assigning images. So, instead of altering products in the database the extension will log all required actions in the custom log file specified (yes, the extension offers a custom log file, see below for more information).

Shell script for bulk update

Since the extension is primarily designed as event observer for saving parent configurable products an additional shell script is included in the package. This script will either process all configurable products and check their child products for the required image types set in the configuration or limit the list of products to be checked by an additional product-IDs parameter.

Custom log file

Finally, in order to track the extension’s step you can specify a custom log file that will enable you to easily monitor actions carried out by the extension. Thus, you can easily simulate a bulk update and track events through the log file.

Download the extension

Feel free to download the extension from Magento Connect published by Both Interact.

Alternatively, you can download the latest version 1.6.3 here: BothInteract_ConfigurableProductVariantsImageAssignment-1.6.3.

Posted on Leave a comment

Handling cookies in Magento with JavaScript

Magento Logo

In order to handle cookies in Magento refer to /js/mage/cookies.js. The functions for getting, settings and clearing cookies are:

  • Mage.Cookies.set
  • Mage.Cookies.get
  • Mage.Cookies.clear

Below you find the corresponding JavaScript functions from cookies.js:

Mage.Cookies.set = function(name, value) {
     var argv = arguments;
     var argc = arguments.length;
     var expires = (argc > 2) ? argv[2] : Mage.Cookies.expires;
     var path = (argc > 3) ? argv[3] : Mage.Cookies.path;
     var domain = (argc > 4) ? argv[4] : Mage.Cookies.domain;
     var secure = (argc > 5) ? argv[5] : Mage.Cookies.secure;
     document.cookie = name + "=" + escape (value) +
       ((expires == null) ? "" : ("; expires=" + expires.toGMTString())) +
       ((path == null) ? "" : ("; path=" + path)) +
       ((domain == null) ? "" : ("; domain=" + domain)) +
       ((secure == true) ? "; secure" : "");
};

Mage.Cookies.get = function(name){
    var arg = name + "=";
    var alen = arg.length;
    var clen = document.cookie.length;
    var i = 0;
    var j = 0;
    while(i < clen){
        j = i + alen;
        if (document.cookie.substring(i, j) == arg)
            return Mage.Cookies.getCookieVal(j);
        i = document.cookie.indexOf(" ", i) + 1;
        if(i == 0)
            break;
    }
    return null;
};

Mage.Cookies.clear = function(name) {
  if(Mage.Cookies.get(name)){
    document.cookie = name + "=" +
    "; expires=Thu, 01-Jan-70 00:00:01 GMT";
  }
};

Generic cookie clear function

In case you are searching for a simple, generic function to clear (Magento) session cookies you might want to use the following snippet ($vhost is the current VHost determined by PHP’s globals, e.g. www.yoursite.com):

function clearCookie(name, domain, path) {
  var domain = domain || document.domain;
  var path = path || "/";
  console.debug(name + "=; expires=" + +new Date + "; domain=" + domain + "; path=" + path);
  document.cookie = name + "=;expires=Thu, 01 Jan 1970 00:00:00 GMT;domain=" + domain + ";path=" + path;
}

try {
  console.debug('Removing session cookie...');
  clearCookie('frontend', '<!--?php echo $vhost; ?-->', '/');
  console.debug('Removed cookie');
} catch (Error) {
  console.error('Failed to remove cookie');
}
Posted on Leave a comment

Magento M2E Pro Newsletter Subscription Extension released

Magento Logo

Note: The terms and conditions for using this extension are currently being reviewed to further conform to eBay’s terms and conditions. Thus, this extension is temporarily disabled on Magento Connect until the approval process is completed.

Both Interact today released the M2E Pro Newsletter Subscription extension. This extension takes care of automatically subscribing your eBay customers to your Magento newsletter. Future releases will give you further options to customize the settings, such as

  1. newsletter confirmation options
  2. e-mail blacklists
  3. and much more

The extension is free of charge – feedback is always welcome!

Posted on Leave a comment

Process product attribute HTML values in Magento using template processor

Magento Logo

In case you are wondering how to parse HTML content of product attributes here is how:

// load product model resource
$productResource = Mage::getSingleton('catalog/product')->getResource();
  
// load HTML product attribute content and process using CMS template processor
$helper = Mage::helper('cms');
$processor = $helper->getPageTemplateProcessor();
$attributeContent = $processor->filter(Mage::registry('current_product')->getSomeAttributeByCode());

Using this approach you can of course process CMS block template placeholders too.

Posted on 1 Comment

Magento produces duplicate customer EAV entries instead of updating existing ones

Magento Logo

In a recent Magento 1.8.0.0 project updating customer entities resulted in duplicate EAV entries instead of updating existing ones. Some of the symptoms of this problem were:

  • Customer attributes not being updated, instead new duplicate entries produced
  • Password reset not working, instead duplicate entries produced
  • Login not working

In general, anything related to updating customer EAV entries caused duplicate entries. Furthermore, although new (duplicate) entries were generated still only the old values were taken into consideration. Thus, having a closer look at the corresponding database tables revealed that the unique indexes over entity_id and attribute_id for these tables were missing, e.g. customer_address_entity_datetime:

Magento missing unique index

… which actually should look like this:

magento-unique-indexes

Due to the missing unique index over entity_id and attribute_id columns (here UNQ_CUSTOMER_ADDRESS_ENTITY_DATETIME_ENTITY_ID_ATTRIBUTE_ID) entries were not updated but rather duplicated, as shown below for entity_id 37339 and attribute_id 29:

magento-missing-unique-indexes-duplicate-values

Apart from the missing unique index also the index for entity_id was missing too.

How to fix it

Note: Make sure to backup your database before adding the required indexes!

We need to add unique indexes over entity_id and attribute_id for all EAV tables. This includes tables ending in

  1. _entity_datetime
  2. _entity_decimal
  3. _entity_int
  4. _entity_text
  5. _entity_varchar

But, since duplicate entries already exist we first need to clean up the corresponding tables.

Remove duplicate entries

In order to remove duplicates while keeping the newest values only we can use the SQL query for customer_address_entity_int, as shown below:

Again: Make sure to backup your database first!

DELETE caei1 
FROM customer_address_entity_int caei1,
customer_address_entity_int caei2 
WHERE caei1.value_id < caei2.value_id 
AND caei1.`entity_type_id` = caei2.`entity_type_id` 
AND caei1.`attribute_id` = caei2.`attribute_id` 
AND caei1.`entity_id` = caei2.`entity_id`;

In case you want to test it with one entity_id first simple append e.g.:

 
 ... AND caei1.`entity_id` = XYZ

Do this step for all related EAV tables.

Add unique indexes

Now that duplicates have been removed we can add the required unique indexes over entity_id and attribute_id for all related tables. For instance, the following query adds a unique index over entity_id and attribute_id for table customer_entity_varchar:

 
ALTER TABLE `magento`.`customer_entity_varchar` 
ADD UNIQUE `UNQ_CUSTOMER_ENTITY_VARCHAR_ENTITY_ID_ATTRIBUTE_ID` ( `entity_id` , `attribute_id` ) 

Furthermore, check that there exists an index for entity_id too (this was missing in this setup too):

ALTER TABLE `customer_entity_varchar` 
ADD INDEX `IDX_CUSTOMER_ENTITY_VARCHAR_ENTITY_ID` ( `entity_id` )

That’s it! Based on the added (and required) unique indexes Magento will automatically overwrite any existing EAV entrues, thus updating them instead of creating new ones.

Posted on 6 Comments

Enable Address Fields in Magento Customer Registration Forms

Magento Logo

In order to enable address fields in Magento customer registration forms you only need to enable the attribute setShowAddressField.

To do so either add the following in your template or in the base local.xml (/app/design/frontend/base/default/layout/local.xml):

<customer_account_create> 
  <reference name="customer_form_register"> 
    <action method="setShowAddressFields">
      <param>true</param>
    </action> 
  </reference>
</customer_account_create>

As always, make sure to reload your cache afterwards. This setting will enable to execution of

<?php if($this->getShowAddressFields()): ?>

in register.phtml (/app/design/frontend/base/default/template/customer/form/register.phtml). That’s it 🙂

Posted on Leave a comment

Magento Error ESI processing not enabled

Magento Logo

In a recent Magento setup with a Varnish caching server an undefined PHP variable in a template resulted in the error message “ESI processing not enabled“.

Due to the undefined PHP variable the underlying Varnish caching server’s Edge-side includes (ESI) capabilities, allowing content assembly by HTTP surrogates through in-markup XML-based language failed. After a quick code review of a just installed Magento extension there existed a newly introduced undefined PHP variable that caused Varnish to fail.

Thus, when using Varnish double check for undefined variables in Magento code 😉

Posted on Leave a comment

Display localized dates in Magento

Magento Logo

Since Magento uses the Zend Framework you are able to use Zend_Date to easily display localized dates in Magento.

$date = new Zend_Date(Mage::getModel('core/date')->timestamp(time(), false, Mage::app()->getLocale()->getLocaleCode()));
echo $date->toString("EEEE, d. MMMM YYYY");

So, first create a Zend_Date object based on the locale set in the administration backend:

Mage::app()->getLocale()->getLocaleCode()

Then simply output your date as localized string using Zend_Date->toString:

echo $date->toString("EEEE, d. MMMM YYYY");

Us usual, find formating rules in the Zend_Date constants documentation.

Posted on 3 Comments

Setup Varnish Cache Server for Magento 1.9 CE on Mac OSX

Magento Logo

When dealing with high traffic Magento installations you definitely want to implement caching mechanisms to ensure proper response times for your visitors. Apart from internal caching and indexing functionality provided by Magento there exist a couple of (external) extensions that help you speed of content delivery by e.g. caching entire pages, such as PageCache for Varnish.

Simply put, Varnish represents a reverse-proxy cache that serves as entry-point into you Magento installation and returns cached content rather than requesting Magento to re-build entire pages.

The following steps show you can setup Varnish cache server for Magento 1.9 CE on Mac OSX.

First off, make sure you have Homebrew installed and configured properly. Based on Homebrew we can easily setup Varnish by issuing the following command:

bash-3.2$ brew install varnish
==> Installing varnish dependency: pkg-config
==> Downloading http://pkgconfig.freedesktop.org/releases/pkg-config-0.28.tar.gz
######################################################################## 100,0%
==> ./configure --prefix=/usr/local/Cellar/pkg-config/0.28 --disable-host-tool --with-internal-glib --with-pc-path=/usr/local/lib/p
==> make
==> make check
==> make install
  /usr/local/Cellar/pkg-config/0.28: 10 files, 600K, built in 97 seconds
==> Installing varnish dependency: pcre
==> Downloading ftp://ftp.csx.cam.ac.uk/pub/software/programming/pcre/pcre-8.33.tar.bz2
######################################################################## 100,0%
==> ./configure --prefix=/usr/local/Cellar/pcre/8.33 --enable-utf8 --enable-unicode-properties --enable-pcregrep-libz --enable-pcregrep-libbz2 --
==> make
==> make test
==> make install
   /usr/local/Cellar/pcre/8.33: 140 files, 4,1M, built in 49 seconds
==> Installing varnish
==> Downloading http://repo.varnish-cache.org/source/varnish-3.0.3.tar.gz
######################################################################## 100,0%
==> ./configure --prefix=/usr/local/Cellar/varnish/3.0.3 --localstatedir=/usr/local/var
==> make install
==> Caveats
To have launchd start varnish at login:
    ln -sfv /usr/local/opt/varnish/*.plist ~/Library/LaunchAgents
Then to load varnish now:
    launchctl load ~/Library/LaunchAgents/homebrew.mxcl.varnish.plist
Warning: /usr/local/sbin is not in your PATH
You can amend this by altering your ~/.bashrc file
==> Summary
 /usr/local/Cellar/varnish/3.0.3: 51 files, 1,8M, built in 43 seconds

As shown in Brew’s output you can start Varnish by issuing the following command:

==> Caveats
To have launchd start varnish at login:
    ln -sfv /usr/local/opt/varnish/*.plist ~/Library/LaunchAgents
Then to load varnish now:
    launchctl load ~/Library/LaunchAgents/homebrew.mxcl.varnish.plist

In case the following error occurs:

bash-3.2$ sudo launchctl load ~/Library/LaunchAgents/homebrew.mxcl.varnish.plist"
launchctl: Couldn't" stat("~/Library/LaunchAgents/homebrew.mxcl.varnish.plist"): No such file or directory nothing found to load

try a re-install first (and additionally do a brew-update/updgrade):

bash-3.2$ sudo brew uninstall varnish
Uninstalling /usr/local/Cellar/varnish/3.0.3...

After a brew update/upgrade Varnish 4.0.0 is now being installed:

bash-3.2$ sudo brew install varnish
==> Downloading https://downloads.sf.net/project/machomebrew/Bottles/varnish-4.0.0.mavericks.bottle.tar.gz
######################################################################## 100,0%
==> Pouring varnish-4.0.0.mavericks.bottle.tar.gz
==> Caveats
To have launchd start varnish at login:
    ln -sfv /usr/local/opt/varnish/*.plist ~/Library/LaunchAgents
Then to load varnish now:
    launchctl load ~/Library/LaunchAgents/homebrew.mxcl.varnish.plist
==> Summary
 /usr/local/Cellar/varnish/4.0.0: 93 files, 1,9M

Unfortunately, this does not fix the missing plist file problem every time.

Nevertheless, you can easily create the plist file yourself, as mentioned here https://github.com/Homebrew/homebrew/issues/15922:

Simply copy the contents for the plist file by calling brew cat varnish:

bash-3.2$ sudo brew cat varnish
require 'formula'

class Varnish < Formula
  homepage 'http://www.varnish-cache.org/'
  url 'https://repo.varnish-cache.org/source/varnish-4.0.0.tar.gz'
  sha1 '6ad4fcf42a505a748ae993cb6ed579159e023633'

  bottle do
    sha1 "190bba2cf0e521b489cbd64cc698ba769b18cccd" => :mavericks
    sha1 "fd12d468c2328179bd402f1592b65ef69d6f1baa" => :mountain_lion
    sha1 "0e9717e705541a34906947eb7e49ab800204e2ff" => :lion
  end

  depends_on 'pkg-config' => :build
  depends_on 'pcre'

  resource "docutils" do
    url "https://pypi.python.org/packages/source/d/docutils/docutils-0.11.tar.gz"
    sha1 "3894ebcbcbf8aa54ce7c3d2c8f05460544912d67"
  end

  def install
    ENV.prepend_create_path "PYTHONPATH", buildpath+"lib/python2.7/site-packages"
    resource("docutils").stage do
      system "python", "setup.py", "install", "--prefix=#{buildpath}"
    end

    system "./configure", "--disable-dependency-tracking",
                          "--prefix=#{prefix}",
                          "--localstatedir=#{var}",
                          "--with-rst2man=#{buildpath}/bin/rst2man.py",
                          "--with-rst2html=#{buildpath}/bin/rst2html.py"
    system "make install"
    (var+'varnish').mkpath
  end

  test do
    system "#{opt_sbin}/varnishd", "-V"
  end

  def plist; <<-EOS.undent
      <?xml version="1.0" encoding="UTF-8"?>
      <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
      <plist version="1.0">
      <dict>
        <key>Label</key>
        <string>#{plist_name}</string>
        <key>ProgramArguments</key>
        <array>
          <string>#{opt_sbin}/varnishd</string>
          <string>-n</string>
          <string>#{var}/varnish</string>
          <string>-f</string>
          <string>#{etc}/varnish/default.vcl</string>
          <string>-s</string>
          <string>malloc,1G</string>
          <string>-T</string>
          <string>127.0.0.1:2000</string>
          <string>-a</string>
          <string>0.0.0.0:80</string>
        </array>
        <key>KeepAlive</key>
        <true/>
        <key>RunAtLoad</key>
        <true/>
        <key>WorkingDirectory</key>
        <string>#{HOMEBREW_PREFIX}</string>
        <key>StandardErrorPath</key>
        <string>#{var}/varnish/varnish.log</string>
        <key>StandardOutPath</key>
        <string>#{var}/varnish/varnish.log</string>
      </dict>
      </plist>
    EOS
  end
end

The plist content in this case is:

<?xml version="1.0" encoding="UTF-8"?>
      <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
      <plist version="1.0">
      <dict>
        <key>Label</key>
        <string>#{plist_name}</string>
        <key>ProgramArguments</key>
        <array>
          <string>#{opt_sbin}/varnishd</string>
          <string>-n</string>
          <string>#{var}/varnish</string>
          <string>-f</string>
          <string>#{etc}/varnish/default.vcl</string>
          <string>-s</string>
          <string>malloc,1G</string>
          <string>-T</string>
          <string>127.0.0.1:2000</string>
          <string>-a</string>
          <string>0.0.0.0:80</string>
        </array>
        <key>KeepAlive</key>
        <true/>
        <key>RunAtLoad</key>
        <true/>
        <key>WorkingDirectory</key>
        <string>#{HOMEBREW_PREFIX}</string>
        <key>StandardErrorPath</key>
        <string>#{var}/varnish/varnish.log</string>
        <key>StandardOutPath</key>
        <string>#{var}/varnish/varnish.log</string>
      </dict>
      </plist>

Now starting Varnish via launchctl succeeds:

bash-3.2$ sudo launchctl load ~/Library/LaunchAgents/homebrew.mxcl.varnish.plist

Time to test the service

Open a browser and navigate to 127.0.0.1:8080 (which is the port Varnish is listening to set by the configuration in the plist file from above) and check out the headers:

Accept-Ranges	bytes, bytes
Age	0
Connection	keep-alive
Content-Language	de
Content-Length	1087
Content-Type	text/html; charset=utf-8
Date	Fri, 30 May 2014 07:31:12 GMT
Server	Apache/2.4.4 (Unix) PHP/5.4.7 OpenSSL/1.0.1e
Vary	accept-language,accept-charset
Via	1.1 varnish
X-Varnish	1661470811

Congratulations, as you can see Varnish is working properly!

The next step is to configure Apache to listen to port 8080 (or any other port apart from 80) and Varnish on port 80.

That’s it 🙂