Showing posts with label Liferay. Show all posts
Showing posts with label Liferay. Show all posts

Monday, March 16, 2015

Receive parameter from AJAX call in Liferay

One very important thing you should noted is set these property in portlet definition (in liferay-portlet.xml) at the end of <portlet> tag:

<add-default-resource>true</add-default-resource>

and

<requires-namespaced-parameters>false</requires-namespaced-parameters>

under <icon> tag.

If not, you can not get parameter's value out in  Portlet Implementation.

Liferay Scheduler

Very good Liferay Scheduler post:
http://itsliferay.blogspot.com/2012/08/implement-scheduler-in-liferay-61.html
https://liferayazam.wordpress.com/2012/06/10/create-a-scheduler-in-liferay-6/
http://pmkathiria.blogspot.com/2013/06/how-to-write-simplecron-scheduler-in.html

Friday, March 6, 2015

Using servlet in Liferay portlet

Create a simple Servlet:

public class MyServlet extends HttpServlet {

    private static final long serialVersionUID = 1L;
   
    public MyServlet() {
        super();
    }

    public void service(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        System.out.println("inside servlet");
    }

}


In web.xml, add:

    <servlet>
        <servlet-name>My Servlet</servlet-name>
        <servlet-class>com.example.servlet.MyServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>My Servlet</servlet-name>
        <url-pattern>/myServlet</url-pattern>
    </servlet-mapping>


Remember url path to servlet is: /plugin-context-path/myServlet, not just /myServlet.

Thursday, February 5, 2015

Custom JSON web service in Liferay

We can publish JSON webservice from Liferay portlet or hook by 2 ways (at my knowledge at the time of writing this code).

Way No. 1: using Service builder

 Specify "remote-service="true"" in service.xml like below:

<entity name="Employee" local-service="true" remote-service="true">

and run Liferay->SDK->build-service.
It automatically create some interfaces and classes named: *Service, *ServiceImpl or *ServiceUtil... (These files differ with *LocalService, * LocalServiceImpl or *LocalServiceUtil which are used internally JVM).

JSON service has not exposed yet until you write some functions in *ServiceImpl and run Liferay->SDK->build-service again.

The magic is clearly explained in the websites listed in References. Basically, it will add @JSONWebService to *Service interface. By this way, Liferay can scan and publish function in this interface as an API webservice.

Test it by type: http://your-ip:port/plugin-context-path/api/jsonws. It will list out all your service functions that has been published.

Way No. 2: write custom classes and register to Liferay manually

Liferay portal 6.1 is slightly different with Liferay portal 6.2 in the way it scans service functions.

  • In Liferay 6.1:
Create an interface in your package. One thing should be noted is that this interface has to be suffix by "Service" like: "HelloworldService" and mark with @JSONWebService at class scope definition.

@JSONWebService
public interface HelloworldService {
    public java.lang.String sayHello();
}


Corresponding to Service inferface, Liferay need a "Util" class. It will be the endpoint that catches the request from client.

public class HelloworldServiceUtil {
    public static String sayHello() {
        HelloworldServiceImpl service = null;
        if (service == null) {
            HelloworldServiceImpl invokableLocalService = (HelloworldServiceImpl) PortletBeanLocatorUtil.locate("ws-test-portlet", "hellworld");

            if (invokableLocalService != null) {
                service = invokableLocalService;
            }           
        }
       
        if(service != null) {
            return service.sayHello();
        } else {
            return "null";
        }
    }
}


In this case, HelloworldServiceImpl does not take part in directly to the process of  public JSON service. HelloworldService is used to create signature. HelloworldServiceUtil is to handle request. It calls HelloworldServiceImpl to do the business logic and return the result. As you can see, we can get an instance of "HelloworldServiceImpl" from ext-spring.xml through PortletBeanLocatorUtil.locate().
Noted that functions in HelloworldServiceUtil are all static.

Test it by type: http://your-ip:port/plugin-context-path/api/jsonws.

  • In Liferay 6.2:
Things become simpler. Just create an interface and name it any thing you like.

public interface Helloworld {
    public String sayHello();
}


Implement it by an implementation class:

@JSONWebService
public class HelloworldImpl implements Helloworld {

    @Override
    public String sayHello() {
        return "Hello baby";
    }
}


But put @JSONWebService at implementation class like above.
That's all you have to do.

Both in Liferay 6.1 and In Liferay 6.2, you need add JSON Service servlet in web.xml file:

