Posting to a new Portlet Action Using a Liferay Hook v6.1-6.2

Well it has been a while since I have written here. Currently I am dealing with Liferay and Liferay Hooks version 6.1.2 RC2 I haven’t really been enjoying liferay as much as I had hopped. I find I spend way more time trying to figuring out the liferay way of doing thing rather than focusing on writing clean code.

If you need to add new functionality to an existing liferay portlet you use a hook. I had a requirement to add a dismiss-all button to the announcements portlet. This tutorial will not cover creating a liferay hook but will assume you are working with an existing hook for the portlet you want to modify.

I sometimes find Liferay documentation to be lacking. Well, I almost always find it lacking, so hopefully writing this will helpo me to remember / clarify the process for my own future reference. I want the button in the portlet I am adding to fire an action then refresh the page. This means we will be using actionURL instead of renderURL when triggering the button press. Since this will be a new action rather than overriding an existing liferay action we will start by adding a new struts action to our hook in the WEB-INFliferay-hook.xml

<hook>
    <!-- identifies the hooks portal.properties file name -->
    <portal-properties>myportal.propertie</portal-properties>
    <!-- specifies the root directory from which we can override jsp files in a liferay porlet -->
    <custom-jsp-dir>/override_jsp</custom-jsp-dir>
    <!-- specify the new action path that will fire the new action -->
    <struts-action>
        <struts-action-path>/nameofportletweareextending/mynewactionpath</struts-action-path>
        <struts-action-impl>com.mycompany.action.MyNewStrutsPortletAction</struts-action-impl>
    </struts-action>
<hook>

In order for this new portlet action path to be made available to liferay publicly, we need to add a line to the myportal.properies we specified in the liferay-hook.xml. In this case however,it is not necessary to add this properties file configuration. We will instead be calling this path from within the portlet!

This configuration would be more useful for a path you wanted to be available portal wide, not something specific to the portlet. In our case we are just using the action within the portlet we are hooking into. In our case, notice that in this file I am using /portal prefix which we did not use above. This will not map to the action path we are creating. Since this is a reference you might need to add this depending on your use case!

auth.public.paths=/portal/mynewactionpath

Next we will create the new action class. This class will extend BaseStrutsPortletAction and will be placed in the package location we specified in the liferay-hook.xml (srccommycompanyactionMyNewStrutsPortletAction)

public class MyNewStrutsPortletAction extends BaseStrutsPortletAction {
    private static Log log = LogFactoryUtil.getLog(DismissAllActiveEntriesStrutsPortletAction.class);
 
    @Override
    public void processAction(StrutsPortletAction originalStrutsPortletAction, PortletConfig portletConfig,
                              ActionRequest actionRequest, ActionResponse actionResponse) throws Exception {
        // you can access the ThemeDisplay here from the action request
        ThemeDisplay themeDisplay = (ThemeDisplay)actionRequest.getAttribute(WebKeys.THEME_DISPLAY);
 
        // access parameters sent from the post here
        String portletName = ParamUtil.getString(actionRequest, "portletName");
        int flagValue = AnnouncementsFlagConstants.NOT_HIDDEN;
        long loggedInUserId = themeDisplay.getUserId();
 
        // this will return the proper scope we need to get all the entries on the page. Basically it just
        // executes the code already available within the liferay package that were not able access from the hook.  
        // This was previously (unfortunately) executed from within the JSP via scriptlets in order to find all the
        // announcements that were to be displayed on the page ...
         LinkedHashMap<Long, long[]> scopes = MyAnnouncementsUtil.getCurrentScopes(loggedInUserId, themeDisplay);
 
        // We can access liferay local service utility classes from within the hook.
        // This query was found in a JSP fragment inside of a scriptlet as well (in entry_iterator.jspf )
        List<AnnouncementsEntry> activeEntries = AnnouncementsEntryLocalServiceUtil.getEntries(loggedInUserId, scopes,
                !portletName.equals(PortletKeys.ANNOUNCEMENTS), flagValue, QueryUtil.ALL_POS, QueryUtil.ALL_POS);
        dismissAllActiveEntries(activeEntries);
    }
 
    @Override
    public String render(StrutsPortletAction originalStrutsPortletAction, PortletConfig portletConfig,
                         RenderRequest renderRequest, RenderResponse renderResponse) throws Exception {
        // normally you might want to just refresh the page.
        // return originalStrutsPortletAction.render(null, portletConfig, renderRequest, renderResponse);
        // Instead it is safer to take the user to the view.jsp directly in this case because the URL they fired
        // the action from may have contained information about an announcement that has since been
        // dismissed (causes an exception to fire depending on the page where they clicked the button from)
        return "/portlet/announcements/view.jsp";
    }
 
    @Override
    public void serveResource(
            StrutsPortletAction originalStrutsPortletAction, PortletConfig portletConfig, ResourceRequest
            resourceRequest, ResourceResponse resourceResponse) throws Exception {
        originalStrutsPortletAction.serveResource(
                originalStrutsPortletAction, portletConfig, resourceRequest, resourceResponse);
    }
 
    private void dismissAllActiveEntries( List<AnnouncementsEntry> activeEntries) throws Exception {
    // This is what liferay source does when you click on the dismiss button for an announcement
    // so we will do this for each announcement in the list. You can do something else here, 
    // I just like keeping the bulk of the business logic in a different method. 
    // We should now have the same active entries that were loaded into the page.
        for(AnnouncementsEntry entry : activeEntries) {
            // This was found in the liferay source when the user selects the dismiss button 
            // so we will do the same for each entry in the list.
            AnnouncementsFlagServiceUtil.addFlag(entry.getEntryId(), AnnouncementsFlagConstants.HIDDEN);
        }
 
    }
}

Finally we create the button to call the action. Since the page we are adding the button to did not have a form action we are about to take advantage of that and write out own. You might want to use ajax and javascript to post instead if the page already has a dedicated action. We will add the button to view.jsp by copying in liferays’s view.jsp for the portlet then appending code to it. Since we want to override lfierays file this file will be located under the directory we specified in the liferay-hoook.xml following the same package structure as found in liferay (docrootoverride_jsphtmlportletannouncementsview.jsp).

<portlet:actionURL var="postURL">
    <portlet:param name="struts_action" value="/nameofportletweareextending/mynewactionpath" />
    <portlet:param name="portletName" value="<%= portletName %>" />
</portlet:actionURL>
<aui:form action="<%= postURL %>" method="post">
    ...
<%-- if you don't want to post using the form action you could write an ajax call instead --%>
      <aui:button value="Dismiss All" type="submit" />
</aui:form>

Now when you select this button, the action will fire, and then the specified page “htmlportletannouncementsview.jsp” will be rendered.




No Comments


You can leave the first : )



Leave a Reply

Your email address will not be published. Required fields are marked *