Site icon Pro Liferay

Configurable Portlet In Liferay 7 / Liferay DXP

liferay-dxp-configuration-portlet


We can make our portlet configurable at runtime. Portlet Preferences are the key points of configurable portlet.In Liferay 7 or Liferay DXP the process of making configurable portlet is different from Liferay 6.2. In this article we will discuss portlet configuration on Liferay 7. We will create a sample portlet before enabling the portlet configuration.


Use Case:

Consider that we want to display weather information in our custom portlet. We want City and Temperature Unit(celsius  or fahrenheit) to be configurable through UI so that it can be configured by each user.


Step 1 : Create a sample Portlet

To create a portlet in Liferay 7, follow the below link

Liferay 7-OSGI Module – MVC Portlet by Maven

Step 2: Create Java interface for configuration

This interface represents the configuration of the portlet. Please read the comments for better understandings.

package com.proliferay.configuration;

import aQute.bnd.annotation.metatype.Meta;
/**
 * 
 * (1) Meta.OCD: Registers this class as a configuration with a specific id. We can choose any string we want, but it should be unique.
 *  A common pattern is to use the fully qualified class name.
 *
 * (2) Meta.AD: This represent the default value of a configuration field as well as whether it’s required or not. 
 * Note that if we set a field as required and don’t specify a default value, the system administrator must specify 
 * a value in order for your application to work properly. Use the deflt property to specify a default value.
 * 
 * (3)id = "com.proliferay.configuration.DemoConfiguration" represents the configuration id which will be reffred from other classes.
 * This is a unique id. A common pattern is to use the fully qualified class name of the interface for the ID since fully
 * qualified class names are unique
 * 
 * (4) To specify that a field is optional, set required=false
 * 
 * (5) @Meta.OCD,@Meta.AD are part of biz.aQute.bndlib-3.1.0-sources.jar file. Check the pom.xml
 * 
 * (6) For more information on meta type http://bnd.bndtools.org/chapters/210-metatype.html 
 * 
 * (7)We must add  -metatype: * 
 * in our bnd.bnd file to include this metatype in the final bundle Once the bundle is created
 * it will include DemoConfiguration.xml under OSGI-INF\metatype
 * 
 * (8)The configuration will create one User Interface in the Liferay Control Panel
 * where we can set our configuration values 
 */
@Meta.OCD(  
        id = "com.proliferay.configuration.DemoConfiguration"
    )
public interface DemoConfiguration {
    @Meta.AD(required = false)
    public String city();

    @Meta.AD(required = false)
    public String temperatureUnit();
}

The above will create one interface in Liferay Control Panel. You can navigate Control Panel →System Settings →Other →Demo Configuration.

Step 3: Create the portlet class

In this step we will create the Portlet Class and refer com.proliferay.configuration.WeatherConfiguration (the id of the configuration) as created in the above Step 2. Below is the portlet class

package com.proliferay.portlet;

import com.liferay.portal.configuration.metatype.bnd.util.ConfigurableUtil;
import com.liferay.portal.kernel.log.Log;
import com.liferay.portal.kernel.log.LogFactoryUtil;
import com.liferay.portal.kernel.portlet.bridges.mvc.MVCPortlet;
import com.proliferay.configuration.DemoConfiguration;

import java.io.IOException;
import java.util.Map;

import javax.portlet.Portlet;
import javax.portlet.PortletException;
import javax.portlet.RenderRequest;
import javax.portlet.RenderResponse;

import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Modified;
//import aQute.bnd.annotation.metatype.Configurable;

/**
 * (1)configurationPid represents the configuration for this portlet class
 */
@Component(
        configurationPid = "com.proliferay.configuration.DemoConfiguration",
        immediate = true, property = {
        "com.liferay.portlet.display-category=Pro Liferay", "com.liferay.portlet.instanceable=true",
        "javax.portlet.display-name=Configuration Demo Portlet", "javax.portlet.init-param.template-path=/",
        "javax.portlet.init-param.view-template=/view.jsp", "javax.portlet.resource-bundle=content.Language",
        "javax.portlet.security-role-ref=power-user,user" }, service = Portlet.class)
public class DemoPortlet extends MVCPortlet {
    
    @Override
    public void doView(
            RenderRequest renderRequest, RenderResponse renderResponse)
        throws IOException, PortletException {
        
        /**
         renderRequest.setAttribute(
                 DemoConfiguration.class.getName(),
                 _demoConfiguration);
        */

        /**
         * Configuration can be directly set in the control panel of liferay 
         * We can access those here 
         */


        _log.info("#########City##########"+_demoConfiguration.city()); 
        _log.info("#########Unit##########"+_demoConfiguration.temperatureUnit());
        super.doView(renderRequest, renderResponse);
    }
    
    /**
     * 
     * (1)If a method is annoted with @Activate then the method will be called at the time of activation of the component
     *  so that we can perform initialization task
     *  
     * (2) This class is annoted with @Component where we have used configurationPid with id com.proliferay.configuration.DemoConfiguration
     * So if we modify any configuration then this method will be called. 
     */
    
    @Activate
    @Modified
    protected void activate(Map<Object, Object> properties) {
        _log.info("#####Calling activate() method######");
        
        _demoConfiguration = ConfigurableUtil.createConfigurable(DemoConfiguration.class, properties);
        //_demoConfiguration = ConfigurableUtil.createConfigurable(DemoConfiguration.class, properties);
        
    }

