Liferay

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.

How to create a taglib with JSP’s for Liferay

This is a bit tricky since we want to be able to nicely use our tag library in all our multiple Portlet projects, so the taglib should be a maven dependency. This usually means a Jar file. This is all nice and we can easily fit our Tag classes in the jar. We can also easily fit our TLD file in META-INF folder and it’s nicely available. Now comes the tricky part – What if we want our tag output to be a separate JSP page. Like almost all the Liferay tags are. JSP allows us to access all Liferay theme variables, other taglibs and all other useful things while generating our tag output.

The problem is that you can’t load the JSP from a jar file easily so the JSP’s have to be actually outside of your reusable jar project. So Liferay has an IncludeTag class that we want to extend that provides us with JSP output.

So we end up having 2 projects:

  • One for the Taglib TLD file and actual Tag classes that’s turned into JAR that we can use as a dependency in our other portlet projects.
  • One for the JSP files that is a Hook so the JSP’s are always available.

Here’s the example Tag class, this goes into our “example-userlist-taglib” project.
Notice that we have a JSP path that follows the Liferay convention on naming the files.

import javax.servlet.http.HttpServletRequest;
import com.liferay.taglib.util.IncludeTag;

public class UserList extends IncludeTag {

	private static final boolean _CLEAN_UP_SET_ATTRIBUTES = true;

	private static final String _PAGE = "/html/taglib/example/userlist/page.jsp";

	private String label;

	@Override
	protected String getPage() {
		return _PAGE;
	}

	@Override
	public int doStartTag() {
		return EVAL_BODY_INCLUDE;
	}

	@Override
	protected void cleanUp() {
		setLabel(null);
	}

	@Override
	protected boolean isCleanUpSetAttributes() {
		return _CLEAN_UP_SET_ATTRIBUTES;
	}

	@Override
	protected void setAttributes(HttpServletRequest request) {
		request.setAttribute("example:user-list:label", getLabel());
	}

	public String getLabel() {
		return label;
	}

	public void setLabel(String label) {
		this.label = label;
	}

}

Now an example TLD file on how to define a tag in taglib that should be in /src/main/resources/META-INF/ if you are running a maven based project:


<?xml version="1.0"?>
<taglib version="2.0" xmlns="http://java.sun.com/xml/ns/j2ee"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-jsptaglibrary_2_0.xsd">
	<tlib-version>1.0</tlib-version>
	<short-name>example</short-name>
	<uri>http://example.com/tld/liferay</uri>
	<tag>
		<name>user-list</name>
		<tag-class>com.example.package.UserList</tag-class>
		<body-content>JSP</body-content>
		<attribute>
			<name>label</name>
			<required>true</required>
			<rtexprvalue>true</rtexprvalue>
		</attribute>
	</tag>		
</taglib>

Solution to our JSP loading problem is actually rather simple: Create a Hook project that contains all your taglib JSP’s and deploy that on any Liferay server you need to use your taglib. The files for my userlist tag have to go into META-INF/custom_jsps/html/taglib/example/userlist/ folder for example.

I actually have an init.jsp and a page.jsp file there, examples follow:
init.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/portlet" prefix="liferay-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/util" prefix="liferay-util"%>
<%@ page import="javax.portlet.PortletRequest"%>
<%@ page import="javax.portlet.PortletResponse"%>
<%@ page import="javax.portlet.PortletPreferences"%>
<portlet:defineObjects />
<liferay-theme:defineObjects />
<%
PortletRequest portletRequest = (PortletRequest)request.getAttribute(JavaConstants.JAVAX_PORTLET_REQUEST);
PortletResponse portletResponse = (PortletResponse)request.getAttribute(JavaConstants.JAVAX_PORTLET_RESPONSE);

String label = (String)request.getAttribute("example:user-list:label");

%>

and now the userlist tag jsp:


<%@ include file="init.jsp"%>
<div class="userlist">
	<strong><%=label %></strong>:
	<!-- Add code here for listing your users. -->
</div>


So there you have it! Now we can easily use our tag in any portlet jsp, provided we first include our taglib like this:

<%@ taglib uri="http://example.com/tld/liferay" prefix="example" %>

and then we can do:

<example:user-list label="Admin users" />