Develop new widget - Part 1 - Create and install widget

From TomatoCMS Documentation

Jump to: navigation, search

Contents

Step 1: Create widget directory

Example, we have to create widget with name is links to view title and colection of links to put into the footer of page.
It should belong to the utility module because it won't take data from other modules.
Inside the /tomatocms/application/modules/utility/widgets directory, create a directory named links.

In the links directory, create widget information file named about.xml:

/application/modules/utility/widgets/links/about.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE widget SYSTEM "http://schemas.tomatocms.com/dtd/widget_about.dtd">
<widget>
   <name>Links</name>
   <title>Show the links</title>
   <module>utility</module>
   <description>Show the links</description>
   <thumbnail></thumbnail>
   <author>TomatoCMS Core Team</author>
   <email>core@tomatocms.com</email>
   <version>2.0.8</version>
   <license>free</license>
   <params>
      <param name="title">
         <description>The title</description>
      </param>
      <param name="links">
         <description><![CDATA[The links in JSON format]]></description>
      </param>
   </params>
   <resources></resources>
</widget>

Note that in the about.xml, there are two parameters named title and links which will be used to store the title and collections of links in JSON format.
Next, in the links directory, create a file named Widget.php. In this file, we create a class named Utility_Widgets_Links_Widget extending from the Tomato_Widget class.

/application/modules/utility/widgets/links/Widget.php

class Utility_Widgets_Links_Widget extends Tomato_Widget 
{
   protected function _prepareShow() 
   {
   }
}

This class plays a role as a controller class for widget. It has to implement a method named protected function _prepareShow() to render the widget.

Step 2: Install widget

In the back-end section, go to the System > Extend > Widget menu.
Click on the utility from the list of modules, click on the Not Installed button on the right side to see the list of not installed widgets.
Finally, click on the Install button in the links widget section.

File:create_widget_install_links.jpg

You can see that, all widget information here including version, author, name and description are taken from the widget information file - about.xml

Step 3: Put the widget on page using the Layout Editor

Assume that we want to show the widget on the home page.
Click on the System > Customize > Template menu, next, click on the List of pages link belonging to the current template. Click on the Edit Layout link associating with the Home page section.

Under the Layout tab, drag the 12 cols and drop it to the layout of home page.
Next, under the Widget tab, click on the utility from the list of modules, drag the links widget and drop it to the container you have just dropped above.
At this moment, you will see the default output when using the Layout Editor including the cache lifetime in seconds and the option to load the widget by Ajax.
Don't forget to press the Save button at the top of page.

File:create_widget_edit_layout.jpg

Step 4: Build widget configuration form

How to build the widget configuration form?

We will add some HTML elements allowing user to set the title of all links, the label and the URL of link to add and show the list of links at the end.

File:create_widget_configuration_form.jpg

In widget directory, create a file named config.phtml and put the following content to it:

/application/modules/utility/widgets/links/config.phtml

<!-- Title (1) -->
<?php echo $this->translator()->widget('config_title'); ?>
<br />
<!-- Store the title parameter (2) -->
<input type="text" class="t_widget_input" name="title" style="width: 200px" />
<hr />
 
<!-- Label and URL of new link (3) -->
<div class="t_a_bottom">
   <?php echo $this->translator()->widget('config_label'); ?>:
   <br />
   <input type="text" style="width: 200px" id="utilityLinksLabel" />
</div>
<div class="t_a_bottom">
   <?php echo $this->translator()->widget('config_url'); ?>:
   <br />
   <input type="text" style="width: 200px" id="utilityLinksUrl" />
</div>
<!-- Button for adding new link (4) -->
<div class="t_a_bottom">
   <button id="utilityLinksButton" class="t_a_ui_button_link"><span><?php echo $this->translator()->widget('config_add_button'); ?></span></button>
</div>
 
<div class="clearfix t_a_bottom"></div>
 
<!-- List of links (5) -->
<div class="t_a_bottom">
   <table id="utilityLinksTable" class="t_a_ui_table">
      <thead>
         <tr>
            <th style="width: 200px"><?php echo $this->translator()->widget('config_url'); ?></th>
         </tr>
      </thead>
      <tbody>
      </tbody>
   </table>
</div>
 
<!-- Store the links in JSON format (6) -->
<input type="hidden" id="utilityLinks" name="links" class="t_widget_input" />

(1): We render the Title label on the form by using

<?php echo $this->translator()->widget('config_title'); ?>

Of course, you can show the Title label directly by displaying

Title

but in this case, we want to support multilingual in the widget.
In the config.phtml, you also see that we used this method to render the label, URL, and button label.
So, in the widget directory, create a file named lang.en_US.ini and map the language keys with its values:

/application/modules/utility/widgets/links/lang.en_US.ini

[config]
config_title = "Title:"
config_label = "Label"
config_url = "URL"
config_add_button = "Add"

