References:
http://www.liferaysavvy.com/2013/02/liferay-important-code-snippets.html
Showing posts with label Liferay. Show all posts
Showing posts with label Liferay. Show all posts
Wednesday, May 6, 2015
Tuesday, April 21, 2015
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.
<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.
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.
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.
@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.
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
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:
@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:
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
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.
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.
<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"));
}
}
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/
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?
<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.
<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();
}
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:
<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:
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:
Look at portlet-model-hints.xml file,
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:
/htm/menu/buttons.jsp file:
But wait a minute, how can we save data with localization? Here it is:
Add Menu portlet to Control Panel:
Add 2 control panel entry element to portlet definition in liferay-portlet.xml, like below:
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:
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">In this case, pay your attention on:
<!-- 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>
<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>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:
<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>
<%@ include file="/html/menu/init.jsp"%>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.
<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>
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"%>MenuItem model has function help us to fetch Menu label corresponding to current locale.
<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>
/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");See my highlighted code. We save a map with locale value, not just a single value as usual.
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);
}
Add Menu portlet to Control Panel:
Add 2 control panel entry element to portlet definition in liferay-portlet.xml, like below:
<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:
<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>
<category name="category.hidden">Embed Display Menu portlet to theme:
<portlet id="menu"></portlet>
</category>
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">That's all. Tell me if you have any problem. All comments are welcome. See you next time.
#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>
Subscribe to:
Posts (Atom)