Tutorial: Creating a Magento Widget - Part 2
Introduction
We've already learned how to create a simple widget (i.e. create a module, define widgets and create frontend blocks). Now we are going to try to go deeper into the available widget configuration options to see what makes Magento widgets so beneficial to both the store owners and extension developers.
Learn available widget configuration options, types and definitions
Let's look into an abstract widget.xml file:
<?xml version="1.0"?>
<widgets>
<some_unique_widget_name type="block_group/block_path" translate="name description" module="modulename">
<name>Widget name</name>
<description>Short widget description</description>
<!-- Additional javascript files to be loaded
on the widget configuration edit form if needed. -->
<js>mage/adminhtml/first.js,mage/adminhtml/second.js</js>
<!--
It should contain comma separated list
of javascript files paths under the /js directory.
This tag is optional.
-->
<parameters>
<first_option translate="label">
<!-- General option properties -->
<!-- Defines if the option is allowed to be empty -->
<required>1</required>
<!-- Defines if the option is visible in the edit form -->
<visible>1</visible>
<!--
In case if you need some hidden input
in the widget configuration form,
set 'visible' to 0
-->
<!-- Label for the edit form -->
<label>Option name</label>
<!-- Option type -->
<type>select</type>
<!--
It can be either one of the simple form element types, e.g.:
<type>text</type>
<type>select</type>
<type>multiselect</type>
<type>label</type>
...
or it can define a renderer which will be used to create
this configuration field in the edit form.
Renderer is supposed to be a block reference
in 'block_group/block_path' format, e.g.:
<type>mymodule/some_custom_block</type>
-->
<!-- Source values for drop-downs and multiselects -->
<!--
There are two possible ways to define a set of available values.
The first way is to specify the list of available values right here:
-->
<values>
<value_one translate="label">
<value>1</value>
<label>One</label>
</none>
<two translate="label">
<value>2</value>
<label>Two</label>
</two>
<three translate="label">
<value>3</value>
<label>Three</label>
</three>
</values>
<!--
The second way is to specify the source model,
which must have toOptionArray() public method available.
The method should return an array of values and labels
in the following format:
array(
array('value' => 'value1', 'label' => 'Label 1'),
array('value' => 'value2', 'label' => 'Label 2'),
array('value' => 'value2', 'label' => 'Label 3'),
);
Source model name is specified in usual
'model_group/model_path' format, e.g.:
-->
<source_model>adminhtml/system_config_source_yesno</source_model>
<!-- Additional helper block to be created on the edit form page, optional -->
<helper_block>
<!-- Helper block reference in regular 'block_group/block_path' format -->
<type>module/block_type</type>
<!-- Arguments for the block constructor, optional -->
<data>
<value1>Value1</value1>
<value2>Value1</value2>
<value3>
<one>One</one>
<two>Two</two>
</value3>
</data>
</helper_block>
<!--
Here is the full example of helper block definition
from catalog module widgets:
<helper_block>
<type>adminhtml/catalog_product_widget_chooser</type>
<data>
<button translate="open">
<open>Select Product...</open>
</button>
</data>
</helper_block>
-->
</first_option>
<!--
...
any number of other widget configuration options goes here
...
-->
</parameters>
</some_unique_widget_name>
<!--
...
other widgets declarations go here
...
-->
</widgets>
Create a module
Let's create a module directory structure and necessary files
Declare a widget
app/code/local/Sample/WidgetTwo/etc/widget.xml:
<?xml version="1.0"?>
<widgets>
<widgettwo_list type="widgettwo/list" translate="name description" module="widgettwo">
<name>Social Bookmarking Links</name>
<description>Adds a simple list of social bookmarking links</description>
<parameters>
<enabled_services>
<label>Enabled Services</label>
<visible>1</visible>
<required>1</required>
<type>multiselect</type>
<source_model>widgettwo/services</source_model>
</enabled_services>
<template translate="label">
<label>Frontend Template</label>
<visible>1</visible>
<required>1</required>
<type>select</type>
<values>
<text translate="label">
<value>widgettwo/text.phtml</value>
<label>Text Links</label>
</text>
<icons translate="label">
<value>widgettwo/icons.phtml</value>
<label>Icon Links</label>
</icons>
</values>
</template>
</parameters>
</widgettwo_list>
</widgets>
Create a source model to use it with service multiselect in widget configuration
app/code/local/Sample/WidgetTwo/Model/Services.php:
<?php
class Sample_WidgetTwo_Model_Services
{
/**
* Provide available options as a value/label array
*
* @return array
*/
public function toOptionArray()
{
return array(
array('value' => 'digg', 'label' => 'Digg'),
array('value' => 'delicious', 'label' => 'Delicious'),
array('value' => 'twitter', 'label' => 'Twitter'),
);
}
}
Create a frontend widget block
app/code/local/Sample/WidgetTwo/Block/List.php:
<?php
class Sample_WidgetTwo_Block_List
extends Mage_Core_Block_Template
implements Mage_Widget_Block_Interface
{
/**
* A model to serialize attributes
* @var Varien_Object
*/
protected $_serializer = null;
/**
* Initialization
*/
protected function _construct()
{
$this->_serializer = new Varien_Object();
parent::_construct();
}
/**
* Produce links list rendered as html
*
* @return string
*/
protected function _toHtml()
{
$html = '';
$config = $this->getData('enabled_services');
if (empty($config)) {
return $html;
}
$services = explode(',', $config);
$list = array();
foreach ($services as $service) {
$item = $this->_generateServiceLink($service);
if ($item) {
$list[] = $item;
}
}
$this->assign('list', $list);
return parent::_toHtml();
}
/**
* Generate link attributes
*
* The method returns an array, containing any number of link attributes,
* All values are optional
* array(
* 'href' => '...',
* 'title' => '...',
* '_target' => '...',
* 'onclick' => '...',
* )
*
* @param string $service
* @return array
*/
protected function _generateServiceLink($service)
{
/**
* Page title
*/
$pageTitle = '';
$headBlock = $this->getLayout()->getBlock('head');
if ($headBlock) {
$pageTitle = $headBlock->getTitle();
}
/**
* Current URL
*/
$currentUrl = $this->getUrl('*/*/*', array('_current' => true, '_use_rewrite' => true));
/**
* Link HTML
*/
$attributes = array();
$icon = '';
switch ($service) {
case 'digg':
$attributes = array(
'href' => 'http://www.digg.com/submit?url=' . rawurlencode($currentUrl) . '&phase=2',
'title' => 'You Digg?',
);
$icon = 'digg.gif';
break;
case 'delicious':
$attributes = array(
'href' => 'http://del.icio.us/post?url=' . rawurlencode($currentUrl),
'title' => 'Add to del.icio.us',
'onclick' => 'window.open(\'http://del.icio.us/post?v=4&noui&jump=close&url='
. rawurlencode($currentUrl) . "&title=" . rawurlencode($pageTitle)
. "','delicious', 'toolbar=no,width=700,height=400'); return false;",
);
$icon = 'delicious.gif';
break;
case 'twitter':
$attributes = array(
'href' => 'http://twitter.com/home?status='
. rawurlencode('Currently reading ' . $pageTitle . ' at ' . $currentUrl ),
'title' => 'Tweet This!',
'target' => '_blank',
);
$icon = 'twitter.gif';
break;
default:
return array();
break;
}
$item = array(
'text' => $attributes['title'],
'attributes' => $this->_serializer->setData($attributes)->serialize(),
'image' => $this->getSkinUrl("images/" . $icon),
);
return $item;
}
}
Create templates
app/design/frontend/default/default/widgettwo/text.phtml:
<?php foreach ($list as $item) : ?>
<a <?php echo $item['attributes']; ?>><?php echo $this->escapeHtml($item['text']); ?></a>
<?php endforeach; ?>
app/design/frontend/default/default/widgettwo/icons.phtml:
<?php foreach ($list as $item) : ?>
<a <?php echo $item['attributes']; ?>><img src="<?php echo $item['image']; ?>" title="<?php echo $this->escapeHtml($item['text']); ?>" border="0" /></a>
<?php endforeach; ?>
Add widget instances in the admin panel
Let's go to CMS > Pages in the admin panel and add a widget instance to the homepage in a WYSIWYG editor or plain text mode:

Look how does the inserted widget look like in the plain text mode:

Check over the frontend

Conclusion
As you can see, there is no need anymore to follow the long complicated instructions, like ... and after you install our extension please insert this code into a CMS static block, and set proper IDs and values which you can find by looking into the ... or ... insert this code into ... template and then you will be able to add the following layout update instructions to your layout files or to the selected catalog categories on the 'Custom Design' tab ...
Magento Widgets allow a store owner with no technical knowledge to easily add configurable content block to frontend pages in his Magento store. That saves a lot of time and results in mutual benefit for store owners and extension developers.
This module is available on the Magento Connect.
You can also download the full source code archive in tar.gz, tar.bz2 or zip formats.




