Search This Blog

Tuesday 17 December 2013

Creating & Using a language.properties for your custom portlet





STEP:1 Create a Package named-'content' under the.. '/docroot/WEB-INF/src'
Note: Create a Package and not a folder. [i did that mistake for the first time ;-)]

STEP:2 now create a file named - '/content/language.properties' under the content folder created above..

STEP:3 now to recognize this file @ portlet-level you need to make an entry in the 'portlet.xml' file present under the -'/docroot/WEB-INF/portlet.xml'
Add the properties file entry in this file using the resource-bundle tag as following :-
--------------------------------------------------------------------
<resource-bundle>content.language</resource-bundle>

--------------------------------------------------------------------

Note: add this Tag @ following location for each portlet (that intends to use this language.properties file) in the portlet.xml:-
--------------------------------------------------------------------
<portlet>
    <portlet-name>portfolio</portlet-name>
    ...
   <resource-bundle>content.language</resource-bundle>
    <portlet-info>...</portlet-info>
    ...
</portlet>
--------------------------------------------------------------------
Note: Also , the correct entry format is : <resource-bundle>content.language</resource-bundle>
often mistaken with --
<resource-bundle>language</resource-bundle>  OR
<resource-bundle>language.properties</resource-bundle>  OR
<resource-bundle>content.language.properties</resource-bundle>

To populate a jQuery-DataTable by passing JSON Response(JSonArray) as parameter.

Recently, i came across a Reqirement where i need to populate datatable(i used jQuery-DataTable) by passing JSON array as input parameter.
For example , i had a JSON ajax call which returns me a JSON array data,i would like to populate datatable with this JSON array,
 i know i can do AJAX call from datatable itself, but i would like to explore this option of getting data first 
and then building table using this data..

[download link -- http://datatables.net/download/]
use the following link to download the all the required JS libs/files (probably you will get a complete JQuery Datatable project ..
and you need to extract the following files from the /js folder in there..)
- dataTables.js
- dataTables.min.js
Alongwith these you will be needing -
- jquery.js
- jquery-1.10.2.min.js    as well(download and include ,incase you haven't uptill now..)

Ok, so here the input Text in my 'jsp' which will invoke the ajax-call onKeyUp -
<input id="organizationName" name="organizationName" onkeyup="doSearchAjaxCall();" type="text" value="" />

here's the JavaScript function which is responsible for making the ajax call.....
----------------------------------------------------------------------------------------------
Also, before jumping to script keep note of the Portlet-URL(required for the ajax call) ,
i created a resourceURL (given below) which will hit the serveResource in my PortletClass ..(you may create as per your requirement)
-- -- -- -- -- -- -- -- -- -- -- -- -- --
<portlet:resourceURL var="searchRequestURL">
</portlet:resourceURL>
-- -- -- -- -- -- -- -- -- -- -- -- -- --
<script>
function doSearchAjaxCall() {
 // fetching various field params from the jsp..
var acbno = jQuery("#acbno").val();    
var orgName = jQuery("#organizationName").val();
var field = jQuery("#field").val();          

//dataType:'json',
Note: mentioning dataType in the ajax-call is note required ...only 'setContentType("application/json")' is enough [set in portlet class --see complete code below]
jQuery.ajax({
type: "POST",
url: "<%= searchRequestURL.toString() %>",
data:"acbno="+acbno+"&orgName="+orgName+"&field="+field,
error: function(data) {
alert(" inside error >>> "+data);
},
success: function(data) {
//var stringResponse = JSON.stringify(data);
Note: Dont make the Mistake of converting the data-array into string before passing into datatable - as it requires data-array in json form only..
//alert("data stringify = >>>> "+stringResponse);
// console.log("success data>>  "+data);
// pass the data-array obtained in success-fn to another js fn created -'loadDataTable()' which will futher //populate the Datatable..
loadDataTable(data);          // custom -function Call

}
});
 }

  function loadDataTable(data){

  // console.log("loadDataTable >>  "+data);
   $("#tableId1").dataTable().fnDestroy();
var oTable = $('#tableId1').dataTable({
"aaData" : data,
"aoColumns" : [
{"sTitle" : "SammNo" },
{ "sTitle" : "OrganizationName" },
{ "sTitle" : "Field" },
{"sTitle" : "Scope" }
]
});
}
// Also, you may add the ajax call on jQuery ready as well here...see explanation below >>>
</script>

Note: aaData - 
Note: aoColumns -
Note :    $("#tableId1").dataTable().fnDestroy();   ---bcz Datatables cannot be reinitialised hence, need to destroy the existing datatable before poputaing again for consecutive ajax call for same datatable..

----------------------------------------------------------------------------------------------
 Also, you may add the ajax call on jQuery ready as well Incase you want the datatable to be populated on page load as well...
 bcz above code will populate the datatable as & when the 'onkeyup()' function is called for the input-text -'organizationName'.
 ----------------------------------------------------------------------------------------------------
jQuery().ready(function(){

var acbno = jQuery("#acbno").val();
var orgName = jQuery("#organizationName").val();
var field = jQuery("#field").val();

    jQuery.ajax({
type: "POST",
url: "<%= searchRequestURL.toString() %>",
data:"acbno="+acbno+"&orgName="+orgName+"&field="+field,
error: function(data) {
alert(" inside error >>> "+data);

},
success: function(data) {

//console.log(" jQuery-ready >> success data>>  "+data);

loadDataTable(data);

}
});
});
------------------------------------------------------------------------------------------------------------


NOTE : STRICTLY AVOID USING 'console.log();' - This gives an error saying 'console is unDefined' in most of the IE version browsers. This error often breaks the complete JavaScript used ..In my case i wasn't able to display jQuery DataTables in IE whereas it was working  F9 in FF && Chrome..!!!
Though could use the same for debugging purpose OR incase the target browser for your application duznt includes IE (which is often not the case ) ;-)






Dynamically Creating a URL for a DLFileEntry or a File stored in Document & Media

Recently, i came across a requirement where i need to provide a view link to the various files stored in Liferay Document & Media.ie on click of these links the File(.pdf stored in our case) needs to open in a new Tab in Browser.
Hence, a url is required for that file :
I came to know that the complete URL formed is composed of the following components:
host+documents+groupId+folderId+fileTitle
where,
host - you can obtain as following- 'PortalUtil.getPortalURL(portletRequest)'
-will return something like this (http://localhost:8080/) incase of local machine..
documents - you can hardcode as this is hardly going to change in the D&M path..
groupId - you better fetch from the dlFileEntry Obj as - 'dlFileEntry.getGroupId()'
folderId - you better fetch from the dlFileEntry Obj as - 'dlFileEntry.getFolderId()'
fileTitle - you better fetch from the dlFileEntry Obj as - 'dlFileEntry.getTitle()'
- Note: Don't fetch 'dlFileEntry.getName()' instead of 'dlFileEntry.getTitle()'

Hence,
The complete url will look something like this:
 'http://localhost:8080/documents/10179/0/Flipkart-Induct-meet'

Here's how i formed this URl in my PortletClass:
------------------------------------------------------------------------------------------------------------
final String filePath_fromDM = "documents/"+dlFileEntry.getGroupId()+"/"+dlFileEntry.getFolderId()+"/"+dlFileEntry.getTitle();
System.out.println(">>>>>>  filePath_fromDM >>> = "+filePath_fromDM);
-will return something like this (documents/10179/0/Flipkart-Induct-meet) incase of local machine..

String completeFilePathUrl = StringPool.BLANK;
completeFilePathUrl = PortalUtil.getPortalURL(resourceRequest)+"/"+filePath_fromDM;
System.out.println(" >>> completeFilePathUrl () = "+completeFilePathUrl );
-will return something like this (http://localhost:8080/documents/10179/0/Flipkart-Induct-meet) incase of local machine..
------------------------------------------------------------------------------------------------------------

And, finally here how i formed a Link for this URL on click of which it will open the doc in a New-Tab..
String linkFormed = "<a href='"+scopeUrl+"' target='_blank'>View</a>" ;
Note: target="_Blank" is required only if you want to open the link in a new Browser Tab.
which you can pass in a response , use in a jsp , etc as per your requirement..


Tuesday 10 December 2013

Custom queries in Liferay

Sometimes it is needed to perform joined queries with the Service Builder. It is not possible to do it with dynamic queries - custom queries are necessary.
This article explains how to create custom queries , i faced lot of issues when working on the same for the first time, unware for various minor concepts which were hard to be found at one-place . Thus ,decided to document my understanding of the same along withe the issues faced during implementation alongwith their fixes. Plz don't ignore the various -'Note' throughout the blog.


STEP:1 First of all create a folder with the name - "custom-sql"  under the src folder...
Note: create a folder & NOT a package .(see image below.)



STEP:2 Now create a file- "default.xml" in this custom-sql folder...(Though you can write your sql-querries over here as well , but its always a good practice to write your qurries in
a diff .xml file & map that file int this default.xml)
Add something like this to your default.xml:
---------------------------------------------------------------------------------
 <?xml version="1.0"?>
<custom-sql>
    <sql file="/custom-sql/myProject-portal.xml" />
</custom-sql>
---------------------------------------------------------------------------------

STEP:3 Now open your 'myProject-portal.xml' , and put  all you querries here as below... each identified by a id.
Something like this :
---------------------------------------------------------------------------------
<?xml version="1.0" encoding="UTF-8"?>
<custom-sql>
 
    <sql id="">
        <![CDATA[
Select * from user_ where firstName ="Test"
        ]]>
    </sql>
</custom-sql>

---------------------------------------------------------------------------------
STEP:4 Add the following in ur portal-ext.properties

custom.sql.configs=\
custom-sql/default.xml, \
custom-sql/default-ext.xml

---------------------------------------------------------------------------------

Step:5 Create a FinderImpl class bearing your Entity name that must extends BasePersistenceImpl.
public class CABDisplayFinderImpl extends BasePersistenceImpl<CABDisplay>{ }

Step:6 Now run the 'build-service' task.You will observe that the service-builder has generated two more files for you in '../service/persistence' under the 'docroot/WEB-INF'-namely
-CABDisplayFinder AND
-CABDisplayFinderUtil
  Now go back to your FinderImpl class and add 'implements 'CABDisplayFinder' as below and run the Build-Service task again..
---------------------------------------------------------------------------------
  public class CABDisplayFinderImpl extends BasePersistenceImpl<CABDisplay> implements CABDisplayFinder{

}
---------------------------------------------------------------------------------
Note:
Do Keep Note of these two configuration related points -
[1] Next ,you need to create a finderImpl under the "service/persistence" under the "/docroot/WEB-INF/src" && Not in 'service/persistence' under "/docroot/WEB-INF"
[2] Also , the FinderImpl must start with the name of any defined Entity in your service.xml (say - for entity-'ABC' it would be 'ABCFinderImpl' & not 'MyABCFinderImpl',etc)
This is very imp to mention as i wasted a reasonably good amount of time breaking my head on this issue, being unaware of this concept that your FinderImpl class must bear & start with the name of a defined Entity.
I did the Mistake of creating a FinderImpl with the name 'ABCDisplayFinderImpl' for an entity -'ABCDisplay_Metadata' , whereas it should be 'ABCDisplay_MetadataFinderImpl' which solved the Issue.




Monday 9 December 2013

How to expose Liferay Service as a Soap Web Service

Having spend myself a reasonably good amount of time in various issues ,hereby , i have shared my understanding of -'How to expose Liferay Service as a Soap Web Service'

I used Liferay6.1.1 source and Plugin-SDK(liferay-plugins-sdk-6.1.1-xx) along with eclipse juno The base platform was Java1.7.x and MySQL5.1..x.
The deployment server was Liferay-bundled-JBoss GA3. (liferay-portal-jboss-6.1.30-ee-ga3-xx)

Assuming that you are already thru the following steps mentioned below :
Step 1: Setup liferay source project and tomcat/JBoss/etc
Step 2: Setup plugin SDK.
Step 3: Create the service project (say i gave the project-name as 'cab_portletname')
Step:4 Create a new liferay-service builder file (service.xml)

Now, you can expose the liferay-service to any of the available Entities (Entities you mentioned in the service.xml).
Lets say you created an entity named- 'CABDisplay' && namespace as -'CAB' as below:-
---------------------------------------------------------------------------
<namespace>CAB</namespace>
<entity name="CABDisplay" local-service="true"
remote-service="true">

<!-- Primary Key -->
<column name="SammNo" type="long" primary="true" />
<!-- Audit fields -->
<column name="OrganizationName" type="String" />
<column name="Field" type="String" />
</entity>
---------------------------------------------------------------------------
Step:5 Save this service.xml && run the 'CABDisplay-Portlet/build-service' task.(say 'ant clean build-service' from command-prompt ,etc)
If the build fails, please check if your service.xml for any syntax-error (also, check the console & act accordingly). On successful build, the ant task creates the files related to CABDisplay entity.
You can now refresh your project (F5) to see the generated files.

Step:6 Incase you wanna expose any of the default liferay-services generated for you entity(you can check the same in the your serviceImpl -say 'CABDisplayServiceImpl' in our case)
Now Add the CRUD and finder methods into your com.liferay.test.service.impl.CABDisplayServiceImpl as listed below:
---------------------------------------------------------------------------------------
public CABDisplay fetchMetadataBySammNo(long sammno){

return cabDisplayLocalService.fetchCABDisplay(sammno);
}
---------------------------------------------------------------------------------------
public CABDisplay createCabDisplay(long sammno){

return cabDisplayLocalService.createCABDisplay(SammNo);
}
---------------------------------------------------------------------------------------

Step:7 Now you need to run the 'CABDisplay-Portlet/build-wsdd' task to generate the webservices..(say 'ant build-wsdd' from command-prompt ,etc)
Now you can simply deploy the portlet

Step:8 Now, type the following composed url in the browser && check the respective web-services exposed along with thier wsdl - "http://host/portletname-portlet/api/axis"

In our case the url will be  - " http://localhost:8080/cab_portletname-portlet/api/axis " ,which will show you all the services exposed for this respective Portlet..

where, you can check the value of 'cab_portletname-portlet' from the following locations in case of JBoss App Server "jboss-7.1.1\standalone\deployments\" &&
 "tomcat-7.x.x\webapps\" folder in case of TOMCAT App Server.

Step:9 hence, you have successfully exposed your custom liferay-services avaliable as a web service. Now in case you wanna consuming the same-- you can simply build a client using the above wsdl and consume the same...!!! ;-)