Recent Bloggers

Paulo Fernandes

Staff
15 Publications
22 août 2014

Scott Lee

Staff
3 Publications
21 août 2014

Martin Yan

Staff
5 Publications
20 août 2014

Olaf Kock

Staff
77 Publications
19 août 2014

Miguel Ángel Pastor Olivar

Staff
15 Publications
19 août 2014

James Falkner

Staff
94 Publications
18 août 2014

Josiah "Duke" Harrist

Staff
1 Publications
13 août 2014

James Min

Staff
30 Publications
12 août 2014

Adam Victor Brandizzi

Staff
3 Publications
12 août 2014

Yogesh Sharma

1 Publications
12 août 2014

OAuth Client Portlet Implementation

Company Blogs 23 décembre 2013 Par 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.

Architecture

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 liferay-plugin-package.properties:

portal-dependency-jars=\
    httpcore.jar,\
    ...,\
    json-java.jar,\
    scribe.jar

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 {

	@Override
	protected String getAccessTokenEndpoint() {
		if (Validator.isNull(_accessTokenEndpoint)) {
			_accessTokenEndpoint = OAuthUtil.buildURL(
				"oauth-portal-host", 80, "http",
				PortletPropsValues.OSB_LCS_PORTLET_OAUTH_ACCESS_TOKEN_URI);
		}

		return _accessTokenEndpoint;
	}

	@Override
	protected String getRequestTokenEndpoint() {
		if (Validator.isNull(_requestTokenEndpoint)) {
			_requestTokenEndpoint = OAuthUtil.buildURL(
				"oauth-portal-host", 80, "http",
				PortletPropsValues.OSB_LCS_PORTLET_OAUTH_REQUEST_TOKEN_URI);
		}

		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 portal.properties and liferay-hook.xml. Defaults are

auth.public.paths=\
    /portal/oauth/access_token,\
    /portal/oauth/authorize,\
    /portal/oauth/request_token

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 portlet.properties to set all OAuth related constants:

oauth.access.token.uri=/c/portal/oauth/access_token
oauth.authorize.uri=/c/portal/oauth/authorize?oauth_token={0}
oauth.consumer.key=42c56e22-d5a2-4003-86f4-cbc34b6de3e3
oauth.consumer.secret=793195c2936a85649042b24ed843a036
oauth.request.token.uri=/c/portal/oauth/request_token

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",
			PortletPropsValues.OSB_LCS_PORTLET_OAUTH_AUTHORIZE_URI);

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

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

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

			oAuthServiceBuilder.apiKey(
				PortletPropsValues.OSB_LCS_PORTLET_OAUTH_CONSUMER_KEY);
			oAuthServiceBuilder.apiSecret(
				PortletPropsValues.OSB_LCS_PORTLET_OAUTH_CONSUMER_SECRET);
			oAuthServiceBuilder.provider(OAuthAPIImpl.class);

			_oAuthService = oAuthServiceBuilder.build();
		}

		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 http://www.consumersite.com/registered. 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.

Authentication

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"/>
	</a>
</div>

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(
		Token.class.getName());

	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",
	"/api/secure/jsonws/context.service/method/parms");

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 Liferay.com, 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 19 avril 2013 Par 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://www.liferay.com/something 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 15 mars 2013 Par 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 liferay-plugin-package.properties 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" />
</project>
  • 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 12 juin 2012 Par 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
  KEY EXAMPLE VALUE CLASS
  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
  articleURL LINK TO ARTICLE IN MAXIMIZED MODE 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
  content

This is my web content body....

java.lang.String
  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
  KEY EXAMPLE VALUE CLASS
  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
  content

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

java.lang.String
  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
  editor

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

java.lang.String
  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
  KEY EXAMPLE VALUE CLASS
  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
  KEY EXAMPLE VALUE CLASS
  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 23 février 2012 Par 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 7 février 2012 Par 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.

Kaleo

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:

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

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 3 février 2012 Par 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 mydomain.com
  • Set email domain to mail.mydomain.com
  • Set liferay user email (eg. liferay@mydomain.com)
  • 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:

pop.server.notifications.enabled=true
pop.server.notifications.interval=1
pop.server.subdomain=

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:

pop.server.subdomain=event

or override it with some your value:
pop.server.subdomain=mboards

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 mb_message.NNNNN.NNNNN@events.mydomain.com.  If your email server is not setup to handle emails to events.mydomain.com 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 <mb_message.12204.12405@events.mydomain.com>: 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 mydomain.com and its aliases. In my case I have:

  • mail.mydomain.com as main mx record and
  • events.mail.mydomain.com 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 main.cf to use virtual domains:
Line with mydestination setting must be commented out and we are adding new lines:
# mydestinations = mydomain.com
myhostname= mail.mydomain.com
mydomain = mydomain.com
# Virtual mailbox settings
virtual_mailbox_domains = events.mail.mydomain.com,  mydomain.com
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 main.cf 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:
info@mydomain.com contact@mydomain.com
administrator@mydomain.com root@mydomain.com
 
/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 11 janvier 2012 Par 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 pop.gmail.com and port to 995
  • Check Use Secure Network Connection
  • Username set to your email address (eg. clark.kent@gmail.com)
  • Outgoing SMTP Server set to smtp.gmail.com and port to 465
  • Check Use Secure Network Connection
  • Username set to your email address (eg. clark.kent@gmail.com)
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.
portal-ext.properties that have to be set are:
pop.server.notifications.enabled=true
pop.server.notifications.interval=1
 

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 (workflow@liferay.com) 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 30 décembre 2011 Par 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!

Affichage de 9 résultat(s).
Items 20
de 1