Wednesday, December 29, 2010

Liferay (s)mashup - The Beginning


Part Two - Liferay (s)mashup - Google Maps

There are many popular web services available online. From the rich set of APIs from Google and Yahoo to social sites likeTwitter and Facebook, almost all service providers make available their services in the form of an open API. These rich APIs has given rise to new kind of applications called mashups. They are basically an integration of various web APIs from different providers put together to form new kind of applications.

In this and the next series of blogs, I will show how to integrate with some of these APIs.

To make meaningful use of these APIs, let me propose a sample application where these services can be used. Let us develop a travel planning site that helps user to plan their travel dates and location. Travelling is just not about location and date. There are other factors like weather conditions, transport availabiity, political turmoil, other events clashing with date. Questions such as 'Are any of my friends travelling on that date or to that location?', 'Do I know anyone from that location I am going to?' frequently comes up. Our site will try to resolve these questions so that a probable date and location can be zeroed on.

Features of the Application
 - Display input form for receiving the source, destination, journey date and duration.
 - Show the road route of the origin and destination in Google map
 - Show weather information of origin and destination
 - Show the event happening in origin and destination on and around the journey date
 - Show news related to origin and destination
 - Moving the marker on the Google map must also update all other information in real time like weather, events, news and  the input form.
 - More services as we go along

For the first requirement, create a new portlet. In the view.jsp file, design a basic form  for the user to fill in: Origin, Destination, Journey Date and Duration.

Code Listing

<form action="<liferay-portlet:actionURL portletConfiguration="true" />" method="post" name="<portlet:namespace />fm">
    <aui:input name="origin" label="Origin" />
    <aui:input name="destination" label="Destination" />
    <aui:input name="journeyDate" label="Journey Date" />
    <aui:input name="duration" label="Duration" suffix="(in days)" />
    <input type="button" value="Search" onclick="<portlet:namespace />postPlanDetails()" />
</form>

The journeyDate is shown as a calendar when user clicks on the input field. Define it in javascript as:

AUI().ready('aui-calendar', function(A) {
    var calendar1 = new A.Calendar({
        trigger : '#<portlet:namespace />journeyDate',
        dateFormat : '%d/%m/%Y',
        setValue : true,
        selectMultipleDates : true,
        on : {
            select : function(event) {
                var normal = event.date.normal;
                var detailed = event.date.detailed;
                var formatted = event.date.formatted;
            }
        }
}).render();

A.on('mousedown', function() {
    A.CalendarManager.hideAll();
}, document);

});

The postPlanDetails() javascript function is defined as follows:

function <portlet:namespace />postPlanDetails() {
    var origin = document.<portlet:namespace />fm.<portlet:namespace />origin.value;
    var dest = document.<portlet:namespace />fm.<portlet:namespace />destination.value;
    var journeyDate = document.<portlet:namespace />fm.<portlet:namespace />journeyDate.value;
    var duration = document.<portlet:namespace />fm.<portlet:namespace />duration.value;

    Liferay.fire('planTravel', {
        origin : origin,
        destination : dest,
        journeyDate : journeyDate,
        duration : duration
    });

}

The code in bold above functions publishes the 4 parameters as an event using Liferay client side Inter Portlet Communication. For more details on IPC, see Portlet to Portlet Communication.

This portlet also receives the 'directionsChanged' event from the Google Maps portlet. We will discuss this in the next blog.

Liferay.on('directionsChanged',
    function(event) {
        document.<portlet:namespace />fm.<portlet:namespace />origin.value = event.origin;
        document.<portlet:namespace />fm.<portlet:namespace />destination.value = event.destination;
});

Friday, December 24, 2010

Speeding up page loading

Developing fast loading pages is a complex task. Some have even defined it as an art. Large companies have dedicated professionals or hire external consultants to audit their applications for speed and responsiveness.

Here I provide some simple tips to speed up page loading in Liferay. Liferay also provides some in built features for fast page loading. Whenever available, Liferay support will be shown for each tip.


Tip 1: Sharing script and css files across portlets

For each script and css file included in a page, the browser makes separate requests to fetch them. This takes time. If the same file is included many times, the browser only makes requests the first time and for subsequent access, it picks from the cache. Therefore, common javascript code should be placed in a single file and this file should be included in all portlets that need it.

To share scripts across portlets, use the <header-portlet-javascript> tag or <footer-portlet-javascript> tag in the liferay-portlet.xml file. Similarly, use <header-portlet-css> tag or the <footer-portlet-css> tag in the liferay-portlet.xml file.


Tip 2: Lazy loading of scripts

Sometimes, scripts are not immediately needed after page load but only after user takes some action. To prevent such scripts being loaded on page load, we can load in the background after the page has loaded. There are two ways for lazy loading. One, the script is added to the <head> tag and the other is to add it to the <body> tag.