<filter>
        <filter-name>Secure JSON Web Service Servlet Filter</filter-name>
        <filter-class>com.liferay.portal.kernel.servlet.PortalClassLoaderFilter</filter-class>
        <init-param>
            <param-name>filter-class</param-name>
            <param-value>com.liferay.portal.servlet.filters.secure.SecureFilter</param-value>
        </init-param>
        <init-param>
            <param-name>basic_auth</param-name>
            <param-value>true</param-value>
        </init-param>
        <init-param>
            <param-name>portal_property_prefix</param-name>
            <param-value>jsonws.servlet.</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>Secure JSON Web Service Servlet Filter</filter-name>
        <url-pattern>/api/jsonws/*</url-pattern>
    </filter-mapping>

    <servlet>
        <servlet-name>JSON Web Service Servlet</servlet-name>
        <servlet-class>com.liferay.portal.kernel.servlet.PortalClassLoaderServlet</servlet-class>
        <init-param>
            <param-name>servlet-class</param-name>
            <param-value>com.liferay.portal.jsonwebservice.JSONWebServiceServlet</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>JSON Web Service Servlet</servlet-name>
        <url-pattern>/api/jsonws/*</url-pattern>
    </servlet-mapping>


Try and happy coding!
-----
References:

http://www.liferay.com/documentation/liferay-portal/6.1/development/-/ai/json-web-services

http://www.liferaysavvy.com/2013/11/liferay-custom-json-web-services-on.html

http://www.endeios.io/blog/-/blogs/leverage-liferay-service-s-structure-for-you-own-personal-back-end-s-service

Good Liferay and weblogic sharing

http://bestjavasolutions.blogspot.com/

Tuesday, January 27, 2015

Create Liferay plugin with Maven (Liferay Portal 6.2)

Step 1:
Download Liferay Portal Maven 6.2.1 CE.
Extract and run: ant install

Step 2:
Download liferay-plugin-maven-support 6.2.2.
Extract and run: mvn clean install

Step 3:
Create Liferay plugin in Liferay IDE with maven build type.
Add pom.xml, like below:

<?xml version="1.0"?>

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.example.plugins</groupId>
    <artifactId>test-service</artifactId>
    <packaging>war</packaging>
    <name>test-service Portlet</name>
    <version>1.0.0-SNAPSHOT</version>
    <properties>
        <liferay.version>6.2.1</liferay.version>
    </properties>
    <build>
        <pluginManagement>
            <plugins>
                <!--This plugin's configuration is used to store Eclipse m2e settings only. It has no influence on the Maven build itself.-->
                <plugin>
                    <groupId>org.eclipse.m2e</groupId>
                    <artifactId>lifecycle-mapping</artifactId>
                    <version>1.0.0</version>
                    <configuration>
                        <lifecycleMappingMetadata>
                            <pluginExecutions>
                                <pluginExecution>
                                    <pluginExecutionFilter>
                                        <groupId>
                                            com.liferay.maven.plugins
                                        </groupId>
                                        <artifactId>
                                            liferay-maven-plugin
                                        </artifactId>
                                        <versionRange>
                                            [6.2.1,)
                                        </versionRange>
                                        <goals>
                                            <goal>build-service</goal>
                                        </goals>
                                    </pluginExecutionFilter>
                                    <action>
                                        <ignore></ignore>
                                    </action>
                                </pluginExecution>
                            </pluginExecutions>
                        </lifecycleMappingMetadata>
                    </configuration>
                </plugin>
            </plugins>
        </pluginManagement>
        <plugins>
            <plugin>
                <groupId>com.liferay.maven.plugins</groupId>
                <artifactId>liferay-maven-plugin</artifactId>
                <version>${liferay.maven.plugin.version}</version>
                <executions>
                    <execution>
                        <id>build-service</id>
                        <phase>generate-sources</phase>
                        <goals>
                            <goal>build-service</goal>
                        </goals>
                    </execution>
                </executions>
                <configuration>
                    <autoDeployDir>${liferay.auto.deploy.dir}</autoDeployDir>
                    <appServerDeployDir>${liferay.app.server.deploy.dir}</appServerDeployDir>
                    <appServerLibGlobalDir>${liferay.app.server.lib.global.dir}</appServerLibGlobalDir>
                    <appServerPortalDir>${liferay.app.server.portal.dir}</appServerPortalDir>
                    <liferayVersion>${liferay.version}</liferayVersion>
                    <pluginType>portlet</pluginType>
                </configuration>
            </plugin>
            <plugin>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>2.5</version>
                <configuration>
                    <encoding>UTF-8</encoding>
                    <source>1.6</source>
                    <target>1.6</target>
                </configuration>
            </plugin>
            <plugin>
                <artifactId>maven-resources-plugin</artifactId>
                <version>2.5</version>
                <configuration>
                    <encoding>UTF-8</encoding>
                </configuration>
            </plugin>
        </plugins>
    </build>
    <dependencies>
        <dependency>
            <groupId>com.liferay.portal</groupId>
            <artifactId>portal-service</artifactId>
            <version>${liferay.version}</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>com.liferay.portal</groupId>
            <artifactId>util-bridges</artifactId>
            <version>${liferay.version}</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>com.liferay.portal</groupId>
            <artifactId>util-taglib</artifactId>
            <version>${liferay.version}</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>com.liferay.portal</groupId>
            <artifactId>util-java</artifactId>
            <version>${liferay.version}</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>javax.portlet</groupId>
            <artifactId>portlet-api</artifactId>
            <version>2.0</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>servlet-api</artifactId>
            <version>2.4</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>javax.servlet.jsp</groupId>
            <artifactId>jsp-api</artifactId>
            <version>2.0</version>
            <scope>provided</scope>
        </dependency>
    </dependencies>
<profiles>
<profile>
    <id>Liferay-v6.2-CE-(Tomcat-7)</id>
    <properties>
        <liferay.version>6.2.1</liferay.version>
        <liferay.maven.plugin.version>6.2.1</liferay.maven.plugin.version>
        <liferay.auto.deploy.dir>D:\Liferay\Soft\Liferay 6.2\liferay-portal-6.2-ce-ga2\deploy</liferay.auto.deploy.dir>
        <liferay.app.server.deploy.dir>D:\Liferay\Soft\Liferay 6.2\liferay-portal-6.2-ce-ga2\tomcat-7.0.42\webapps</liferay.app.server.deploy.dir>
        <liferay.app.server.lib.global.dir>D:\Liferay\Soft\Liferay 6.2\liferay-portal-6.2-ce-ga2\tomcat-7.0.42\lib\ext</liferay.app.server.lib.global.dir>
        <liferay.app.server.portal.dir>D:\Liferay\Soft\Liferay 6.2\liferay-portal-6.2-ce-ga2\tomcat-7.0.42\webapps\ROOT</liferay.app.server.portal.dir>
    </properties>
    </profile>
</profiles>
</project>


Now, you can run generate-sources to build Liferay service.
Important: Liferay IDE must fully support Maven plugin type. Should download the newest version.

Monday, January 19, 2015

Override session in Liferay

With the same user login in the same browser, you open up a page and working on it. This page uses session to store some data. If you open another tab, enter the same page with the previous tab and doing it, the session in new tab may override the old stored by the previous tab. So the data may be incorrect. Be careful!

Wednesday, January 7, 2015

Receive file upload in Liferay portlet

Upload

Using UploadPortletRequest to receive file input stream:

UploadPortletRequest uploadRequest = PortalUtil.getUploadPortletRequest(resourceRequest);

long                  fileSize = uploadRequest.getSize("field_name");
InputStream              is = uploadRequest.getFileAsStream("field_name");
String          mimeType = uploadRequest.getContentType("field_name");
String sourceFileName = uploadRequest.getFileName("field_name");

Save

To create file in Document and Media Library, using this one:

ThemeDisplay themeDisplay = (ThemeDisplay) request.getAttribute(WebKeys.THEME_DISPLAY);
ServiceContext serviceContext = ServiceContextFactory.getInstance(request);
User currentUser = PortalUtil.getUser(request);

// with default repository, repositoryId is equals to scopeGroupId
long repositoryId = themeDisplay.getScopeGroupId(); 
long userId = themeDisplay.getUserId();
long groupId = themeDisplay.getScopeGroupId();
long fileEntryTypeId = ParamUtil.getLong(serviceContext, "fileEntryTypeId", -1L);

Map<String, Fields> fieldsMap = new HashMap<String, Fields>();
// Now, add new one

DLFileEntry newFile = DLFileEntryLocalServiceUtil.addFileEntry(userId, groupId,
                                    repositoryId, folderId, sourceFileName, mimeType, title, "", "", fileEntryTypeId,
                                    fieldsMap, file, is, size, serviceContext);


DLFileEntryLocalServiceUtil.updateFileEntry(userId, newFile.getFileEntryId(),
                                    sourceFileName, mimeType, title, "", "", true, fileEntryTypeId, fieldsMap, null,
                                    null, uploadRequest.getSize(posterName), serviceContext);


In case, you want to create folder to save the file, this is how to do:
            // parentId = 0L means Root folder in Document Library
            long parentId = 0L; 

            // with default repository, repositoryId is equals to scopeGroupId
            long repositoryId = themeDisplay.getScopeGroupId();

            long userId = themeDisplay.getUserId();
            long groupId = themeDisplay.getScopeGroupId();

            DLFolder folder = DLFolderLocalServiceUtil.addFolder(userId, groupId, repositoryId, false, parentId, folderName, "", serviceContext);
            newFolderId = folder.getFolderId(); 


What about creating a list folder in hierarchy?
public long createFolders(String[] folders, ThemeDisplay themeDisplay, ServiceContext serviceContext) {
        int num = folders.length;

        long parentId = 0L;
        long newFolderId = 0L;

        for (int i = 0; i < num; i++) {
            newFolderId = MyUtilClass.findFolderByName(folders[i], parentId, themeDisplay.getScopeGroupId());
            if (newFolderId == -1) {
                newFolderId =
MyUtilClass.createFolder(folders[i], parentId, themeDisplay, serviceContext);
            }
            parentId = newFolderId;
        }

        return newFolderId;
    }
 

Get Link

To get file URL:
 
/* format file url: domain/documents/[groupid]/[folderid]/[fileName]/[uuid]
* e.g: http://localhost:8080/documents/10828/22503/Penguins2_1390451913869.JPG/0da81b4e-37cf-45a4-9872-8ac3447d9bba
* */
                            

String fileUrl = themeDisplay.getPortalURL() + themeDisplay.getPathContext() + SLASH + "documents"
                                    + SLASH + themeDisplay.getScopeGroupId() + SLASH + folderId + SLASH
                                    + HttpUtil.encodeURL(HtmlUtil.unescape(title)) + SLASH + newFile.getUuid();

Using Spring in Liferay portlet

In src/main/resources folder, create META-INF folder, inside this folder, create ext-spring.xml file. This file contains bean definition, like below:

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:oxm="http://www.springframework.org/schema/oxm"
    xmlns:p="http://www.springframework.org/schema/p"
    xmlns:jms="http://www.springframework.org/schema/jms"
    xmlns:jee="http://www.springframework.org/schema/jee"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context-3.0.xsd
        http://www.springframework.org/schema/oxm
        http://www.springframework.org/schema/oxm/spring-oxm-1.5.xsd
        http://www.springframework.org/schema/jms http://www.springframework.org/schema/jms/spring-jms-3.0.xsd
        http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.0.xsd
        ">
       
    <bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
        <property name="locations">
           <list>
               <value>/WEB-INF/config/props.properties</value>
            </list>
        </property>
    </bean>
   
    <!-- Some bean -->
    <bean id="myBean" class="com.example.MyBean" /> 
