Featured post

Docker setup for Liferay 7 with MySQL

Monday, 17 April 2017

Spring portlet handler interceptor or spring MVC interceptor Liferay


Today we will be learning about portlet handler interceptor or simply you can say MVC interceptor with spring.


To create an osgi spring portlet please look at the spring osgi portlet.

In this example, we will create an annotation and use that annotation in our portlet class and then by reflection we will call that method in our interceptor.We can do it without all of it, but as we are doing example, let's learn something more out of it.
It has nothing to do with Liferay, but as we are using Liferay environment to run portlet, it's spring osgi liferay portlet.

You can find this source code here - https://github.com/bardiavipin/osgi-spring-interceptor

By Creating this interceptor you create one more layer between View and Controller. You can use filters as well for the same tasks you want to perform with interceptors. Interceptors are more coupled with request/response objects. With Spring interceptor, you can execute before and after the phase executes.

Interceptors are executed in below cases


  • Pre
  • Post
  • After

Methods are available for each of the phase of portlet


  • Render
  • Action
  • Event
  • Resource


I have created two modules for this example, one contains portlet and another contains interceptor and annotation class. Reason behind creating two modules is to demonstrate bundle development where you can interact between these.

Bundle 1

Portlet Interceptor - MyPortletInterceptor

public class MyPortletInterceptor extends HandlerInterceptorAdapter {

    public Gson gson;    

    @Override    public void postHandleRender(RenderRequest request, RenderResponse response, Object portletController, ModelAndView modelAndView) throws Exception {
        Class<?> clazz = portletController.getClass();

        Method[] methods = ReflectionUtils.getUniqueDeclaredMethods(clazz);
        for (Method method : methods) {
            if (method.isAnnotationPresent(LoadJson.class)) {
                Object result = method.invoke(portletController);
                modelAndView.addObject("LoadJson", gson.toJson(result));
                break;
            }
        }
        System.out.println("Your custom spring portlet interceptor called!");
}


I removed Gson setter/getter to make it short.

About MyPortletInterceptor
  • It extends HandlerInterceptorAdapter
  • Override one method postHandleRender
  • Invoke method from annotation
  • Add value to ModelAndView "LoadJson" after invoking the method
  • Even if you don't have annotation in your class, It will print a simple line output

Annotation - LoadJson
You can read about custom annotations from - https://www.javatpoint.com/custom-annotation

We just created a basic annotation with target as method.

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface LoadJson {
}

Bundle 2

Controller - PortletViewController

public class PortletViewController {

   @LoadJson   public Map loadJson(){
      Map map = new HashMap<String, String>();

      map.put("name", "vipin bardia");
      return map;
   }

What happens when we add it to our controller

  • After adding dependency of Bundle 1 to our portlet, we can use this LoadJson annotation
  • We wrote a method loadJson and returned a map
  • This method will be called from our interceptor and output will be added to "LoadJson" attribute as interceptor have access to it

View - view.jsp


Json Data : <c:out escapeXml="true" value="${LoadJson}" />

Let's make the main entry which connects this interceptor to our portlet.

spring-portlet.xml - osgi-spring-portlet.xml

<bean name="gsonbean" class="com.google.gson.Gson" />

<bean name="portletHandlerInterceptor" class="com.osgi.spring.interceptor.MyPortletInterceptor">
    <property name="gson" ref="gsonbean" />
</bean>

<!-- Handler mappings for annotation based controllers --><bean class="org.springframework.web.portlet.mvc.annotation.DefaultAnnotationHandlerMapping">
    <property name="interceptors">
        <list>
            <ref bean="portletHandlerInterceptor"/>
        </list>
    </property>
</bean>

About spring portlet xml

  • We defined gson bean
  • We defined portletHandlerInterceptor and provided property as gson bean
  • In default annotation handler we added the interceptor


I tried to make it work with bean creation inside interceptor module only and use it inside portlet module, but was unable to. If you know this way please share it here.


Advantage out of this interceptor is you can call this interceptor in any of your portlet by just making an entry in your spring portlet xml.
If you don't want it in one of your portlet, simply remove it.


Note : You need to deploy gson bundle as well to access it!


You are just done, Try & Enjoy the function.............:)

Friday, 7 April 2017

Spring OSGI portlet or bundle with Liferay 7


When you see the blog title, The question arises, is what's new in this post?


Everyone knows how to create Spring MVC Portlets! 
But do we know how to create a portlet which is OSGI enabled and can run parallel with other osgi bundles inside Liferay.

If you have been through these links and many more -

https://dev.liferay.com/develop/tutorials/-/knowledge_base/7-0/spring-mvc
https://github.com/vernaillen/liferay7-springmvc-portlet

You must have noticed it's the same old way which includes all required jars inside the application's lib because we do not have support of global library now.
Liferay 7 is OSGI way now and by OSGI convention you do not include library into each other, but you share them from same place :)

