Sunday, February 10, 2013

STRUTS ACTION HOOK


To extend the CreateAccountAction ,  there are different types of hook to achieve  . Here we are going to discuss about Struts Action Hook.


Steps 1 : Add the struts action path and your custom implementation file in liferay-hook.xml :

<struts-action>
<struts-action-path>/login/create_account</struts-action-path> 
<struts-action-impl>com.liferay.login.hook.action.CustomCreateAccountAction</struts-action-impl>
</struts-action>

Step 2: Create custom implemetation file [CustomCreateAccountAction] in above mentioned location . [com.liferay.login.hook.action]

public class CustomCreateAccountAction extends BaseStrutsPortletAction{

public CustomCreateAccountAction() {
// TODO Auto-generated constructor stub
}

@Override
public void processAction(StrutsPortletAction originalStrutsPortletAction,
PortletConfig portletConfig, ActionRequest actionRequest,
ActionResponse actionResponse) throws Exception {
// TODO Auto-generated method stub
System.out.println("inside the process actionwhile creating the account>>>");
super.processAction(originalStrutsPortletAction, portletConfig, actionRequest,
actionResponse);
}

@Override
public String render(StrutsPortletAction originalStrutsPortletAction,PortletConfig portletConfig,
RenderRequest renderRequest, RenderResponse renderResponse)
throws Exception {
// TODO Auto-generated method stub
System.out.println("inside the render action while creating the account>>>");
return super.render(portletConfig, renderRequest, renderResponse);
}
}


Step 3: deploy.

Done. But sometime you might confuse when we need to extend BaseStrutsPortletAction and BaseStrutsAction.

Struts action interfaces:

com.liferay.portal.kernel.struts.StrutsAction
com.liferay.portal.kernel.struts.StrutsPortletAction

The StrutsAction interface is for regular Struts actions, like /c/portal/update_email_address, from the portal
The StrutsPortletAction interface is used for similar Struts actions from portlets.

Note: If you use BaseStrutsAction, you should override "execute" method:

public class SampleStrutsAction extends BaseStrutsAction {  
@Override 
public String execute(StrutsAction originalStrutsAction, HttpServletRequest request, HttpServletResponse response) throws Exception {  
System.out.println("SampleLayoutStrutsAction.execute() - " + request.getRequestURI());  
return originalStrutsAction.execute(request, response); 
}  
}


Exceptions :


1. "Cannot be Cast" Exception

Some time if you get an exception like "ActionAdapter cannot be cast to PortletActionAdapter" or "PortletActionAdapter cannot be cast to ActionAdapter"

To resolve that:

ActionAdapter cannot be cast to PortletActionAdapter :

    you have implemneted BaseStrutsAction instead of BaseStrutsPortletAction

2) PortletActionAdapter cannot be cast to ActionAdapter :

    you have implemneted BaseStrutsPortletAction instead of BaseStrutsAction

2.  "Forward Does not exist" Exception

If you get an exception like this , then In render method of custom implementation file [CustomCreateAccountAction ] before return statement, 
we need to change that behavier by setting renderRequest.setAttribute(WebKeys.PORTLET_DECORATE, Boolean.TRUE); 

use this :

String ret = originalStrutsPortletAction.render(null, portletConfig, renderRequest, renderResponse);
 renderRequest.setAttribute(WebKeys.PORTLET_DECORATE, Boolean.TRUE);
 return ret;


instead of ,

return originalStrutsPortletAction.render(null, portletConfig, renderRequest, renderResponse);

3.  If It doesn't redirect to Original Path

After successfully implementing struts action hook, If it doesn't redirect to it's original path, you need to use originalStrutsPortletAction in processAction method.

 For example after creating the user , if it stays in same create account page instead of login page which is default behaviour, then use like this :

originalStrutsPortletAction.processAction(portletConfig, actionRequest, actionResponse);

instead of,

super.processAction(originalStrutsPortletAction, portletConfig, actionRequest,actionResponse);

if any queries , please contact gnaniyar@gmail.com 

