Portlet

Liferay Gallery Portlet

Our latest Liferay Image Gallery Portlet is finally available on the marketplace. We have teamed up with our good friends at Infira and implemented a really neat looking image gallery that you can use to present your portfolio, products, contacts or team event pictures.

Our Fancy animated liferay gallery portlet with document library support has these features:

  • Uses your specified Liferay built-in Document Library Folder as the image source.
  • Fully Responsive Design
  • Automatic Image Positioning
  • Click-to-enlarge
  • Ability to show/hide title and description of image
  • Supports multiple instances per page
  • Configurable (you can adjust all aspects of the portlet)
  • Adjustable colors and thumbnail size

screen4

screen5

screen1

 

This portlet is based on a special version of Opie Scripts Responsive Fully Customizable jQuery Portfolio Gallery.

For those of you who have been following our latest activity in the Liferay world you might want to know that we are working hard at the moment to release our next item into the marketplace which will be a super fancy dashboard theme that transforms your Liferay into really nice dashboard that’s properly suitable for the big screens and small screens. Stay tuned!

 

Liferay Birthday List portlet

cake

Just wanted to let you guys know that our very basic version of Liferay Birthday List portlet is now available for FREE on Liferay Marketplace.

You can download and install it straight from the Marketplace.
liferay birthday list portlet

The portlet relies on the Liferay’s internal user database and displays the birthdays of Yesterdays, Todays and Tomorrows birthdays.

Can’t really say that’s an achievement, but at least it’s our first Portlet on the Marketplace.
We plan to start publishing many useful Portlets to make the Liferay Ecosystem a little bit friendlier to people who don’t have development teams just hanging around, waiting for work…

We will probably publish a more feature-rich Liferay Birthday List portlet that would also show Work Anniversaries and allow sending quick Congratulations using Liferay Notifications.

Liferay allPortletsReady event not firing

Today I stumbled upon a problem where I noticed that on some of my pages my portlet was acting weird and the culprit of the problem was the Liferay specific javascript “allPortletsReady” event not firing. I was using the latest version of Liferay 6.2 CE.

So I debugged and Googled around and went through a pretty large array of possible problems for why the allPortletsReady was not working for other people.

Some results suggested that the Related Assets portlet might cause the problem – That’s not the case this time though as I wasn’t using that portlet on my pages.

Next up on the list is Notifications Portlet and sure enough I had no Notification Portlet on those pages either.

Finding no obvious solutions using quick searches on Google and StackOverflow I decided to start testing and debugging. So I created a new clean page and added my portlet on that one. This time the portlet had it’s “allPortletsReady” event nicely firing so I now know that my portlet works on new and clean pages.

Then I deleted all portlets, including my own, from an old page where the portlet was not getting it’s allPortletsReady Javascript event called and then I added my portlet back in there. This time the Javascript event remained unfired and problem was still around the corner.

So by now we know that it works on clean new pages and does not work on old pages even after removing all portlets. I even tried switching themes which had no result.

Liferay allPortletsReady Solution (One of many)

Finally it dawned upon me – there has to be a difference on the pages between the new and old page – I checked the page’s embedded portlets section and discovered that even though I no longer had a Login portlet on those pages, they still had Liferay built-in Login portlet ID 58 listed there with some preferences saved. I removed those preferences and sure enough the allPortletsReady started firing again as normal.

Here’s a screenshot of the embedded portles section (I already removed the Login portlet from the list):
Liferay page embedded portlets

So it seems Liferay has some kind of an issue with old embedded Portlet preferences sticking around and causing issues with our Javascript events.


Solution: Remove old unused embedded portlet preferences from the page.

If you have a question or having trouble getting this thing working then feel free to write me a comment and I’ll do my best to help you out. If you would like to read more about Liferay related subjects then also make sure to comment on that and if you liked what you read then make sure to subscribe – It’s FREE!

Liferay portlet file upload tutorial

Here’s a Liferay portlet file upload tutorial to show you how to handle file uploading by clients.
First we need to define our upload form JSP. This can be shown using Liferay MVCPortlet’s default view.jsp.
We will define our upload form as an AlloyUI form element and w

view.jsp