Basic diagram of OSGI, copied from  -

https://fredhsu.wordpress.com/2013/05/03/opendaylight-and-osgi-basics/


To make it work for spring, we need to do two things

  • We will be using Apache Service Mix Spring Bundles(OSGI bundles for spring)
  • Support of few maven plugins, major one is Maven Bundle Plugin to add require entries to the manifest file and prepare it like a bundle but in a war type.

You can find the source code here - https://github.com/bardiavipin/osgi-spring
Meanwhile, you are checking out source code, I will explain minimal changes in your spring portlets to make them bundles.

Create a basic Spring portlet from your IDE or from command prompt or manually with Maven.

Copy below dependencies to deploy folder

Apache ServiceMix :: Bundles :: spring-beans (4.3.1.RELEASE_1)
Apache ServiceMix :: Bundles :: spring-web (4.3.1.RELEASE_1)
Apache ServiceMix :: Bundles :: spring-core (4.3.1.RELEASE_1)
Apache ServiceMix :: Bundles :: spring-aop (4.3.1.RELEASE_1)
Apache ServiceMix :: Bundles :: spring-expression (4.3.1.RELEASE_1)
com.github.ben-manes.caffeine (2.3.2)
Apache ServiceMix :: Bundles :: spring-context-support (4.3.1.RELEASE_1)
Apache ServiceMix :: Bundles :: spring-context (4.3.1.RELEASE_1)
Apache ServiceMix :: Bundles :: spring-webmvc (4.3.1.RELEASE_1)
Apache ServiceMix :: Bundles :: commons-configuration (1.9.0.2)
Apache ServiceMix :: Bundles :: spring-webmvc-portlet (4.3.1.RELEASE_1)

Note:Caffeine is a required library for one for the spring modules.

You can check the status of bundles from Gogo Shell

Now we will be going through major changes inside Portlet files-


Context file for sample portlet is - spring portlet xml

<!--Not Working--><!--<context:component-scan base-package="com.osgi.spring" />-->

<bean class="com.osgi.spring.PortletViewController"/> <!-- Handler mappings for annotation based controllers --><bean class="org.springframework.web.portlet.mvc.annotation.DefaultAnnotationHandlerMapping"/> <bean class="org.springframework.web.portlet.mvc.annotation.AnnotationMethodHandlerAdapter"/>


As you can see in the context file, component scan is commented as it is not working for this example. I used the workaround to create simple bean and pass the controller class. Spring players can tell me what is the issue here :)

BND Dependency Fix - BNDDependencyFix

@SuppressWarnings("unused")
public class BNDDependencyFix {