- Gnaniyar Zubair
gnaniyar@gmail.com

Thursday, June 21, 2012

Publish Version Hook Plugin

Publish Version Hook  Plugin

This article is described about the Custom Publish Version hook which is available in Community plugins. Using this hook, we can revert back old version of journal aticle easily.

Journal Portlet:

The Journal portlet provides a user interface to the admin or content owner to add , edit , delete ,expire  the articles and displaying all the versions of  article.

All versions will be displayed in the list of available Journal Articles as shown below:

(Edit Article --> View History)

A new version will be generated automatically if article is modified

Version Handling

Managing version is having  some limitations in Liferay that users cannot publish the old version of the article. If user s want to publish the old version, they should expire all the versions of the article which we cannot revert back once done. 

eg.,  a  article has more than 100  versions since  it has been modified for 100 times, but  user wants to re-publish   1.1 version which is initial version of the article. Then, they have to expire all  99 versions [ 1.2  to  1.100 ]  which is hectic and not safe for expiring all the articles  in a huge content management portal as anytime we may need to revert back  some version of the article.

Publish Version Hook

 

So,  I have developed  struts action Hook plugin by customizing jsp(s)  and action files of the  journal article  for publishing any version of the article.

This  PublishVersion-Hook  plugin helps  article's admin or content owner to re-publish the article without expiring the old versions.  User cannot publish the Multi Articles or Expired article  and Latest version.

 

 

Use Cases :

 

  1. Update the Article through web content display portlet  or control panel.

     2.   A new version will be generated automatically if article is modified each time.

     3.   Click  View History section where all the version of the articles will be displayed as shown below

 

 

4.    Select old version of the article and click Publish.

 

 

5.  You cannot publish Latest / Expired / Multiple articles at a time .

 

 

Technical Explanation:

I have developed this plug-in in Struts Action Hook.   Reason for developing Struts Action Hook is, I was planning to learn Struts Action Hook for long time since this new feature introduced. So, finally done it. J

I have overridden the existing Struts Action /journal/edit_article which extends BaseStrutsPortletAction and    In View History Page [view_article_history.jsp &   article_version_action.jsp], new Publish buttons are added to publish the selected version.   

When publishing  the  selected version, it is redirecting to Custom Struts Action class where I have written my logic to change the selected version's count  as latest version by increasing 1 with latest version.   So automatically latest version will be displayed.

 

JournalArticle selectedVersionObject = JournalArticleLocalServiceUtil.getArticle(selectedId);

selectedVersionObject.setVersion(latestVersion+Math.abs(0.1));   //increasing 1 with latest version

JournalArticleLocalServiceUtil.updateJournalArticle(selectedVersionObject);

 

If any queries / suggestion about this hook, Please feel free to reach me : gnaniyar@gmail.com

 

-  Gnaniyar Zubair

gnaniyar@gmail.com

Thursday, March 3, 2011

Solr Integration with Liferay

Solr Integration with Liferay

Solr is the popular, blazing fast open source enterprise search platform from the Apache Lucene project. Its major features include powerful full-text search, hit highlighting, faceted search, dynamic clustering, database integration, and rich document (e.g., Word, PDF) handling. Solr is highly scalable, providing distributed search and index replication, and it powers the search and navigation features of many of the world's largest internet sites. Refer to Apache Solr

This document shows how to integrate Solr within Liferay Portal. Note that Liferay portal version must be 6 or above.

Integration steps :

1 . Download the latest Solr from this link. Extract somewhere this zip file, and I will call this extracted folder as SOLR_HOME.

  1. Download the solr-web-6.0.1.1 war file and deploy the war file in liferay tomcat server.

  2. Copy the schema.xml file from the solr-web/WEB-INF/conf folder which is deployed and paste/replace in SOLR_HOME/example/solr/conf folder.

  3. Change the solr url as follows in solr-spring.xml which is located in solr-web/WEB-INF/classes/META-INF/ folder.

Change from this content :

<bean id="solrServer"
class="com.liferay.portal.search.solr.server.BasicAuthSolrServer">