</beans>


Also, in src/main/resources folder, create service.properties file:

##
## Properties Override
##

    #
    # Specify where to get the overridden properties. Updates should not be made
    # on this file but on the overridden version of this file.
    #
    include-and-override=${base.path}/service-ext.properties


##
## Spring
##

    #
    # Input a list of comma delimited Spring configurations. These will be
    # loaded after the bean definitions specified in the
    # portalContextConfigLocation parameter in web.xml.
    #
    spring.configs=\
        WEB-INF/classes/META-INF/base-spring.xml,\
        \
        WEB-INF/classes/META-INF/hibernate-spring.xml,\
        WEB-INF/classes/META-INF/infrastructure-spring.xml,\
        \
        WEB-INF/classes/META-INF/cluster-spring.xml,\
        \
        WEB-INF/classes/META-INF/portlet-spring.xml,\
        \
        WEB-INF/classes/META-INF/dynamic-data-source-spring.xml,\
        WEB-INF/classes/META-INF/shard-data-source-spring.xml,\
        \
        WEB-INF/classes/META-INF/ext-spring.xml


Now, your bean will loaded every time Liferay started.

In your *LocalServiceUtil, if you would like to use the bean in ext-spring.xml file, use this code:

public class YourServiceUtil {
     public static void someFunc() {
        getService().someFunc();
    }
   
    public static void clearService() {
        _service = null;
    }

    public static YourServiceImpl getService() {
        if (_service == null) {
           
_service = (YourServiceImpl ) PortletBeanLocatorUtil
                    .locate(ClpSerializer.getServletContextName(), "your_bean_name");
        }

        return _service;
    }

    private static YourServiceImpl _service;
}


ClpSerializer.getServletContextName() is a class return the contextName of the context where bean located. Usually, it is your project name.

Monday, January 5, 2015

Paging with Simple Pagination in Liferay

Reference: http://flaviusmatis.github.io/simplePagination.js/

In Liferay,

function renderPaginator(total, url, currentPage) {
    if(total > 0) {
        $(function() {
            $('#paginator').pagination({
                items: total,
                itemsOnPage: 20,
                cssStyle: 'light-theme',
                currentPage:currentPage,
                onPageClick : function(page) {
                    //update page
                }
            });
        });
        $('#page-no-result').html("");
 

    } else {
        $('#paginator').html("");
        $('#page-no-result').html(Liferay.Language.get("content.noresult"));
    }
}

Make a request in Liferay using AlloyUI

AUI().ready('aui-io-request', function(A) {
    Liferay.on(
             'event_name',
             function(data) {
                 var param = event.data;
                 if(param != null) {

                      //use blockUI to block screen
                     $.blockUI({ message: null }); 

                     A.io.request(searchUrl, {
                         data: {
                             keyword: param ,
                         },
                        dataType: 'json',
                        on: {
                            success : function() {
                                     // do something here

                                    $.unblockUI();
                            },
                            failure : function() {
                                $.unblockUI();
                            }
                        }
                    });
                 }

         })
});

To fire event, using:

Liferay.fire('event_name', {data: param });

References:
http://proliferay.com/alloy-ajax-liferay-portlet/ 

Create popup in Liferay

Liferay Dialog

function createDialog(param) {
    AUI().use(
            'liferay-portlet-url',
            'aui-dialog',
            'aui-dialog-iframe',
            function(A) {
                var url = Liferay.PortletURL.createRenderURL();
                url.setPortletId("portletName_WAR_contextName");
                url.setParameter("param", param);
                url.setWindowState('pop_up');
                var dialog = new A.Dialog({
                    id : 'mydialog',
                    centered : true,
                    title : 'Popup',
                    height : 'auto',
                    resizable : false,
                    modal : true,
                    scrollable : false,
                    destroyOnClose : true,
                    on : {
                        close : function() {
                            //do something here
                        },
                    }
                }).plug(A.Plugin.DialogIframe, {
                    uri : url,
                    iframeCssClass : 'dialog-iframe',
                }).render();
            });
}


In this case, we create url by using Liferay.PortletURL. If you want to create it in view.jsp, use:

<liferay-portlet:renderURL var="pagelLink"
    plid="<%=pageLayoutId%>"
    portletName="<%=portletName%>">
</liferay-portlet:renderURL>


