Posted on Leave a comment

Fixing Magento 1 newsletter queue bug due to missing encoding in grid renderer class

Magento Logo

In a recent Magento 1.9.3.2 project we experienced a strange behavior related to the built-in newsletter module in admin grid. When trying to add a newsletter template to the queue using the action dropdown in the admin grid the following JavaScript error showed up:

Uncaught SyntaxError: Unexpected end of JSON input
    at JSON.parse (<anonymous>)
    at String.parseJSON [as evalJSON] (prototype.js:720)
    at Object.execute (grid.js:717)
    at HTMLSelectElement.onchange (085f35f…:722)

A quick look at the option value for the admin grid’s row select input showed that the JSON was not properly escaped:

<select class="action-select" onchange="varienGridAction.execute(this);"><option value=""></option><option value="{" href":"https:\="" \="" www.someshop.com\="" index.php\="" __ma2ge_a5dm2in__\="" newsletter_queue\="" edit\="" template_id\="" 1\="" key\="" e5bdca9b9185cd175c6f9d297127d238\="" "}"="">Newsletter Warteschlange ...</option><option value="{" popup":true,"href":"https:\="" \="" www.someshop.com\="" index.php\="" __ma2ge_a5dm2in__\="" newsletter_template\="" preview\="" id\="" 1\="" key\="" b4b16e0fa2fb208b6191e6ddb3a6282c\="" ","onclick":"popwin(this.href,'_blank','width="800,height=700,resizable=1,scrollbars=1');return" false;"}"="">Vorschau</option></select>

As you can see the double quotes for the JSON option value was broken, thus resulting in a JavaScript exception when varienGridAction.execute(this) is triggered, e.g.:

<option value="{" href":"https:\="" \=""...

Since the built-in newsletter uses a custom row renderer for this action dropdown a check in Mage_Adminhtml_Block_Widget_Grid_Column_Renderer_Action was required, in particular _toOptionHtml:

  protected function _toOptionHtml($action, Varien_Object $row) {
        $actionAttributes = new Varien_Object();

        $actionCaption = '';
        $this->_transformActionData($action, $actionCaption, $row);
        $htmlAttibutes = array('value'=> $this->escapeHtml(Mage::helper('core')->jsonEncode($action)));

        $actionAttributes->setData($htmlAttibutes);
        return '<option ' . $actionAttributes->serialize() . '>' . $actionCaption . '</option>';
  }

The solution in this case is rather simple:

$htmlAttibutes = array('value'=> htmlentities($this->escapeHtml(Mage::helper('core')->jsonEncode($action))));

Further investigation is needed in this case as to why additional encoding is required. In the meantime the offending class was overwritten with the corresponding local version.

Posted on Leave a comment

Magento Newsletter Unsubscribe Form

Magento Logo

One of the most missed functions in Magento 1 is a newsletter unsubscribe form. Although, by default you can generate newsletter unsubscribe links to be used for instance in newsletters sent out to subscribers there’s no nice out-of-the-box way to integrate a newsletter unsubscribe form in Magento 1.

Which is why there already exist some extensions on the Magento Connect Marketplace that extend the existing newsletter functionality by e.g. adding a simple unsubscribe field and toggling the action submitted (“subscribe” vs. “unsubscribe”). Unfortunately, having a look at most of the existing extensions made clear that they either include too much functionality or simply are outdated.

Implementing the Magento Newsletter Unsubscribe Extension

Luckily, implementing the newsletter unsubscribe functionality in Magento is pretty straight forward. In order to share this information here’s the gist on how to create a Magento Newsletter unsubscribe form extension.

Basically, what we are trying to achieve here is an additional Magento page with a distinct URL that incorporates our newsletter unsubscribe form, i.e. “newsletter-unsubscribe”. This form handles submissions and checks if the e-mail submitted currently is subscribed to the Magento newsletter. If that’s the case it will unsubscribe this e-mail based on the built-in Magento functionality, i.e. it will automatically sent out the un-subscription confirmation mail and execute any other events that you might have integrated for newsletter related events. Last but not least, as always everything should be translatable.

Custom route instead of CMS page

Instead of creating an additional CMS page and loading our newsletter unsubscribe form into it (e.g. using layout updates) we are going to create a custom route for our extension. By default, this route will be newsletter-unsubscribe. The relevant entries in the config.xml are as follows:

<?xml version="1.0"?>
<config>
 
  <modules>
    <BothInteract_NewsletterUnsubscribe>
      <version>1.0.1</version>
    </BothInteract_NewsletterUnsubscribe>
  </modules> 
 
  <frontend>
    <routers>
      <bothinteract_newsletterunsubscribe>
        <use>standard</use>
        <args>
          <module>BothInteract_NewsletterUnsubscribe</module>
          <frontName>newsletter-unsubscribe</frontName>
        </args>
      </bothinteract_newsletterunsubscribe>
    </routers>
 
    <secure_url>
      <bothinteract_newsletterunsubscribe>/newsletter-unsubscribe/</bothinteract_newsletterunsubscribe>
    </secure_url>

    ...
</config>

As you can see an additional frontend route is added (newsletter-unsubscribe) that maps to our extensions’s controller.

Display newsletter unsubscribe form

Now that URLs starting with “newsletter-unsubscribe” are routed to our controller we need to make sure that our custom block is loaded and unsubscribe submissions are processed correctly.

As always, the default action is index and in our case it’s responsible for rendering or newsletter unsubscribe form:

public function indexAction() {

  $this->loadLayout();

  // load 1-column page layout
  $this->getLayout()
    ->getBlock('root')
    ->setTemplate('page/1column.phtml');

  // load breadcrumb bar
  $breadcrumbs = $this->getLayout()->getBlock('breadcrumbs');
  $helper = Mage::app()->getHelper('bothinteract_newsletterunsubscribe');

  if ($breadcrumbs) {
    $breadcrumbs->addCrumb('home', array(
      'label' => $helper->__('Home'),
      'title' => $helper->__('Go to Home Page'),
      'link' => Mage::getBaseUrl()));
    $breadcrumbs->addCrumb('newsletter-unsubscribe', array(
      'label' => $helper->__('Newsletter Unsubscribe'),
      'title' => $helper->__('Newsletter Unsubscribe'),
      'link' => Mage::getUrl('newsletter-unsubscribe')));
  }

  // render our custom block
  $block = $this->getLayout()
    ->createBlock('bothinteract_newsletterunsubscribe/customblock')
    ->setTemplate('bothinteract_newsletterunsubscribe/form.phtml');

  // append to CMS content block
  $this->getLayout()->getBlock('content')->append($block);

  $this->renderLayout();
}

There’s no magic here but let’s quickly dissect what is happening:

First, we are loading the layout and setting the 1-column page layout by default .

Hint: Using additional extension backend options we can make these settings available for users to edit directly without touching the code. This will be available in version 1.1.

Since we want our page to appear in the breadcrumbs too we manually add it. Finally, we create and append our custom block while setting the corresponding template and render the final layout.

Process newsletter unsubscribe form submissions

Now that we are able to display the newsletter unsubscribe form it’s time to process form submissions. This is done using the unsubscribe action:

public function unsubscribeAction() {
  if ($this->getRequest()->isPost() && $this->getRequest()->getPost('email')) {
    $session = Mage::getSingleton('core/session');
    $email = (string) $this->getRequest()->getPost('email');

  try {
    if (!Zend_Validate::is($email, 'EmailAddress')) {
      Mage::throwException($this->__('Please enter a valid email address.'));
    }

    $subscriber = Mage::getModel('newsletter/subscriber')
      ->loadByEmail($email);

    if ($subscriber && $subscriber->getId()) {
      // check already unsubscribed/inactivated before
      if ($subscriber->getStatus() == Mage_Newsletter_Model_Subscriber::STATUS_NOT_ACTIVE ||
        $subscriber->getStatus() == Mage_Newsletter_Model_Subscriber::STATUS_UNSUBSCRIBED) {
        $session->addSuccess($this->__('You are not registered with this e-mail.'));
      } else {
        $subscriber->unsubscribe();
        $session->addSuccess($this->__('You have been successfully unsubscribed.'));
      }
    } else {
      $session->addSuccess($this->__('You are not registered with this e-mail.'));
    }
   } catch (Mage_Core_Exception $e) {
     $session->addException($e, $this->__('There was a problem with the unsubscription: %s', $e->getMessage()));
   } catch (Exception $e) {
     $session->addException($e, $this->__('There was a problem with the unsubscription.'));
   }
  }
  $this->_redirectReferer();
}

Again, no magic is happening here. We are simply checking the submitted e-mail, try to load the corresponding newsletter subscriber object and check it’s status. Depending on the status we are able to unsubscribe this e-mail or display the appropriate message.

Our actual custom Magento block is very slim on purpose and only returns the form action URL of our custom extension:

class BothInteract_NewsletterUnsubscribe_Block_Customblock extends Mage_Core_Block_Template {

  public function getFormActionUrl() {
    return $this->getUrl('newsletter-unsubscribe/index/unsubscribe/', array('_secure' => true));
  }
}

Yes, that’s all there is to it. Sorry to disappoint you in case you are expecting magic stuff happening here. Thus, based on Magento’s stable architecture adding a newsletter unsubscribe form can be easily achieved.

Download Magento Newsletter Unsubscribe Extension

This extension has been released by Both Interact and is available for free on the Magento Connect marketplace as Both Interact Newsletter Unsubscribe extension. Feel free to download and rate the extension.

 

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!