(2), (6): Store the widget paramaters
As mentioned at the beginning of this tutorial, the widget has two parameters: title and collections of links which have the name of title and links respectively.
Also, you have to add an CSS class named t_widget_input to all widget parameter elements. This CSS class is used to indicate which elements are the widget parameters and which are not, and TomatoCMS will save the widget parameters values to the layout file automatically based on this CSS class.

We allow user to enter the title but all the links - which will be stored in the JSON format - will be generated from the collection of links, that is why we created it as the hidden element:

  • The title element:
<input type="text" class="t_widget_input" name="title" style="width: 200px" />
  • The links element:
<input type="hidden" id="utilityLinks" name="links" class="t_widget_input" />

(3), (4): Render the label, URL text boxes and the button to add the link
(5): The table for showing all the links

Next, we will handle the event when user click on the Add button to add new link.

/application/modules/utility/widgets/links/config.phtml

...
<script type="text/javascript">
$(document).ready(function() {
   // (1)
   var utilityLinks = Array();
 
   $('#utilityLinksButton').click(function() {
      // (2)
      var tr = $('<tr/>');
      var td = $('<td/>');
      $(td).html($('#utilityLinksLabel').val());
      $(tr).append($(td)).appendTo($('#utilityLinksTable tbody'));
 
      // (3)
      utilityLinks.push({
         'label': $('#utilityLinksLabel').val(),
         'url': $('#utilityLinksUrl').val()
      });
 
      // (4)
      $('#utilityLinks').val($.toJSON(utilityLinks));
   });
});
</script>

We use a simple Javascript array to store the array of links (1).
After clicking on the Add button, we have to add new row to the links table (2), also, push the added link to the array of links (3), and set the encoded links in JSON format for the utilityLinks element (4).

The question is how to remove the added link?
When adding new link, we insert new a element to new row in the links table, and remove the link from the collections if user click on it.

/application/modules/utility/widgets/links/config.phtml

...
<script type="text/javascript">
// (1)
function getLinks() {
   var utilityLinks = Array();
   $('#utilityLinksTable tbody').find('a').each(function() {
      utilityLinks.push({
         'label': $(this).html(),
         'url': $(this).attr('href')
      });
   });
   return utilityLinks;
};
 
$(document).ready(function() {
   $('#utilityLinksButton').click(function() {
      var tr = $('<tr/>');
      var td = $('<td/>');
 
      // (2)
      var a  = $('<a/>').attr('href', $('#utilityLinksUrl').val())
         .html($('#utilityLinksLabel').val())
         .click(function() {
            $(link).parents('tr').remove();
            $('#utilityLinks').val($.toJSON(getLinks()));
            return false;
         });
 
      $(td).append($(a));
      $(tr).append($(td)).appendTo($('#utilityLinksTable tbody'));
 
      // (3)
      $('#utilityLinks').val($.toJSON(getLinks()));
   });
});
</script>

In the above code section, we create a function named getLinks() to return all the links (1). It finds all the a elements in the links table and put the found link to an array.
This function will be called to update the value of the utilityLinks element when removing the added link from the table (2).

Now, try to add some links with labels and URLs using the configuration form:

File:create_widget_add_link.jpg

and you will see how it stored the link collections in JSON format:

File:create_widget_how_store_link.jpg

Finally, press the Save button at the top of page to save the layout.
Open the layout configuration file, core_index_index.xml, located at the /application/templates/default/layouts directory, you will see that the Layout Editor saved our widget configurations like that:

<container cols="12">
        <widget module="utility" name="links" load="php">
            <title><![CDATA[Show the links]]></title>
            <params>
                <param name="title"><value><![CDATA[Categories]]></value></param>
                <param name="links"><value><![CDATA[[{"label": "v2.0.0: Initial version", "url": "http://localhost/tomatocms/index.php/news/category/view/1/"}, {"label": "v2.0.1: Install Wizard", "url": "http://localhost/tomatocms/index.php/news/category/view/2/"}, {"label": "v2.0.2: Tag Module", "url": "http://localhost/tomatocms/index.php/news/category/view/3/"}, {"label": "v2.0.3: Improves Hook", "url": "http://localhost/tomatocms/index.php/news/category/view/4/"}, {"label": "v2.0.4: Improves Core", "url": "http://localhost/tomatocms/index.php/news/category/view/5/"}, {"label": "v2.0.5: Multiple Databases", "url": "http://localhost/tomatocms/index.php/news/category/view/6/"}, {"label": "v2.0.6: Mail Module", "url": "http://localhost/tomatocms/index.php/news/category/view/7/"}, {"label": "v2.0.7: SEO Module", "url": "http://localhost/tomatocms/index.php/news/category/view/8/"}, {"label": "v2.0.7.1: Page Module", "url": "http://localhost/tomatocms/index.php/news/category/view/9/"}, {"label": "v2.0.7.8: Localization", "url": "http://localhost/tomatocms/index.php/news/category/view/9/"}]]]></value></param>
            </params>
        </widget>
    </container>