<%@ taglib uri="http://java.sun.com/portlet_2_0" prefix="portlet" %>
<%@ taglib uri="http://liferay.com/tld/aui" prefix="aui"%>
<%@ taglib uri="http://liferay.com/tld/ui" prefix="liferay-ui"%>
<portlet:defineObjects />

<portlet:actionURL name="upload" var="uploadFileURL"></portlet:actionURL>

<aui:form action="<%= uploadFileURL %>" enctype="multipart/form-data" method="post">

	<aui:input type="file" name="fileupload" />
	
	<aui:button name="Save" value="Save" type="submit" />

</aui:form>

And now we need to handle the upload action that we are calling by submiting this form.
Note some important parts that people often miss in their code, like checking if there’s enough room on the device to actually store the file and refuse additional file uploads when the device has less than 1GB of free space.

Full example class


import java.io.File;

import javax.portlet.ActionRequest;
import javax.portlet.ActionResponse;

import org.apache.commons.io.FileUtils;

import com.liferay.portal.kernel.upload.UploadPortletRequest;
import com.liferay.portal.util.PortalUtil;
import com.liferay.util.bridges.mvc.MVCPortlet;

public class RelatedFilesPortlet extends MVCPortlet {

	private final static int ONE_GB = 1073741824;
	
	private final static String baseDir = "/tmp/uploaded/";
	
	private final static String fileInputName = "fileupload";

	public void upload(ActionRequest request, ActionResponse response)
			throws Exception {

		UploadPortletRequest uploadRequest = PortalUtil.getUploadPortletRequest(request);

		long sizeInBytes = uploadRequest.getSize(fileInputName);

		if (uploadRequest.getSize(fileInputName) == 0) {
			throw new Exception("Received file is 0 bytes!");
		}

		// Get the uploaded file as a file.
		File uploadedFile = uploadRequest.getFile(fileInputName);

		String sourceFileName = uploadRequest.getFileName(fileInputName);

		
		// Where should we store this file?
		File folder = new File(baseDir);

		// Check minimum 1GB storage space to save new files...
		
		if (folder.getUsableSpace() < ONE_GB) {
			throw new Exception("Out of disk space!");
		}

		// This is our final file path.
		File filePath = new File(folder.getAbsolutePath() + File.separator + sourceFileName);

		// Move the existing temporary file to new location.
		FileUtils.copyFile(uploadedFile, filePath);
	}

}

Java tutorial: Add Portlet to Liferay Control Panel

You can add portlet to liferay control panel. For your custom control panel portlet you have to modify liferay-portlet.xml file and add 2 lines there:

<control-panel-entry-category>portal</control-panel-entry-category>
<control-panel-entry-weight>1.0</control-panel-entry-weight>

The control-panel-entry-category has to be one of “my”, “content”, “portal” or “server” and specifies under which category should the portlet be displayed.
The weight specifies the ordering or the elements under the different categories. It’s a double number, and the higher you specify the lower your portlet is placed. So if you want to appear in the bottom, add “99.0”.
The portlet has to be non-instanceable (the Control Panel may have only such portlets) so you also have to have this line:

<instanceable>false</instanceable>

Please note that Control Panel by default does not allow you to enter CONFIG mode of the portlet. For a work-around you can use

<preferences-unique-per-layout>false</preferences-unique-per-layout>

Full example liferay-portlet.xml

<portlet>
 <portlet-name>hello-world-portlet</portlet-name>
 <icon>/icon.png</icon>
 <control-panel-entry-category>portal</control-panel-entry-category>
 <control-panel-entry-weight>99.0</control-panel-entry-weight>
 <action-url-redirect>true</action-url-redirect>
 <instanceable>false</instanceable>
 <preferences-unique-per-layout>false</preferences-unique-per-layout>
</portlet>

add portlet to liferay control panel

Why use Liferay?

The Good side of Liferay

Liferay is NOT just a Java based CMS. It’s a complete portal environment that’s suitable for large enterprise usage. It should be used as a platform to develop in-house applications or intranet. If you need just a small website to promote something then Liferay is not for you.

Some people compare Liferay with PHP based CMS systems like Joomla or Wordpress but that’s completely wrong. Liferay does contain CMS and it’s not a bad one. It’s rather useful for providing small applications and even better for big applications.

Supporting JSR 168 and JSR 286 means you can deploy Portlet applications that others have developed. Liferay also has a very good support for groups and communities allowing you to build team sites and custom environments for different user-groups with ease. Some cool features that you get with Liferay out of the box are:

  • User and Group management
  • Multi-site support for having different pages and themes for different user groups
  • Proper Access rights and Role management
  • Internationalization
  • Document library management
  • Wiki, Blogging and Forums
  • Service Oriented Architecture
  • Theme support
  • Integrates with other existing systems using LDAP, RSS, iFrames and other technologies

Liferay

 

The Bad side of Liferay

Developing applications and portals for the first time is a taunting task and comes with a rather massive learning curve. If you plan to use Liferay Service Builder and do everything “the Liferay way” then you have to plan probably at least couple of weeks for learning it first.

If you need to develop a small application quickly AND you don’t have an existing Liferay installation already in-house then you can get your application done way faster by NOT using Liferay. But once you have learned Liferay and have enough need for proper portal then Liferay becomes rather irresistible.

When it comes to documentation and tutorials then it’s not the best situation either. It does have a massive developer’s guide. But the Wiki is filled with tons of content that’s rather old and not updated for the latest releases.

The official developer and administration guides are good but often lack information about some crucial parts or fail to provide decent examples. While they claim to be developer friendly I have to admit that some parts of the development platform is severely lacking. Also note that releases are rather infrequent. Also one should be very cautious because early versions are always rather buggy.

AlloyUI that’s provided with Liferay for Javascript and UI is rather difficult to learn and seems also a very large investment time-wise – save yourself the trouble and use jQuery or some other favorite JS library instead.

Download PDF or JPG from portlet

To create a Download PDF link or serve images in your portlet, you need to implement serveResource method in your Liferay portlet class. Here’s an example on how to serve a JPG file from the server.

First we need to define a link to our file, this can be done using portlet taglib resourceURL like this.

<portlet:resourceURL var="niceImageUrl">
	<portlet:param name="fileName" value="flowers.jpg" />
</portlet:resourceURL>
<img src="<%= niceImageUrl %>" alt="flowers.jpg" />

Please note that you need to have portlet taglib in your JSP file header.

<%@ taglib uri="http://java.sun.com/portlet_2_0" prefix="portlet" %>

This generates a resource URL, so now we must implement the serveResource method to handle this call.

public class ImagesPortlet extends MVCPortlet {

	@Override
	public void serveResource(ResourceRequest request, ResourceResponse response)
			throws IOException, PortletException {

		String fileName = ParamUtil.getString(request, "fileName");

		File outputFile = new File("/Folder/On/Server/"+fileName);

		response.setContentType("image/jpg");
		OutputStream out = response.getPortletOutputStream();
		InputStream in = new FileInputStream(outputFile);
		IOUtils.copy(in, out);
		out.flush();
	}	
}

For PDF files you could easily tune it to have a link to your PDF like this:

<portlet:resourceURL var="pdfDownloadUrl">
	<portlet:param name="fileName" value="document.pdf" />
</portlet:resourceURL>
<a href="<%= pdfDownloadUrl %>">Download PDF</a>

And in the portlet class you can use code like this

public class PDFPortlet extends MVCPortlet {

	@Override
	public void serveResource(ResourceRequest request, ResourceResponse response)
			throws IOException, PortletException {

		String fileName = ParamUtil.getString(request, "fileName");

		File outputFile = new File("/Folder/On/Server/"+fileName);

		response.setContentType("application/pdf");
		OutputStream out = response.getPortletOutputStream();
		InputStream in = new FileInputStream(outputFile);
		IOUtils.copy(in, out);
		out.flush();
	}	
}

It’s important to use application/pdf content type so that the browser would know how to properly handle the file.

If you need to output multiple file types or handle different actions then you can pass an extra parameter that decides what file type to serve. Another idea could be to use a parameter to decide between serving a file or serving a JSON response, in case you need need to retrieve some data using AJAX. Here’s a simple example on how to pass content type in the URL to use it during serving the file.

<portlet:resourceURL var="pdfDownloadUrl">
	<portlet:param name="type" value="application/pdf" />
	<portlet:param name="fileName" value="document.pdf" />
</portlet:resourceURL>
<a href="<%= pdfDownloadUrl %>">Download PDF</a>