    static {
        System.out.println(ModelAndViewResolver.class);


When you deploy the portlet without this class, you get errors like this -
java.lang.ClassNotFoundException: org.springframework.context.config.ContextNamespaceHandler cannot be found.

In this class we are initializing or loading the necessary classes from static method.
If you find more errors with same type, you can add your class name here.

Major Maven Plugins - pom.xml

  • War plugin to exclude all jars from final war
  • Bundle plugin make necessary changes in Manifest file to create it a bundle
  • Shade plugin to make single files from spring.schemas and spring.handlers and transform  them to a single file independently
  • Truezip plugin to move spring.handlers and  spring.schemas  to classes/META-INF

When you are done with building and deploying this portlet, you can see your portlet is interacting to another Liferay bundle - portal-kernel to fetch release information. Each of the library is deployed as OSGI only and shared between applications.

You can include jar as well inside apps lib with little change in configuration, but until and unless it is necessary don't break the architecture :)

You can use Maven Bundle plugin for any of your independent project and convert it to a osgi bundle.


You are just done, Try & Enjoy the function.............:)

Wednesday, 5 April 2017

Docker setup for Liferay 7 with SMTP, document library and more as Part II

In my previous blog we talked about setting up docker with Liferay and MySQL!


In this blog we will integrate SMTP server inside docker and setup mount for document_library.
We will go through content of Dockerfile which we cloned from repo and made necessary changes.

Few terms we need to understand before jumping to the setup

Dockerfile : Docker can build images automatically by reading the instructions from a Dockerfile. A Dockerfile is a text document that contains all the commands a user could call on the command line to assemble an image. Using docker build users can create an automated build that executes several command-line instructions in succession.


Volume : data volume is a specially-designated directory within one or more containers that bypasses the Union File System.

Mount : A mount point is a directory in a file system where additional information is logically connected  from a storage location outside the operating system’s root drive and partition.

Cool, Let's move forward and have a look on docker-compose.yml


version: '2'
services:
  portal:
    build: .
    ports:
     - "8080:8080"
     - "11311:11311"
    environment:
      - MYSQL_ROOT_PASSWORD=my-secret-pw
      - MYSQL_DATABASE=lportal
      - MAIL_SERVER=mail-server
      - MAIL_SERVER_SMTP_PORT=25
      - MAIL_SERVER_USER_FROM=admin@mail.vipin.org
      - MAIL_SERVER_USER_FROM_PASSWORD=admin
      - MAIL_SERVER_AUTH=true
    volumes:
    - "./data/deploy:/usr/local/liferay-ce-portal-7.0-ga3/deploy"
    - "./data/document_library:/usr/local/liferay-ce-portal-7.0-ga3/data/document_library"
    depends_on:
     - mysql
     - mail-server
  mail-server:
    image: million12/citadel
    ports:
      - "25:25"
      - "110:110"
      - "143:143"
      - "465:465"
      - "587:587"
      - "993:993"
      - "995:995"
      - "9090:8080"
      - "10022:22"
    environment:
      - ROOT_PASS=root
      - PASSWORD=admin
      - DOMAIN=vipin.org
    hostname: mail.vipin.org
  mysql:
    image: mdelapenya/mysql-utf8
    ports:
     - "3306:3306"
    environment:
      - MYSQL_ROOT_PASSWORD=my-secret-pw
      - MYSQL_DATABASE=lportal
      - character-set-server=utf8mb4
      - collation-server=utf8mb4_unicode_ci

This is the same file which we used earlier with addition of mail server setup and mounting document library.

SMTP

We are using million12/citadel repository for SMTP server. There are so may smtp server available in docker hub but this is a easy one with web ui and authentication support.

As you can see in yml file we have added one more dependency service mail-server.  We have exposed ports for smtp, ssl, web ui etc. hostname is your mail extension here.
So you create a user inside smtp server with web support which is available on port 9090, e.g. 192.168.99.100:9090

You can login as admin/admin for administrator account or create user from outside with Register New User link, e.g. vipin.bardia@mail.vipin.org.
Use the same user and create that account in Liferay portal and check on the same mail id if you get a welcome mail like this or not.



Amazing, now you have your own SMTP server with few lines of configuration :)

Document Library

Let's talk about Volume and Mount again. I've already shared there definition above so will go through the use of it in our case.

Let's say I want to move my server to somewhere else, what is required other than database?
It's my documents which is stored in data/document_library folder.

So what can I do about this?

- "./data/deploy:/usr/local/liferay-ce-portal-7.0-ga3/deploy"
- "./data/document_library:/usr/local/liferay-ce-portal-7.0-ga3/data/document_library"

As you can see here ./data/document_library  is my local folder where [.]  represents current directory and /usr/local/liferay-ce-portal-7.0-ga3/data/document_library is my document library folder which is separated by [:] this line creates a volume inside docker container and mount it our local folder.

Whenever we upload anything like this in Liferay


It will be synced to our local folder like this

In the same manner we mount deploy folder and as soon as we drop the file war/jar inside it, it is copied to volume deploy folder and starts deployment.


Dockerfile: These are instruction which is executed step by step to create a new image.


FROM mdelapenya/liferay-portal:7-ce-ga3-tomcat-hsql
MAINTAINER Manuel de la Peña <manuel.delapenya@liferay.com>

COPY ./configs/portal-ext.properties $LIFERAY_HOME/portal-ext.properties

ENTRYPOINT ["catalina.sh", "run"]


  1. Fetch image from repository
  2. Information about Maintainer
  3. Copy portal-ext to docker image at specified path
  4. $LIFERAY_HOME is environment variable
  5. Entrypoint will execute catalina


You are just done, Try & Enjoy the function.............:)

Friday, 24 March 2017

Docker setup for Liferay 7 with MySQL

Docker is among the most famous DevOps tool these days which eliminates “works on my machine”  problem.

Prerequisite:

Docker, Yes, you do not need any developer setup other than Docker!

About: 

Use docker and distribute the same image to your team and start the development. No setup is required like configure liferay,sql, etc.

There are multiple ways to use docker, but easiest is to use yml and docker-compose.
I am using Manuel de la Peña's docker files to make it work.

Download repository from git - https://github.com/mdelapenya/docker-liferay-portal/tree/7-ce-ga3-tomcat-MySQL (If you are using windows, keep your folder in User directory)

Structure:

Folder structure with slight change  is -
Portal ext is placed inside configs and will be copied to your docker file system, you can make require changes in this file.

I have made few changes in docker-compose.yml to add sql port and mount deploy folder.
Do not use tab while changing yml files,use space instead.

version: '2'
services:
  portal:
    build: .
    ports:
     - "8080:8080"
     - "11311:11311"
    volumes:
    - "./data/deploy:/usr/local/liferay-ce-portal-7.0-ga3/deploy"
    depends_on:
     - mysql
  mysql:
    image: mdelapenya/mysql-utf8
    ports:
     - "3306:3306"
    environment:
      - MYSQL_ROOT_PASSWORD=my-secret-pw
      - MYSQL_DATABASE=lportal
      - character-set-server=utf8mb4
      - collation-server=utf8mb4_unicode_ci


To start the server all you need to do is use docker terminal or normal command window and move to corresponding directory and run command - "docker-compose up". 





This command will download images from docker hub and create a new image from your local Dockerfile and start your liferay server on 8080 port with MySQL on 3306.

Use docker-machine ip to fetch machine address and then you can hit your portal e.g. 192.168.99.100:8080

While doing docker-compose up if you see this error for memory





Then go to Virtual Box > Settings > System and increase memory and CPU accordingly.You can perform the same thing with config.json of your docker machine.










You need to set port forwarding in Oracle Virtual Box. It is shipped with Docker ToolBox as well.
Oracle Virtual Box > Settings > Network > Advance Port Forwarding.(If you do not want to access it from localhost then leave it)









Now we will go one by one to understand content of the file


  1. version : We are using version 2 of yml.
  2. services: Which services needs to be started. Right now there are two one is portal and other is MySQL.
  3. build: Inside portal we are using build [.] which indicates we are using current directory to build docker image from Dockerfile. In normal case we use image same as MySQL section.
  4. ports: What ports must be exposed to host environment, right now we are accessing portal from 8080, we can change it to 8090:8080 like this.
  5. volumes: We will use this section to deploy war or jar in docker. Just copy your file inside your local folder which you mounted to docker's deploy folder.
  6. depends_on: On which services your service rely, like MySQL.
  7. image: docker URL to fetch image.

To see your volume, port settings and environment, you can use docker inpsect [CONTAINER_ID].

Use "docker ps" to see running containers


Take initial string of container id and run "docker inpsect [CONTAINER_ID]"
e.g. docker inspect b32

Output of this command contains multiple sections - 


Environment Variables like LIFERAY_HOME or JAVA_HOME


Ports / Network Settings


Mounts - In our case we created a folder data/deploy which is mounted to volume liferay/deploy folder.





Some useful commands:

docker-compose up -d: To create image and start docker container in background
docker-compose down: To stop
docker-compose start: To start container from previously created images
docker-compose stop: To stop container and persist the state.
docker images: To list all available images
docker exec -it [CONTAINER_ID] bash: To access file system like putty
docker logs -f  [CONTAINER_ID]: To check logs

Note: You can use unique initial string for CONTAINER_ID e.g. If container id is "234sdfs234" you can use "234".



You are just done, Try & Enjoy the function.............:)

Wednesday, 26 October 2016

jQuery UI with Liferay 7

If you want to integrate liferay 7 theme/portlet/application with jQuery UI, you need to make subtle change in jQuery UI library itself.

By default library starts with -