    private static final Log _log = LogFactoryUtil.getLog(DemoPortlet.class);

    private volatile DemoConfiguration _demoConfiguration;  

}

Step 4: Write the Configuration Action

In this step we will write a class which extends DefaultConfigurationAction. For your reference you can click portlet gear icon →Configuration→Setup. Below code is responsible for Portlet Setup where by default configuration.jsp is rendered.  The portlet preferences are set in the process action.

package com.proliferay.configuration;

import com.liferay.portal.configuration.metatype.bnd.util.ConfigurableUtil;
import com.liferay.portal.kernel.log.Log;
import com.liferay.portal.kernel.log.LogFactoryUtil;
import com.liferay.portal.kernel.portlet.ConfigurationAction;
import com.liferay.portal.kernel.portlet.DefaultConfigurationAction;
import com.liferay.portal.kernel.util.ParamUtil;

import java.util.Map;

import javax.portlet.ActionRequest;
import javax.portlet.ActionResponse;
import javax.portlet.PortletConfig;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.ConfigurationPolicy;
import org.osgi.service.component.annotations.Modified;

import aQute.bnd.annotation.metatype.Configurable;
@Component(
        configurationPid = "com.proliferay.configuration.DemoConfiguration",
        configurationPolicy = ConfigurationPolicy.OPTIONAL, immediate = true,
        property = {
            "javax.portlet.name=com_proliferay_portlet_DemoPortlet"
        },
        service = ConfigurationAction.class
    )
/**
 * 
 * (1) configurationPolicy optional means that the component is created regardless of whether or not the configuration was set
 * (2) The property javax.portlet.name indicates that this configuration is for com_proliferay_portlet_DemoPortlet
 * (3 )This component should be registered as a configuration action class so it should specify service = ConfigurationAction.class
 *  in the @Component annotation.
 *
 */
public class DemoConfigurationAction extends DefaultConfigurationAction {

    @Override
    public void include(PortletConfig portletConfig, HttpServletRequest httpServletRequest,
            HttpServletResponse httpServletResponse) throws Exception {

        httpServletRequest.setAttribute(DemoConfiguration.class.getName(), _demoConfiguration);

        super.include(portletConfig, httpServletRequest, httpServletResponse);
    }

    @Override
    public void processAction(PortletConfig portletConfig, ActionRequest actionRequest, ActionResponse actionResponse)
            throws Exception {

        

        String city = ParamUtil.getString(actionRequest, "city");
        String unit = ParamUtil.getString(actionRequest, "unit");  
        

        
        setPreference(actionRequest, "city", city);
        setPreference(actionRequest, "unit", unit);
    

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

    /**
     * 
     * (1)If a method is annoted with @Activate then the method will be called at the time of activation of the component
     *  so that we can perform initialization task
     *  
     * (2) This class is annoted with @Component where we have used configurationPid with id com.proliferay.configuration.DemoConfiguration
     * So if we modify any configuration then this method will be called. 
     */
    @Activate
    @Modified
    protected void activate(Map<Object, Object> properties) {
        _log.info("#####Calling activate() method######");
        
        _demoConfiguration = Configurable.createConfigurable(DemoConfiguration.class, properties);
        //_demoConfiguration = ConfigurableUtil.createConfigurable(DemoConfiguration.class, properties);
    }

    private static final Log _log = LogFactoryUtil.getLog(DemoConfigurationAction.class);

    private volatile DemoConfiguration _demoConfiguration;  

}

Step 5: Writing the configuration page

<%@ include file="/init.jsp"%>

<liferay-portlet:actionURL portletConfiguration="<%=true%>"
    var="configurationActionURL" />

<liferay-portlet:renderURL portletConfiguration="<%=true%>"
    var="configurationRenderURL" />

<aui:form action="<%=configurationActionURL%>" method="post" name="fm">
    <aui:input name="<%=Constants.CMD%>" type="hidden"
        value="<%=Constants.UPDATE%>" />

    <aui:input name="redirect" type="hidden"
        value="<%=configurationRenderURL%>" />

    <aui:fieldset>
        <aui:select name="city" label="City" value="<%=city%>">
            <aui:option value="Delhi">Delhi</aui:option>
            <aui:option value="Bangalore">Bangalore</aui:option>
            <aui:option value="Chennai">Chennai</aui:option>
            <aui:option value="Hyderabad">Hyderabad</aui:option>
        </aui:select>

        <aui:select label="Unit" name="unit" value="<%=unit%>">
            <aui:option value="Celsius">Celsius</aui:option>
            <aui:option value="Fahrenheit">Fahrenheit </aui:option>
        </aui:select>
    </aui:fieldset>

    <aui:button-row>
        <aui:button type="submit"></aui:button>
    </aui:button-row>
</aui:form>

Step 6: Add meta type in bnd.bnd 

Open the bnd.bnd file and add the below 

-metatype: *

We must add this in the BND file. Because we are using OSGI Metatype (look Step 2

TODO: Look the Control Panel & explore

Control Panel →System Settings →Other →Demo Configuration.

Screen Shot:

Source Code Download:

configuration-demo-portlet

Exit mobile version