<portlet:resourceURL var="niceImageUrl">
	<portlet:param name="type" value="image/jpg />
	<portlet:param name="fileName" value="flowers.jpg" />
</portlet:resourceURL>
<img src="<%= niceImageUrl %>" alt="flowers.jpg" />

And in the portlet class you can just output the provided content type without having to detect it during download time. This allows you to define the expected content types dynamically, which can be very useful when you are generating a file list.

public class FilesPortlet extends MVCPortlet {

	@Override
	public void serveResource(ResourceRequest request, ResourceResponse response)
			throws IOException, PortletException {

		String type = ParamUtil.getString(request, "type");
		String fileName = ParamUtil.getString(request, "fileName");

		File outputFile = new File("/Folder/On/Server/"+fileName);

		response.setContentType( type );
		OutputStream out = response.getPortletOutputStream();
		InputStream in = new FileInputStream(outputFile);
		IOUtils.copy(in, out);
		out.flush();
	}	
}

More info on how to generate PDF on the fly can be read in this useful blog post.

As always, free feel to ask questions and provide suggestions in the comments.

AUI Autocomplete with service builder json datasource

Since it’s not so trivial to correctly understand AlloyUI Autocomplete that’s shipped with Liferay 6.2 then here’s an example on how to make an autocomplete that uses a custom service builder json web service as a datasource.
We will use AUI Autocomplete widget and then user Liferay Service library to fetch the result from our own custom service builder built JSON web service.

This is the look we are trying to achive:
aui autocomplete service builder

Full portlet view.jsp code:


<%@ taglib uri="http://java.sun.com/portlet_2_0" prefix="portlet" %>
<%@ taglib uri="http://liferay.com/tld/theme" prefix="liferay-theme" %>
<%@ taglib uri="http://liferay.com/tld/ui" prefix="liferay-ui"%>
<%@ taglib uri="http://liferay.com/tld/aui" prefix="aui" %>

<portlet:defineObjects/>
<liferay-theme:defineObjects />

<aui:input name="contactName" type="text" />

<aui:script use="autocomplete-list,aui-base,aui-io-request-deprecated,autocomplete-filters,autocomplete-highlighters,datasource,datasource-get,datatable-datasource">

// Please note that this contact portlet service is a service builder generated JSON web service.
// We pass the groupId as a query param because our service expects it. Liferay has a nice javascript method for finding the group id.
var contactSearchDS = new A.DataSource.IO({source: '/api/jsonws/contact-portlet.contact/get-contacts-by-name?groupId='+Liferay.ThemeDisplay.getScopeGroupId()});

var contactSearchQueryTemplate = function(query) {
        // Here's an example on how to pass additional parameters to the query for you service
        // In our case we are fetching only the first 20 items and specify the ordering by name
	var output = '&name='+query.trim()+'&sort=name&dir=asc&start=0&end=20';
	return output;
}

var contactSearchLocator = function (response) {
	var responseData = A.JSON.parse(response[0].responseText);
// For debugging you can do: console.debug(responseData);
    return responseData;
};

var contactSearchFormatter = function (query, results) {
	return A.Array.map(results, function (result) {
// For debugging: console.debug(result.raw);
		return '<strong>'+result.raw.fullName+'</strong><br/>'+result.raw.mobile+' '+result.raw.phone+' '+result.raw.email;
	});
};

var contactSearchTextLocator = function (result) {
// This is what we place in the input once the user selects an item from the autocomplete list.
// In our case we want to put contact full name in there.
	return result.row.fullName;
};

var contactSearchInput = new A.AutoCompleteList({
	allowBrowserAutocomplete: 'false',
	resultHighlighter: 'phraseMatch',
	activateFirstItem: 'false',
	inputNode: '#<portlet:namespace/>contactName',
	render: 'true',
	source: contactSearchDS,
	requestTemplate: contactSearchQueryTemplate,
	resultListLocator: contactSearchLocator,
        resultFormatter: contactSearchFormatter,
	resultTextLocator: contactSearchTextLocator		
});

</aui:script>		

We’ll post an example of “multi-autocomplete” input also soon. That one requires a lot more code but allows selecting multiple contacts and shows them as “tags” above the input box. Much like the facebook name finder works.

As always, if you have questions or suggestions then please comment below.