(function( factory ) {
 if ( typeof define === "function" && define.amd ) {

  // AMD. Register as an anonymous module.
  define([ "jquery" ], factory );
 } else {

  // Browser globals
  factory( jQuery );
 }
}(function( $ ) {


If you add library directly in your liferay application, you will be getting an error - Mismatched anonymous define() module:

To overcome this issue, you need to change the library like



(function( factory ) {
 
  factory( jQuery );
 
}(function( $ ) {

What you are doing is removing the reference of define call, which causes the issue, as jQuery is already loaded by default.
Same thing you need to perform for other JS libraries which contains the call.


You are just done, Try & Enjoy the function.............:)

Monday, 24 October 2016

3 ways to use serve resource in portal

JSR-286 introduced resource serving and the same method can perform AJAX requests as well.
It's a  long time but I am just describing here with code snippets all you can do with resource method.

This example works with Liferay 7.

There are three ways you can use resource method from your portlet.
We will be looking at these with code snippets
  • You can download a file
  • You can fetch piece of data
  • You can return a jsp/ftl...

1) Download File

Create a hyperlink and create resource url(Same step needs to be done for all calls)

<portlet:resourceURL var="downloadURL" />

<a href="<%=downloadURL%>">Download Users</a>

Method in your portlet class

public void serveResource(ResourceRequest resourceRequest, ResourceResponse resourceResponse)
   throws IOException, PortletException {
   resourceResponse.setContentType("text/csv");
   resourceResponse.addProperty(HttpHeaders.CONTENT_DISPOSITION,
      "attachment;filename=sample.csv");
   OutputStream out = resourceResponse.getPortletOutputStream();
   String names = "Vipin , Bardia";
   
   InputStream in = new ByteArrayInputStream(names.getBytes());
   IOUtils.copy(in, out);
   
   out.flush();
}



2) Fetch dynamic data

Javascript call

jQuery(".htmlButton").click(function() {
      jQuery.ajax({
          url:$("#resource_url").val(),
          success: function(data)
          {
              jQuery('#result').html(data);
          }
  });
 });

Method in your portlet class


public void serveResource(ResourceRequest resourceRequest, ResourceResponse resourceResponse)
   throws IOException, PortletException {
        resourceResponse.getWriter().write("I am sending my name : Vipin Bardia");
}

3) Return the jsp in AJAX call

Javascript call

Same javascript call as we used in example 2 to fetch dynamic data.


Method in your portlet class

public void serveResource(ResourceRequest resourceRequest, ResourceResponse resourceResponse)
   throws IOException, PortletException {
        include("/MYAJAX.jsp", resourceRequest, resourceResponse);
}

You are just done, Try & Enjoy the function.............:)

Thursday, 25 August 2016

Portal events in Liferay 7


Portal events or sometimes referred as action in Liferay are of different types.
They executes on different time and a developer can choose relevant action to perform.
You can find these entries in portal.properties-

1) Servlet service event
The pre-service events process before Struts processes the request.
The post-service events process after Struts processes the request.

servlet.service.events.pre
servlet.service.events.post

2) Login Events process after successful login only.

login.events.pre
login.events.post

3) Logout events called after successful logout.

logout.events.pre
logout.events.post

In this post we will be setting landing page in Liferay 7 OSGI way with post login action.
But at the end you will explore that all of the actions can be implemented by a single change of property.

I will be adding class only in the post, you can choose build type from - Gradle/Maven

To add events you need to -
1) Implement com.liferay.portal.kernel.events.LifecycleAction
2) Add annotation
@Component(
immediate = true, property = {"key=login.events.pre"},
service = LifecycleAction.class
)

that's all, you need to do :)

Below is the code you can refer -


package com.sample;

import javax.servlet.http.HttpSession;

import org.osgi.service.component.annotations.Component;

import com.liferay.portal.kernel.events.ActionException;
import com.liferay.portal.kernel.events.LifecycleAction;
import com.liferay.portal.kernel.events.LifecycleEvent;
import com.liferay.portal.kernel.log.Log;
import com.liferay.portal.kernel.log.LogFactoryUtil;
import com.liferay.portal.kernel.struts.LastPath;
import com.liferay.portal.kernel.util.StringPool;

@Component(
  immediate = true, property = {"key=login.events.post"},
  service = LifecycleAction.class
 )
public class LandingRedirectAction implements LifecycleAction {
 public static final Log LOGGER = LogFactoryUtil.getLog(LandingRedirectAction.class);
 @Override
 public void processLifecycleEvent(LifecycleEvent lifecycleEvent) throws ActionException {
  LOGGER.info("PostLogin called");
        HttpSession session = lifecycleEvent.getRequest().getSession();
        session.setAttribute("LAST_PATH", new LastPath(StringPool.BLANK, "/web/guest/landMeOnMoon"));
 }
}


Now, in here if you want to change this action/event to be called on each page just change the property "key" to "servlet.service.events.pre" and add your code accordingly.

You are just done, Try & Enjoy the function.............:)

Monday, 25 July 2016

Osgi Console and commands

Liferay is now fully supporting osgi, So here are some steps to connect with osgi consoles and some basic commands

OSGI console is the way to deploy and manage state of modules/components.



Connect gogo shell


 Enable telnet in windows
Go to command prompt
 command - telnet localhost 11311

OR

use putty with localhost and port 11311



Install osgi web console

Go to gogo shell

command - install 

http://mirror.switch.ch/mirror/apache/dist/felix/org.apache.felix.webconsole-4.2.16-all.jar

OR

command -  install file:///C:/org.apache.felix.webconsole-4.2.16-all.jar

After installing bundle, use the bundle id to start the bundle. By default bundle is in installed state.

command - start [bundle id]

Access - http://localhost:8080/o/system/console/bundles- (admin/admin)

[bundle id] - is an integer which is identifier for particular bundle.



Gogo shell commands


install file:///C:/liferay-blade-samples/maven/hook/target/hook-1.0.0.jar - Install the bundle
diag [bundle id] - Check for issues
update [bundle id] - Pick up from the last location
lb - List all the bundles
 ss - List all the bundles(Different View)
lb [bundle name or part of the string] - List all the bundles with matching string
uninstall [bundle id] - Uninstall the bundle
start [bundle id] - Start the bundle
stop [bundle id] - Stop the bundle
 b [bundle id] - Provides bundle details
refresh [bundle id] - Refresh bundles
disconnect [bundle id] - Disconnect from Gogo shell



Osgi Status

INSTALLED - Bundle is available in container
ACTIVE - Bundle is available to use or can be activated lazily
RESOLVED - All prerequisite are available to activate the bundle
STARTING
STOPPING

You are just done, Try & Enjoy the function.............:)

Wednesday, 13 February 2013

Add / Edit Web Content through custom portlet

Hi,

May be you have used it earlier but it was never so easy.

In every project we need to add/edit/delete web content from our custom portlet.
Hope it helps...

<!--

<%@page import="com.liferay.portlet.journal.service.JournalArticleLocalServiceUtil"%>
<%@page import="com.liferay.portlet.journal.model.JournalArticle"%>
<%@page import="java.util.List"%>
<%@page import="com.liferay.portal.util.PortalUtil"%>
<%@page import="com.liferay.portal.util.PortletKeys"%>
<%@page import="javax.portlet.WindowState"%>


<%@ taglib uri="http://java.sun.com/portlet_2_0" prefix="portlet" %>
<%@ taglib prefix="liferay-portlet" uri="http://liferay.com/tld/portlet" %>
<%@ taglib prefix="liferay-ui" uri="http://liferay.com/tld/ui" %>
<%@ taglib uri="http://liferay.com/tld/theme" prefix="liferay-theme" %>

<portlet:defineObjects />
<liferay-theme:defineObjects />
This is the <b>Add Web Content</b> portlet.

// Add Web Content Link

<liferay-portlet:renderURL windowState="<%= WindowState.MAXIMIZED.toString() %>" var="addArticleURL" portletName="<%= PortletKeys.JOURNAL %>">
<portlet:param name="struts_action" value="/journal/edit_article" />
<portlet:param name="redirect" value="<%= PortalUtil.getCurrentURL(renderRequest) %>" />
<portlet:param name="portletResource" value="<%= portletDisplay.getId() %>" />
<portlet:param name="groupId" value="<%= String.valueOf(scopeGroupId) %>" />
</liferay-portlet:renderURL>

<liferay-ui:icon image="add_article" message="add-web-content" url="<%= addArticleURL %>" />

<div>
<%
   List<JournalArticle> listJournalartArticles=JournalArticleLocalServiceUtil.getArticles(scopeGroupId);
   for(JournalArticle journal:listJournalartArticles){
%>

// Edit Web Content Link

<liferay-portlet:renderURL windowState="<%= WindowState.MAXIMIZED.toString() %>" var="editURL" portletName="<%= PortletKeys.JOURNAL %>">
        <portlet:param name="struts_action" value="/journal/edit_article" />
        <portlet:param name="redirect" value="<%= PortalUtil.getCurrentURL(renderRequest) %>" />
        <portlet:param name="originalRedirect" value="<%= PortalUtil.getCurrentURL(renderRequest) %>" />
        <portlet:param name="groupId" value="<%=String.valueOf(scopeGroupId)%>" />
        <portlet:param name="articleId" value="<%=journal.getArticleId()%>" />
        <portlet:param name="version" value="<%=String.valueOf(journal.getVersion())%>" />
</liferay-portlet:renderURL>
<liferay-ui:icon image="edit" message="edit-web-content" url="<%= editURL %>" />

<liferay-ui:journal-article articleId="<%=journal.getArticleId()%>" groupId="<%=scopeGroupId%>" />
<%
    }