Step 5: Show the links collection in the configuration form

At this time, we can add, remove the links and save them to the layout.
The purpose of this section is that help you how to get the links collection from the layout and show them in the configuration form.

In the widget class, add a method named protected function _prepareConfig().

/application/modules/utility/widgets/links/Widget.php

class Utility_Widgets_Links_Widget extends Tomato_Widget 
{
   protected function _prepareShow() 
   {
   }
 
   protected function _prepareConfig() 
   {
   }
}

In this method, we can get the parameter value from the layout as follow:

/application/modules/utility/widgets/links/Widget.php

class Utility_Widgets_Links_Widget extends Tomato_Widget 
{
   protected function _prepareShow() 
   {
   }
 
   protected function _prepareConfig() 
   {
      $params = $this->_request->getParam('params');
      if ($params) {
         // (1)
         $params = Zend_Json::decode($params);
         $links  = $params['links']['value'];
 
         // (2)
         $links  = Zend_Json::decode($links);
         $this->_view->assign('ultilyLinks', $links);
      }
   }
}

(1) is used to get the links collection in JSON format and (2) is used to decode the links collection and assign it to the view layer.
Next, after assigning the links collection to the view layer, we can loop through the collections and show each link in the links table:

/application/modules/utility/widgets/links/config.phtml

...
<table id="utilityLinksTable" class="t_a_ui_table">
   <thead>
      ...
   </thead>
   <tbody>
      <?php if ($this->ultilyLinks) : ?>
      <?php foreach ($this->ultilyLinks as $link) : ?>
      <tr>
         <td><a href="<?php echo $link['url']; ?>" onclick="javascript: return removeLink($(this));"><?php echo $link['label']; ?></a></td>
      </tr>
      <?php endforeach; ?>
      <?php endif; ?>
   </tbody>
</table>
...

User can removing the link by clicking on the link, so, we have to create a new Javascript method named removeLink. Of course, we can reuse this method to handle the click event when user create new link:

/application/modules/utility/widgets/links/config.phtml

...
<script type="text/javascript">
function removeLink(link) {
   $(link).parents('tr').remove();
   $('#utilityLinks').val($.toJSON(getLinks()));
   return false;
};
 
function getLinks() {
   ...
};
 
$(document).ready(function() {
   $('#utilityLinksButton').click(function() {
      var tr = $('<tr/>');
      var td = $('<td/>');
      var a  = $('<a/>').attr('href', $('#utilityLinksUrl').val())
         .html($('#utilityLinksLabel').val())
         .click(function() {
            return removeLink($(this));
         });
 
      ...
   });
});
</script>

The interesting point is that you don't need to initialize the values for the widget elements, such as the title and the hidden links elements, because it is done by TomatoCMS automatically.

Step 6: Render the links on the front-end

In order to render the links on the front-end section, first, we get the parameters from the request (1), decode the links collection (2), and assign them to the view layer (3):

/application/modules/utility/widgets/links/Widget.php

class Utility_Widgets_Links_Widget extends Tomato_Widget 
{
   protected function _prepareShow() 
   {
      // (1)
      $title = $this->_request->getParam('title');
      $links = $this->_request->getParam('links');
 
      // (2)
      $links = Zend_Json::decode($links);
 
      // (3)
      $this->_view->assign('title', $title);
      $this->_view->assign('links', $links);
   }
 
   protected function _prepareConfig() 
   {
      ...
   }
}

Next, we create a file named show.phtml in the widget directory to render the links:

/application/modules/utility/widgets/links/show.phtml

<?php if ($this->links) : ?>
<div class="t_utility_links">
   <h2><?php echo $this->title; ?></h2>
 
   <ul>
   <!-- Loop over the links collection -->
   <?php foreach ($this->links as $link) : ?>
      <li><a href="<?php echo $link['url']; ?>"><?php echo $link['label']; ?></a></li>
   <?php endforeach; ?>
   </ul>
</div>
<?php endif; ?>

Come back to the front-end section and see the result:

File:create_widget_front_end.jpg

Step 7: Finally, style it

Above, we set the CSS class named t_utility_links for the widget container. Therefore, to style the widget output, we can add the following CSS styles to the skin file:

/skins/default/default/default.css

...
/** links */
.t_utility_links h2 { font-family: Georgia, Tahoma, Arial, sans-serif; font-size: 15px; font-weight: normal; height: 30px; line-height: 30px; color: #999; }
.t_utility_links li { background: url(images/square.jpg) left 8px no-repeat; padding-left: 10px; padding-bottom: 5px; }

The output looks nice:

File:create_widget_nice_output.jpg

Personal tools