Adding to head tag:
function loadScript() {
    var script = document.createElement("script");
    script.type = "text/javascript";
    script.src = "";
    var head = document.getElementsByTagName("head")[0];
    head.appendChild(script);

}

window.onload = loadScript;


The function loadScript() above creates a dynamic script tag and adds it to the first head tag available. It registers this function to be called on load of the page.

Adding to body tag:

function loadScript() {
    var script = document.createElement("script");
    script.type = "text/javascript";
    script.src = "";
    document.body.appendChild(script);
}

window.onload = loadScript;


The function loadScript() above creates a dynamic script tag and adds it to the body. It registers this function to be called on load of the page.


Tip 3: Minimize script, stylesheet and html output

Whitespace and comments in script file or html pages takes up bandwith and so slows down page load. While comments and whitespaces are necessary for development, in production they should be removed. It would be nice to not to change the source code for production while still be able to remove the comments and whitespaces. A simple technique for this would be to use server side comments in jsp files and not to use html or javascript comments. Whitespaces cannot be handled this way.
Liferay provides a minifier process that strips down both whitespaces and comments from script and css files. The minifier also shortens function and variable names to make the script even smaller. To enable the minifier, set the option in the portlet-ext.properties file.

com.liferay.portal.servlet.filters.minifier.MinifierFilter=true

Another option Liferay provides is to load packed (compressed) versions of Liferay scripts and css. Enable it using

javascript.fast.load=true

By default, both the options are true. Make sure, they are not false on production systems.


Tip 4: Specify image and table width and height

If the browser can immediately determine the height and/or width of images and tables, it will be able to display a web page without having to reflow the content. This not only speeds the display of the page but prevents annoying changes in a page's layout when the page completes loading. For this reason, height and width should be specified for images and tables, whenever possible.

Monday, November 15, 2010

Integrating Coin Slider with Liferay with zero development

In this blog, I will show you how to integrate the Coin Slider library in a Liferay portlet without having to develop any portlet. Coin Slider is a jQuery based image slider with many effects for image transition.

Note: This solution is based on Liferay 6.0.5

Download and extract the latest version of the Coin Slider library from http://workshop.rs/projects/coin-slider/

Create a folder slideshow in the document library of the community where you want to use the slider. Add the files coin-slider-min.js, coin-slider-styles.css, jquery-1.4.2.min.js to the slideshow folder. Create a new file javascript.js file in filesystem. Add the following code to this file:

$(document).ready(function() {
    $("#coin-slider").coinslider({ hoverPause: false });
});
 

Upload the javascript.js file to the slideshow document library. The slideshow folder should now look like this:

Next upload few images that you want to display in the slider in the Image Gallery.

There are 3 images in the image gallery above.

Now create a new web content structure. Name it coin-slideshow. Add xml schema definition to the structure as shown below:
Click Save when done.

Create a new web content template and name it coin-slideshow-template. Select coin-slideshow as the structure for the new template. Click the Launch Editor button. In the dialog that appears, paste the following code:


<link href="$css.getData()" rel="stylesheet" type="text/css" />
<script type="text/javascript" src="$jquery.getData()"></script>
<script type="text/javascript" src="$coinjs.getData()"></script>
<script type="text/javascript" src="$slidestart.getData()"></script>

<div id="coin-slider">
#foreach ($item in $slide.getSiblings())
   #foreach ( $itemData in $item.getChildren() )
        #if ( "$itemData.getName()" == "href" )
          #set ( $href = $itemData.getData() )
        #elseif ( "$itemData.getName()" == "img_src" )
          #set ( $img_src = $itemData.getData() )
        #elseif ( "$itemData.getName()" == "description" )
          #set ( $description = $itemData.getData() )
        #end
    #end
    <a href="$href"><br />
        <img src="$img_src" />
        <span>$description</span>
    </a>
#end
</div>



Click Update button when done. Click Save again in the main dialog.

Now is the time to add the slider in our page. Add Web Content Display portlet to the page where you want the slider. Click the Add Web Content link in the portlet to create content for the portlet. In the new dialog box, give a name to the content, say Slideshow. Change the structure of the web content by clicking the Choose button under the Structure section on the right. Select the structure coin-slideshow that we created earlier. This will automatically choose the template that we associated with the structure. Instead of the HTML editor, a web form with fields are shown.

In the css field, choose the coin-slider-styles.css from the document library. Similarly, choose jquery-1.4.2.min.js for jquery field, coin-slider-min.js for coinjs field and javascript.js for coinstart field. For the first slide, name it slide1. Select a image from the Image Gallery for the img_src field. Select target location when an image is clicked from the dropdown list for the href field. Type some text to be displayed for the image in the description field. Similarly, add more slides for each image that you want to be displayed.


Click Publish button to save the content and make it viewable.