%>

</div>

-->

With help of above code you can add/edit web content. It will redirect you on same portlet after adding/editing portlet.

You need to make some changes in above code which i used to display  list of web contents so you can get unique one.

Note: If there is a hyphen(-) in your portlet name then it will through an exception in second deployment.
           Either create portlet without hyphen(-) or follow this link to resolve this issue and have hyphens.

You are just done, Try & Enjoy the function.............:))

Tuesday, 6 November 2012

Reset or Recover Admin / User's password by Hook


Sometimes we go through such kind of problem that we lost all the details of Admin user/User or other users and unable to access portal.

One thing we can to rid out of this problem is to change the User_ table directly
 i.e. change it manually.
For That you can follow this link .

But in production we don't have that much access on server side, so what else we can do?
Thanks to Samuel Liu and Lisa Simpson who make this way so easy.

You can create a hook to reset user's password by specifying some properties like user's name, screenName, virtualHost or webId depends on which liferay version you are using.

Here is the link from where you can download/checkout this Hooks as per your liferay version.

If it's not matching your liferay version then create your own hook for this.

1) Create a startup hook.
2) Use PasswordUpdater.java in application.startup.events .



PasswordUpdater.java


package hu.bzz.liferay;

import java.util.Date;
import java.util.Properties;

import com.liferay.portal.kernel.events.ActionException;
import com.liferay.portal.kernel.events.SimpleAction;
import com.liferay.portal.kernel.log.Log;
import com.liferay.portal.kernel.log.LogFactoryUtil;
import com.liferay.portal.model.Company;
import com.liferay.portal.model.User;
import com.liferay.portal.service.UserLocalServiceUtil;
import com.liferay.portal.service.persistence.CompanyUtil;

public class PasswordUpdater extends SimpleAction {

@Override
public void run(String[] arg0) throws ActionException {
Properties props = new Properties();
try {
props.load(this.getClass().getClassLoader().getResourceAsStream("password.changer.properties"));
String type = props.getProperty("type");

                        //If Liferay version is less than 6.1
String virtualHost = props.getProperty("virtualhost");
Company c = CompanyUtil.fetchByVirtualHost(virtualHost);

                        /* If Liferay version is  6.1

                        String webId = props.getProperty("webId");
Company c = CompanyUtil.fetchByWebId(webId);

                         */
User u = null;
String name = null;
if ("screenname".equals(type)) {
String screenName = props.getProperty("screenname");
u = UserLocalServiceUtil.getUserByScreenName(c.getCompanyId(), screenName);
name = screenName;
} else if ("e-mail".equals(type)) {
String emailAddress = props.getProperty("emailaddress");
u = UserLocalServiceUtil.getUserByEmailAddress(c.getCompanyId(), emailAddress);
name = emailAddress;
}
else {
_log.error("You should set type to screenname or e-mail if you want to use the password updater.");
}
String password = props.getProperty("password");
UserLocalServiceUtil.updatePasswordManually(u.getUserId(), password, false, true, new Date());
_log.info("Password for " + name + " was updated.");
} catch (Exception e) {
_log.error(e);
}
}

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

}


3) create "password.changer.properties" in src and put required details.


password.changer.properties


# type should be e-mail or screenname
type=e-mail

# This is the webId of the company (not of communities or organizations!), if you have 1 instance, it's webId most of the time
webId=liferay.com

# This is the virtualhost of the company (not of communities or organizations!), if you have 1 instance, it's localhost most of the time
virtualhost=localhost

# Read only if the type is e-mail
emailaddress=test@liferay.com

# Read only if the type is screename
screenname=test

password=changeIt



3) Deploy hook & login with new password which you set in property file.
4) After login, liferay will ask to set up a new password.
5) Undeploy the Hook, so it will not set the password again after server restart.

You are just done, Try & Enjoy the function.............:))

There was an error in this gadget