Liferay Connected Services - 2016 Q2 Updates

Company Blogs May 30, 2016 By Igor Beslic Staff

Last two months Liferay Connected Service (LCS) team spent testing system for upcomming changes related to subscription management. It should allow enterprise customers more flexible license management and elastic subscription mode.

I guess now I have enough time to put some news for all of you who follow LCS progress.

  • in the next release of LCS system we  will stop support and development for all 6.1.x platforms

  • browseable page metric history have been available since April 2016
  • notifications/alerts improvements were deployed early in May 2016
  • LCS system is tested and ready for incoming family of 7.0 portal based solutions

I hope you will find time to explore and evaluate the leatest LCS features soon.

How to write integration test for service builder generated classes in portlet plugins

Technical Blogs September 2, 2015 By Igor Beslic Staff

Recently LCS team had really hard time with writing integration tests for service builder generated classes. Arquillian test patterns written for core portal plugin simply didn't work for our case and mocking service layer was pointles. As time passed by and code base grow we simply got angry and decided to stop with all development untill we are not able to run test as any other decent software engineer in the world.

Here I'll write exact steps we did in our EE PLUGINS SDK 6.2 and Liferay Portal EE 6.2 SP10 to make Arquillian tests work for us. Of course without help from awesome core engineering team which filled some gaps in the SDK infrastructure even this wouldn't be possible.

Step 1 - unzip new tomcat

I do this from my portal's source home directory:

ant -f build-dist.xml unzip-tomcat

Step 2 - manager webapp

Once you have fresh, clean tomcat in bundles directory (or whatever directory you use), you have to make sure there is manager webapp present. Arquillian uses manager application to deploy your plugin so it is required. I usually go to tomcat download site, download archive, and extract manager app from it.

Step 3 - modifiy

Go to TOMCAT_HOME/bin directory and add this line after original CATALINA_OPTS definition:


Step 4 - modify tomcat-users.xml

<?xml version='1.0' encoding='utf-8'?>

<role rolename="tomcat"/>
<role rolename="manager-gui"/>
<role rolename="manager-script"/>
<role rolename="manager-jmx"/>
<role rolename="manager-status"/>
<user password="tomcat" roles="tomcat,manager-gui,manager-script,manager-jmx,manager-status" username="tomcat"/>


Step 5 - prepare database and compile portal

Dedicate new database instance (ieg. lportalee62x_test) for your integration tests and set it in and run from portal source home directory:

ant all

After ant task is completed you have portal environment ready for tests. You may wonder why I do all this like idiot? Well, because each time some plugin or some setting either destroys attempt of testing either wastes couple hours of my time until I find the problem. So each time I run tests in completely fresh environment.

Step 6 - Initalize portal infrastructure

Start portal once to initalize DB, download market-place plugin etc. Once you are surre everything is there deploy portlet plugin you want to test (you may need to deploy other dependencies also). After your plugin initalizes its own DB space, undeploy it. Again, you will question yourself if everything is OK with me - yes, don't worry, you will avoid million unexplainable issues if you follow this steps.

In this moment you have enviriontment ready to test.

Configure portlet plugin for integration test with Arquillian

Step 7 - declare dependecies in ivy.xml

<?xml version="1.0"?>
<ivy-module version="2.0" xmlns:m2="..." xmlns:xsi="..." xsi:noNamespaceSchemaLocation="...">
	<info module="my-portlet" organisation="com.liferay">
		<extends ... />

	<dependencies defaultconf="default">
		<dependency conf="test->default" name="arquillian-junit-container" org="org.jboss.arquillian.junit" rev="1.1.6.Final" />
		<dependency conf="test->default" name="arquillian-tomcat-remote-7" org="org.jboss.arquillian.container" rev="1.0.0.CR6" />
		<dependency conf="test->default" name="com.liferay.ant.arquillian" org="com.liferay" rev="1.0.1" />
		<dependency conf="test->default" name="javassist" org="org.javassist" rev="3.17.1-GA" />

Step 8 - create arquilian.xml

Create PLUGIN_HOME_DIR/test/integration directory and place arquillian.xml file in it. Content should look like this:

<?xml version="1.0"?>
<arquillian xmlns="..." xmlns:xsi="..."	xsi:schemaLocation="...">
	<container qualifier="tomcat" default="true">
			<property name="jmxPort">8099</property>
			<property name="pass">tomcat</property>
			<property name="user">tomcat</property>

Step 9 - write an test

Here is one simple test, placed into PLUGIN_HOME/test/integration/com/my/package:

import org.jboss.arquillian.junit.Arquillian;

import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;

 * @author Igor Beslic
public class LCSProjectServiceTest {
	public void testAddDefaultLCSProject() throws Exception {
		LCSProject lcsProject = LCSProjectServiceUtil.addDefaultLCSProject();

			LCSConstants.CORP_PROJECT_DEFAULT_NAME, lcsProject.getName());

		List lcsClusterEntries =

		Assert.assertTrue(lcsClusterEntries.size() == 1);

		LCSClusterEntry lcsClusterEntry = lcsClusterEntries.get(0);


Step 10 - run test

I always run test against one class because my work day is not endless, and I have no infinite amount of RAM to avoid PermGen. I leave the rest of work to QA and I just do this:

test-class -Dtest.class=LCSProjectServiceTest

Step 11 - observe with connected services at

Optionally, you can download lcs-portlet from and deploy to your test portal. If correctly connected to LCS system, it would stream service statistics to LCS where you would be able to analyse it. More about it read in Marko's blog on how to monitor user expirence

OAuth Client Portlet Implementation

Company Blogs December 23, 2013 By Igor Beslic Staff

Hi all, recently our team finished the first real-world OAuth case, so I'll share with you code and knowledge you would probably need if you choose Liferay OAuth Plugin.


Our case requires two kind of portals:

  • Liferay Service Portal - further in text LSP - EE 6.1.x portal - this is central point where resources and services are being served from. Here we have resource administrator users, and end users (mostly "consumer" portal administrators). Here OAuth Portlet was deployed.
  • Liferay Client Portal - further in text LCP - any EE/CE 6.1.x/6.2.x portal - this is portal instance that consumes particular resource (imagine it like your portal instance accessing Marketplace to fetch an portlet, but with more resources and services). Here OAuth Client Portlet was deployed

LCP instances are consumers, consuming resources and services provided by LSP. Consuming is done with simple portlet (we refer to it as OAuth Client Portlet) whose threads wait in the background and from time to time does some work with LSP. Each request for service or resource must be authorized, but without human interference.

Note that I use term OAuth Portlet which is portlet plugin that makes particular portal instance OAuth friendly, and term OAuth Client Portlet which is portlet plugin that performs authorized requests using OAuth principles.

Authorization problem

LCP instances need way to authenticate and authorize its actions at LSP. It could be done using username/password with each request, but no one administrator wants his username and password in properties or database (even encrypted).

As we already have OAuth Portlet which setups an Liferay Portal to be OAuth friendly, we decided to go further with OAuth.

OAuth Client Portlet registered as OAuth ApplicationWe had deployed OAuth Portlet at LSP portal and registered our OAuth Client Portlet as OAuth Application to obtain CONSUMER KEY and CONSUMER SECRET. With consumer credentials we were ready to implement clent side. Further in text you can read how we did it, and what are limitations related to current OAuth Portlet implementation (don't panic we are working on improvements).

OAuth Client Portlet Implementation

There are so many code examples at Internet on how to use OAuth. Here I'll describe how we did it in portlet and what was different comparing to some other cases (for example if you build mobile application).

OAuth Client Library

We choose Scribe as OAuth library of our choice. Simply because it is available in portal and you can easily include it in your plugin by refering it from


Current version available in portal is pretty old 1.0.8 but it would be quite enough for our portlet plugin.

Scribe OAuth Service implementation

The first step is Scribe's OAuth service implementation. Here is code:

public class OAuthAPIImpl extends DefaultApi10a {

	protected String getAccessTokenEndpoint() {
		if (Validator.isNull(_accessTokenEndpoint)) {
			_accessTokenEndpoint = OAuthUtil.buildURL(
				"oauth-portal-host", 80, "http",

		return _accessTokenEndpoint;

	protected String getRequestTokenEndpoint() {
		if (Validator.isNull(_requestTokenEndpoint)) {
			_requestTokenEndpoint = OAuthUtil.buildURL(
				"oauth-portal-host", 80, "http",

		return _requestTokenEndpoint;

	private String _accessTokenEndpoint;
	private String _requestTokenEndpoint;


In latest version of Scribe you will need to implement getAuthorizeToken method also. It is starting step for client accessing OAuth platform the first time, and here it is implemented in OAuthUtil class. By implementing this we provide Scribe with informations about OAuth platform we are accessing. It is very important to say that OAuth Portlet protocol URLs are defined in and liferay-hook.xml. Defaults are


and in our example we use them as they are. At an LSP you are connecting these might be modified, so in your case consult with provider portal administrator if these properties were modified. We use to set all OAuth related constants:


and finally OAuthUtil class to implement what we were missing:

public class OAuthUtil {

	public static String buildURL(
		String hostName, int port, String protocol, String uri) {

	public static Token extractAccessToken(
		Token requestToken, String oAuthVerifier) {

		Verifier verifier = new Verifier(oAuthVerifier);

		OAuthService oAuthService = getOAuthService();

		return oAuthService.getAccessToken(requestToken, verifier);

	public static String getAuthorizeURL(
		String callbackURL, Token requestToken) {

		if (Validator.isNull(_authorizeRequestURL)) {
			authorizeRequestURL = buildURL(
			"oauth-portal-host", 80, "http",

			if (Validator.isNotNull(callbackURL)) {
				authorizeRequestURL = HttpUtil.addParameter(
					authorizeRequestURL, "oauth_callback",

		_authorizeRequestURL.replace("{0}", requestToken.getToken());

	public static OAuthService getOAuthService() {
		if (_oAuthService == null) {
			ServiceBuilder oAuthServiceBuilder = new ServiceBuilder();


			_oAuthService =;

		return _oAuthService;

	public static Token getRequestToken() {
		OAuthService oAuthService = getOAuthService();

		return oAuthService.getRequestToken();

	private static String _authorizeRequestURL;
	private static OAuthService _oAuthService;


Please take moment and stop at method String getAuthorizeURL(String callbackURL, Token requestToken). This method is very related to CALLBACK URI parameter you are obligatory to provide during registration of an OAuth Application. CALLBACK URI is uri where user will be redirected by LSP once user is authenticated and access authorized. For an mobile application, or 3rd party web application with known domain we can use CALLBACK URI as my-android-app://main-activity or In our example, consumer portal domain in build time is unknown, or LCP portal could be behind firewall and admin referrs to it via IP or internal alias when performing OAuth authorization. To support case, we need to override CALLBACK URI and hopefully we can do it with additional oauth_callback parameter as shown in example. Remeber this if you plan your own OAuth case.


If accessing OAuth platform first time, portlet needs UI to start OAuth cycle which would result in ACCESS TOKEN and ACCESS SECRET. Those are credentials we will store for further usage. Portlet will use them each time we are accessing LSP (in combination with CONSUMER KEY and CONSUMER SECRET). Our front end code works this way:

  • if there are ACCESS TOKEN and ACCESS SECRET show threads activity status
  • otherwise tell user he/she needs to authorize portlet with target OAuth Platform

This is code sample in JSP which initiate OAuth authorization process:

<portlet:actionurl name="setupOAuth" var="setupOAuthURL">
Token requestToken = OAuthUtil.getRequestToken();

portletSession.setAttribute(Token.class.getName(), requestToken);
<div class="button-container"%>
	<a class="lcs-portal-link" href="<%= OAuthUtil.getAuthorizeURL(setupOAuthURL, requestToken) %>"><liferay-ui:message key="authorize-access"/>

After Authorize button is clicked, user is taken to OAuth Provider where he is authenticated and asked to authorize Portlet to access resources and services on his behalf.

OAuth Client Portlet shows to user authorize buttonClick to authorizeOAuth Portlet Requests user to authorize access

On success, OAuth platform redirects user back to CALLBACK URL (where you want your portlet to continue with process). In our case it is portlet action setupOAuth where we will extract and persist access token and access secret.

public void setupOAuth(
		ActionRequest actionRequest, ActionResponse actionResponse)
	throws Exception {

	PortletSession portletSession = actionRequest.getPortletSession();

	Token requestToken = (Token)portletSession.getAttribute(

	String oAuthVerifier = ParamUtil.getString(
		actionRequest, "oauth_verifier");

	Token token = OAuthUtil.extractAccessToken(requestToken, oAuthVerifier);

	// store token.getSecret() and token.getToken()

Once we have access token and secret our threads can use it for background communication with LSP. In our case I'm accessing services via JSON WS at LSP. Here is simple example how you can do it:

Token token = new Token(getAccessToken(), getAccessSecret());

String requestURL = OAuthUtil.buildURL(
	"oauth-portal-host", 80, "http",

OAuthRequest oAuthRequest = new OAuthRequest(Verb.POST, requestURL);

OAuthService oAuthService = OAuthUtil.getOAuthService();

oAuthService.signRequest(token, oAuthRequest);

Response response = oAuthRequest.send();

if (response.getCode() == HttpServletResponse.SC_UNAUTHORIZED) {
	String value = response.getHeader("WWW-Authenticate");

	throw new CredentialException(value);

if (response.getCode() == HttpServletResponse.SC_OK) {
	// do something with results from response.getBody();

Liferay OAuth Portlet Limitations

Please DON'T consider this case as ultimate one. This can be pattern for most projects where client is built for known Liferay Portal Platform (let's say, or any other amoung our respected customers). If you build Portlet Plugin or Mobile Application for "Any Liferay Portal" OAuth won't work for you. It won't work for you since you will need to feed your code with CONSUMER KEY and CONSUMER SECRET which in "Any Liferay Portal" case will be known after "Any Liferay Portal" was installed and OAuth Plugin is deployed. For mobile applications downloaded from Play, App Store or Marketplace it will be very hard to fetch these in easy and secure way.

Please, prior considering using OAuth Portlet take note that current OAuth Portlet implementation relys to standard portal permissions. That means if user Europa authorizes portlet Atlass to access LSP portal resources, portlet Atlass will be able to do what ever Europa can do. In next version of OAuth Portlet, we will engage Access Control API where consuming applications would be forced to declare which resources will access, and OAuth Portlet would check it.

This is The End (of blog article)

At the end, I hope it will be very easy to get your OAuth clients to life. Also I expect lot of critics mostly because of limitations, but hey, we are working to improve it.

What about OAuth?

Company Blogs April 19, 2013 By Igor Beslic Staff

Hi all, I'll share with you our latest progress made with supporting OAuth authorized requests. I'll make example with android application since I'm familiar with it (enough to display button).

OAuth server support comes as OAuth 1.0a spec based portlet plugin with application registration UI, user authorization approval and secure filter that checks validity of oauth credentials (thank you Tomas for the filter, and Ivica for all hours we spent together). OAuth is very pratical since it moves authentication actions to platform side (Liferay Portal), and application doesn't need handle security issues regarding credentials storing. If you are application developer, and want your application to access Liferay portal resources this could be a way to do it:

1. Go to OAuth admin

2. Register application

3. Get yours consumer key and secret

Now... You should take an OAuth api (scribe or signpost) and make your consumer application. My application is simple android application whic would do nothing awesome, but will make authorized document library access:

- make oauth request token and bring user to Liferay portal application authorization page. If user is not signed in, he/she would be asked to do it.


- Once user is signed in authorization page will be shown. After user confirms he/she grants access to her/his liferay resources Liferay redirects user to defined redirect URL (not clear from screenshots, but as a redirect I'm using my-application:// so that android browser knows where to pass redirect).

- user acces token and token secret are being stord in application properties, an I'm able to query portal (I'll grab some folders and display it):

So what do you think?

I used this links to assembly android application:

Liferay SDK shared plugins

Company Blogs March 15, 2013 By Igor Beslic Staff


Hi all, I want to share with you something that is pretty cool for me and I found it super useful. It is shared type of Liferay SDK plugin we use to generate jar entry that can be referred by your other plugins as its library entry.

To show how shared plugin is useful lets just mention that already supports some widely used dependency declarations:

  1. dependency to Liferay Portal jars can be declared using
    • portal-dependency-jars - during compile phase it will use portal-dependency.jar but it will be excluded from created archive
    • portal-dependency-tlds
  2. dependency to other plugins auto-generated *-service.jar entry you declare using:
    • required-deployment-contexts - during compile phase it will fetch required-deployment-context-service.jar and include it in created archive

Why shared plugin?

Once in your life, your system design will assume more than one interdependent plugin (and even simple Liferay integration cases can't avoid it). Your team will end up with common code used in each plugin such are various utilities and own APIs. Good starting point for refactoring it is creation of shared type of plugin, extracting common code to it and declaring dependency to it in all other plugins code was used. I'll demonstrate it using always funny batman plugin invented by my coleague Igor:
  • Once you're inside your SDK go to shared folder and create structure for your new plugin:
mkdir batman-shared
cd to batman-shared
  • Create ant build file (you can copy an existing from other plugin):
vim build.xml
adopt it to suite your shared plugin:
<?xml version="1.0"?>
<!DOCTYPE project>
<project name="batman-shared" basedir="." default="deploy">
 <property name="plugin.version" value="1" />
 <import file="../build-common-shared.xml" />
  • Configure project files

mkdir src

mkdir lib (this is optional if your code doesn't need any additional jar file)

ant setup-eclipse

  • Put your packages and classes into src folder
ant jar
In this point you have plain java project and you can continue developing it.

How to refer shared plugin from other plugin?

Let say we have batman-portlet plugin. To refer batman-shared we need to update batman-portlet plugin build.xml with new line:
<property name="import.shared" value="batman-shared" />
If you have more than one shared plugin you can refer all of them:
<property name="import.shared" value="batman-shared,robin-shared" />
If ant compile was issued from batman-portlet plugin batman-shared.jar will be created and placed into plugins lib folder. Also it will be included in generated archive file.
Note if your shared plugin use own lib to declare additional jars, all those jars will be copied also. So if you care about your final product size be aware of these details.
Hope this will help you organise your code better - it helped us.

Workflow in Action - Kaleo workflow context variables

Company Blogs June 12, 2012 By Igor Beslic Staff

In this article I'll try to list workflow and service context content for some common asset types. You can use this information later when you build your notification templates or refer to it. When accessing variables I marked with "!" (like taskComments or transitionName) make sure they are present since its presence depends on workflow activity.

You will notice that I separated serviceContext since its keys depend on asset type.

Workflow context variables for WEB CONTENT
  companyId 10152 java.lang.String
  entryClassName com.liferay.portlet.journal.model.JournalArticle java.lang.String
  entryClassPK 11302 java.lang.String
  entryType Web Content java.lang.String
  groupId 10178 java.lang.String
! taskComments Comment somebody put when assigning... java.lang.String
! transitionName approve java.lang.String
  userId 10194 java.lang.String
  serviceContext content - obtain via key serviceContext
  articleId   java.lang.String
  assetLinkEntryIds   java.lang.String
  assetLinksSearchContainerPrimaryKeys   java.lang.String
  assetTagNames   java.lang.String
  autoArticleId true java.lang.String
  classNameId 0 java.lang.String
  classPK 0 java.lang.String

This is my web content body....

  defaultLanguageId en_US java.lang.String
  description_en_US   java.lang.String
  displayDateDay 11 java.lang.String
  displayDateHour 11 java.lang.String
  displayDateMinute 37 java.lang.String
  displayDateMonth 5 java.lang.String
  displayDateYear 2012 java.lang.String
  doAsGroupId 10178 java.lang.String
  folderId 0 java.lang.String
  indexable true java.lang.String
  indexableCheckbox on java.lang.String
  inputPermissionsShowOptions false java.lang.String
  inputPermissionsViewRole Guest java.lang.String
  languageId en_US java.lang.String
  localized true java.lang.String
  neverExpire true java.lang.String
  neverExpireCheckbox true java.lang.String
  neverReview true java.lang.String
  neverReviewCheckbox true java.lang.String
  refererPlid 10422 java.lang.String
  smallImage false java.lang.String
  smallImageURL   java.lang.String
  structureDescription   java.lang.String
  structureId   java.lang.String
  structureName Default java.lang.String
  structureXSD STRUCTURE XSD (removed to improve readability) java.lang.String
  templateId   java.lang.String
  title_en_US This is English title java.lang.String
  type general java.lang.String
  variableName content java.lang.String
  version 1.0 java.lang.String
  workflowAction 1 java.lang.String


Workflow context variables for BLOG ENTRY
  companyId 10152 java.lang.String
  entryClassName com.liferay.portlet.blogs.model.BlogsEntry java.lang.String
  entryClassPK 11329 java.lang.String
  entryType Blogs Entry java.lang.String
  groupId 10178 java.lang.String
! taskComments Comment somebody put when assigning... java.lang.String
! transitionName approve java.lang.String
  userId 10194 java.lang.String
  serviceContext content - obtain via key serviceContext
  assetLinkEntryIds   java.lang.String
  assetLinksSearchContainerPrimaryKeys   java.lang.String
  assetTagNames history,news,programming java.lang.String
  attachments   java.lang.String

Always blog and share, and soon you'll get feedback!

  description   java.lang.String
  displayDateAmPm 0 java.lang.String
  displayDateDay 12 java.lang.String
  displayDateHour 0 java.lang.String
  displayDateMinute 26 java.lang.String
  displayDateMonth 5 java.lang.String
  displayDateYear 2012 java.lang.String
  doAsGroupId 10178 java.lang.String

Always blog and share, and soon you'll get feedback!

  entryId 11329 java.lang.String
  refererPlid 10422 java.lang.String
  smallImage false java.lang.String
  smallImageURL   java.lang.String
  title Today we blog... java.lang.String
  workflowAction 1 java.lang.String


Workflow context variables for MESSAGE BOARD MESSAGE
  companyId 10152 java.lang.String
  entryClassName com.liferay.portlet.messageboards.model.MBMessage java.lang.String
  entryClassPK 11362 java.lang.String
  entryType Message Boards Message java.lang.String
  groupId 10178 java.lang.String
! taskComments Comment somebody put when assigning... java.lang.String
! transitionName reject java.lang.String
  userId 10194 java.lang.String
  serviceContext content - obtain via key serviceContext
  anonymous false java.lang.String
  assetLinkEntryIds   java.lang.String
  assetLinksSearchContainerPrimaryKeys   java.lang.String
  assetTagNames sea, sailing,swimming java.lang.String
  attachments false java.lang.String
  body This is my question - give me answer! java.lang.String
  editor This is my question - give me answer! java.lang.String
  mbCategoryId 0 java.lang.String
  messageId 0 java.lang.String
  parentMessageId 0 java.lang.String
  preview false class java.lang.Boolean
  question false java.lang.String
  subject My MB Subject java.lang.String
  threadId 0 java.lang.String
  workflowAction 1 java.lang.String


Workflow context variables for DOCUMENT LIBRARY ENTRY
  entryClassName com.liferay.portlet.documentlibrary.model.DLFileEntry class java.lang.String
  groupId 10179 class java.lang.String
  entryType Documents and Media Document class java.lang.String
  event add class java.lang.String
  userId 10195 class java.lang.String
  taskComments   class java.lang.String
  companyId 10153 class java.lang.String
  entryClassPK 11802 class java.lang.String
  serviceContext content - obtain via key serviceContext
  p_p_auth xA64wzT5 class java.lang.String
  p_p_lifecycle 1 class java.lang.String
  referringPortletResource   class java.lang.String
  p_p_id 20 class java.lang.String
  assetLinksSearchContainerPrimaryKeys   class java.lang.String
  backURL   class java.lang.String
  formDate 1391035297271 class java.lang.String
  inputPermissionsViewRole Site Member class java.lang.String
  folderId 11701 class java.lang.String
  p_p_state maximized class java.lang.String
  title application for leave class java.lang.String
  uploadProgressId dlFileEntryUploadProgress class java.lang.String
  description application for leave class java.lang.String
  workflowAction 1 class java.lang.String
  fileEntryTypeId 0 class java.lang.String
  doAsGroupId 10179 class java.lang.String
  redirect http://localhost:8080/group/control_panel/manage?p_&p_p_modpandentdoAsGroupId=10179 class java.lang.String
  p_p_mode view class java.lang.String
  groupPermissions [Ljava.lang.String;@6a88faf5 class [Ljava.lang.String;
  inputPermissionsShowOptions false class java.lang.String
  p_l_id 10174 class java.lang.String
  uploader classic class java.lang.String
  repositoryId 10179 class java.lang.String
  fileEntryId 0 class java.lang.String
  cmd add class java.lang.String
  refererPlid 10182 class java.lang.String
  assetLinkEntryIds   class java.lang.String
  struts_action /document_library/edit_file_entry class java.lang.String
  assetTagNames   class java.lang.String
  p_v_l_s_g_id 0 class java.lang.String
For the purpose of readability of article I skipped some of serviceContext key-values since I don't find it interesting for templateing.

I'll update this blog entry for other asset types soon.

Izišao novi Liferay 6.1 EE

Company Blogs February 23, 2012 By Igor Beslic Staff

[Czech] [Deutsch] [English] [Español] [Portuguese] [中国的]

Liferay tim sa zadovoljstvom objavljuje izlazak Liferay Portal 6.1 EE! Liferay Portal 6.1 EE tvrtkama pruža jedinstvenu web platformu za socijalnu suradnju, upravljanje web sadržajem te razvojne alate za izradu prilagođenih rješenja.

Pogledajmo neke od važnijih novosti i promjena u ovoj verziji:

  • Liferay Sync za dijeljenje dokumenata - Liferay Sync će vas razriješiti brige vezane za pristup datotekama tako što ih čini dostupnim u svakom trenutku gdje god je to potrebno. Uz Liferay Sync, dokumenti pohranjeni u biblijoteci dokumenata su automatski sinkronizirani sa svojim inačicama u vašem mobilnom, stolnom i tablet uređaju. Broj sinkroniziranih dijeljenih dokumenata nije limitiran a sinkronizacija se obavalja sa vašom vlastitom Liferay implementacijom stoga ne morate brinuti o sigurnosnim problemima koji dolaze s javno dostupnim cloud rješenjima.
  • Potpuno redizajnirana i obnovljena biblioteka dokumenata - Biblioteka dokumenata je značajno unaprijeđena! Prenamjenom biblioteke da primi sve vrste datoteka - poslovni dokumenti, slike, video i ostalih medijski sadržaji - u jednu jedinstvenu zbirku, biblioteka je ujedno i preimenovana u Biblioteku dokumentata i medije. Nove značajke koje vrijedi istaknuti su:
    • Automatski pretpregled - zavirite u sažetke svojih dokumenata, slike i videa prije nego što ih povučete na svoje računalo!
    • Razvrstavanje dokumenta po području poslovanja - stvaranje i opisivanje vlastitih vrsta dokumenata prilagođenih specifičnim potrebama poslovanja. Izvlačenje vlastitih i metapodataka ugrađenih u datoteke poznatih formata se odvija automatski.
    • Dojam desktopa - Potpuno remontirano i redizajnirano sučelje za optimalno korisničko iskustvo s poboljšanjima kao što su drag and drop, višestruki odabir dokumenata te kontekstualni izbornici.
  • Bolji rezultati uz manje programiranja s Kaleo Formama i Workflow dizajnerom - Kaleo Forme i Workflow dizajner pružaju okolinu za dizajn i implementaciju formi namjenjenih za primjenu u poslovnom procesu krajnjih poslovnih korisnika. Od sada je moguće proces brzo i jednostavno prilagođavati stalnim promjenama u poslovanju bez intervencije informatičkih stručnjaka.
  • Višestruke inačice web sitea - imate potrebu raditi na novoj verziju svoje web stranice, a pritom održavati sadržaj na postojećoj? Nema problema. Liferay je poboljšao staging funkcionalnost i sad vam omogućuje istovremeni rad i praćenje izmjena na nekoliko verzija site na bilo kojoj poziciji. Više timova za oblikovanje sadržaja od sada može lako paralelno stvarati, održavati i objavljivati svoju verziju sitea.
  • Liste, liste i još lista - umorni ste od svih tih podsjetnika na monitoru? Probajte koristiti nove Liferay Dinamične Liste Podataka za spremanje i dijeljenje vaših lista sa suradnicima.
  • Jednostavnije do Mobilnog web sitea - vjerojatno ste već čuli: mobinost na prvom, web na drugom mjestu. Sve više i više korisnika pristupa vašim web stranicama mobilnim i tablet uređajima. Liferay pruža alate za oblikovanje sadržaja i njegovog izgleda temeljeno na vrsti uređaja posjetitelja web stranice.
  • Prilagodi to na svoj način s korisničkim prilagodljivim stranicama - Administrator web sitea na određenim stranicama ili njihovim dijelovima može odabrati da se prilagođuju korisnicima. Vaši korisnici će od sada moći sami odabrati što dodati, ukloniti ili kako konfigurirati sadržaj i aplikacije u prilagodljivim odjeljcima, a administrator web sitea kontrolira fiksna područja stranica.
  • Upravljanje velikim mrežama povezanih siteova - Ukoliko upravljate stotinama sličnih cookie-cutter siteova Liferay omogućava kreiranje web siteova iz predložaka. Sve promjene napravljene na predlošku se u sekundama primijenjuju na bilo koji site izrađen iz njega. Ovo će omogućiti web administratoru lako obnavljanje i upravljanje stotinama srodnih siteova vezanih na isti predložak.
  • Integracija s postojećim repozitorijima dokumenata - Da li imate više odredišta za pohranjivanje dokumenata? Ujedinite pristup svim svojim repozitorijima kroz Liferay. Liferay nudi konektore za SharePoint, Documentum i CMIS implementacije.

Liferay 6.1 EE se isporučuje sa još mnogo, mnogo toga - više od 100 noviteta u kombinaciji s više od 400 poboljšanja. Provjerite naš vodič za korisnke za više informacija. Ako ste spremni probati Liferay, možete preuzeti pokusnu inačicu ovdje.

Workflow in Action - Kaleo email notifications

Company Blogs February 7, 2012 By Igor Beslic Staff

In one of my previous blogs I wrote about how to setup email settings and configure Web Content notifications triggered by workflow.

Liferay portal 6.1 relays to external workflow engine Kaleo which also has ability to send notifications. As our intention is to centralize workflow related code in  Kaleo and users workflow definitions, eventually we will remove legacy workflow notifications (such those in web content managment). So to prepare you for things that are comming check this blog.


To constrain your business process with some rules, you need:

  1. rules
  2. language to describe those rules to mchine
  3. workflow engine
  4. assets and resources aware of workflow engine
  5. people who want to play by the rules

Kaleo and Liferay give us 1*, 2, 3, 4*. Once you have idea what are yours company rules, you will ask somebody to write definition in an xml like language. Than you will run those rules in portal and apply it to you asset and resources.

1* kaleos default rules are very generic and may be good as starting point. Depending to your company business you will probably need your own rules writen as definition.
4* by default those assets are workflow aware: Blogs Entries, Web Content, Comments, Message Boards Messages, Wiki Pages and Page Revisions (when you do staging). Your and any other assets you want in game must implement workflow interfaces.

What is interesting you will probably want some actions performed to asset to trigger messages to get users attention. You want to notify user there is job to do, he done great job or there is awful escalation that needs immediate intervention. As part of workflow definition you will be able to create some actions eather real scripted actions or notifications. Notifications can be emails, instant messages or private messages. In the rest of blog I will focus on email notifications.

Kaleo email notifications

As kind of action we form notifications as part of process state or tasks. Triggering of notification is done in a three ways:

  • on assignment - when task is assigned to user
  • on entry - when asset enters some state or is ready for an task within state
  • on exit - when asset leaves the state or all job required by the task is done by particular user

At the end we must choose do we need some simple messages in our email or we want to build more specific messages aware of business and workflow context. Kaleo definitions will allow you to write messages as plain text but in most cases that wouldn't be enough. also for advanced users there is possibility to do that in freemarker or velocity.

Write your own notifications

For example I'll existing default single-approver-definition.xml notification defined for review task. New notification will use new email subject and will include some data related to asset that is being workflowed. My new notification related to review task is following:

<name>Review Notification</name>
<description>New Submission Is Ready For Review</description>
      <#assign comments    = taskComments!"">
      <!-- email body -->
   Please review the ${entryType} waiting for you in your workflow tasks.
   <#if comments != "" >
     <br />Assignment comment says: <strong>${comments}</strong>
       <!-- signature -->
   <p>Sincerely,<br /><strong>Liferay Portal Workflow</strong></p>

Now, lets go step by step:

node name - it will just define notification identification name

node description - it serves as description, but kaleo will populate an email subject field with its value so be as more creative when you setting this value

node template-language - tells kaleo to use freemarker engine to merge template and data

template - there are two things to note. First it is assignment of variable comment:

<#assign comments    = taskComments!"">

expression taskComments!"" will take value from freemarker context variable taskComments and if it is null en ampty string will be returned. We have to do that because when initaly enters review task, it is done by engine so there won't be available taskComments. In moment when manager assigns task to user he will be prompted to set comment so we will be able to display comment inside email message.

Second thing is use of freemarker context variable entryType. Expression ${entryType} will be evaluated as asset type like Web Content, Comment or Wiki.

This is how received email looks now:

For next time I will display how to make more advanced templates.

Message Boards - post replys from your email client

Company Blogs February 3, 2012 By Igor Beslic Staff

My goal here will be to explain influence of pop.server.subdomain setting to setup external email server and enable Liferay 6.1 Message Boards to process email replies and post them to correct MessageBoards thread. For me, this feature is extremly handy because I can post to MessageBoards from my smart phone without need to sign in to portal.

Basic configuration requires just an dedicated email account for Liferay Portal. More advanced configuration requires:

  • have command line access to your email server
  • have available domain
  • access to domain register tools to create MX record and new domain aliases.

STEP 1 - Set your company data

First you will need to set your company host and mx record. You do next:

  • Go to Control Panel, Portal Settings - Set company domain to
  • Set email domain to
  • Set liferay user email (eg.
  • Go to server administration and click Email tab
  • According to this article (Step 2)  set Incoming and Outgoing email server data

STEP 2 - Change portal-ext

This settings in portal-ext will allow us notifications and will discard email subdomain settings:


In this point you have basic configuration setup. Liferay will process replys and according to message subject your replys will be posted to right thread.

If you leave default subdomain setting:


or override it with some your value:

You will need to setup your email server to correctly process (forward) replys to liferay email account.

pop.server.subdomain=events will cause that liferay builds Message Boards email notifications to have reply-to attribute like  If your email server is not setup to handle emails to domain, replys will be rejected from destination email server with an error: The error that the other server returned was: 550 550 5.1.1 <>: Recipient address rejected: User unknown in virtual mailbox table (state 14). If you set pop.server.subdomain property to empty string, portal would turnoff this feature. In that case reply-to email attribute would be set to email address set in your portal preferencies settings.

STEP 4 - Setup Email Subdomains

To make use of subdomains funcionality we will configure our email server to use virtual email inbox. That is needed bacause Liferay Portal will compose email notification messages such way that you will be replying your emails to generic unexisting users that will be later decoded to correct message boards threads. To achive that our users will be virtual and configured with regular expressions.

Use your domain admin tools to define mx record for and its aliases. In my case I have:

  • as main mx record and
  • as alias

Following postfix server setup example is done with dedicated postfix email server installed at Ubuntu OS with dovecot as POP3 server. Note that you might tune dovecot also.

At your email server host create user we will use to handle virtual inbox:

adduser --uid 500 --disabled-password --disabled-login vmail

Now change postfix to use virtual domains:
Line with mydestination setting must be commented out and we are adding new lines:
# mydestinations =
mydomain =
# Virtual mailbox settings
virtual_mailbox_domains =,
virtual_mailbox_base = /var/mail
virtual_mailbox_maps = pcre:/etc/postfix/vmailbox
virtual_minimum_uid = 100
virtual_uid_maps = pcre:/etc/postfix/virtual_uids
virtual_gid_maps = pcre:/etc/postfix/virtual_gids
virtual_alias_maps = hash:/etc/postfix/virtual_alias
virtual_mailbox_limit = 0
virtual_transport = virtual
local_transport = local
local_recipient_maps = $alias_maps $virtual_mailbox_maps unix:passwd.byname

Create files we will use to configure virtual domains and users. We already refer to those files in so without them postfix won't be able to startup:

/etc/postfix# cat vdomains
add those lines to file and save:
/events\.mail\.mydomain\.com/ OK
/mydomain\.com/ OK
/etc/postfix# cat virtual_alias
add those lines to file and save:
/etc/postfix# cat virtual_gids
add those lines to file and save:
/.*@events\.mail\.mydomain\.com/ 500
/.*@mydomain\.com/ 500
/etc/postfix# cat virtual_uids
add those lines to file and save:
/root@mydomain\.com/ 0
/.*@events\.mail\.mydomain\.com/ 5000
/liferay@mydomain\.com/ 5000
/etc/postfix# cat vmailbox
add those lines to file and save:
/.*@events\.mail\.mydomain\.com/ liferay
/liferay@mydomain\.com/ liferay
/root@mydomain\.com/ root 


Workflow in Action - setup email notifications

Company Blogs January 11, 2012 By Igor Beslic Staff

Hi all! As workflow for Liferay 6.1 sligtly changed from workflow in Liferay 6, I'll spent some time to provide ways to setup it and use it. My idea here is describe how fast you can setup and apply Liferay workflow to your business. I'll to write series of blogs with cases describing how to apply workflow in publishing, banking or city government.

Before I start hassle with problems you are realy interested in I'll describe how to configure and test basic modules neccessary to achieve the final one. Yes it is technical stuff, but we must "build crane to build a crane".

Setup Email Notification and test it with Web Content Management

For purposes of this demonstration we will need:
  • two users + administrator
  • email accounts for both users (example is done with gmail accounts - I'm using my private and companys email)
  • existing built in Liferay site,
  • installed kaleo web plugin

STEP 1 - Create users

User1: Clark Kent that will be able to add web Content scoped to Liferay site
User2: Loise Lane that will be given Site Content Reviewer Role

STEP 2 - setup email server to enable notifications

I will use gmail to demonstrate email services setup as most of us have gmail account.
  • Go to Control Panel
  • In left menu go to Server section and click Server Administration
  • Choose Mail tab
  • Incoming POP Server set to and port to 995
  • Check Use Secure Network Connection
  • Username set to your email address (eg.
  • Outgoing SMTP Server set to and port to 465
  • Check Use Secure Network Connection
  • Username set to your email address (eg.
When setting Outgoing SMTP Server be aware that some ISPs will prevent you to use your custom email server as outgoing server (even via secure connection). So you will need to set this attributes according ISP rules. that have to be set are:

STEP 3 - Kaleo workflow

Now we will activate workflow for Web Content Management so before make sure you have deployed Kaleo Web plugin. (developers: go to your liferay-plugins repository, position to webs/kaleo-web and type ant deploy; other: download plugin and copy it to liferays deploy folder)
  • Go to Control Panel
  • In left menu go to Portal section and click Workflow
  • Choose Definitions tab
  • Make sure you have listed Single Approver workflow model and if not add it
  • Activate Single Approver workflow
  • In Control Panels left menu make sure Liferay site is in the scope. In Liferay section click Workflow Configuration
  • For Web Content from drop down at the right choose Single Approver (Version 1) and click Save

STEP 4 - setup Web Content Notifications

It is good to know that WCM notifications are optional and in current 6.1 relese. If you enable it you will receive both Kaleo and WCM notifications.
If you have no workflow enabled for Web Content Management, tabs in Configuration screen related to workflow notifications wont be available.
Now, when Kaleo is ON, we will setup user notifications for Review Request for reviewer and Article Granted notification for author.
  • Go to Control Panel
  • Make sure that in left menu scoped site is Liferay
  • In Liferays site section click Web Content
  • In the right side of opened page click Options icon and choose Configuration to open Web Content Configuration pop-up
  • Email From Tab - fill in Name (Workflow Liferay) and Email address ( that will show up as notification email sender. Click Save.
  • Web Content Granted Email Tab - Check Enabled checkbox and depending your needs adopt email template. Be careful with template variables that will be substituted with real values in runtime. Meaning of each variable is explained below template. Click Save
  • Web Content Review Email Tab - Check Enabled checkbox and depending your needs adopt your email template with "Article url: [$ARTICLE_URL$]" at the end of the body. Click Save.

STEP 5 - Now we are ready to do some work

User Clark Kent
  • Sign in as Clark Kent
  • Go to Control Panel
  • Make sure Liferay site is in the scope and click Web Content
  • In Web Content tab click Add
  • Enter Title enter Body click Submit for Publication
User Administrator
Now person that has role able to Approve Web Content articles will get an email with request and link to target web content (Figure1). Lets say it is an administrator. Administrator does next
  • Go to Control Panel
  • Click My Workflow Tasks in the left menu
  • Since administrator is not explicitly assigned for Review he will see pending request into "Assigned to My Roles" section.
  • From Action menu select Assign To and in pop-up window chose user Loise Lane and write down comment (something like Please do review we are in hurry...)
User Loise Lane - already received email notification from Liferay Workflow engine that review request is waiting for her.
  • Sign in as Loise Lane
  • Go to Control Panel
  • Click My Workflow Tasks in the left menu
  • You will se pending request - from Action menu choose Approve and in pop-up dialog write down comment "Clark you impress me each time" and click Approve
In this moment Web Content Article is Approved and author Clark Kent recives email:
In this moment your email notifications are ready and we can start building more interesting case.

Useful links

Github Cheating

Company Blogs December 30, 2011 By Igor Beslic Staff

Have you ever faced github pull request bug which manifests in a way that list of peopole you want to send pull request to doesn't contain that person?

I did, in moment when pull request should be sent ASAP. So same moment I got a workaround link but my happyness went away from my windows user smiled face when I noticed that .sh and .py endings. OK, house is burning, what is next step? Install pyton for windows? Install cygwin? Instal vmware and call friend to give you some ready linux image....

No, I do it better way also known as harder way. When you are in github and you selected Pull request you will be able to select person you want to send pull request to from drop down list. If person is not in a list do following:

  1. make sure you are browsing github from latest version of Google Chrome
  2. acquire target person github username (lets say ivicacardic)
  3. select branch you want to send pull request for, click Pull Request, click Change Commits to bring up form with drop down for target user selection
  4. right click to dropdown, choose Inspect element what will bring up chrome developer tools window
  5. expand html select element and position to any of option nodes eg. <option value="igorbeslic/liferay-portal">igorbeslic/lifera...</option>
  6. right click to value attribute and chose Edit Attribute
  7. now change value to starts with github username of person you are sending pull request like value="ivicacardic/liferay-portal"
  8. go back to page select igor beslic from drop down and than click Update Commit Range

BRAVO! Github shows that PR for ivica is ready!

Showing 11 results.
Items 20
of 1