Liferay Friendly URL Mapper

Liferay provided a way to allow you to construct the friendly url path. But from my point of view, it is not really friendly for developer to construct and understand from begin. In the follow section, i’ll try to explain as details as possible for you.

1) To make your portlet url look like friendly, first you need to create a class and extends the BaseFriendlyURLMapper

  1. package ….
  2. import    ….
  3.  
  4. public class MyPortletUrlMapper extends BaseFriendlyURLMapper {
  5.  …..
  6.  …..
  7. }

2) Next, implement and override the methods buildPath(LiferayPortletURL portletURL)

  1. public String buildPath(LiferayPortletURL portletURL) {
  2.  …..
  3. }

Basically buildPath method is used for reconstruct the url. For instance, original url look like this :

http://localhost:8080/en/web/global/page2?p_p_id=stock_WAR_webstock&p_p_lifecycle=0&p_p_state=normal&p_p_mode=edit&p_p_col_id=column-1&p_p_col_count=1&_stock_WAR_webstock_configId=&_stock_WAR_webstock_portletId=stock_WAR_webstock&_stock_WAR_webstock_plid=10177

and you can format it into a friendly path like below:

http://localhost/en/web/global/stock/-/action/view/product/123/version/v-10

*Note: If you do not want to format the url into friendly path, just return null value and it will return the original url by liferay.

Usecase

In some cases, your portlet may have VIEW, EDIT or HELP mode. You may need to extra carefully when construct your new path in this method.

Example, you just want to format the friendly URL only for “VIEW” mode. So, you should write the code to cater for it and allow the unfriendly url for “EDIT” mode.

  1. public String buildPath(LiferayPortletURL url)
  2. {
  3.      String friendlyURLPath = null;
  4.      String p_p_mode = null;
  5.  
  6.      try {
  7.          p_p_mode = url.getPortletMode().toString();
  8.      }
  9.      catch(Exception e){
  10.              return null;
  11.      }
  12.  
  13.    String param1 = GetterUtil.getString(url.getParameter("action"));
  14.    String param2 = GetterUtil.getString(url.getParameter("productId"));
  15.    String param3 = GetterUtil.getString(url.getParameter("version"));
  16.  
  17.    if(Validator.isNotNull(p_p_mode) && Validator.isNotNull(param1))
  18.    {
  19.         if(p_p_mode.equalsIgnoreCase("view"))
  20.         {
  21.               // format whatever url pattern u like
  22.               friendlyURLPath = StringPool.FORWARD_SLASH + getMapping() +
  23.                                StringPool.FORWARD_SLASH + "action" +
  24.                                StringPool.FORWARD_SLASH + param1+
  25.                                StringPool.FORWARD_SLASH + "productId" +
  26.                                StringPool.FORWARD_SLASH + param2  +
  27.                                StringPool.FORWARD_SLASH + "version" +
  28.                                StringPool.FORWARD_SLASH + param3;
  29.  
  30.             // Exclude the following parameter to your new construct URL          
  31.             // remove the query string from build path.. eg id=1
  32.             portletURL.addParameterIncludedInPath("p_p_id");
  33.             portletURL.addParameterIncludedInPath("p_p_lifecycle");
  34.             portletURL.addParameterIncludedInPath("p_p_state");
  35.             portletURL.addParameterIncludedInPath("p_p_mode");
  36.             portletURL.addParameterIncludedInPath("p_p_col_id");
  37.             portletURL.addParameterIncludedInPath("p_p_col_count");
  38.             portletURL.addParameterIncludedInPath("productId");
  39.             portletURL.addParameterIncludedInPath("version");
  40.             portletURL.addParameterIncludedInPath("action");
  41.  
  42.             // return new pattern url
  43.             return friendlyURLPath;
  44.         }
  45.    }
  46.    // return orignal unformatted url
  47.    return friendlyURLPath;
  48. }

4) Next, implement and override the methods populateParams(String friendlyURLPath, Map params)

  1. populateParams(String friendlyURLPath, Map params)  {
  2.      …..
  3. }