<constructor-arg type="java.lang.String" value="http://localhost:8080/solr" />

</bean>

to this :

<bean id="solrServer"
class="com.liferay.portal.search.solr.server.BasicAuthSolrServer">

<constructor-arg type="java.lang.String" value="http://localhost:8983/solr" />

</bean>


Note :
8983 is the default port for Solr.
  1. Now shutdown the tomcat server.

  2. Start the Solr server using the following command from SOLR_HOME/examle folder in command prompt/terminal.

Java -jar start.jar

  1. Start the liferay tomcat server now.

  2. Now add the search portlet in your home page and test your searches. When ever you search anything you can see some log info in solr server.

Note : By defaut Solr searches for the new webcontent, wiki, blogs... etc from the time Solr is integrated. To search the complete portal reindex all search indexs from the server administration which is in control panel.

Friday, December 24, 2010

Mail Portlet Synchronization Issue

Liferay Portlet doesn't Synchronize all the mails properly. Though it shows the Mail Count in Pagination,the Pagination fails to work properly at a certain level.

This is due to some unusual characters in subject/sender/to/body in your mails.

You can resolve this by changing some column types from "mail_message" table.

Step 1: Please make sure that your database character set is "UTF-8".If it isn't then create it as follows:

create database testing character set utf-8



Step 2: Change "longtext" to "binary" for some column from "mail_message" table

as follows:

alter table Mail_Message modify column sender longtext character set binary;

alter table Mail_Message modify column to_ longtext character set binary;

alter table Mail_Message modify column cc longtext character set binary;

alter table Mail_Message modify column bcc longtext character set binary;

alter table Mail_Message modify column body longtext character set binary;

alter table mail_message modify column sender longtext charecter set binary

Fileupload using service.xml file

I have done this in MVC portlet. File upload in liferay using service.xml file is bit tricky. With small modification in portlet-model-hints.xml we can achieve this.

Step 1: Give the data type as string to store the uploaded file.

entry in service.xml file

<entity name="FileUploader" table="fileuploader" local-service="true">

<column name="fid" type="long" primary="true"/>

<column name="content" type="String"/>

</entity>

Step 2 : Do 'ant build-service' from specific portlet level. As you know that which is used to generate the api to interact with database.

Step 3 : Open 'portlet-model-hints.xml' file which is in 'WEB-INF/src/META-INF'.

Initially this xml file look like this

<model-hints>

<model name="com.sample.mvc.model.FileUploader">

<field name="fid" type="long" />

<field name="content" type="String" />

</model>

</model-hints>

Depending on this xml file script files will generate which are in 'sql' folder.

Define 'hint-collection' tag for the 'content' column where i am storing the file content in this example.

Modified file will be as shown below.

<model-hints>

<model name="com.sample.mvc.model.FileUploader">

<field name="fid" type="long" />

<field name="content" type="String">

<hint-collection name="CLOBTYPE" />

</field>

</model>

<hint-collection name="CLOBTYPE">

<hint name="max-length">2000000</hint>

</hint-collection>

</model-hints>

Step 4 : Now do again 'ant build-service' to update the corresponding script files. This will update the sql script file. We can see the datatype of 'content' column in sql script as TEXT. When we deploy it will create CLOB type column in your database.

Step 5 : Write this logic in your action class to get the file from the jsp page and store it into the database.

public void abc(ActionRequest arq,ActionResponse ars) throws Exception {

UploadPortletRequest uploadRequest = PortalUtil.getUploadPortletRequest(arq);

FileUploader fileUp = new FileUploaderImpl();

fileUp.setFid(CounterLocalServiceUtil.increment("FileUploader.class"));

File file = uploadRequest.getFile("file");

byte[] bytes = FileUtil.getBytes(file);

fileUp.setContent(Base64.objectToString(bytes));

FileUploaderLocalServiceUtil.addFileUploader(fileUp);

}

Your JSP page something look like this :

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

<portlet:defineObjects />

<%@page import="javax.portlet.PortletURL"%>

