Publish Version Hook

Community Blogs January 21, 2014 By Gnaniyar Zubair

Publish Version Hook  Plugin

This article is described about the Custom Publish Version hook which is available in Market Place Utility plugins. Using this hook, we can revert back old version of the journal aticle easily withour deleting or expiring previous versions.

Journal Portlet:

The Journal portlet provides a user interface to the content publisher to add , edit , delete ,preview and expire  the articles and displays all the versions of  article whenever article is updated.

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

(Edit Article --> View History)

 

By default, 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

 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 without expiring or deleting previous articles.

This  PublishVersion-Hook  plugin helps to content publisher to re-publish the article without expiring the old versions and 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(MathUtil.format(lVersion + 0.1, 1, 1));   //increasing 1 with latest version

JournalArticleLocalServiceUtil.updateJournalArticle(selectedVersionObject);

Download URL 

/for 6.2.0 GA2 release :

Publish-Version-hook-6.2.0.1.war/download

 

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

 

-  Gnaniyar Zubair

gnaniyar@gmail.com

 

LDAP with AD in Liferay

Community Blogs January 13, 2014 By Gnaniyar Zubair

1. Introduction :

This is an example of a basic LDAP integration in Liferay 6.0.5. In the example we import users/groups into Liferay via LDAP and vice versa.

2. Environment

Windows Server 2003, Active Directory
Liferay 6.0.5


3. LDAP & AD

LDAP

LDAP (Lightweight Directory Access Protocol) is a directory publishing service and specially designed for directory service providers.

Active Directory

AD (Active directory) is a directory service provider, where you can add new user to a directory, remove or modify, specify privilages, assign policy etc. Its just like a phone directory where every person have a unique contact number. Everything in AD(Active Directory) are considered as Objects and every object is given a Unique ID.

LDAP vs AD

AD is a directory services database, and LDAP is one of the protocols you can use to talk to it.


4 : AD Users

First you need to setup the Active Directory.

In Active Directory you need to define 2 OU(Organizational Unit). One is for storing or categorizing all the users. The second is to Create the groups.

For example:-

a) OU=Users

b) OU=Groups

4.1 Create OU in AD:

Go to DN – right click – New – Organizational Unit

4.2 Create Users in AD:
Go to OU (mpower) – right click – New – User

4.3 User’s Attributes:

You can test the users account by login in Active Directory machine with newly created users.

5. LDAP Configuration in Liferay

5.1 : Integration

a. Login as Administrator in liferay portal.

b. Go to Control Panel -> Settings -> Authentication -> LDAP select tab.



c. Check the Enabled box.

d. Id. If the Required box is checked only users in the LDAP server will be able to log into Liferay Portal. For this demonstration leave the box unchecked.

5.2: Server Name

Liferay Portal supports other directory servers in addition to the ones provided. The Apache Directory ServerMicrosoft Active Directory Server, and Novell eDirectory comes preconfigured.

Select Server Name : Microsoft Active directory Server

5.3: Connection :

Provide given values and Test LDAP Connection :

Base Provider Url : ldap://172.22.1.26:389

Base DN : ou=Tech,dc=mpowerglobal,dc=com

Principal : mpowerglobal\administrator

credentials : abc123.


mpowerglobal - domain name in AD

Tech - Organizational Unit

172.22.1.26 : IP in AD Machine

389 : Port No.

ldap://172.22.1.26:389: This tells the portal where the LDAP server is located )

5.4: TEST LDAP CONNECTION :

Now test your connection to see if it validates.. You can test the connection by clicking “Test LDAP Connection” button. If it shows successful message, your connection is successful....

5.5: Users :

If you wish to change how users login (Ex. Login with screen name / emailaddress), then change the settings in the Authentication Search Filter field.

5.5.1 Users:

Provide these values :

Authentication Search Filter : (&objectCategory=Person)(mail=@email_address@)

import search filter: (objectClass=User)

( if objectClass is defined as “User” in AD, then we have to give “User” .

[ObjectClass = UserInetOrgPerson,...etc ] )

5.5.2 User Mapping :

Provide these values :

screenName : sn

password: userPassword

EmailAddress : mail

first Name : givenName

LastName : sn

Jobtitle : title

Group : memberOf

5.6 TEST LDAP USERS:

Now you can test the groups by clicking “Test LDAP Users” button. It will show all the Users from Active Directory.

Step 5.7: Import Users to Liferay

if you checked "Import on Startup Enabled", then restart your App Server.

Log back in, go the Control Panel->Users . Your imported Users will be there!

Imported Users :

Step 5.8: Groups

5.8.1 Groups

Provide these values:

importSearchfilter : (objectClass=group)

5.8.2: GroupMapping:

GroupName : cn

Description: description

User: member


5.9: TEST LDAP GROUPS:

Now you can test the groups by clicking “Test LDAP Groups” button. It will show all the Groups under the given OU ( Tech) from Active Directory. Also It will show Members of that groups.


5.10. Import Groups to Liferay User Group

LDAP groups are pulled into Liferay as UserGroups

Follow the “Step 4.9: Import Users to Liferay” which will import groups from AD to Liferay as follows:

6. AD-GROUPS – USERS Structure

( Create the following groups in AD, test1ss & sss under the “Tech” [OU] and assign some users to that groups)


7. Export Users to AD


We can exports all the users from liferay to AD by providing these values.

Users DN = ou=Tech,dc=mpowerglobal,dc=com

User Default Object Classes = top,person,User,organizationPerson

Groups DN = ou=groups,dc=mpowerglobal,dc=com

Group Default Object Classes= top, group

( Note : if you give object name “User” in User Default Object Classes ,

In AD, object type will be “User” )

7.1: Add Liferay users: Go to Control Panel – Users – Add users

After setting the above value in control panel, if you create users in Liferay, it will be exported to AD.

 

7.2: Verify AD Users from Liferay:

All the Users from Liferay will be populated in AD as follows:

Selected Users from below screen has been exported from Liferay)

 

8. Login into Liferay with Imported User

We need to uncheck this below option in AD : “User must change password at next logon”

If this checkbox is unchecked , then you can login with that particular user into Liferay.

If not, we need to login again in AD and change password, then Import to Liferay.


9. Troubleshooting

1. If User is not imported AD to Liferay,

a. Restart AD Server / Liferay

b. Make sure all the attributes are given while creating User in AD.

For ex, (Screen Name, Password, Email Address, First Name, and Last Name)

 


2. If imported user is not able to login into Liferay, uncheck the below option in AD while

creating user:

“User must change password at next logon”

 


3. If Group is not imported from AD to Liferay,

a. Make sure all the attributes are given while creating Group in AD,

For ex, (name,description)

b. If No member is assigned to Group in AD, That group will not import to Liferay.

 


4. Password Policy

Portal can be configured to use LDAP password policies. To configure the Portal to use LDAP's password policy, go to

· > Control Panel

· > "Settings" tab

· > "Authentication" tab

· > "LDAP" tab

· > and under the "Password Policy" section, click the "Use LDAP Password Policy" checkbox on (at the bottom)

If that has been done correctly, when you try and view the liferay Control Panel - Password Policy, you will get a message saying that “You are using LDAP's password policy. Please change your LDAP password policy settings if you wish to use a local password policy”.

- Gnaniyar Zubair
gnaniyar@gmail.com

Inter Portlet Communication

Community Blogs January 13, 2014 By Gnaniyar Zubair

Introduction

The first version of the portlet specification, JSR-168/Portlet 1.0, did not include any support for Inter Portlet Communication. The second version, JSR-286/Portlet 2.0, has supported for IPC Mechanism.

JSR-286 makes it easy 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
    1. Public Render Parameters
    2. Event
    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 ",PortletSession.APPLICATION_SCOPE);

2. Inter Portlet Communication (IPC) Mechanism

2.1 Public-render-parameter Inter-portlet Communication

 Illustration of IPC through public-render parameters

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</identifier>
            <qname xmlns:x="http://abc.com/userId">x:param1</qname>
        </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>
            <identifier>id1</identifier>
            <qname xmlns:x="http://abc.com/userId">x:param1</qname>
        </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 Inter-portlet Communication

 Comic Illustration of IPC using Events

Portlet events that a portlet can receive and send.

In JSR-168 The only way to achieve eventing was through portlet session. The limitation of this approach was that the Portlets 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.properties, 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.

Add this inside <portlet> tag:

    <portlet-app>
        <portlet>
            <supported-publishing-event xmlns:x='http://liferay.com'>
                <qname>x:empinfo</qname>
            </supported-publishing-event>>
        </portlet>

        <event-definition xmlns:x='http://liferay.com'>
            <qname>x:empinfo</qname>
            <value-type>java.lang.String</value-type>
        </event-definition>
    </portlet-app>

Step 2: Set the event in process action

    javax.xml.namespace.QName qName = new QName("http://liferay.com", "empinfo", "x");
    response.setEvent(qName, "Hi! You have received Event Data sent from Sender Portlet");

Step 3: Listener Portlet

portlet.xml

    <portlet-app>

        <portlet>
            <supported-processing-event xmlns:x='http://liferay.com'>
                <qname>x:empinfo</qname>
            </supported-processing-event>
        </portlet>
        <event-definition xmlns:x='http://liferay.com'>
            <qname>x:empinfo</qname>
            <value-type>java.lang.String</value-type>
        </event-definition>
    </portlet-app>

Step 4: get the EVENT

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

Lifecycle for IPC Event

 Lifecycle illustrated

    @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(
            '<eventName>', {
            name: value 
        }
    );

E.g.

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

Event Listener (call from Portlet-B):

    Liferay.on(
            '<eventName>',
            function(event) {
                // your code
            }
    );

E.g.

    Liferay.on(
            '<eventName>',
            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 through Cookies.

But there are some limitations for cookies:

  1. It will not accept more than 4KB size of data
  2. 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:

    <script src="/html/js/jquery/cookie.js" type="text/javascript" > </script>
    
    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 < cookies.length; i++) {
            if (cookies[i].getName().equals("cookieParam ")) {
                sessionid = cookies[i].getValue();
                break;
            }
        }
    }

- Gnaniyar Zubair

gnaniyar@gmail.com

STRUTS ACTION HOOK

Community Blogs April 17, 2013 By Gnaniyar Zubair

Link to the official documentation about Struts Action Hook: Overriding and adding Struts actions

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
Showing 4 results.