with pageLayoutId got from:

    long pageLayoutId = 0l;
    if (PortletUtility.getPlid("Page Name") != null) {
        pageLayoutId = Long.valueOf(PortletUtility.getPlid("Page Name");
    }


If you want to add parameters to link, append this link with format like below:
&_PortletName_WAR_portletProjectName_paramName=paramValue

Ex: '&_ATMSeriesInfo_WAR_atmuiportlet_id=' + id

Liferay Popup

function showPopup(title, content, okLabel, cancelLabel, OKHandler, cancelHandler) {
    AUI().use('aui-dialog',
      function(A) {
        var buttons = [];
        if(okLabel) {
            var okBut = {
                    handler: function() {
                        if(OKHandler) {
                            OKHandler();
                        }
                        this.close();
                    },
                    label: okLabel
                  };
            buttons.push(okBut);
        }
       
        if(cancelLabel) {
            var cancelBut = {
                    handler: function() {
                        if(cancelHandler) {
                            cancelHandler();
                        }
                        this.close();
                    },
                    label: cancelLabel,
                  };
            buttons.push(cancelBut);
        }

        new A.Dialog(
          {
            bodyContent: content,
            buttons: buttons,
            centered: true,
            draggable: true,
            resizable: false,
            title: title,
            width: 400,
            height: 150,
            modal: true
          }
        ).render();
      }
    );
}


Liferay.Util javascript

For example creating a link display your account info in a popup:

<a href=\"javascript:Liferay.Util.openWindow({dialog: {destroyOnHide: true}, title: '" + LanguageUtil.get(pageContext, "my-account") + "', uri: '" + HtmlUtil.escape(myAccountURL) + "'});\">

Sunday, January 4, 2015

Connect external DB in Liferay

Sometimes, we need connect to external DB in Liferay. How to do it?

JNDI Name

In tomcat server:

In server.xml file in Server in Eclispe:

        <GlobalNamingResources>
              <!-- Editable user database that can also be used by UserDatabaseRealm
                     to authenticate users -->
              <Resource auth="Container" description="User database that can be updated and saved"
                     factory="org.apache.catalina.users.MemoryUserDatabaseFactory" name="UserDatabase"
                     pathname="conf/tomcat-users.xml" type="org.apache.catalina.UserDatabase" />

              <Resource description="My DataSource" driverClassName="oracle.jdbc.driver.OracleDriver"
                     maxActive="20" maxIdle="10" maxWait="5000" name="jdbc/mypool"
                     password="mypass" testOnBorrow="true" type="javax.sql.DataSource"
                     url="jdbc:oracle:thin:@localhost:1521:mydb" username="myname"
                     validationQuery="SELECT 1 FROM DUAL" />

       </GlobalNamingResources>

In context.xml file in Server in Eclispe:

<!-- Default set of monitored resources -->
    <WatchedResource>WEB-INF/web.xml</WatchedResource>

    <!-- Uncomment this to disable session persistence across Tomcat restarts -->
    <!--
    <Manager pathname="" />
    -->
   
    <ResourceLink global="jdbc/mmtpool" name="jdbc/mypool" type="javax.sql.DataSource"/>

    <!-- Uncomment this to enable Comet connection tacking (provides events
         on session expiration as well as webapp lifecycle) -->
    <!--
    <Valve className="org.apache.catalina.valves.CometConnectionManagerValve" />
    -->

The JNDI name highlighted in green must be the same with JNDI define in code.

Using Liferay util class and Hibnerate:

<!-- configuration for external db -->
    <bean id="myDataSourceTarget" class="com.liferay.portal.spring.jndi.JndiObjectFactoryBean"
        lazy-init="true">
        <property name="jndiName">
            <value>jdbc/mypool</value>
        </property>
    </bean>
    <bean id="myDataSource"
        class="org.springframework.jdbc.datasource.LazyConnectionDataSourceProxy">
        <property name="targetDataSource">
            <ref bean="myDataSourceTarget" />
        </property>
    </bean>
    <bean id="myHibernateSessionFactory"
        class="com.liferay.portal.spring.hibernate.PortletHibernateConfiguration">
        <property name="dataSource">
            <ref bean="myDataSource" />
        </property>
    </bean>
    <bean id="mySessionFactory" class="com.liferay.portal.dao.orm.hibernate.SessionFactoryImpl">
        <property name="sessionFactoryImplementor">
            <ref bean="myHibernateSessionFactory" />
        </property>
    </bean>
    <bean id="myTransactionManager"
        class="org.springframework.orm.hibernate3.HibernateTransactionManager">
        <property name="dataSource">
            <ref bean="myDataSource" />
        </property>
        <property name="sessionFactory">
            <ref bean="myHibernateSessionFactory" />
        </property>
    </bean>


In *FinderImpl file, add function like this:

        public int countTicket(String status, String priority, String ticketNo, String assigneeId)
            throws SystemException {

        Session session = null;

        try {
            // open a new hibernate session in normal case when you are opening
            // session for same entity
            session = openSession();
            // pull out our query from default.xml, created earlier
            String sql = CustomSQLUtil.get(COUNT_TICKET);
            // create a SQLQuery object
            SQLQuery query = session.createSQLQuery(sql);

            // Get query position instance
            QueryPos qPos = QueryPos.getInstance(query);
            qPos.add(status);
            qPos.add(priority);
            qPos.add(ticketNo);
            qPos.add(assigneeId);
            query.addScalar("total", Type.INTEGER);
            List results = (List) query.list();

            Integer count = (Integer) results.get(0);
            return count;


        } catch (Exception e) {
            throw new SystemException(e);
        } finally {
            closeSession(session);
        }


*FinderImpl class should extends from BasePersistenceImpl<Your_Entity> and implements *Finder.

Using Spring util class and JPA:

<!-- DataSource -->
    <jee:jndi-lookup id="dataSourceTarget" jndi-name="jdbc/mypool" expected-type="javax.sql.DataSource" />
      
    <bean id="dataSource"
        class="org.springframework.jdbc.datasource.LazyConnectionDataSourceProxy">
        <property name="targetDataSource" ref="dataSourceTarget" />
    </bean>
       
    <!-- Entity Manager Factory for Spring Data JPA -->
    <bean id="jpaVendorAdapter" class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
        <property name="showSql" value="false"/>
        <property name="generateDdl" value="false"/>
        <property name="database" value="ORACLE"/>
    </bean>
   
    <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
        <property name="dataSource" ref="dataSource"/>
        <property name="jpaVendorAdapter" ref="jpaVendorAdapter"/>
        <!-- spring based scanning for entity classes-->
        <property name="packagesToScan" value="com.example.dao.entity"/>
    </bean>
   

    <!-- Transaction Manager for Spring Data JPA -->
    <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
        <property name="entityManagerFactory" ref="entityManagerFactory"/>        
    </bean>
       
    <tx:annotation-driven transaction-manager="transactionManager"/>    


From now, we can use JPA Repository. Reference: http://www.petrikainulainen.net/spring-data-jpa-tutorial/ OR write your own query function, like below:

@PersistenceContext
protected EntityManager em;

public List getProgram() {

        String sql = "SELECT * FROM program";
        Query query = em.createNativeQuery(sql);
        return query.getResultList();
    }

Saturday, June 28, 2014

Changes from Liferay 6.1 to 6.2

  • ThemeSetting:
In liferay-look-and-feel .xml:
<setting configurable="true" key="header-banner" type="textarea" value=""/>

In portal_normal.vm:
#set($bg_header = $theme_display.getThemeSetting("header-banner"))

In this case, we use $theme_display.getThemeSetting(key)  instead of $theme.getSetting(key)

  • Ajax using AUI:
AUI().use('node','aui-base','aui-io-request', function(A) {
        var groupId = A.one('#' + namespace + "groupId").get("value");
        A.io.request(ajaxPageLayoutUrl, {
            dataType : 'json',
            data : {
                <portlet:namespace/>groupId: groupId
            },
            on : {
                success : function() {
                    var data = this.get('responseData');
                    for(var i = 0; i < data.length; i++) {
                        var page = data[i];
                        var selected = page.plid == curPageSelected ? "selected" : "";
                        var newOption = A.Node.create('<option value="' + page.plid + '"' + selected + '>'+ page.title +'</option>');
                        A.one('#' + namespace + "plid").appendChild(newOption);
                    }
                }
            }
        });
    });

Friday, June 27, 2014

Accessing Service and Util in Application Display Template



See: (in content and comment)

1. http://liferaytrends.blogspot.com/2012/05/access-liferay-service-util-and-custom.html

2. http://stackoverflow.com/questions/12106827/liferay-obtain-filesize-in-template

Sunday, June 22, 2014

Using Application Display Template in (ADT) Liferay 6.2

If you want to get portlet title in ADT, using this code snippets:

#set ($portletTitle = $themeDisplay.getPortletDisplay().getTitle())

If you want to use localization, using code like this:

$languageUtil.get($locale, "read-more")

Wednesday, April 30, 2014

Create Liferay portlet with localized input value

Hi all,
Today, I am going to show you how to create a Liferay portlet with localized input value. You also know how to put a portlet in Control Panel and embed a portlet in theme.

When doing with localization, you can have a couple of choices. If you are in theme template files, you can use #language("your_language_key"), and if you are in portlet view file (.jsp file...), it is LanguageUtil.get(pageContext, "your_language_key"). You will have localization ability.

But it is all the things you know already. You prepare your language properties files, put words on it, along with the same key, and then you show the correct language sentence when user change their language in Portal. What about the value that user puts? Do you want to localize it? Sure. You can see what it looks like by creating a structure or a template in Web Content in Control Panel.

Now, for example, we have to create a manual Menu portlet that allows admin user create the menu on his own. We will localize the Menu label.

We have two portlets, one for managing Menu item in Control Panel, and the another for displaying Menu bar in your theme. These are working on the same Menu Item data model.

Firstly, create Menu Item entity in service.xml file, like below:
<entity name="MenuItem" local-service="true" remote-service="true" cache-enabled="false">

        <!-- PK fields -->

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

        <!-- Audit fields -->

        <column name="companyId" type="long" />
        <column name="userId" type="long" />
        <column name="userName" type="String" />
        <column name="createDate" type="Date" primary="false"/>
        <column name="modifiedDate" type="Date" primary="false"/>

        <!-- Other fields -->

        <column name="name" type="String" localized="true"/>
        <column name="description" type="String" localized="true" />
        <column name="path" type="String" />
        <column name="targetType" type="String" />
        <column name="parentId" type="long" />
        <column name="weight" type="int"></column>

        <order by="asc">
            <order-column name="menuItemId" order-by="asc"/>
        </order>

        <!-- Finder methods -->

        <finder name="Name" return-type="Collection">
            <finder-column name="name" />
        </finder>
        <finder name="ParentId" return-type="Collection">
            <finder-column name="parentId" />
        </finder>
    </entity>
In this case, pay your attention on:
<column name="name" type="String" localized="true"/>
<column name="description" type="String" localized="true" />
It makes the Portal save the user input value in xml format that supports localization. One thing else you should know is I set cache-enabled="false". I don't know how cache in portlet level works but if you set this to true, when user adds new value to database, it does not affect to view immediately until I redeploy source code again.

Look at portlet-model-hints.xml file,
<model-hints>
    <model name="vn.edu.bvu.model.MenuItem">
        <field name="menuItemId" type="long" />
        <field name="companyId" type="long" />
        <field name="userId" type="long" />
        <field name="userName" type="String" />
        <field name="createDate" type="Date" />
        <field name="modifiedDate" type="Date" />
        <field name="name" type="String" localized="true" />
        <field name="description" type="String" localized="true" />
        <field name="path" type="String" />
        <field name="targetType" type="String" />
        <field name="parentId" type="long" />
        <field name="weight" type="int" />
    </model>
</model-hints>
From there, in view files, you can use aui taglib to display form input that supports localization. This is my code on edit.jsp file:
 <%@ include file="/html/menu/init.jsp"%>

<portlet:actionURL var="addMenuItemURL" name="add"></portlet:actionURL>

<%
long menuItemId = ParamUtil.getLong(renderRequest, "menuItemId");
MenuItem mi = null;
if (menuItemId > 0) {
    mi = MenuItemLocalServiceUtil.getMenuItem(menuItemId);
}

List<MenuItem> list = MenuItemLocalServiceUtil.getMenuItems(0, MenuItemLocalServiceUtil.getMenuItemsCount());

%>

<aui:form action="<%= addMenuItemURL %>" method="post">
    <aui:fieldset>
        <aui:model-context model="<%= MenuItem.class %>" bean="<%= mi %>" />  

        <aui:input name="menuItemId" type="hidden" />
      
        <aui:input name="name" label="menu-item-name"/>
      
        <aui:input name="description" label="menu-item-description"/>
      
        <aui:input name="path" label="menu-item-path"/>
      
        <aui:select name="targetType" label="menu-item-target-type">
            <aui:option selected="<%= mi != null ? (mi.getTargetType().equals(\"_self\") ? true : false) : false %>" value="_self">_self</aui:option>
            <aui:option selected="<%= mi != null ? (mi.getTargetType().equals(\"_blank\") ? true : false) : false %>" value="_blank">_blank</aui:option>
            <aui:option selected="<%= mi != null ? (mi.getTargetType().equals(\"_parent\") ? true : false) : false %>" value="_parent">_parent</aui:option>
            <aui:option selected="<%= mi != null ? (mi.getTargetType().equals(\"_top\") ? true : false) : false %>" value="_top">_top</aui:option>
        </aui:select>

        <aui:input name="weight" label="menu-item-weight"/>
      
        <aui:select name="parentId" label="menu-item-parentId">
            <aui:option selected="<%= mi != null ? (mi.getParentId() == 0 ? true : false) : false %>" value="<%= 0 %>"><%= StringPool.BLANK %></aui:option>
            <%
                for(int i = 0; i < list.size(); i++) {
                    MenuItem item = list.get(i);
                    if(item.getMenuItemId() != menuItemId) {
                        %>
                        <aui:option selected="<%= item.getMenuItemId() == menuItemId %>" value="<%= item.getMenuItemId() %>"><%= item.getName(currentLocale) %></aui:option>
                        <%
                    }
                }
            %>
        </aui:select>

        <aui:button name="submit" type="submit" value="Add"/>
      
    </aui:fieldset>
</aui:form>
The magic thing comes from portlet-model-hints.xml file to <aui:input ...> taglib. AUI taglib automatically recognize localized=true and add more component to allow user add more language translation.
See more benefits from model-hints from this link https://www.liferay.com/documentation/liferay-portal/6.1/development/-/ai/using-model-hints-liferay-portal-6-1-dev-guide-en.

In view result file, see my code below:
 <%@ include file="/html/menu/init.jsp"%>

<portlet:renderURL var="addMenuItemUrl" portletMode="edit" />

<liferay-ui:search-container delta="5" emptyResultsMessage="no-menu-items-were-found">
    <liferay-ui:search-container-results>
        <%
            List<MenuItem> list = MenuItemLocalServiceUtil
                            .getMenuItems(0, MenuItemLocalServiceUtil
                                    .getMenuItemsCount());
                    List<MenuItem> tempResults = ListUtil.subList(list,searchContainer.getStart(), searchContainer.getEnd());
                    int tempTotal = list.size();
                    pageContext.setAttribute("results", tempResults);
                    pageContext.setAttribute("total", tempTotal);
        %>
    </liferay-ui:search-container-results>

    <liferay-ui:search-container-row className="vn.edu.bvu.model.MenuItem"
        keyProperty="menuItemId" modelVar="item">
        <liferay-ui:search-container-column-text name="menu-item-id"
            value="<%=StringUtil.valueOf(item.getMenuItemId()) %>" />
        <liferay-ui:search-container-column-text name="menu-item-name"
            value="<%=item.getName(currentLocale) %>" />
        <liferay-ui:search-container-column-text name="menu-item-description"
            value="<%=item.getDescription(currentLocale)%>" />
        <liferay-ui:search-container-column-text name="menu-item-path"
            value="<%=item.getPath()%>" />
        <liferay-ui:search-container-column-text name="menu-item-target-type"
            value="<%=item.getTargetType()%>" />
        <liferay-ui:search-container-column-text name="menu-item-weight"
            value="<%=String.valueOf(item.getWeight())%>" />
        <liferay-ui:search-container-column-text name="menu-item-parentId"
            value="<%= item.getParentId() == 0 ? StringPool.BLANK : MenuItemLocalServiceUtil.getMenuItem(item.getParentId()).getName(currentLocale) %>" />
        <liferay-ui:search-container-column-jsp path="/html/menu/buttons.jsp"
            align="right" />
    </liferay-ui:search-container-row>

    <liferay-ui:search-iterator />
</liferay-ui:search-container>

<a href="<%=addMenuItemUrl%>">Add new</a>
MenuItem model has function help us to fetch Menu label corresponding to current locale.

/htm/menu/buttons.jsp file:
<%@ include file="/html/menu/init.jsp" %>

<%
    ResultRow rslt = (ResultRow)renderRequest.getAttribute(WebKeys.SEARCH_CONTAINER_RESULT_ROW);
    MenuItem mi = (MenuItem)rslt.getObject();
%>
<liferay-ui:icon-menu>
    <portlet:actionURL name="edit" var="editUrl">
        <portlet:param name="menuItemId" value="<%= StringUtil.valueOf(mi.getMenuItemId()) %>"/>
    </portlet:actionURL>
    <portlet:actionURL name="delete" var="deleteUrl">
        <portlet:param name="menuItemId" value="<%=StringUtil.valueOf(mi.getMenuItemId()) %>"/>
    </portlet:actionURL>
   
    <liferay-ui:icon image="edit" message="Edit" url="<%= editUrl %>"/>
    <liferay-ui:icon image="delete" message="Delete" url="<%= deleteUrl %>"/>
</liferay-ui:icon-menu>

But wait a minute, how can we save data with localization? Here it is:
public void add(
            ActionRequest actionRequest, ActionResponse actionResponse)
        throws IOException, PortletException, SystemException, PortalException {
        Map<Locale, String> name = LocalizationUtil.getLocalizationMap(actionRequest, "name");
        Map<Locale, String> description = LocalizationUtil.getLocalizationMap(actionRequest, "description");
        String path = ParamUtil.getString(actionRequest, "path");
        String targetType = ParamUtil.getString(actionRequest, "targetType");
        int weight = ParamUtil.getInteger(actionRequest, "weight");
        long parentId = ParamUtil.getLong(actionRequest, "parentId");
       
        long menuItemId = ParamUtil.getLong(actionRequest, "menuItemId");
       
        MenuItem menuItem = null;
        if(menuItemId > 0) {
            menuItem = MenuItemLocalServiceUtil.getMenuItem(menuItemId);
        } else {
            menuItemId = CounterLocalServiceUtil.increment(MenuPortlet.class.getName());
            menuItem = MenuItemLocalServiceUtil.createMenuItem(menuItemId);
        }
       
        menuItem.setNameMap(name);
        menuItem.setDescriptionMap(description);
        menuItem.setPath(path);
        menuItem.setTargetType(targetType);
        menuItem.setWeight(weight);
        menuItem.setParentId(parentId);
       
        MenuItemLocalServiceUtil.updateMenuItem(menuItem);
       
        actionResponse.setPortletMode(PortletMode.VIEW);
    }
See my highlighted code. We save a map with locale value, not just a single value as usual.

Add Menu portlet to Control Panel:
Add 2 control panel entry element to portlet definition in liferay-portlet.xml, like below:
<portlet>
        <portlet-name>menu</portlet-name>
        <icon>/icon.png</icon>
        <control-panel-entry-category>
            content
        </control-panel-entry-category>
        <control-panel-entry-weight>1.5</control-panel-entry-weight>
        <instanceable>false</instanceable>
        <header-portlet-css>/css/menu/main.css</header-portlet-css>
        <footer-portlet-javascript>
            /js/menu/main.js
        </footer-portlet-javascript>
        <css-class-wrapper>menu-portlet</css-class-wrapper>
    </portlet>
Also, in liferay-display.xml, add this portlet to hidden categrory to hide it from user selection in add Portlet list on their pages:
 <category name="category.hidden">
        <portlet id="menu"></portlet>
    </category>
Embed Display Menu portlet to theme:
After you get all menu items you need to display in menu bar, embed Menu Display portlet to theme to make it visible to all pages and does not let user change, move or re-configure it. In navigation.vm file, do something like this:
<nav class="$nav_css_class menu" id="navigation">
    #set ($VOID = $velocityPortletPreference.setValue('display-style', '1'))
    #set ($VOID = $velocityPortletPreference.setValue('portlet-setup-show-borders', 'false'))
   
    #set ($portletId = "menudisplay_WAR_portalportlet")
   
    $theme.runtime($portletId, '', $velocityPortletPreferences.toString())
    #set ($VOID = $velocityPortletPreferences.reset())
</nav>
That's all. Tell me if you have any problem. All comments are welcome. See you next time.