<portlet:actionURL name="abc" var="actionURL1"></portlet:actionURL>

<form action="<%= actionURL1.toString() %>" method="post" enctype="multipart/form-data">

Name : <input type="text" name="urname" />

<input type="file" name="file"/>

<input type="submit" value="Submit" />

</form>

Saturday, December 4, 2010

Get Remote IP

To get the Client System's (Remote) IP Address through portlet request:


HttpServletRequest request = PortalUtil.getHttpServletRequest(actionrequest);
String clientIp = PortalUtil.getOriginalServletRequest(request).getRemoteAddr();


or


String clientIp = PortalUtil.getHttpServletRequest(request).getRemoteAddr();

--
Warm Regards,

K.Gnaniyar Zubair,

Sunday, November 28, 2010

Portlet to Portlet Communication


Introduction

The first version of the portlet specification, JSR-168/portlet1.0, did not include any support for Inter Portlet Communication. The second version, JSR-286/ portlet2.0, which is supported for IPC Mechanism.

IPC is made easy with JSR-286 to share the data between two portlets. Using IPC mechanisms, we can share the data from ACTION to VIEW phase and VIEW-VIEW Phase.

There are 3 ways to share the data between 2 portlets.

1. Portlet session

2. IPC Mechanisms

2.1 Public Render Parameters

2.2 Event

2.3 Client-Side IPC

3. Cookies

1.Portlet Session

By default , Each war has its own session and will not be shared with other wars. Liferay provides a mechanism by which Portlets can share session attributes across WARs.

A PortletSession is created for each user per portlet application. This makes the PortletSession useful for communicating all user related information among different portlets in the same portal application.

Step 1: set below attributes in Portlet1

liferay-portlet.xml :


<portlet>

<private-session-attributes>false</private-session-attributes>

</portlet>

Step 2: To set the Session:


PortletSession session = renderRequest.getPortletSession();

session.setAttribute("sessionValue",some-value , PortletSession.APPLICATION_SCOPE);

Step 3 : Get the Session Value in Portlet2

PortletSession ps = renderRequest.getPortletSession();

String tabNames = (String)ps.getAttribute("sessionValue ",ps.APPLICATION_SCOPE);

=====================================================================

2. IPC Mechanism

2.1 Public Render Parameter : IPC ( Inter Portlet Communication) :


In JSR 168, the render parameters set in processAction is only available in the render of the same portlet. With the Public Render Parameters feature, the render parameters set in the processAction of one portlet will be available in render of other portlets also.

By adding the following property in portlet-ext, we can enable portlets to share render states with other portlets that are on different pages:

portlet.public.render.parameter.distribution=ALL_PORTLETS

Step 1: Add below attribute in “Sender-Portlet”

<portlet-app>

<portlet>

<supported-public-render-parameter>

id1

</supported-public-render-parameter>
</portlet>

<public-render-parameter>

<identifier>id1

<qname xmlns:x="http://abc.com/userId">x:param1

</public-render-parameter>

</portlet-app>

Note: We can declare a list of public paramters for a portlet application.

Step 2:

We can set render parameter in the processAction() method by using the defined public render parameter identifier as the key.


response.setRenderParameter("id1", “someIdValue”);

e.g.

public void processAction(ActionRequest request, ActionResponse response)

throws IOException, PortletException { ........

response.setRenderParameter("id1", “someIdValue”); ........

}

Step 3 : Receiver Portlet Portlet “portlet.xml”

Specify the render parameter the portlet would like to share in the

portlet section.

<portlet-app>
< portlet >

< portlet-name >PortletB< /portlet-name >

<supported-public-render-parameter >id1< /supported-public-render-parameter >

< /portlet >

<public-render-parameter>

id1

x:param1

</public-render-parameter>

</portlet-app>

Step 4 :

A portlet can read public render parameter using following method

request.getPublicParameterMap()

Note:

Public render parameters are merged with regular parameters so can also be read using


request.getParameter(“id1”);

Step 5:

A portlet can remove a public render parameter by invoking following methods.

response.removePublicRenderParameter(“id1”)

------------------------------------------------------------------------------------------


2.2
Event : IPC ( Inter Portlet Communication) Mechanisms :

Portlet events that a portlet can receive and send.

In JSR-168 :
The only way to achive eventing was through portlet session.
Limitation : Portlet has to be in the same web application.

In JSR-286 :
JSR 286 (Portlet 2.0) defines a lifecycle for events, so that eventing is possible between portlets that are in different web applications.

By adding the following property in portal-ext, we can enable portlets to send and receive events from other portlets that are on different pages

portlet.event.distribution=ALL_PORTLETS


Step 1: Sender Portlet

portlet.xml

-----------

The portlet standard defines a way of telling the portlet container which portlet is responsible for sending an event.

<portlet-app>

<portlet>

<supported-processing-event xmlns:x='http://liferay.com'>

<qname>x:empinfoqname>

supported-processing-event>

</portlet>

<event-definition xmlns:x='http://liferay.com'>

<qname>x:empinfoqname>

<value-type>java.lang.Stringvalue-type>

</event-definition>

</portlet-app>

Step 3 : Set the event in process action:


javax.xml.namespace.QName qName =

new QName("http://liferay.com", "empinfo", "x");

response.setEvent(

qName,

"Hai You have received Event Data sent from Sender Portlet");

Step 4: Listner Portlet


portlet.xml:

-----------


<portlet-app>

<portlet>

<supported-processing-event xmlns:x='http://liferay.com'>

<qname>x:empinfoqname>

supported-processing-event>

</portlet>

<event-definition xmlns:x='http://liferay.com'>

<qname>x:empinfoqname>

<value-type>java.lang.Stringvalue-type>

</event-definition>

</portlet-app>



Step 5: get the EVENT:

This Even will be called after processAction as shown in the picture:

Lifecycle for IPC Event:

@javax.portlet.ProcessEvent(qname = "{http://liferay.com}empinfo")

public void handleProcessempinfoEvent(

javax.portlet.EventRequest request, javax.portlet.EventResponse response)

throws javax.portlet.PortletException, java.io.IOException {

javax.portlet.Event event = request.getEvent();

String value = (String) event.getValue();

System.out.print("value in process event>>>>>>>>>" + value);

response.setRenderParameter("empInfo", value);

}


------------------------------------------------------------------------------------------

2.3 Client-Side IPC :

There are 2 APIs for client side IPC.

Event generation (call from portlet A):


Liferay.fire('', {
name : value
});


e.g.
Liferay.fire('planTravel', {
origin : 'pune',
destination : 'mumbai'
});

Event Listener ((call from portlet B):


Liferay.on('', function(event) {

});


e.g.
Liferay.on('planTravel', function(event) {
showNews('', event.origin);
showNews('', event.destination);

});

===============================================

3. Cookies

Other than the IPC mechanism, There is an easiest way to get the data between portlets on different pages called COOKIES.

But there are some limitations for cookies that it will not accept more than 4KB size datas and the biggest limitation is, the 20 cookies per server limit, and so it is not a good idea to use a different cookie for each variable that has to be saved

Portlet 1 :

To Set the Cookies through jQuery :


function setCookie(docURL) {

jQuery.cookie("cookieParam",docURL);

}

To Set the Cookies through java / jsp:


HttpServletResponse response = PortalUtil.getHttpServletResponse(

actionResponse);

Cookie cookieParam = new Cookie("cookieParam ", password);

response.addCookie(cookieParam);

Portlet 2:

To get the Cookies through jQuery :


jQuery.cookie("cookieParam ");


To get the Cookie through java/ jsp :


String sessionid = "";

Cookie[] cookies = request.getCookies();

if (cookies != null) {

for (int i = 0; i <>

if (cookies[i].getName().equals("cookieParam ")) {

sessionid = cookies[i].getValue();

break;

}

}

}

Gnaniyar Zubair

rasikow@gmail.com

Share & Enjoy

Twitter Delicious Facebook Digg Stumbleupon Favorites More