populateParams() method is revert concept from build path. The method is responsible to mapping and added back the query string to the path.

  1. private static final String PORTLET_ID = "stock_WAR_webstock";
  2. private static final String PORTLET_MAPPING = "stock";
  3. private static final String LIFECYCLE_RENDER = "0";
  4. private static final String LIFECYCLE_ACTION = "1";
  5. private static final String LIFECYCLE_RESOURCE = "2";
  6. ….
  7. @Override
  8. public String getPortletId()  {
  9.        return PORTLET_ID;
  10. }
  11.  
  12. @Override
  13. public String getMapping()  {
  14.        return PORTLET_MAPPING;
  15. }
  16.  
  17. @Override
  18. public void populateParams(String friendlyURLPath, Map params)
  19. {
  20.       String[] items = friendlyURLPath.split("/");
  21.  
  22.        if (items.length > 2) {
  23.               // added back to query string
  24.               addParam(params, "p_p_id", PORTLET_ID);
  25.               addParam(params, "p_p_mode", PortletMode.VIEW);
  26.               addParam(params, "p_p_lifecycle", LIFECYCLE_ACTION);
  27.               addParam(params, "p_p_state", "normal");
  28.  
  29.                addParam(params, "action", getParameter(3,items));
  30.                addParam(params, "productId", getParameter(5,items));
  31.                addParam(params, "version", getParameter(7,items));
  32.  
  33.        } else {
  34.                addParam(params, "p_p_id", PORTLET_ID);
  35.                addParam(params, "p_p_lifecycle", LIFECYCLE_RENDER);
  36.                addParam(params, "p_p_mode", PortletMode.VIEW);
  37.                addParam(params, "action", "view");
  38.         }
  39. }
  40.  
  41. public String getParameter(int index, String[] items)
  42. {
  43.       String paramValue = "";
  44.       try {
  45.              paramValue = items[index];
  46.       }catch(ArrayIndexOutOfBoundsException are){
  47.              paramValue = "";
  48.       }catch(Exception e){
  49.              paramValue = "";
  50.       }
  51.        return paramValue;
  52. }

If you want to format a form post or the action link .You need to supply the p_p_lifecycle to 1.
0 = RenderMapping, 1 = ActionMapping and 2 = ResourceMapping.

Avoid confuse

If you want to avoid the confuse/clash with the porlet parameter with others portlet, a good idea is add the portlet namespace to you own parameter. eg

  1. public void populateParams(final String friendlyURLPath, final Map params) {
  2.     String[] items = friendlyURLPath.split("/");
  3.     ……
  4.     String namespace = getNamespace();
  5.     …..
  6.     addParam(params, namespace + "action", getParameter(3,items));
  7.     addParam(params, namespace + "productId", getParameter(5,items));
  8. }

Remove Dash

You can remove the dash from you new build path by override this method and return false.

  1. @Override
  2. public boolean isCheckMappingWithPrefix() {
  3.         return false;
  4. }

Liferay-Portlet.xml

  1.  
  2. <liferay-portlet-app>
  3.     <portlet>
  4.         <portlet-name>carousel</portlet-name>
  5.         <friendly-url-mapper-class>
  6.                 com.foo.module.MyPortletUrlMapper
  7.         </friendly-url-mapper-class>   
  8.     </portlet>
  9.    []
  10. </liferay-portlet-app>
  11.  
You can leave a response, or trackback from your own site.

5 Responses to “Liferay Friendly URL Mapper”

  1. Shrinivas says:

    Super blog. I really trying very hard to understand and now I got the complete concept

  2. loongest says:

    yeah, feeling the same painful to learn on it. I hope that will help others to get understand how it work.

  3. [...] you. Do I have to build a friendly url just for the page or I have to implement it for the portlet too? I've been trying to get the page url, to extract the parameters, by using this [...]

  4. migual says:

    Very well explained, this is definitely the best wiki for friendlyurl out there.

    Ive just a little confusion… i want 1 mapper class to map All portlets, is this possible or do i need an mapper for different portlets?

  5. loongest says:

    Hi, If i can remember correctly, that was one-to-one relationship. Meaning that 1 portlet with 1 mapper class. If you found with alternative solution, please let me knw : )

Leave a Reply

Security Code: