Monday, February 27, 2012

Liferay Portal 6.1 - Development Guide

Monday, February 6, 2012

Liferay India developers group on Facebook

This group is running successfully from past few months.It helps me out to learn many new things in Liferay.I just take the initiate but all the members Satish,Mazhar,Vamc (i just remeber these names only ) even everyone who are here contribute alot. Hope this will continue and get a large number of members this year. "http://www.facebook.com/groups/180811265343833/" Thanks alot for being a part of this community

Wednesday, January 18, 2012

Liferay 6.1 and SASS

Lieray 6.1 CE is released and many of you have started using it. There are lots of features in Liferay 6.1 and one among them is the facility to use SASS in Liferay.

SASS:

SASS( http://sass-lang.com ) stands for Syntactically Awesome StyleSheets. It is a tool that allows you to write CSS more easily It is an extension of CSS3 which means whatever CSS file is a valid SassyCSS (SCSS). It allows you to use variables, nested rules, mixins, inline imports, interpolation, arguments, manipulating colors and more with a fully CSS-compatible syntax. Sass generates well formatted CSS and it is much easier to organize and maintain your stylesheets.

Compass :

Compass( http://compass-style.org ) is an open-source CSS authoring framework which makes easier to build and maintain your markup and stylesheets. By using compass, you can write your own stylesheet in Sass instead of CSS.

How does Compass Work?

It can be imported using @import "compass" in CSS. SCSS is built based on the CSS3 specification which means that it is highly compatible with CSS3. SCSS also has all the power of Sass.

We’ll see here as how to use the variables, nested rules, mixins, inline imports, interpolation and arguments through CSS files.

Variables:

We’ll see how to use variables and a custom mixin to show how Compass makes it easy to reuse colors, sizes, and other values without repeating.

/* Variable Declaration */

/*These variables can be used globally throughout your stylesheets */ 

 

Example:

Liferay6.1     Liferay 6.0.x

Mixins:

By using the mixin, you can re-use the styles, properties & selectors without copying to other selectors. To achieve this , you need to package the properties with @mixin and to include it in other element with @include. 

Example:

Liferay 6.1   Liferay 6.0.x

Nested Rules:

Nesting avoids repeating CSS selectors and also keeps the style compact for a certain area of the User Interface. 

Let us have a look at an example of custom.css code for navigation part in themes. After using the nesting rule, the code should look like this :

Liferay 6.1     Liferay 6.0.x

Parent Reference:

Parent reference allows you to reference a parent selector  in the case of pseudo-classes like :hover, :link, :visited and :active states.

Liferay 6.1 Liferay 6.0.x

Interpolation:

We can also use SassScript variables in selectors and property names using #{} interpolation. Here’s the example:

Liferay 6.1 Liferay 6.0.x

Liferay6.1 allows you to use Sass which helps the code be simpler for mobile site design. (Thanks to BradleyWood’s solution in this  forum post ). 

As said in Sass , it makes CSS fun again and with Liferay 6.1, you can easily create manageable style sheets and start updating to Liferay 6.1 and use Sass to manage your style sheets  smiley .

 

Friday, January 13, 2012

Upgrading Liferay Portal 6.0 to Liferay Portal 6.1

Upgrading Liferay

Liferay upgrades are fairly straightforward. A consistent set of steps is all you need to follow to upgrade a standard Liferay installation. Things do get more complicated if your organization has used Ext plugins to customize Liferay. It's possible that API changes in the new version will break your existing code. This, however, is usually pretty easy for your developers to fix. Portlet plugins which use Liferay APIs should be reviewed and their services rebuilt against the new release. Theme plugins may require some modifications in order to take advantage of new features, and if they're using Liferay APIs, they should be reviewed. Much effort has been made to make upgrades as painless as possible; however, this is not a guarantee that everything will work without modification. Ext plugins are the most complicating factor in an upgrade, so it is important to test as much as possible.

As a general rule, you can upgrade from one major release to the next major release. For example, you can upgrade directly from Liferay 5.2.x to 6.0.x, but not from 5.2.x to 6.1.x. If you need to upgrade over several major releases, you'll need to run the upgrade procedure for each major release until you reach the release you want. This doesn't mean you need to run the procedure for every point release or service pack; you only need to run the procedure for the major releases. A good practice is to use the latest version of each major release to upgrade your system.

Now that we've gotten the general philosophy of upgrading out of the way, let's outline the procedure you'll undergo for upgrading a Liferay 6.0 installation to a 6.1 installation. If you're running a previous version of Liferay and need to upgrade to 6.0 first, please see the instructions in the previous version of this document.

Upgrading Liferay Portal 6.0 to Liferay Portal 6.1

There are a few things you'll want to prepare before you actually perform the upgrade. Specifically, you'll need to review your image gallery usage, review new Liferay 6.1 defaults, and catalog all the plugins you have installed. After you've performed these three tasks, you'll be ready to upgrade.

Liferay 6.1 introduces a major change to how Liferay handles files. No longer do we have a separate Document Library and Image Gallery; instead, these have been combined into Documents and Media. If you were using Liferay's Image Gallery to store images, these can be migrated over during an upgrade, but you'll have to take some extra steps first.

In Liferay 6.0, you had three ways you could store images in the Image Gallery. You could use the DatabaseHook and store them as BLOBs in the database; you could use the DLHook to store them in the Document Library, or you could use the FileSystemHook to store them in a folder on your server's file system. Before you upgrade, you'll need to set whichever property you were using in your 6.0 portal-ext.properties file, because by default, none of them are enabled in 6.1. Setting one of the properties triggers the migration during the upgrade process. Below are the three properties; you'll need to set only one of them (the one you were using).

image.hook.impl=com.liferay.portal.image.DatabaseHook

image.hook.impl=com.liferay.portal.image.DLHook

image.hook.impl=com.liferay.portal.image.FileSystemHook

The next thing you'll need to look at are the defaults that have changed from 6.0 to 6.1. These are preserved in portal-legacy-6.0.properties in the source. The 6.0 values are:

users.last.name.required=true layout.types=portlet,panel,embedded,article,url,

link_to_layout editor.wysiwyg.portal-web.

docroot.html.portlet.message_boards.edit_message.bb_code.jsp=bbcode

setup.wizard.enabled=false discussion.subscribe.by.default=false

message.boards.subscribe.by.default=false

The 6.1 values have changed to:

users.last.name.required=false layout.types=portlet,

panel,embedded,url,link_to_layout editor.wysiwyg.portal-web.

docroot.html.portlet.message_boards.edit_message.bb_code.jsp=ckeditor_bbcode

setup.wizard.enabled=true discussion.subscribe.by.default=true message.boards.subscribe.by.default=true

If you don't like the defaults, you can change them back in one shot by adding a system property to your JVM's startup. This differs by application servers. In Tomcat, you'd modify setenv.sh / setenv.bat and append the option -Dexternal-properties=portal-legacy-6.0.properties to the environment variable JAVA_OPTS. The scripts setenv.sh or setenv.bat are not delivered with default Tomcat, but do exist in the bundles. If they're there, Tomcat uses them in the startup process, so it's a nice way to separate your own settings from Tomcat's default shell scripts. Alternatively, of course, you can override some or all of them in your portal-ext.properties along with your other overrides.

Finally, you need to take note of any plugins you have installed. Liferay's plugins are usually version-specific, so you'll need to obtain new versions of them for the new release of Liferay. If you have custom plugins created by your development team, they'll need to build, test, and optionally modify them to work with the new release of Liferay. Don't attempt an upgrade without collecting all the plugins you'll need first.

Once you've reviewed your properties and collected all the plugins you'll need, you're ready to follow the upgrade procedure. Remember to back up your system before you begin.

There are two different procedures to upgrade Liferay. The first one, upgrading a Liferay bundle, is the most common. The second procedure is for upgrading a Liferay installation on an application server. We'll go over both.

In both cases, Liferay auto-detects whether the database requires an upgrade the first time the new version is started. When Liferay does this, it upgrades the database to the format required by the new version. In order to perform this task, Liferay must be accessing the database with an ID that can create, drop, and modify tables. Make sure that you have granted these permissions to the ID before you attempt to upgrade Liferay. And, of course, we'll run the risk of overly repeating ourselves: back up your database.

Let's look at upgrading a bundle, which is the easiest upgrade path.

Upgrading a bundle

If you're running a Liferay bundle, the best way to do the upgrade is to follow the steps below. The new Liferay is installed in a newer version of your bundle runtime. For example, the Liferay/Tomcat bundle for 6.0 used Tomcat 6 by default; the 6.1 bundle uses Tomcat 7. Though there is a Tomcat 6 bundle of Liferay 6.1, that bundle also uses a newer release of Tomcat than the one from 6.0. This is the case for all runtimes that Liferay supports. We generally recommend that you use the latest version of your runtime bundle, as it will be supported the longest.

  1. Obtain the new bundle. Unzip the bundle to an appropriate location on your system.
  2. Copy your portal-ext.properties file and your data folder to the new bundle.
  3. Review your portal-ext.properties file as described above. If you were using the Image Gallery, make the necessary modifications so that your files are migrated to Documents and Media. Review the new defaults and decide whether you want to use them. Review any other modifications you've made.
  4. Start your application server. Watch the console as Liferay starts: it should upgrade the database automatically.
  5. When the upgrade completes, install any plugins you were using in your old version of Liferay. Make sure you use the versions of those plugins that are designed for Liferay 6.1. If you have your own plugins, your development team will need to migrate the code in these ahead of time and provide .war files to you.
  6. Browse around in your new installation and verify that everything is working. Have your QA team test everything. If all looks good, you can delete the old application server with the old release of Liferay in it from the bundle directory. You have a backup of it anyway, right?

As you can see, upgrading a bundle is generally pretty simple. But not everybody can use bundles: sometimes, specific application servers or application server versions are mandated by the environment you're in or by management. For this reason, Liferay also ships as an installable .war file that can be used on any supported application server.

Upgrading using a .war file

Running a manual upgrade is almost as easy as upgrading a bundle:

  1. Obtain the Liferay Portal .war file and the dependency .jars archive.
  2. Copy your customized portal-ext.properties file to a safe place and review it as described above, making all the appropriate changes.
  3. Undeploy the old version of Liferay and shut down your application server.
  4. Copy the new versions of Liferay's dependency .jars to a location on your server's class path, overwriting the ones you already have for the old version of Liferay. This location is documented for your application server in chapter 11.
  5. Deploy the new Liferay .war file to your application server. Follow the deployment instructions in chapter 11.
  6. Start (or, if your app server has a console from which you've installed the .war, restart) your application server. Watch the console as Liferay starts: it should upgrade the database automatically. Verify that your portal is operating normally, and then install any plugins you were using in your old version of Liferay. Make sure you use the versions of theose plugins that are designed for Liferay 6.1. If you have your own plugins, your development team will need to migrate the code in these ahead of time and provide .war files to you.
  7. Browse around in your new installation and verify that everything is working. Have your QA team test everything. If all looks good, you're finished.

That's all there is to it. Most everything is handled by Liferay's upgrade procedure. Note that as stated above, if you have to upgrade over several Liferay versions, you will need to repeat these steps for each major release.

Thursday, January 12, 2012

New Liferay 6.1 Feature - Remote Development from Liferay IDE

I'd like to mention one of the new features in the upcoming release of Liferay 6.1, which is the remote development and deployment support from Liferay IDE.  The idea is that with this feature you can build Liferay projects in Liferay IDE like normal but instead of the Liferay server running locally it is running on a remote host.  This is accomplished through two things, 1) new remote server adapter in Liferay IDE and 2) Server manager plugin (new in 6.1) deployed to the remote Liferay server 

This feature will be officially available with the Liferay 6.1 CE release, but since it is only in beta, now is a good time to try it out and give feedback.

For those who wish to try this out here are the steps to setting it up.

1. Download the Liferay Portal 6.1 Beta4 Tomcat bundle (Only tomcat is supported in beta4,  both JBoss and Glassfish will be supported by 6.1 GA release)

2. Start Tomcat bundle on remote host (or even localhost)

3. Download the Server Manager plugin for 6.1 Beta4 (This plugin provides the API needed by Liferay IDE to administer remote plugins)

4. Deploy Server Manager plugin into remote Tomcat server

5. Install the latest stable or nightly  version of Liferay IDE (links are updatesite URLs to use in Eclipse install manager)

6. Create a new server in Liferay IDE, File > New > Server > Liferay > Remote Liferay Server

7. Specify a local tomcat bundle for the runtime stub (this is needed for libraries to compile project)

8. Specify the IP address of remote tomcat server along with Omni admin username/password

Once you have done this you should see the server console log messages printed in the normal Eclipse console view.

9. Create and deploy Liferay projects as usual then publish (deploy) them to the server, see Liferay IDE getting started guide for a walkthrough of this.

Now when you modify files in your project, every 15 seconds it will publish the delta of any changes to remote server.  However, you can publish changes immediately using the "publish" action in the servers view.  If you want to change the automatic publish rate you can do that in the server configuration editor (double click server in Servers view).

For anyone who decides to try this out and want to give feedback with suggestions or problems please either open a JIRA ticket for the IDE project or enter a new topic on the IDE forums .

Writing Liferay Apps with Web Content Templates

One of the often overlooked features of Liferay's WCM system is the ability to write non-trivial apps using it.  There have been a few blog posts about this, notably Ray Augé's Advanced Web Content Example With AJAX .  In the community, it's great for me because I can quickly create interesting visualizations of community data and share it with you immediately.  There are some pros and cons to this approach:

Benefits:

  • No compilation needed - WCM relies on the use of Templates , written in interpreted (i.e. scripted) languages such as Velocity Templates .  This means you can quickly make a change and see your results quickly.
  • No deployment needed - since Web Content isn't a java portlet, you don't need to re-deploy.   More importantly it means you don't have to wait for a website administrator to deploy it if you cannot deploy yourself!
  • You can combine presentation (e.g. HTML/JS/CSS) and logic (e.g. Velocity) into the same template, keeping related code together.

Drawbacks:

  • Velocity is first and foremost a templating/presentation language.  It is not a general purpose computing language, so die hard MVC types will probably dismiss the use of Velocity in this way and call me a heretic/lunatic.  It's great for prototyping though!  
  • Currently, Structures and Templates aren't versioned, and they do not participate in Liferay's Workflow system.  So you can't revert to older (working) versions of templates if you make a mistake.
  • No compilation needed - so it's not as fast as the native bytecode that would result from the equivalent java source code.  But it's still quite fast.
  • Velocity and other scripting languages have weird quirks that often cannot be caught except through trial and error, and limitations (e.g. no use of generics) that compiled/strongly typed languages have.
  • You can combine presentation (e.g. HTML) and logic (e.g. Velocity) into the same template :)

In my opinion, Liferay WCM is a very good solution for app prototyping or for non-trivial apps that don't have tons of logic or page flows in them.  You have already seen an example of this in the Community Activity Map , and the example I use below forms the basis for the Hot Topics app that you can now see on liferay.org .

Basic Template Template

To get started creating an app of this nature, you need to start with simple Web Content Template that is itself a template:

#if ($request.lifecycle == "RENDER_PHASE")
 ## This phase will handle the presentation code (i.e. HTML/CSS/JS). Any calls ## to the ${request.resource-url} will retrieve the result of evaluating the below ## RESOURCE_PHASE below. 
#elseif ($request.lifecycle == "RESOURCE_PHASE")
 ## This phase will handle the AJAX request like a portlet's serveResource() method 
#end

So decide what needs to be executed on the server side, and put it in the RESOURCE_PHASE .  This is typically where most if not all of the business (i.e. non-presentation) logic goes.  Put the presentation logic in the RENDER_PHASE .

Hot Topics Example

For this app, I want to show which threads have the most posts in the last week.  So, I needed to query Liferay's Message Boards.  Since there is no getMostActiveThreadsInTheLastWeek() method (I know.. what's up with that??), I needed a custom query.  This means using Liferay's DynamicQuery feature.  But from Velocity?  Turns out it's not that bad.  Here's the full RESOURCE_PHASE code to create and execute a Dynamic Query, and generate a JSON object as a result which contains the most active threads in the last week:

#set ($portletNamespace = $request.portlet-namespace) #set ($scopeGroupId = $getterUtil.getLong($request.theme-display.scope-group-id)) #if ($request.lifecycle == "RENDER_PHASE") ## bunch of display logic to show the JSON result nicely #elseif ($request.lifecycle == "RESOURCE_PHASE") #set ($logFactory = $portal.getClass().forName('com.liferay.portal.kernel.log.LogFactoryUtil')) #set ($log = $logFactory.getLog('mylog')) #set ($portalBeanLocator = $portal.getClass().forName("com.liferay.portal.kernel.bean.PortalBeanLocatorUtil")) #set ($jsonFactory = $portalBeanLocator.locate("com.liferay.portal.kernel.json.JSONFactoryUtil")) #set ($mbMessageLocalService = $portalBeanLocator.locate("com.liferay.portlet.messageboards.service.MBMessageLocalService.velocity")) #set ($mbThreadLocalService = $portalBeanLocator.locate("com.liferay.portlet.messageboards.service.MBThreadLocalService.velocity")) #set ($calClass = $portal.getClass().forName("java.util.GregorianCalendar")) #set ($mbMessageClass = $portal.getClass().forName("com.liferay.portlet.messageboards.model.MBMessage")) #set ($mbThreadClass = $portal.getClass().forName("com.liferay.portlet.messageboards.model.MBThread")) #set ($dqfu = $portal.getClass().forName("com.liferay.portal.kernel.dao.orm.DynamicQueryFactoryUtil")) #set ($pfu = $portal.getClass().forName("com.liferay.portal.kernel.dao.orm.ProjectionFactoryUtil")) #set ($ofu = $portal.getClass().forName("com.liferay.portal.kernel.dao.orm.OrderFactoryUtil")) #set ($now = $calClass.getInstance()) #set ($weeksago = $calClass.getInstance()) #set ($prevweeks = 0 - $getterUtil.getInteger($period.data)) #set ($V = $weeksago.add(3, $prevweeks)) #set ($q = $dqfu.forClass($mbThreadClass)) #set ($rfu = $portal.getClass().forName("com.liferay.portal.kernel.dao.orm.RestrictionsFactoryUtil")) #set ($groupIdCriteria = $rfu.ne("categoryId", $getterUtil.getLong("-1"))) #set ($V = $q.add($groupIdCriteria)) #set ($groupIdCriteria = $rfu.eq("groupId", $getterUtil.getLong($scopeGroupId))) #set ($V = $q.add($groupIdCriteria)) #set ($companyIdCriteria = $rfu.eq("companyId", $getterUtil.getLong($companyId))) #set ($V = $q.add($companyIdCriteria)) #set ($statusCriteria = $rfu.eq("status", 0)) #set ($V = $q.add($statusCriteria)) #set ($lastPostDateCriteria = $rfu.between("lastPostDate", $weeksago.getTime(), $now.getTime())) #set ($V = $q.add($lastPostDateCriteria)) #set ($V = $q.setProjection($pfu.property("threadId"))) #set ($res1 = $mbMessageLocalService.dynamicQuery($q)) #set ($q2 = $dqfu.forClass($mbMessageClass)) #set ($inCriteria = $rfu.in("threadId", $res1)) #set ($V = $q2.add($inCriteria)) #set ($createDateCriteria = $rfu.between("createDate", $weeksago.getTime(), $now.getTime())) #set ($V = $q2.add($createDateCriteria)) #set ($V = $q2.setProjection($pfu.projectionList().add($pfu.groupProperty("rootMessageId")).add($pfu.alias($pfu.rowCount(), "msgCount")))) #set ($V = $q2.addOrder($ofu.desc("msgCount"))) #set ($V = $q2.setLimit(0, 7)) #set ($res2 = $mbMessageLocalService.dynamicQuery($q2)) #set ($jsonArray = $jsonFactory.createJSONArray()) #foreach ($msgSum in $res2) #set ($rootMsgId = $msgSum.get(0)) #set ($msgCount = $msgSum.get(1)) #set ($subject = $mbMessageLocalService.getMessage($rootMsgId).getSubject()) #set ($jsonObject = $jsonFactory.createJSONObject()) #set ($V = $jsonObject.put("subject", $stringUtil.shorten($htmlUtil.escape($subject), 55))) #set ($V = $jsonObject.put("msgid", $rootMsgId)) #set ($V = $jsonObject.put("msgCount", $msgCount)) #set ($V = $jsonArray.put($jsonObject)) #end { "jsonArray": $jsonArray } #end 

Details

There are many things going on here:

Velocity Debugging/Logging

 #set ($logFactory = $portal.getClass().forName('com.liferay.portal.kernel.log.LogFactoryUtil')) #set ($log = $logFactory.getLog('mylog')) 
This gives me a way to debug the code by looking at the server log (if you are using this kind of app so that you can bypass your website admin, chances are you won't have access to the server logs, so this won't help you).  To emit debug info, I can do things like $log.error($msgCount) or $log.error("Hi There").
 

Creating references for arbitrary JVM classes

 #set ($calClass = $portal.getClass().forName("java.util.GregorianCalendar")) 
This allows me to create references to any class known in the JVM for doing things like calling static methods, etc.  Many of these are needed for constructing Dynamic Queries.
 

Calculating Now and a Week Ago

 #set ($now = $calClass.getInstance()) #set ($weeksago = $calClass.getInstance()) #set ($prevweeks = 0 - $getterUtil.getInteger($period.data)) #set ($V = $weeksago.add(3, $prevweeks)) 
This creates Calendar objects representing the current time, and a week ago.  Note that the number of weeks is specified in a web content structure using the period structure element.
 
The rest of the code constructs two dynamic queries:
  • The first one ( $q ) queries for MBThread entities that have a categoryId of -1 (MBThreads that do not have a categoryId of -1 are not threads from the message boards portlet, instead they are threads for things like comments on document library entries, etc).  The query also includes other criteria, like groupId/companyId must match the "current" groupId/companyId of the site in which the web content is placed, the status must be 0 (indicating it is an approved (i.e. not draft or deleted) entry), and most importantly the lastPostDate must be between my desired time period start and end.  Finally, I am not interested in all of the MBThread entity - I just need the threadId .  So my query includes a Projection that only returns the threadId .
  • The second query ( $q2 ) queries for all MBMessage entities that match my new critieria: they must have a threadId of one of the threads identified in the first query (hence the in criteria), and the message's createDate must also be between my start/end dates.  This is to avoid counting messages in the thread that occured before the cutoff dates.  Finally, this gem:
 #set ($V = $q2.setProjection($pfu.projectionList().add($pfu.groupProperty("rootMessageId")).add($pfu.alias($pfu.rowCount(), "msgCount")))) #set ($V = $q2.addOrder($ofu.desc("msgCount"))) #set ($V = $q2.setLimit(0, 7)) 
This creates a projection that is grouped by the rootMessageId , since each message in the same thread will have the same rootMessageId (which I eventually use to construct the URL to the message), and includes a count of the messages that match (with an alias defined so I can refer to the row count when specifying the order of the results via addOrder() ).  I also limit the results to 7 because I don't want to show any more than that (this is a simple app).  This second query returns a result table that looks like:
 

So after the Dynamic Queries executes, it's just a matter of constructing a JSONObject (and sanitizing/sizing the actual text of the subject of the thread) and returning it.

Liferay WCM and Velocity Gotchas

Velocity is first and foremost a templating/presentation language.  It is not a general purpose computing language, so die hard MVC types will probably dismiss the use of Velocity in this way and call me a heretic/lunatic.  But it also means that some things are hard (or impossible, for example did anyone catch that I hard-coded Calendar.MONTH to be 3 ?  You can't reference static member variables of a class unless it is already part of the Velocity context in which a template is evaluated).  There are many other perils awaiting the adventurous Velocity coder.  I learned many things through trial and error (and the help of my IRC friends on the #liferay channel!).  Here are some more:
 
  • Don't forget to use $var instead of var .  If you forget the $, you won't get syntax errors, just silent errors and half of your code will next execute.
  • If you can use an intelligent IDE (like IntelliJ IDEA or Eclipse ) and its Velocity syntax checking, do it!  I saved tons of time by using IntelliJ and declaring variable types, which allowed for autocompletion and type checking.  For example, I had tons of these:
#* @vtlvariable name="request" type="java.util.Map" *# #* @vtlvariable name="httpUtil" type="com.liferay.portal.kernel.util.HttpUtil" *# #* @vtlvariable name="htmlUtil" type="com.liferay.portal.kernel.util.HtmlUtil" *# #* @vtlvariable name="obc" type="com.liferay.portal.util.comparator.UserLastNameComparator" *# #* @vtlvariable name="serviceLocator" type="com.liferay.portal.velocity.ServiceLocator" *# #* @vtlvariable name="teamLocalService" type="com.liferay.portal.service.TeamLocalServiceUtil" *# #* @vtlvariable name="mbMessageLocalService" type="com.liferay.portlet.messageboards.service.MBMessageLocalServiceUtil" *# #* @vtlvariable name="mbThreadLocalService" type="com.liferay.portlet.messageboards.service.MBThreadLocalServiceUtil" *#
  • Any time you access things from the ${request.theme-display} , or access one of your WCM structure fields that represent a number (but are of type "Text" in the template), they are probably not of the the type that you want.  You need to use generous amounts of $getterUtil.getXXX calls to make sure.  For example, 
#set ($scopeGroupId = $getterUtil.getLong($request.theme-display.scope-group-id)) 
will work (and makes  $scopeGroupId a Long), whereas
#set ($scopeGroupId = $request.theme-display.scope-group-id) 
Will results in a $scopeGroupId that is not a Long, and so if you pass it in to a method that is expecting a Long, it won't work, and will probably silently fail and you'll be befuddled.
  • If you want to create a new instance of a class (e.g. a HashMap) you can'y say new HashMap() .  Velocity does not know what " new " is - after all, you're using a presentation/templating language, not Java!   But we're using it for more than display.  So as a workaround you can do things like $portal.getClass().forName("java.util.HashMap").newInstance() .
  • Accessing elements of an array cannot be done using $array[0] .  You have to use  $array.get(0) .
 

The Full Source to Hot Topics

Here's the full source, including my display code, and my IntelliJ variable declarations (which may have some unnecessary declarations, but I use this block for other stuff too).  If you spot errors or poor coding technique or whatever else, please let me know so I can learn!
#* @vtlvariable name="portletNamespace" type="java.lang.String" *# #* @vtlvariable name="portal" type="com.liferay.portal.util.Portal" *# #* @vtlvariable name="getterUtil" type="com.liferay.portal.kernel.util.GetterUtil" *# #* @vtlvariable name="stringUtil" type="com.liferay.portal.kernel.util.StringUtil" *# #* @vtlvariable name="max-members" type="com.liferay.portlet.journal.util.TemplateNode" *# #* @vtlvariable name="team-name" type="com.liferay.portlet.journal.util.TemplateNode" *# #* @vtlvariable name="section-members" type="com.liferay.portlet.journal.util.TemplateNode" *# #* @vtlvariable name="groupId" type="java.lang.String" *# #* @vtlvariable name="sectionMembers" type="java.lang.String" *# #* @vtlvariable name="locale" type="java.util.Locale" *# #* @vtlvariable name="companyId" type="java.lang.String" *# #* @vtlvariable name="scopeGroupId" type="java.lang.String" *# #* @vtlvariable name="sectionName" type="java.lang.String" *# #* @vtlvariable name="section-name" type="com.liferay.portlet.journal.util.TemplateNode" *# #* @vtlvariable name="params" type="java.util.LinkedHashMap" *# #* @vtlvariable name="users" type="java.util.List" *# #* @vtlvariable name="user" type="com.liferay.portal.model.User" *# #* @vtlvariable name="themeDisplay" type="com.liferay.portal.theme.ThemeDisplay" *# #* @vtlvariable name="languageUtil" type="com.liferay.portal.kernel.language.LanguageUtil" *# #* @vtlvariable name="request" type="java.util.Map" *# #* @vtlvariable name="httpUtil" type="com.liferay.portal.kernel.util.HttpUtil" *# #* @vtlvariable name="htmlUtil" type="com.liferay.portal.kernel.util.HtmlUtil" *# #* @vtlvariable name="obc" type="com.liferay.portal.util.comparator.UserLastNameComparator" *# #* @vtlvariable name="serviceLocator" type="com.liferay.portal.velocity.ServiceLocator" *# #* @vtlvariable name="teamLocalService" type="com.liferay.portal.service.TeamLocalServiceUtil" *# #* @vtlvariable name="mbMessageLocalService" type="com.liferay.portlet.messageboards.service.MBMessageLocalServiceUtil" *# #* @vtlvariable name="mbThreadLocalService" type="com.liferay.portlet.messageboards.service.MBThreadLocalServiceUtil" *# #* @vtlvariable name="imageToken" type="com.liferay.portal.kernel.servlet.ImageServletToken" *# #* @vtlvariable name="userLocalService" type="com.liferay.portal.service.UserLocalServiceUtil" *# #* @vtlvariable name="groupIdCriteria" type="com.liferay.portal.kernel.dao.orm.Criterion" *# #* @vtlvariable name="groupIdProp" type="com.liferay.portal.kernel.dao.orm.Property" *# #* @vtlvariable name="threadMap" type="java.util.Map<java.lang.Long, java.lang.Integer>" *# #* @vtlvariable name="q" type="com.liferay.portal.kernel.dao.orm.DynamicQuery" *# #* @vtlvariable name="q2" type="com.liferay.portal.kernel.dao.orm.DynamicQuery" *# #* @vtlvariable name="rfu" type="com.liferay.portal.kernel.dao.orm.RestrictionsFactoryUtil" *# #* @vtlvariable name="pfu" type="com.liferay.portal.kernel.dao.orm.ProjectionFactoryUtil" *# #* @vtlvariable name="ofu" type="com.liferay.portal.kernel.dao.orm.OrderFactoryUtil" *# #* @vtlvariable name="msgs" type="java.util.List<com.liferay.portlet.messageboards.model.MBMessage>" *# #set ($portletNamespace = $request.portlet-namespace) #set ($scopeGroupId = $getterUtil.getLong($request.theme-display.scope-group-id)) #if ($request.lifecycle == "RENDER_PHASE") <body onload="${portletNamespace}getTable();"> <article> <h1 class="section-heading section-heading-b"> <div>$title.data</div> <div class="section-heading-hr"></div> </h1> <div id='${portletNamespace}tablediv' style='width: 85%;'><!-- --></div> </article> </body> <script type="text/javascript"> var ${portletNamespace}table = new Object(); var ${portletNamespace}ICON = '<img class="icon" \ src="http://my-liferay-site-cdn.com/osb-theme/images/spacer.png" \ alt="Message Boards" title="Message Boards" \ style=" background-image: url(\'/html/icons/_sprite.png\');\ background-position: 50% -736px; \ background-repeat: no-repeat; height: 16px; width: 16px;">'; function ${portletNamespace}drawChart() { var html = '<div> \ <table style="margin-bottom:0em;"> \ <tbody>'; for (i = 0; i < ${portletNamespace}table.length; i++) { html += '<tr> \ <td class="portlet-icon" style="padding-right:6px;"> \ <table style="margin-top:-4px; margin-bottom:0px;">\ <tr>\ <td>\ <span>' + ${portletNamespace}ICON + '</span> \ </td>\ </tr>\ <tr>\ <td>\ <span style="color:#908E91; font-size:9px;">'+ ${portletNamespace}table[i].msgCount + '</span>\ </td>\ </tr>\ </table>\ </td> \ <td>\ <div>\ <h3 class="txt-n fs-11 m-0 o-h">\ <span class="display-b m-tn3 m-b6">\ <a href="/community/forums/-/message_boards/message/' + ${portletNamespace}table[i].msgid +'">'+ ${portletNamespace}table[i].subject + '</a>\ </span>\ </h3>\ </div>\ </td>\ </tr>'; } html += '</tbody>\ </table>\ </div>'; document.getElementById('${portletNamespace}tablediv').innerHTML = html; } function ${portletNamespace}getTable() { AUI().use( "aui-base", "aui-io-plugin", "aui-io-request", function(A) { A.io.request( '${request.resource-url}', { data: { }, dataType: "json", on: { success: function(event, id, obj) { var responseData = this.get("responseData"); ${portletNamespace}table = responseData.jsonArray || []; ${portletNamespace}drawChart(); }, failure: function(event, id, obj) { } } } ); } ); } </script> #elseif ($request.lifecycle == "RESOURCE_PHASE") #set ($logFactory = $portal.getClass().forName('com.liferay.portal.kernel.log.LogFactoryUtil')) #set ($log = $logFactory.getLog('mylog')) #set ($portalBeanLocator = $portal.getClass().forName("com.liferay.portal.kernel.bean.PortalBeanLocatorUtil")) #set ($jsonFactory = $portalBeanLocator.locate("com.liferay.portal.kernel.json.JSONFactoryUtil")) #set ($mbMessageLocalService = $portalBeanLocator.locate("com.liferay.portlet.messageboards.service.MBMessageLocalService.velocity")) #set ($mbThreadLocalService = $portalBeanLocator.locate("com.liferay.portlet.messageboards.service.MBThreadLocalService.velocity")) #set ($calClass = $portal.getClass().forName("java.util.GregorianCalendar")) #set ($mbMessageClass = $portal.getClass().forName("com.liferay.portlet.messageboards.model.MBMessage")) #set ($mbThreadClass = $portal.getClass().forName("com.liferay.portlet.messageboards.model.MBThread")) #set ($dqfu = $portal.getClass().forName("com.liferay.portal.kernel.dao.orm.DynamicQueryFactoryUtil")) #set ($pfu = $portal.getClass().forName("com.liferay.portal.kernel.dao.orm.ProjectionFactoryUtil")) #set ($ofu = $portal.getClass().forName("com.liferay.portal.kernel.dao.orm.OrderFactoryUtil")) #set ($now = $calClass.getInstance()) #set ($weeksago = $calClass.getInstance()) #set ($prevweeks = 0 - $getterUtil.getInteger($period.data)) #set ($V = $weeksago.add(3, $prevweeks)) #set ($q = $dqfu.forClass($mbThreadClass)) #set ($rfu = $portal.getClass().forName("com.liferay.portal.kernel.dao.orm.RestrictionsFactoryUtil")) #set ($groupIdCriteria = $rfu.ne("categoryId", $getterUtil.getLong("-1"))) #set ($V = $q.add($groupIdCriteria)) #set ($groupIdCriteria = $rfu.eq("groupId", $getterUtil.getLong($scopeGroupId))) #set ($V = $q.add($groupIdCriteria)) #set ($companyIdCriteria = $rfu.eq("companyId", $getterUtil.getLong($companyId))) #set ($V = $q.add($companyIdCriteria)) #set ($statusCriteria = $rfu.eq("status", 0)) #set ($V = $q.add($statusCriteria)) #set ($lastPostDateCriteria = $rfu.between("lastPostDate", $weeksago.getTime(), $now.getTime())) #set ($V = $q.add($lastPostDateCriteria)) #set ($V = $q.setProjection($pfu.property("threadId"))) #set ($res1 = $mbMessageLocalService.dynamicQuery($q)) #set ($q2 = $dqfu.forClass($mbMessageClass)) #set ($inCriteria = $rfu.in("threadId", $res1)) #set ($V = $q2.add($inCriteria)) #set ($createDateCriteria = $rfu.between("createDate", $weeksago.getTime(), $now.getTime())) #set ($V = $q2.add($createDateCriteria)) #set ($V = $q2.setProjection($pfu.projectionList().add($pfu.groupProperty("rootMessageId")).add($pfu.alias($pfu.rowCount(), "msgCount")))) #set ($V = $q2.addOrder($ofu.desc("msgCount"))) #set ($V = $q2.setLimit(0, 7)) #set ($res2 = $mbMessageLocalService.dynamicQuery($q2)) #set ($jsonArray = $jsonFactory.createJSONArray()) #foreach ($msgSum in $res2) #set ($rootMsgId = $msgSum.get(0)) #set ($msgCount = $msgSum.get(1)) #set ($subject = $mbMessageLocalService.getMessage($rootMsgId).getSubject()) #set ($jsonObject = $jsonFactory.createJSONObject()) #set ($V = $jsonObject.put("subject", $stringUtil.shorten($htmlUtil.escape($subject), 55))) #set ($V = $jsonObject.put("msgid", $rootMsgId)) #set ($V = $jsonObject.put("msgCount", $msgCount)) #set ($V = $jsonArray.put($jsonObject)) #end { "jsonArray": $jsonArray } #end 

 

source

Changes in Social Equity in Liferay 6.1

There have been some pretty interesting and important changes regarding Social Equity for Liferay 6.1. While some of the changes are conceptual and others are additional features, there is one thing that is very important: there has been a change of API.

The new equity system is now part of the social activity framework. This is not just a naming concept. As part of the change, several functionalities were moved to different services and the original Social Equity services were removed. The whole framework got its own XML descriptor (formerly activities were configured in resource actions descriptors) that can be used to define activities and everything else related to them. This new descriptor can be used to extend already configured activities and can be used in plugins as well.

The Goal of the New System

While the main goal of being able to measure participation and contribution in a communty hasn't changed, there are some differences how these values are created and maintained. The new social equity system is now part of a broader scoped social activity framework that is designed to log certain activities as well as provide numerical instruments to monitor actvities or simply to provide the means for statistical analysis. 

Activity Counters

This system provides the basis for an extendable metrics system for assets and users.

Statistical Periods

One of the biggest conceptual changes is the introduction of the counter period. Values no longer degrade, rather there is a configurable time interval where points are aggregated resulting in a "current" value for each of these periods. A total value is also maintained. This method suits the human thinking more than the former method of degrading values. We are more used to thinking in periods. We are used to having "employee of the month" or values projected to a certain year - such as balance sheets. 

Equity Counters

The old social equity scores are mostly still there. Fundamentally these values are just counters. Of course there are always those more equals among equals, so equity counters have some special treatment. In addition to the limit system that has been implemented for normal counters these counters have some other checks that for example guard against an asset owner building up its own asset's popularity and a result his or her own contribution score by doing certain actions on his or her asset. 

API changes

One of the most importart API changes is that the triggering of an equity action was moved to the social activity service. In fact, adding a social activity now triggers the new social equity service automatically, so you can simply call one of the addActviity() methods in SocualActivityLocalService and if things are configured and social equity is enabled for the group, everything will be fine. If the activity is configured to be logged, the social activity service will log it the same way it's been doing so far, so that it can appear in the activities portlet. Additionally it will call new SocialActivityCounterLocalService to trigger counter processing. 

To get the value of a counter (including participation and contribution values as they are handled as counters) you can use the fetchLatestActivityCounter() method of the SocialActivityCounterLocalService. There are additional helper methods to retrieve a list of counters by other criteria, but probably this is the most important method of the service.

Liferay 6.1 GA1 in the cloud, step by step

Do you want your new Liferay 6.1 GA1, with all these new functionalities: english and spanish , in the cloud?

Follow this simple steps to achieve it, even if you want to use the new Setup Wizard:

  1. Go to Jelastic ( www.jelastic.com ), create an account depending of your location
  2. Create an enviroment: Tomcat + MySQL
  3. Upload portal libraries ( portal-client and dependencies ) to SERVER/lib.
  4. Upload WAR file from sourceforge ( here ) (Jelastic make it easy that uploading, because directly links to that WAR file)
  5. Create portal-ext.properties at SERVER, with this values:
    • resource.repositories.root=${user.home}/
      ENVIROMENT_NAME
      
    • include-and-override=${liferay.home}/portal-setup-wizard.properties
    • liferay.home=${user.home}/
      ENVIROMENT_NAME
      
    • jdbc.default.jndi.name=
      jdbc/LiferayPool
      
  6. Modify SERVER/context.xml file with this values (note that the "mysql-" is very important at db url):
    • <Resource name="
      jdbc/LiferayPool
      " auth="Container" type="javax.sql.DataSource" maxActive="100" maxIdle="30" maxWait="10000" username="
      USER
      " password="
      PASSWORD
      " driverClassName="com.mysql.jdbc.Driver" url="jdbc:mysql://
      mysql-ENVIROMENT_NAME
      .jelastic.com/
      DATABASE_NAME
      ?useEncoding=true&amp;characterEncoding=UTF-8" />
      
  7. Clean catalina.out log to be sure that your installation is successfull.
  8. Restart server.
  9. Check in catalina.out that Liferay starts reading your portal-ext
  10. Browse to you portal: ENVIROMENT_NAME.jelastic.com
  11. Setup Wizard is the first thing you'll see, but as JNDI is configured, we cannot modify database settings. We should go to SERVER/context.xml for database changes.
  12. Set up for portal (name, language, admin credentials), and...
  13. Here it is! Your portal up and running!

Then, you can tune your portal with portal-ext reminding not to modify properties set in this blog.

Importants (and new things):

As you can see, we are taking care of telling setup wizard where to read the new props file (the include-an-override property), and we are also configuring database with JNDI, but of course you can do it with JDBC, just with the usual way:

  • jdbc.default.driverClassName=com.mysql.jdbc.Driver
  • jdbc.default.url=jdbc:mysql://mysql-ENVIROMENT_NAME/database_name?

    useUnicode=true&characterEncoding=UTF-8&useFastDateParsing=false

  • jdbc.default.username=user
  • jdbc.default.password=password

Hope it helps!

Himanshu

Monday, January 9, 2012

Liferay 6.1 CE released - A first brief review!

Liferay 6.1 CE released - A first brief review!

Its a new year for a new chance to get better in all that things we failed or where we couldn´t gettin better in the past time.
This belongs not only to us but also to our near business partners as well. As that said Liferay does worked out my new-year-call in a pretty short time with Liferay 6.1 CE which has been released recently.
Are you still on Liferay 6.X or lower? Lets migrate your solutions for your customers on sunday to Liferay 6.1 because there are many fancy things around there… i smell something good here and i guess this has something todo with Liferay.

Notice: the next week i will create a review about the CMIS-Interface of Liferay 6.1. I will test it against Alfresco! Stay tuned!

At a glance - all is gettin good - at least at the very end. Until then we see how we can make better our current situation.

In Advance: Many “small” killer changes!

First Start Part 1: The user has to configure inital portal propertiesFirstly, if you start Liferay at the very first time you have to talk with Liferay about your taste regarding initial portal-settings which will be stored in a separated file called wizard.properties. Some initial properties like portals-name can be passed on it here.
More actions available through the dockbars. For instance the action \ If you have reached the main page you see at a glance that the UI has been slightly enhanced and looks more clearly since 6.0. If you going through the dockbar-actions on the top you might imagine some new actions right there.
Enhanced Social Equity: The Settings are more feasable as in Liferay 6.0. For those who dont know: this functionality enables more spice & taste to your social life. Each user can get points for several actions wich points out their contribution-value. Especilly the control-panel (the action has been moved to the “Go To” actions) has been completely refined. Many “small eyes changes”(which were surely big in their development) have been done. For instance, the Social Equity feature says more clearly what settings we can change here and which consequenes collaboration-actions can have on user-listings (Top Users Dashlet, Ranking etc.).
A new nice feature is the ability to get a first imaginatine on how REST-Services can be called. Therfore Liferay introduces a developer-page where you can see and test REST-Services directly in the browser.
Web Service Dictionary enables you to see directly in the browser which REST-Calls exists, how you can use these service for your own AND ...... performing test through a WEB-GUI. Do you want any more? ;-)




Do Document-Management, hurry up!

As im a longterm ECM-Specialist my personal passion belongs to document-mangement. I just loving to increase productivity, lower overhead on customersite. Now you are able to perform better document-management practices with Liferay. You are now able to create custom datatypes with custom metadata. Your metadata can have text, date, number, associations as attributes with multilanguage support - its just the Liferay-Way! Managing own datatypes, properties and metadata-sets (like Alfresco-Aspects). See in the next photo the result of that new document type in real ...


Look at the first picture. Ive created my own datatype called “Offer” with my own attributes. On applying this documenttype on a new document you can see in the image how it feels like: ... my own document type ... without an coding.
As you can see - a webpreview will be created as well. Do you want any more for Liferay 6.1?

Sunday, January 8, 2012

Liferay 6.1 CE released

Liferay Logical Architecture

Liferay Architecture

Enterprise Layer

The enterprise layer forms the top layer of services and components that are grouped into taxonomies which support and realize enterprise functions such as Portal Management, Content Management, Workflow Management, Document Management, User Management, Security Management.  The Enterpise Layer is also comprised of inter related service components and features such as Personalization, Collaboration, Social Networking, Delivery Channels, Virtualization and Tunneling Servlets. These form the basic backbone or core enterprise features of Liferay.

Service Layer

Liferay follows a Model Driven Architecture approach.  In the traditional sense it requires a Platform Independent Model (PIM) from which we derive and Platform Specific Model (PSM) and subsequently generate implementation logic.  In Liferay however, we begin with a Domain Specific Model(DSM) since the root model is only specific to Liferay domain and it defines all the nouns of the system with their interactions and is translated to a Platform Specific Model(PSM) which constitutes the EJB’s, Spring Framework, Hibernate Layer, Web Services and then generates the implementation classes by extending the appropriate services.  This is made possible due to the implementation of Service Builder which is the most integral tool provided by Liferay and enforces the same standards throughout.  The services builder is the Model Driven Transformation (MDT) Tool in this context.

 

Extensions Framework

Liferay Provides extensions framework by use of an Extension Environment and Plugins Framework.  The extension environment has the same directory structure of the Liferay and was implemented by overriding the existing source files by placing them in the same path.  The design also incorporated multiple –ext.properties file to change default settings of the portal by overriding desired properties in the –ext.properties file.  The best approach is the hot deployable plugin framework which provided a number of wizards to construct portlets, themes, layouts and hooks or interceptors, and now provides an ext-plugin which provides a similar offset by only replicating those flies that require modification without the entire portal content.  The Plugins approach provides both an IDE and command line interface.  The IDE takes a wizard approach to create portlets, themes, layouts and hooks.  The hooks are basically interceptors can be categorized into model hooks, JSP hooks, properties hook, event hooks.  Model hooks deal with interception of entity actions in services, JSP hooks are provided to dynamically modify Liferay JSP pages, the event hook is used to intercept portal events, property hook is used to update the Liferay properties.  The ext-hook plugin is a new feature than provides changes to the Liferay structure and can be hot deployed at runtime.

Logical Architecture of Liferay

Liferay supports Windows, Mac and Linux OS.  JRE is installed on the supported OS to host the JVM.  An application server is required to contain the Liferay instance.  The officially supported servers include, but not limited to Apache Tomcat, Glassfish, Geronimo, Jetty, JOnAS, JBoss, and Resin.  Most of these servers are available as bundled versions for download and are deployed in the JVM container.  The server provides connectivity and interoperability using an Enterprise Service Bus (ESB), and there are multiple services offered by the servers which are leveraged by Liferay.  Some of the services, which are primarily used by Liferay on the Application server, include the following: JNDI, JDBC, JTS, JMS, JAAS, JDO, JWS, JSP/Servlets, JavaMail.  Applications can be deployed on the server like SOLR Search engine, or DROOLS Rule Engine, or Tunneling Servlets which can further provide extension or integration of external applications with Liferay.  Liferay uses a number of technologies at its core to offer the various services.  These technologies include EJB, Hibernate, Spring and JBPM.  Liferay implements Lucene Search Engine by default and can be configured to extend the SOLR Search Engine which is built on Lucene to extend capabilities to provide clustering, faceted search, filtering with additional enhancements and scalability.   A Portlet Bridge is provided to deploy JSR 168/286 portlets and supports RIA applications.  Liferay contains Language adaptors such as for Python, Ruby and PHP which allows easy integration.
The Administration Kernel provides the base framework for integration and support of all modules, with tooling support, wizards, service providers, listeners and runtime configuration parameters to tweak the application server in runtime mode.  The services builder provides the basic framework to construct and deploy the services using a Model Driven Development (MDD) approach.  The portlet plug-in leverages on the portlet bridge to provide dynamically generated portlets to the end users and enhanced RIA integration.  The hooks plug-in provide convenient access to intercept and alter the services and functionality of the Liferay instance in a standardized approach.By leveraging on all the services and features extended by Liferay, a robust Enterprise Services layer resides on atop of this providing extended solutions ranging from Portal Management, Web Content Management, Enterprise Content Management, Document Management, User Management, Workflow Management, Security Management.  These in turn provide features such as Personalization, Collaboration, Virtualization, Social Networking and integrates Dynamic Delivery Channels, and Tunneling Services.
 Liferay Logical Architecture Diagram


Friday, January 6, 2012

Add search in theme in LIFERAY

Steps to add search in theme:

1. Open file [LIFRAY_SDK]/themes/{theme-name}/docroot/_diffs/templates/portal_normal.vm

2. Add $theme.search() at your desired location.

3. Build and deploy the theme.

4. A search text box will appear where the code was placed
Steps to customize the search:

In case you need to customize the search functionality to search only specific content types, follow the steps below.

1. Open file [LIFERAY_HOME]/tomcat-6.0.26/webapps/ROOT/WEB-INF/classes/portal-ext.properties

2. Add following lines:

com.liferay.portlet.blogs.util.BlogsOpenSearchImpl=true

com.liferay.portlet.bookmarks.util.BookmarksOpenSearchImpl=true

com.liferay.portlet.calendar.util.CalendarOpenSearchImpl=true

com.liferay.portlet.directory.util.DirectoryOpenSearchImpl=true

com.liferay.portlet.documentlibrary.util.DLOpenSearchImpl=true

com.liferay.portlet.imagegallery.util.IGOpenSearchImpl=true

com.liferay.portlet.journal.util.JournalOpenSearchImpl=true

com.liferay.portlet.messageboards.util.MBOpenSearchImpl=true

com.liferay.portlet.wiki.util.WikiOpenSearchImpl=true

3. Change values to “false” for those you do not want to include in search result.

For example, to search only “Web Content”, leave “com.liferay.portlet.journal.util.JournalOpenSearchImpl” to “true” and make other values to false.

Enjoy the customized search in Liferay :)

Thursday, January 5, 2012

orderable search container in liferay by example

Liferay provide us with many good taglib out of them search container is one of them.It is widely used taglib. For simple understanding of search container please check Wiki here is the link Wiki Search Container. One thing that article is missing how to implement ordering using. For that we will follow few step and will do a simple example.
Step 1: Read the wiki article Search Container.
Step 2:Now write the below code in your jsp.

<%
PortalPreferences portalPrefs = PortletPreferencesFactoryUtil.getPortalPreferences(request);
String orderByCol = ParamUtil.getString(request, "orderByCol");

String orderByType = ParamUtil.getString(request, "orderByType");

if (Validator.isNotNull(orderByCol) && Validator.isNotNull(orderByType)) {
portalPrefs.setValue("KK_3", "kk-order-by-col", orderByCol);
portalPrefs.setValue("KK_3", "kk-order-by-type", orderByType);

} else {

orderByCol = portalPrefs.getValue("KK_3", "kk-order-by-col", "name");
orderByType = portalPrefs.getValue("KK_3", "kk-order-by-type", "asc");

}

OrderByComparator orderByComparator = AddressBookUtil.getAddressBookOrderByComparator(orderByCol, orderByType);
%>


Now I will explain what is happening we are getting the column on which we have to do ordering in orderByCol and order asc or desc in orderByType.
In line portalPrefs.setValue("KK_3", "kk-order-by-col", orderByCol);
here "KK_3" is namespace , "kk-order-by-col" is key and orderByCol is value.
In last line OrderByComparator orderByComparator = AddressBookUtil.getAddressBookOrderByComparator(orderByCol, orderByType);
For this you need to write a comparator , I have written it in com.ext.portlet.addressBook.util.AddressBookUtil and write this code


public class AddressBookUtil {

/**
*
* @param orderByCol
* @param orderByType
* @return
*/

public static OrderByComparator getAddressBookOrderByComparator(
String orderByCol, String orderByType) {

boolean orderByAsc = false;

if (orderByType.equals("asc")) {
orderByAsc = true;
}

OrderByComparator orderByComparator = null;

if (orderByCol.equals("name")) {
orderByComparator = new AddressBookNameComparator(orderByAsc);
} else if (orderByCol.equals("status")) {
orderByComparator = new AddressBookStatusComparator(orderByAsc);
}

return orderByComparator;
}

}

Now in same package write AddressBookNameComparator and AddressBookStatusComparator if you want you can write any where else and do the required imports. Below is the code for AddressBookNameComparator

public class AddressBookNameComparator extends OrderByComparator {

public static String ORDER_BY_ASC = "name ASC";

public static String ORDER_BY_DESC = "name DESC";

public AddressBookNameComparator() {
this(false);
}

public AddressBookNameComparator(boolean asc) {
_asc = asc;
}

public int compare(Object obj1, Object obj2) {
AddressBook project1 = (AddressBook) obj1;
AddressBook project2 = (AddressBook) obj2;

int value = project1.getName().toLowerCase().compareTo(
project2.getName().toLowerCase());

if (_asc) {
return value;
} else {
return -value;
}
}

public String getOrderBy() {
if (_asc) {
return ORDER_BY_ASC;
} else {
return ORDER_BY_DESC;
}
}

private boolean _asc;

}

Do the required imports .Same way you can do for other columns.
Step 3: Use the SearchContainer taglib.

<liferay-ui:search-container
emptyResultsMessage="No Result Found"
orderByCol="<%= orderByCol %>"
orderByType="<%= orderByType %>"
>
<liferay-ui:search-container-results results="<%= AddressBookLocalServiceUtil.getAddressBookList(searchContainer.getStart(), searchContainer.getEnd(),orderByComparator)%>">
total="<%= AddressBookLocalServiceUtil.getAddressBookCount() %>"
/>
<liferay-ui:search-container-row classname="com.ext.portlet.addressBook.model.AddressBook" escapedmodel="<%= true %>">
keyProperty="bookId"
modelVar="addressBook"
>


<liferay-ui:search-container-column-text name="name" orderable="<%= true %>" orderableproperty="name">
>

<%= addressBook.getName() %>

</liferay-ui:search-container-column-text>

<liferay-ui:search-container-column-text name="status" orderable="<%= true %>" orderableproperty="status" property="status">
/>

</liferay-ui:search-container-column-text>


<liferay-ui:search-iterator />
</liferay-ui:search-container-row></liferay-ui:search-container-results>
That is all about orderable search container.If you have read the wiki article then you will easily understand this.

I18N Liferay 6.x

Hi today I will share how to use in built Liferay I18N functionality to your custom portlet in Liferay 6.x

It is very simple to add it , for that we need to modify just 2 files.

1.portlet.xml --- In this file add resource bundle entry.

e.g <resource-bundle>content.Language</resource-bundle>

2.build.xml --- In this file we need to call below target.

<target name="build-lang">

<antcall target="build-lang-cmd">

<param name="lang.dir" value="docroot/WEB-INF/src/content" />

<param name="lang.file" value="Language" />

</antcall>

</target>

Now we need to just add one file with name Language.properties under docroot/WEB-INF/src/content
from build.xml using ant call buil-lang. That it !!!

Wednesday, January 4, 2012

Liferay Search Container

Creating Liferay Search Container GUIs

Time for some basic knowledge:

What does the liferay-ui tld say about the search container? How is it possible to create nice and functional search container GUIS? Read the following and you know more:

The search container is always rendering a set of rows that contain one or more columns to display search results. You may change the way the search container is rendering the rows but in the end it will always be a set of rows. So what we are going to do now will only affect the rows itself. We won't change the search container to display grids or something similar.


The liferay search container is structured as follows:

  1. <liferay-ui:search-container >   
  2. <liferay-ui:search-container-results>  
  3. <!-- Calculate results-->  
  4. </liferay-ui:search-container-results>  
  5. <liferay-ui:search-container-row>  
  6. <!--Create the rows that display the search results-->  
  7. </liferay-ui:search-container-row>  
  8. </liferay-ui:search-container >  


Because we will only care for the rows, we will jump right in and take a look at some of the tags you can use:


search-container-column-text


  1. <liferay-ui:search-container-column-text name="userName" property="userName" />  

The column text tag creates a label filled with a header text (name) and a populated value of the search container entity (property). In our case, the property userName would be taken from our entity and displayed.


search-container-column-jsp


  1. <liferay-ui:search-container-column-jsp path="/your.jsp" align="right" />  

Very powerful! Allows to create a complete JSP that will be displayed in every row. By using this tag you can create your own forms with AlloyUI or standard HTML / Javascript and let the search container do all the magic.

search-container-column-button


  1. <liferay-ui:search-container-column-button align="right" href="<%= yourUrl %>" name='clickMe' />  

Allows to create a button pretty easy and connect it to a URL.


search-container-column-score

  1. <liferay-ui:ratings classPK="<%=user.getUserId()%>" className="<%=User.class.getName() %>" />  
Shows the score for the retrieved object.


These tags allow you to create very fast very powerful search container GUIs. I´ve only touched the surface here and there is a lot more of liferay magic that you can discover. Just take a look at the TLD which you can find here: 


http://code.google.com/p/liferay-book/source/browse/trunk/portlets/library-portlet/docroot/WEB-INF/tld/liferay-ui.tld?spec=svn3&r=3


If you like this tutorial it would be very nice, if you could click on some of the google ads you see on the right side. It helps me run this block and motivates me ;)

If you have any questions, feel free to leave a comment.