Liferay Sync - Release Candidate

Company Blogs February 28, 2012 By James Falkner Staff

 

Liferay Sync Release Candidate 1 is now available [Download]!  Liferay Sync is an add-on product for Liferay 6.1 CE and EE that enables your users to publish and access documents and files from multiple environments, including Windows and Mac OS Desktops, and iOS-based mobile platforms.  You can read more details on its product page.

The initial release earlier this month was a Beta release.   Since then, the Liferay team has worked with our internal and external community to fix bugs and add additional improvements, and you can now download the first release candidate!  For iOS users, the Beta release is available now, but the usual 7-10 day waiting period means that the RC1 build will be available sometime next week, and your iOS device will be sure to tell you about it via its auto-update feature.

Changes since Beta

Many small items have been fixed in this release candidate.  You can browse the full list, but I've highlighted a couple below:

  • Igoring Deletes - if you place a .ignore-deletes file in the root folder on your local client, then deleting local files has no effect on the server, and the files will not be re-downloaded after a client restart.
  • Fixed all known internationalization and filename encoding issues
  • Windows and Mac OS X integration cleanup
  • General usability and stability improvements

The full GA release will happen in late March or early April, after ferreting out any last minute issues with this release candidate.  And for you Android users, I sense a disturbance in the force for you, so stay tuned!

Update on Marketplace

Company Blogs February 11, 2012 By James Falkner Staff

As you know, Liferay announced the Liferay Marketplace with much fanfare last year. In developing the Liferay Marketplace, we had to take an approach different from those that target consumers of mobile devices (such as Apple's wildly successful App Store). We have been feverishly working on its release since then, and are planning to open the marketplace for beta testing in April! This is later than our original date, but I think it will be worth the wait. Through feedback at events, online, and internally, we have decided to delay the release to add in several new enterprise-level features outlined below.

Company Registration and Profiles

One of the features that we’ve been focusing on is the ability for companies and organizations to maintain their company profile on the Marketplace. This profile will allow organizations to establish a presence above and beyond the set of apps they produce. Details such as company logos, descriptions, website links, marketplace activity, and apps will be made available from company profiles.

Company Users

Individual app authors can be associated with a company, such that the apps that they produce are also associated with their company. This is a typical case for companies of all sizes that produce multiple apps. Potentially, one or two
 technical resources can then be assigned to manage the apps on behalf of the company and also manage the company’s user list.

Company Purchases

In many cases, IT departments want to have more control over the purchasing of licenses for apps that are used across the company. Alternatively, they might wish to restrict the set of apps that are allowed within their networks. To that end, on the Liferay Marketplace, IT administrators can bulk-purchase licenses on behalf of the company (and using the company credit card), giving immediate access to these apps by users (employees) associated with the company.

Metrics

Everyone, including Liferay, wants to know how they are doing. For this, fine- grained reports with pretty graphs can be generated for a number of different metrics, including purchases, views, downloads, etc. You can quickly spot trends in your apps and get reports about things like revenue, downloads, etc.

The Rest

All of the other features of Marketplace are more or less as described in the current Marketplace information. There have been some slight tweaks to improve workflows for app developers, and improvements in User Profiles as well.

So please bear with us as we prepare to launch the Liferay Marketplace. It's coming together nicely, and it is an important milestone for the Liferay Platform! We hope you like it!

Introducing Liferay Sync

Company Blogs February 1, 2012 By James Falkner Staff

[中文][Español]

Liferay makes it easy to create compelling websites, publish and share content, and manage documents through its award-winning open web platform.  Today, Liferay is introducing Liferay Sync, an innovative way to extend the reach of your documents and files to desktop and mobile environments.

What is Liferay Sync?

Liferay Sync is an add-on product for Liferay 6.1 CE and EE that enables your users to publish and access documents and files from multiple environments, including Windows and Mac OS Desktops, and iOS-based mobile platforms.  As users add and collaborate on documents and files, Liferay Sync automatically synchronizes them across all configured Sync clients, making documents available for viewing or editing in the native environments in which Liferay Sync is supported.  Liferay Sync is fully integrated into the Liferay Platform --  features such as authentication, versioning, workflow, and social collaboration extend naturally into the supported environments. Liferay Sync also makes documents available while offline -- with automatic synchronization once reconnected.

Downloads

For Mac OS or Windows, visit the Liferay Sync product page, and click Get it Now (on the right-side navigation menu) to download the client application for your desktop environment.  Once installed, follow the on-screen instructions to configure your client to connect to an existing Liferay 6.1 deployment using valid credentials.

For iOS, visit the App Store and search for Liferay, and install the Liferay Sync App.  Once installed, follow the on-screen instructions as above. (Update: We are in the process of working with Apple to get the Liferay Sync iOS app published - we expect it to be available by February 8).

How does it work?

Liferay Sync manages documents and site information through Liferay 6.1's built-in web services.  Clients securely communicate to Liferay using user-supplied credentials, such that each user can only access those documents and sites for which they have permissions.  Changes made through Liferay Sync are immediately available to the rest of the Liferay Platform, including users accessing Liferay through traditional web-based interfaces.

For desktop environments, a new folder structure is created and used for synchronizing files.  Files found therein can be treated as any ordinary file.  Credentials, sync frequency, and other folder options can be configured in-client.  Native desktop notification events keep you abreast of what Sync is doing, and native menu and taskbar integration keep Sync controls within easy reach.

Mobile environments are naturally dependent on the way in which documents are handled.  For iOS, documents are maintained in a file list, and can be viewed by clicking on the files themselves.  External files accessible from other apps can be "opened" using Liferay Sync, thereby dropped into your Sync folder and synchronized across other Sync clients.  "Pulling down" on the Sync file list forces a refresh (automatic sync frequency can be configured as well).

This Liferay Sync release is designed to work with Liferay 6.1 Community Edition and the upcoming Enterprise Edition.  When used with the Enterprise Edition of Liferay, Sync will enable users to synchronize documents and files across all of the sites for which they have access.  

Beta Release

This release is a beta release.  As such, you may come across bugs or unexpected behavior.  Like other Liferay projects and products, we are using our JIRA installation at issues.liferay.com to manage issues. If you find an issue, make sure to read and understand the JIRA Guidelines, and file issues under the new SYNC project.

You may also visit the Liferay Sync forum and post questions or feedback.

What's next?

We hope that the wider Liferay community will find this add-on useful for both CE and EE use cases.  In the coming weeks, we will work with the community to iron out any last minute issues before general availability (GA).  In addition, full documentation will be available for this add-on when the GA release is available.  

Community Roundup

Company Blogs January 19, 2012 By James Falkner Staff

Welcome to my first community roundup of 2012.  Yes, I know I've been slacking big time.  The holiday break, and inevitable pile of work to do post-holiday, has kept me away from the roundup.  But I'm back with a rundown of all that is happening in the Liferay community, so start clickin'!

  • Of course the Big News so far in 2012 is Liferay's release of the 6.1 Community Edition.  Packed with features and improvements, including a new setup wizard, enhanced WCM and user management, and many more.  See some of the external coverage here, here, and here (and here, auf Deutsch).  Also don't miss Seb's first review!  A great start to the new year! (Maven fans - here's your present).
  • One of the things I'd like to see in 2012 is getting back to the source in our open source roots.  In that vain, I've kicked off a new way to be a part of the community: Liferay Community Projects! We are going to elevate these kinds of community-driven open source projects to a higher level of visibility on liferay.org, providing more collaboration tools (like user groups) and project status. If you are interested in starting a new project (or housing an existing project) on liferay.org, fill out the form and get started. 
  • BitNami wasted no time integrating Liferay 6.1 into their set of available stacks.  You can get Liferay as an easy to use installer, a VM image, or an AMI for Amazon EC2!  
  • Late last year, Liferay held it's 2nd annual Liferay Italy Sympsium in the beautiful and historic city of Rome.  Check out the pictures from the venue, and check out SMC's nice video recap and pictures!  Also check out this nice written recap (in Italiano) by Andrea.
  • Speaking of Liferay Symposia, we are gearing up for the 2012 season.  The first one out of the gate will be our first ever in Scandinavia, The Liferay Nordic Symposium in the idyllic city of Stockholm, Sweden.
  • Olaf has published several more Radio Liferay releases, the latest of which features several of our high profile community members discussing their contributions and thoughts on our community. Check out the Radio Liferay feed page for more details and links to the streams.
  • TSG is fresh from Alfresco DevCon and writes up a nice piece on Lessons Learned integrating the two.
  • Liferay's User Groups continue to expand, with new groups beginning in Finland, the UK, and Detroit. Visit the User Groups page to see all the recent activity.
  • Several of you may have heard rumblings about OSGi and Liferay.  Check out this interview with Raymond Augé regarding the Arkadiko project, which is an upcoming Liferay Community Project.  
  • For you Londoners, a first London Liferay Meetup happened on January 19th.  Pics and wrap-ups will hopefully be forthcoming!
  • Liferay strives to keep up with new initiatives in the standards space.  Liferay has of course been invoved in past standards efforts, such as JSR-168, JSR-286, CMIS, and more.  Liferay is once again participating in the (free to all) upcoming OASIS standard around WEMI (Web Experience Management Interoperability).  The title may be buzz-wordy, but the goals are not.  If interested, get involved!
  • Sagar gives a nice rundown of applying advanced workflow to custom assets.  
  • Drew spent some time on Liferay's IRC channel and with the help of the community was able to successfully (and quickly) complete an upgrade from 6.0 -> 6.1.  Nice work!  Drew spent his unexpected free time creating this masterpiece.
  • Here is a very interesting piece of work to reverse-engineer Liferay's entity relationships.  I was really hoping a nice ER diagram was waiting for me at the bottom of this post, but perhaps an astute reader cares to take a stab at making one?
  • Are you kidding me?  An Android app that allows one to peruse social content from a Liferay site?  Wow.  I can't wait for someone to try it out and let me know what it looks like.  Screenshots look very promising!
  • The Liferay Community's BugSquad team recently concluded their efforts for 6.1 and decided on the two best tee shirt designs.  Shirts will begin shipping soon to those who participated.  Wear your shirt with pride, for it is this team that made a huge contribution to Liferay 6.1! 
  • EmDev has released a new version (6.1) of their Activiti Liferay plugin.  This brings support for Liferay 6.1 (yay!), Activiti 5.8 and some other minor improvements.  Thanks Alexey!
  • What do you know about Kaleo?  Now I know 10 more than you you..
  • Many of us are comfortable with Liferay technology and remember what it was like when we were not so familiar with it. There are many more community members who are struggling with basic Liferay concepts, and need a quick intro to common tasks. The Liferay Community is embarking on a new effort to contribute high quality learning guides (in the form of videos and written word).  Check out the Liferay University Video Topics page and sign up for your favorite topic!
  • Attention MacBook Air owners.  You have a new skin.  You can thank me later.
  • Our very own Paul Hinz wrote a nice piece on Portals vs. Web CMS for CMSWire.  A good read, and it's not a blatent plug for Liferay either :)
  • More news on the Liferay IDE front: Liferay IDE 1.5 is now available!  Features such as a hook configuration editor, importing binary projects, JSP debugging over remote Liferay links, and first class support for GlassFish.
  • More from Bradey Wood's Liferay Tips - Embedding Navigation Portlet into Layout File.  Keep 'em coming Brad!
  • If you use Liferay's Friendly URL mapping functionality, you may appreciate this handy friendlier friendly URL Mapper from DevJohnny.  
  • For those of you into BlazeDS and Adobe Flex, here's a nice writeup by fandry explaining how to create Liferay portlets that use both.  
  • Our awesome Liferay Community Verifier team is hard at work scouring the Liferay issues database and improving its quality. A contest is ongoing, if you're interested, follow the thread and sign up!  You can follow the progress of the team here.
  • This Saturday in Alicante the Liferay Spain User Group will hold a meetup.  The meetups thus far have been very well attended, and you do not want to miss this one if you can make it! Arroz y Liferay!  Can't be beat :)
  • Random tweet: @benb3342: #Liferay skills on LinkedIn up 29% year on year.
  • Ever wanted to do an ls -l from a portlet?  Well now you can, with the Liferay Portlet Shell!
  • Liferay Workflow provides a lot of features, one of which is notifications.  When a workflow item lands in your queue, you want to know about it.  Configure email notifications using this handy reference.
  • Wonder what powers Liferay staffers?  Now you don't have to wonder!
  • Chris Stavros from LEVEL Studios is found in this gem from last year's Liferay West Coast Symposium.  I personally found his talk and his demo (which is not in the video) very inspiring and eye-opening to the possibilities of really useful ways to use Liferay.
  • James McGovern, one of our industry's notable thought leaders, provides his thoughts on Liferay's Service Builder and Liferay Security.  Thanks James!
  • Here's a handy script to.. ahem.. kill Liferay processes on unix-based systems.  Hopefully you don't need this too often :)
  • Too many blog posts and wiki page updates to list today.  Well, ok, a couple.  DDL III.  SASS.  Sesame.  Social Equity. Github Cheating.  That is all I have time for.  The rest can be found at blogs.liferay.com.

I hope you all have a great, safe, and productive 2012 in whatever endeavour or passion you might have! I'll leave you with this quote:

"A computer lets you make more mistakes faster than any invention in human history - with the possible exceptions of handguns and tequila." - Mitch Ratcliffe

On with the show!

Writing Liferay Apps with Web Content Templates

Company Blogs January 11, 2012 By James Falkner Staff

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:
 
rootMessageId rowCount (alias="msgCount")
10232 (the id of the first message in the thread) 22 (the number of MBMessages with this rootMessageId in the date range)
11542 21
12323 18
...and so on ... and so on (descending order)

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

 

Liferay 6.1 CE Release

Company Blogs January 6, 2012 By James Falkner Staff

[Magyarul] [中文] [Deutsch][Español]

[Update: Liferay 6.1 EE has been released.  More information about Enterprise Edition can be found in Ed's blog post announcement!]

Today Liferay released the next version of its flagship software: Liferay Portal 6.1 CE! [Download] [Quick Start]

The Liferay product and engineering teams, in close concert with our awesome community, have spent many months getting the 6.1 release ready, and it is finally here.  Read below for the gory details.

Release Nomenclature

Following Liferay's versioning scheme established in 2010, this release is Liferay 6.1 CE GA1.  The internal version number is6.1.0 (i.e. the first release of 6.1).  Future CE releases of 6.1 will be designated GA2, GA3, .. and so on.  See below for upgrade instructions from 6.0 and 5.x.

Downloads

You can find the 6.1 release on the usual downloads page.  If you need additional files (for example, the source code, or dependency libraries), visit the additional files page.

New Features

In addition to the numerous bugs that have been fixed since 6.0 GA4, Many new features and improvements have gone into this release. Highlights include:

  • Updated Support Matrix - Liferay's general policy is to update our support matrix for each release, testing Liferay against newer major releases of supporting operating systems, app servers, browsers, and databases (we reguarly update the bundled upstream open source libraries to fix bugs or take advantage of new features in the open source we depend on).  For example, we are moving to Tomcat 7.x, MySQL 5.5.x, JBoss AS 7, Geronimo 2.2.1, and others.
  • UI Refinements - Too numerous to list here.  Many tasks that used to require a trip to Control Panel (thus losing your UI context) can now be done via the "Manage" menu.  Document Libary has gotten a sweet overhaul.  General improvements in snappiness.  
  • Sites - As described in Jorge's blog and now in the official documentation, the Sites concept has been introduced, decoupling a set of pages from an associated community or organization.  This is one of the big conceptual changes in 6.1.
  • Setup Wizard - To ease the first-time configuration of a portal (and its associated database), when starting a new instance of Liferay, the optional Setup Wizard will prompt for and configure these items for you.  No more mucking about with portal-ext.properties for those basic configurations everyone wants to do initially.
  • Marketplace Support - Groundwork in the form of app hot deploy and marketplace browsing has been introduced into 6.1, gearing up for the opening of the Marketplace later this year.
  • Mobile Device Enhancements -  For example,  mobile device rules allow you to configure sets of rules and use those rules to alter the behavior of the portal based on the device being used to access Liferay. You can also access and evaluate rules through custom scripts.
  • Social Activity Improvements - Many improvements to the social value system (formerly known as Social Equity).  Check out the official documentation on what's new
  • JSON Web Service Improvements - A lot of work has been done in this area, making it much easier to invoke Liferay's services using REST-like (AtomPub-based) JSON (e.g. through supplied JavaScript libraries, or through standard HTTP requests).  Online documentation is also available (check out http://localhost:8080/api/jsonws on your local install. Sweetness!)
  • Asset Publisher Improvements - The darling of the supplied out-of-box portlets, Asset Publisher can now do things like showand publish content from/to multiple scopes, better linking behavior for assets, and many more.
  • Content Management Goodness - One of Liferay's core strengths is its simple yet powerful Web Content Management System.  There have been many usability and functional improvements to it, including inline drag/drop structure editing, internationalized web content titles, preloading of structures on template creation, selection of default display pages, and more!
  • Search Improvements - Lots of performance and accuracy improvements.  Including users in search results.  
  • Unification of the Document Library and Image Gallery - these two apps have historically overlapped each other - that overlap has now been eliminated, by combining the two into a Documents and Media app (with a much fancier UI to boot).  
  • Multiple Repository Mounting.  In the new Documents and Media app, you can now mount multiple repositories (e.g. through CMIS) and develop custom connectors to link to existing CMS repositories.  Features that overlap with Liferay are respected (e.g. permissioning, locking, etc).
  • Native support for storing and serving videos and other media types - Liferay now includes preview functionality for rendering PDFs and other common document formats in-browser, eliminating the need for external apps to view.  In addition, audio and video can be captured and played back from within Liferay.
  • Robust content metadata management - New metadata management tools and UIs to easily capture extra metadata related to documents, for efficient searching and categorization of documents.  Sales team uploading this year's financial data?  Let them enter the "bottom line" numbers into well defined metadata fields for easy sorting and searching.
  • Establishing contextual relationships between content types - with the Related Assets feature, any asset can be dynamically related to any other asset, for easy cross-referencing.  For example, link to a document in a meeting request, or relate a forum post to a blog entry.  Relations can be mined and analyzed later.
  • Enhanced staging support (including Site Branching, Versioning, and Rollback).  A major overhaul of the staging feature, Liferay allows concurrent editing of sites, with versioning and rollback (undo/redo) of changes on the fly.
  • Dynamic Site and Page Templates - One can now create an entire site based on a site template.  When changes are made to the template, the changes are automatically (and smartly) applied to any derived sites.  Sites can later be unlinked if needed, allowing independent forking.
  • User Customizable Pages - Allows your users to customize certain areas of a site's pages, while keeping other areas fixed.
  • User Defined Lists (Dynamic Data Lists) - A very powerful feature that allows one to create a custom data list based on a user-specified schema.  Data can be extracted for reporting, or any other use.
  • Unified User Management - Liferay has always had the ability to be your central directory of users.  The management of said users has not always been easy.  In Liferay 6.1, users and their associated organizations can be viewed hierarchically, allowing easy navigation into your user directory.
  • OpenSocial 1.1 Support - this includes the new pub/sub feature from OpenSocial.
  • Enhancements for Liferay IDE - It's never been easier to develop for the Liferay Platform, using the latest features of Liferay 6.1 and the Liferay IDE.  Liferay 6.1 now includes a remote server deployment plugin, allowing development against a remote instance of Liferay.  Simply throw a switch to publish your tested changes to a production environment.
  • More social networking and collaboration feaures (too many to list them all!) - anonymous comments that are later associated with you when you sign up, follow support, related assets, social activity improvements (already discussed above), wiki images, setting threads as questions by default, and, and.. well.. just try it!
  • Better Scalability
  • Better Auditing, Management and Monitoring
  • Better Documentation
  • Better Security
  • Better Quality
  • ... And more!

Documentation

The Liferay Documentation Team has been hard at work updating all of the documentation for the new release.  This includes updated (and vastly improved/enlarged) javadoc and related reference documentation, and a new User Guide.  You may have been watching the progress of it via its new home on github, but if not, you can access the full documentation on thedocumentation page.
 

Bug Reporting

As always, the project continues to use issues.liferay.com to report and manage bug and improvement tickets.  If you believe you have encountered a bug in the new release (shocking, I know), please be cognizant of the bug reporting standards and report your issue on issues.liferay.com, selecting the "6.1.0 GA" release as the value for the "Affects Version/s" field.
 

Upgrading

As a general rule, you can upgrade from one release of Liferay to the next.  This means that you can upgrade from 5.2 to 6.0, or 6.0 to 6.1, but not from 5.2 to 6.1.  For 5.2 to 6.1, you would need to progress from 5.2 to 6.0, then 6.0 to 6.1.  See the Upgrading Liferay chapter of the Liferay User Guide for more detail on upgrading to 6.1.

Getting Support

Support for Liferay 6.1 CE comes from the wonderful and active community, from which Liferay itself was nurtured into the enterprise offering it is today.  Please visit the community pages to find out more about the myriad avenues through which you can get your questions answered.

Liferay and its worldwide partner network also provides services, support, training, and consulting around its flagship enterprise offering, Liferay Portal 6.1 EE, which is due to be released shortly after this CE release.

What's Next?

Of course we in the Liferay Community are interested in your take on the new features and improvements in Liferay 6.1.  Work has already begun on the next evolution of Liferay.  If you are interested in learning more about how you can get involved, visit theLiferay Community pages and dig in.  

 

Liferay Portal 6.1 Release Candidate Now Available

Company Blogs December 21, 2011 By James Falkner Staff

Liferay is proud to announce that the Liferay 6.1 CE Release Candidate is available! [Download]

The Liferay product and engineering teams, in close concert with our awesome community, have spent many months getting the 6.1 release ready, and this is one of the final builds before it will be generally available. We are making this release in advance of the 6.1 GA release, in order to ferret out last minute issues, and get the great work that the teams have poured into this into your hands earlier.

New Stuff Since Beta-4

The community has been hard at work. Over 800 individual issues (bugs and improvements) have been resolved since Beta-4, and several new major and minor features make up 6.1. Ed Chung gave a great overview of these 6.1 themes and features at the East Coast Symposium. See my blog post regarding 6.1 Beta for a run-down of the major features in 6.1.  Other highlights in this RC include:

  • Setup Wizard - The first time you start Liferay, you are now presented with a final set of setup screens allowing you to configure the initial user, initial name of the instance, and (even more awesome) the ability to configure the database that Liferay uses.  
  • Mobile Device Rules - This has been delivered over various releases, but in the RC it is finally complete.  Mobile device rules allow you to configure sets of rules and use those rules to alter the behavior of the portal based on the device being used to access Liferay.  You can also access and evaluate rules through custom scripts.
  • Site Template Improvements - Another feature that's been there, but the linkage between a site template and the sites that derive from that template is now complete and functional.  You can create 100 sites based off a single template, and then modify something in the template and watch the changes automatically reflect in the sites that derive from the template.
  • Social Activity Improvements - Many improvements to Social Activity (formerly known as Social Equity).  You can see a description in the user's guide section on Social (note that the Liferay User Guide is still a work in progress!).
  • JSON Web Service Docs - Dynamic documentation is now generated for the new JSON Web Services feature.  Simply access http://localhost:8080/api/jsonws to get online documentation for the REST-like JSON Web Services, as well as a simple test harness for accessing services.  Also note that tunnel-web has been integrated into the portal and is no longer a separate web app (But the old paths that used /tunnel-web/ will continue to work and be mapped to /api).
  • ... And more :)

Downloads

The release candidate is available on our downloads page.  The additional files that some will need (for example, for developers) are listed on the RC Additional Files page.

Documentation

Our documentation continues to improve. With the help of the community, our end user, administrator, and developer documentation have all improved tremendously. While the final documentation has not yet been published (it will be published with the GA release), its open source nature means you can take a look at the development, and even contribute to the "code". If there is a specific new feature you are interested in learning about, you are encouraged to use any of the myriad avenues through which the community supports its constituents, or you can just give the RC build a try today!

Bug reporting

If you experience issues with this RC release, and believe it to be a bug in the product, you are encouraged to file a report at issues.liferay.com. There is a specific release to denote in the Affects Version/s field: 6.1.0 RC. The issue will be triaged, and if it has a big enough impact, will be fixed for the final GA release.  Note that only showstopper bugs (insecurity, crashes, severe memory "leak", data corruption, or major loss of functionality with no viable workaround)  will be fixed leading up to the GA release.

Upgrading to 6.1

As this is still not the final release, you should not use this unsupported release in a production environment. However, it is a good way to test that your eventual upgrade will succeed with the minimum of fuss. Several of our BugSquad members have already successfully upgraded their customized Liferay 6.0 environments to the early 6.1 builds, and this build gives you a chance to do the same.

Going Forward

As we approach the final GA release, there may be additional Release Candidate builds to fix any last minute issues. As always, you are encouraged to participate and contribute to the project, and we hope you enjoy this sneak peek!

Liferay Community Projects

Company Blogs December 19, 2011 By James Falkner Staff

If you are reading this blog post, you are undoubtedly familiar with open source in general, and Liferay and its open source mission in particular.  Our community is spread out both globally and with respect to the technology that can be used on the web and on the Liferay Platform.  Over the years, numerous contributors have donated their time and resources into what we now know of as Liferay.  The kinds of contributions that are being made on a daily basis range from simply suggesting a new feature or finding and reporting a bug, through localizations, documentation, bugfixes, new features, feature improvements, and a host of other efforts that can be readily identified.

As Liferay grows, so to do the kinds of contributions that are useful.  At the highest level, we have Official Liferay Projects that you are all familiar with: Liferay Portal, Social Office, AlloyUI, and Liferay IDE round out the current top-level projects.  These are projects that Liferay has deemed important enough to be imbued with special attributes (such as being shipped with Liferay directly, or staffed with Liferay personnel, or using Liferay's software engineering resources like issues.liferay.com). Within these top-level projects are all kinds of contributions and innovation.

Zooming way in, at the lowest level there are contributions such as simple community-contributed bugfixes (e.g. those that have arrived via the 100 PaperCuts or BugSquad projects), one-liner translations, minor code improvements, or javadocs.  While no less important than other types of contributions, these at the lowest level are generally short-lived, focused contributions that do not require a team to contribute or maintain.

As you may have guessed, there is a missing void in the middle - there are non-trivial project ideas that community members have suggested, or some that are underway or have already been completed, and that represent non-trivial, community-led software efforts that make the Liferay Platform a better place to be.  Some have been contributed in the form of community plugins, or simply exist out on the web.  More importantly, they represent innovation coming from outside of the influence of existing Liferay technology.  

In an effort to bring these non-Liferay-led, non-trivial projects to light, and to encourage participation in these projects, we bring you the concept of Liferay Community Projects.

 

What is a Liferay Community Project?

A Liferay Community Project is an open source contribution driven by one or more Liferay Community members.  It represents a non-trivial addition to the open source Liferay ecosystem that is bigger than the simple contributions listed above, but not officially sponsored or developed by Liferay (the company).  Liferay Community projects have space on liferay.org, our community's home, on the new Community Projects Listing page.  As these are open source projects, participation is open to all.

What qualifies as a Liferay Community Project?

There are no hard and fast rules as to what would make up a good project and what would not.  Any non-trivial open source project that relates to and improves Liferay and Liferay technology would make for a good Liferay Community Project.  If you have an idea for a new project, but you haven't written a single line of code, contact us! (see below).  If you have been working on a project for some time, already have builds available, and simply want to more directly associate your project with Liferay (and obtain new developer talent), give us a call! (well, email us)

Why should I care?  What are the benefits?

Of course, open source projects are free to manage their projects on any website of their choosing (or have no web presence at all!).  However, the primary benefit of being listed as a community project at Liferay is to have a more direct relationship with Liferay and its open source ecosystem.  Each projects gets listed on the community projects page in a consistent way, along with a contributor listing, dedicated forum category, wiki space, and links to the project's deliverables.  Developers can connect and collaborate with other team members through the project's homepage.  Prospective contributors can quickly learn about available projects, try them out, and get involved.  On the other side of the coin, Liferay benefits through ever-increasing activity associated with Liferay.  In short, rather than just listeners, we want active participants in our open source conversation.

Are these incubation projects?

Existing incubation projects (such as Apache Incubator) have an implied governance model, one that governs contributions and the progression of a project from an incubation state to some sort of "accepted"/"approved" state.  Liferay Community Projects do not get "promoted" from one state to another, there is no implied governance model for contributions, and there is no promise that a project will be integrated into Liferay proper or otherwise be promoted.

What about Licensing?

Project maintainers are free to choose any open source license they wish.  In addition, it is up to each project to determine the governance model (if any), the rights assigned to contributions, and the mechanism by which contributions are made.  The only requirement for being a Liferay Community Project is that it must be open source (and the code must be licensed using an accepted open source license), be associated with and offer an improvement to Liferay technology in some way, and be willing to accept contributions and participants who which to get involved.

How do I start my own Liferay Community Project?

If you have an idea for a Liferay Project, simply contact community@liferay.com and talk about your idea.  We want to encourage new projects from any and all community members who have the time and energy to lead a project.  We'll get you set up with an information page, project collaboration tools, and appropriate links for potential and existing project members and developers, and answer any questions you may have.  Again, it is not required that your project already exist -- the best ideas often come from an initial idea seed that is acted upon and refined through community collaboration from day one.

Note that this program does not provide facilities for source code management, issue management, or build systems.  It is up to each project to provide that.  There are many resources available for open source, such as Github (which Liferay uses for its top-level projects), SourceForge, and a host of other online resources for maintaining open source.  

Where do we go from here?

As I discussed in an earlier blog post, we already have one community project led by Jan Gregor - The Liferay/JRebel Integration project.  There are a couple more projects in the pipeline which you'll be hearing about soon, but we want more!    As the program grows, we'll be adding more features (blogs anyone?) and streamlining the process by which new projects are born.  I am personally looking forward to 2012 and seeing what is possible with Liferay and open source! Keep an eye on the projects page for new and upcoming projects in which you may be interested in participating.

 

Community Roundup

Company Blogs November 15, 2011 By James Falkner Staff

"Life would be so much easier if we only had the source code." - Author Unknown

Here we go again.  Once again I have failed to live up to my promises of a once-a-month roundup, and for that I apologize! It's been a very busy and exciting 6 weeks in our community, so I am here to give you a digest of the latest coolness that is the Liferay Community.  So please keep arms and legs inside the vehicle at all times, and sit back and enjoy the ride.  Let's do this thing!

  • I was fortunate enough to attend two of Liferay's symposiums last month, the Liferay Europe Symposium in Frankfurt, and the Liferay Spain Symposium in Madrid.  Met many awesome community members, attended a bunch of interesting sessions and activities, and generally had a great time learning from our European community contingent.  Slides from Europe and Slides from Spain are now available!
  • If you are near Rome this week, the final symposium of 2011 is taking place this Friday (November 18).  If you have a chance to attend the Liferay Italy Symposium, you will be pleasantly surprised at the highest ever quality content to date. 
  • We are coming up on the close of the Liferay 6.1 chapter in the epic book of Liferay.  On November 14 Liferay released its Beta-4 build, and it is now downloadable, but if you are participating in Community BugSquad, of course you have already reviewed the sneak-peek 6.1 builds, and several of your feedbacks/suggestions/fixes/improvements have already been incorporated, with more to come!  
  • You may have noticed a new celebrity blogger on liferay.com: Jan Gregor from Mimacom is kicking off the first of many Liferay Community Projects.  This project is implementing a Liferay/JRebel plugin to facilitate easy realtime, no-restart-needed deployment of apps on LIferay.  More notable though is that we at Liferay are going to elevate these kinds of community-driven "incubation" projects to a higher level of visibility on liferay.org, providing more collaboration tools (like user groups) and project status.  If you are interested in starting a new project (or housing an existing project) on liferay.org, contact community@liferay.com
  • Olaf has published several more Radio Liferay releases, the latest of which features several of our high profile community members discussing their contributions and thoughts on our community. Check out the Radio Liferay feed page for more details and links to the streams. Recent Episodes: [9] [8] [7] [6] [5] and [4]
  • Liferay's User Groups continue to expand, with new groups beginning in Portland, Portugal, Norway, and others.  Visit the User Groups page to see all the recent activity.
  • Speaking of User Groups, the Spain User Group had a recent LSUG Meeting in Seville and it was a very successful event (15 attendees).  You can read the minutes and if interested, there are upcoming meetings in Marid, Sevilla, Barcelona, and Alicante.
  • Liferay's Community Leadership Team held its quarterly meetup last month, with several interesting discussions regarding what's buzzing in the community.  Check out the meeting minutes and follow the action on the thread if interested!
  • Notable industry blogger Dana Blankenhorn has an interesting take on Liferay's business model and mission.  Dana gave a rousing talk about the history of open source at WCS, and has written several great articles on open source in general.
  • Bradley's Tips!  This is a great collection of tidbits of useful information. Recently I've seen a lot of these kinds of things coming up (like Ray's gists), I think we need a centralized place into which these are placed, for easy access and searching!  *adds idea to queue*
  • Liferay IDE 1.4 has been released, with support of the upcoming Liferay 6.1, and Tomcat 7, as well as a few other goodies. Check out Greg's blog for details.
  • Random website running Liferay: AXA IM Corporate Website!
  • Remember that "50 Liferay Interview Questions" post from a while back?  Well, now you have the answers!
  • As you look back on the Liferay Community, you can see many innovative contributions and outstanding achievements.  You can also see a large ball of abused and ignored bug reports!  The Liferay Community has deputized a team of experts to help with this: Community Verifiers!  And we need your help!  If you are interested, leave comments below!
  • Wasim shows us how to install Liferay on eApps cloud hosting.  Yet another example of Liferay's cloud-friendliness.  
  • If you missed Oracle OpenWorld 2011, Liferay demonstrated the new features of Liferay 6.1, and was mentioned as one of 12 "hot products to see".  Cool! Well.. Hot!
  • We continue to see Open Source displacing proprietary stacks worldwide.  TechRepublic Live in Louisville mentioned Liferay as one of those displacing forces.
  • Speaking of the spread of Open Source, Gartner's 2011 Magic Quadrant for Horizontal Portals has placed Liferay in the Leader's Quadrant for the second year.  The only open source leader!  More coverage from CMS Report.
  • Mika has posted an excellent set of slides describing Liferay's SAML 2.0 support.  
  • Integrating Liferay with JOSSO (an open source SSO solution) has never been easier with Liferay 6.1.  Check out Gianluca's blog for details.
  • Liferay coverage from PyCON.  A Python conference.  Talking about Java portals?  Nice!
  • We in the Liferay Community are working on a new structured program to more easily get new users over the initial learning hill of Liferay, and provide valuable educational content to all of our community, regardless of expertise level. We need your input to proceed.  How should we structure such a program?  Leave your comments in the thread and learn by teaching!
  • Several people have been playing with Liferay and Jelastic (yet another cloud service).  Some discussion has taken place on the forums, and Marina has taken the time to write down details of this deployment.  Have fun with this one.
  • A thought-provoking examination of portals, and when they make sense.  For this author, it did not, but it provides some interesting guidance about when it does.
  • Activa wrote up a nice piece on the 2011 Spain Symposium.  Activa has been very active in our community and is a valuable community parter for Liferay! 
  • Finally!  Some handy scripts that can be cut/pasted into Liferay's Control Panel scripting console.  Terry provides a nice way to clean up user.  Be careful, there's no going back :)
  • No idea who created this or why, but it sounds fantastic :)
  • I really like Liferay's support for Dynamic Data Lists.  It's been a long time coming and how it's here.  Check out Arvind's description of it, and try it out in the latest beta!
  • Often the bane of developers, writing tests is a crucial part of ensuring quality in your projects.  With Liferay, it's really easy to write selenium tests (we at Liferay use this extensively, along with Jenkins).  Steffen shows how to do it.
  • Your 15 minutes are here.  15 minutes to Liferay on Cloudbees!

Recent Blog Posts: Radio Liferay Episodes [9] [8] [7] [6] [5] [4], WYSIWYG Editors, Philosophy of Writing, Custom Tools for Script Engine, Monitoring through firewalls, Embedding Portlets in Themes, Community Excellence, Spring and OSGi, Liferay @ Gartner ITExpo, Git Tips from Igor, Community Verifier, Fireworks, and finally Ehcache Configuration.

Recent Wiki Updates: CMIS and Alfresco 4.0, Custom Fields, Pagination, Development Style, Mail Integration, SOAP Web Services, Application Adapters (always wondered what those were!), Translation Team, AlloyUI Forms, Faceted Search, Slimming Liferay, Google Analytics

That's all I have for now.  I will do a Holiday Edition next month, with some goodies for all.  Enjoy your day!

 

Liferay 2011 Community Excellence Awards

Company Blogs October 20, 2011 By James Falkner Staff

In addition to the annual "Partner of the Year" award, this year Liferay recognized its partners for their community achievements, awarding the Community Excellence Award.  We consider partners a valuable part of our community (just like we do with "regular" liferay.com denizens, Liferay's employees, and Liferay customers), and wanted to recognize their participation, independent of any revenue figures, deals closed, or any other other business factors.

What We Measured

The actual, super-secret formula has not been revealed in orer to keep things fair, but the formula involves several (over 20) contribution types, including the obvious ones like helpful forum posts (answers) and code contributions, but other non-obvious, yet highly valuable community metrics.  For the 2011 awards, we considered activity that occured between midnight, August 31st, 2010 and midnight, August 1st, 2011.

The Winners

The winning partners have a proven, well-rounded, and highly effective track record of being involved in the community and giving back.  They realize the benefits of open participation: 1) It ultimately helps their business operate more effeciently, because they are more knowledgable, 2) It improves their standing with other community members and clients, because they are seen as having the expertise enough to be able to help others, and 3) It moves the technology forward, by offering innovation that we alone cannot provide.

Here they are:

MIMACOM

MIMACOM is a leading provider in Europe for Enterprise Open Source solutions based on Java.  Headquartered in Bern, with offices in Zurich, Valencia, Stuttgart, and Rome, MIMACOM also makes regular contributions to projects other than Liferay, including ICEfaces, Spring, and Hibernate.  In 2010-2011, MIMACOM was particularly helpful with the 6.0 release (doing much pre-release testing), contributed plugins, and established the Austria and Slovakia user groups.

 

Savoir-faire Linux

Founded in 1999 with locations in Montreal, Quebec City, and Ottawa, Savoir-faire Linux is the reference for open-source software integration. They are the largest and most experienced Liferay integrator in Eastern Canada.  In 2010-2011, Savoir-faire contributed a huge number of open source plugins, and worked closely with the community and Liferay engineering to implement and document the faceted search feature in 6.1.  They also contributed many fixes for the 6.1 release, and we are delighted to have them be part of it all.

 

IBA CZ

IBA CZ is a fast growing IT service provider and development center of the IBA Group headquartered in the Czech Republic. They focus on up-to-date technologies including enterprise portals, enterprise Java, Business Intelligence, and Business Process Management to customers located in Central and Western Europe.  In 2010-2011, IBA CZ was very active in reporting and providing contributions for issues, participated in 100 papercuts, BugSquad, and Community Verifier, and established the Czech Republic User Group.

 

CIGNEX Datamatics

Based in Santa Clara, CIGNEX Datamatics is the world's leading provider of Open Source Enterprise Content Management and SOA & ESB consulting services and software solutions. Their customers include companies in the Media, Healthcare, Education, Government, Gaming and High Tech industry. In 2010-2011, the team had consistent community contributions in many different areas (forum answers, bug reports, plugin contributions, and more) owing to their Liferay Platform expertise.   They also hold 1 of only 4 coveted "Liferay Legend" status indicators.

 

Componence

With locations in Nederland, Ukraine and India, Componence has offered Internet, Social Media and Mobile solutions since 2001. With over 200 portal implementations, Componence is portal market leader in the Netherlands with technology focus on Java and portal.  In 2010-2011, Componence contributed plugins, established the Netherlands User Group, participated in BugSquad, 100 PaperCuts, Community Verifier, and holds 1 of only 4 coveted "Liferay Legend" status indicators. 

 

A big "Thank You!" and congratulations goes to all of our 2011 Community Excellence Award winners!

How Can We Win Next Year?

Think about how participation in Liferay's open source community can help you (see the above winners for some ideas).  Rather than taking an ad-hoc approach, build in community participation into your business model and resourcing plans.  Value will organically flow into the community and back to you in various tangible ways.  For example, you fix a bug, provide a translation, develop and contribute a new feature, or investigate and solve someone else's problem on the forums.  You have helped the community, and without knowing it, got trained in the process!  How cool is that?

 

Liferay 6.1 Beta Release

Company Blogs October 14, 2011 By James Falkner Staff

Liferay is proud to announce the Liferay 6.1 CE Beta release!  The Liferay product and engineering teams, in close concert with our awesome community, have spent many months getting the 6.1 release ready, and this is one of the final builds before it will be generally available.  We are making this release in advance of the 6.1 GA release, in order to ferret out last minute issues, and get the great work that the the teams have poured into this into your hands earlier.

New stuff since 6.0

The community has been hard at work.  Over 2500 individual issues have been resolved, and several new major and minor features make up 6.1.  Ed Chung gave a great overview of these 6.1 themes and features at the East Coast Symposium.  Here's a cheat sheet to some of the major ones (many in the community will undoubtedly be familiar with them, as you helped develop and/or review them!)

  • Multiple Repository Mounting - including CMIS 1.0 Support
  • Unification of the Document Library and Image Gallery
  • Native support for storing videos and other media types
  • Robust content metadata management
  • Establishing contextual relationships between content types (Related Assets)
  • Enhanced staging support (including Branching, Versioning, and Rollback)
  • Dynamic Site and Page Templates
  • User Customizable Pages
  • User Defined Lists (Dynamic Data Lists)
  • Workflow-enabled Forms
  • Mobile Device Detection
  • Unified User Management
  • RESTful Web Services
  • OpenSocial 1.1 Support
  • Enhancements for Liferay IDE 1.4
  • More social networking and collaboration feaures  (too many to list!)
  • Better Scalability
  • Better Management and Monitoring
  • Better Documentation
  • Better Quality
  • ..."And There's More!"

Downloads

The beta release will be available on our downloads page soon.  In the meantime, the usual files (portal bundles, sql scripts, CE plugins, and other supporting files) can be found on SourceForge: portal bundles and plugins.

6.1 Documentation

Our documentation continues to improve.  With the help of the community, our end user, administrator, and developer documentation have all improved tremendously.  The effort to document this release continues through this beta.  While the final documentation has not yet been published (it will be published with the GA release), its open source nature means you can take a look at the development, and even contribute to the "code".  If there is a specific new feature you are interested in learning about, you are encouraged to use any of the myriad avenues through which the community supports its constituents, or you can just give the Beta build a try today!

Bug reporting

If you experience issues with this beta release, and believe it to be a bug in the product, you are encouraged to file a report at issues.liferay.com.  There is a specific release to denote in the Affects Version/s field: 6.1.0 B3 (Beta-3).  The issue will be triaged, and if it has a big enough impact, will be fixed for the final GA release.

Upgrading to 6.1

As this is still a beta, you are should not use this unsupported release in a production environment.  However, it is a good way to test that your eventual upgrade will succeed with the minimum of fuss.  Several of our BugSquad members have already successfully upgraded their customized Liferay 6.0 environments to the early 6.1 builds, and this build gives you a chance to do the same.

Going Forward

As we approach the final GA release, there may be additional Release Candidate builds to fix any last minute issues.  As always, you are encouraged to participate and contribute to the project, and I hope you enjoy this sneak peek!

Community Meetup at the Europe Symposium

Company Blogs October 10, 2011 By James Falkner Staff

Update: The Meetup will take place at Berliner 109 on Monday, October 17, at 18:00!  This is a couple of minutes walk east, from the Symposium venue (Sheraton Offenbach Hotel).

If you are attending this year's European Symposium next week in Frankfurt, on Monday evening (after the pre-symposium training) Liferay is hosting a community meetup.  This will be a free event, starting around 18:00, at a Berliner 109.  Come meet your fellow community members, Liferay staff, and other interested parties with some free drinks, snacks, and interesting conversation!  It'll be a great way to start off your two-day symposium.

If you are interested in attending, let me know by leaving a comment on this blog post, so that we can gauge how many will be there!  See you in Frankfrurt!

Liferay Community Verifier Program

Company Blogs October 5, 2011 By James Falkner Staff

As you look back on the Liferay Community, you can see many innovative contributions and outstanding achievements by our wider community of enthusiasts, users, customers, partners, and employees.  You can also see a large ball of abused and ignored bug reports, connected to us by a big chain, limiting the utility of the reporting system and efficiency of software development on the project!

Today begins a new community program at Liferay: Community Verifier.  I mentioned this a long time back, and we now have the resources in place and I have the time to manage such an undertaking. I have sent instructions to those that have already volunteered (almost 25 initially, with more to come hopefully!)

The goal of this program is to address issues (not fix them per-se, just triage them) in our issue database, especially those issues filed against past releases that may still be valid, but have been ignored for some time. We will make sure the bug still exists in recent releases, and if they do, promote them to the latest release and assign to the appropriate staff to resolve, otherwise we will close them out as fixed or not reproducible. 

Of course, new issues are still coming in, however if you look at the community's performance over the last year, we have chopped that mountain roughly in half (from 1700 "unverified" last year to 867), which is great progress, but we've more to go.  With the community's help, I think we can easily get that down to 0 unverified/unattended bugs.  Wouldn't that be swell?  I think so.

The program begins now, and continues indefinitely, and I will be actively recruiting new members as we go along (if interested, leave comments below).  Note that this is not for the beginner on the Liferay Platform.  Good judgement, technical know-how, and some Liferay experience are all needed. I will also establish a landing page (much like BugSquad) along with a forum category so that the team can hold conversations there instead of email (and so everyone gets a chance to see what we're up to), and get to know each other.

Community Roundup

Company Blogs September 27, 2011 By James Falkner Staff

It's that six dot one bump, make you put yo hands up - Make you put yo hands up, put yo, put yo hands up

Welcome to another installment of Community Roundup!  Your source for a digest of the goings-on in the Liferay Community.  I apologize for the delay, I am targeting a one month period, and promise to do better next time!  It is taking about 6 hours per roundup to sift through the mountain of coolness in the Liferay community. We have lots of material to cover, so sit back, relax, and start your clicking or tapping.

  • Last week, the Liferay Community enjoyed a fantastic 48 hours in sunny Southern California for Liferay's 3rd-annual West Coast Symposium.  As emcee, I was able to attend the first and last 10 minutes of each Track A session.  I will do a wrap-up in a separate post, but wanted to give a special shout-out and congratulations to Cignex Datamatics and Savior-faire Linux, recipients of the first ever Liferay Community Excellence Award for their outstanding achievements and contributions to our community! More coverage from WCS here, here, here, here, here, here, here, and...... wait for it... here.
  • If you attended my Community Talk at WCS (slides available soon), you'll be aware of many of the upcoming features we are working on for you.  Jamie Sammons from CDS Global was kind enough to get up on stage with me and discuss his role in our community, and I described the upcoming features: Community Blogging, Incubation Projects, Community Verifier, a real Ideation site, Badges and Achievements, and new Developer On-Ramp videos.  Good times!
  • With WCS behind us, it is time to look forward to the "Final 3" -- Europe (Frankfurt), Spain (Madrid), and Italy (Rome).  If you have a chance to attend any of these, you will be pleasently surprised at the highest ever quality content to date.  These symposiums are a great opportunity to meet and engage the leaders and active participants in our community.  See you there!
  • We are coming up on the close of the Liferay 6.1 chapter in the epic book of Liferay.  At WCS, we announced availability of a Liferay 6.1 release candidate build on October 14, but if you are participating in Community BugSquad, of course you have already reviewed the sneak-peek 6.1 builds, and several of your feedbacks/suggestions/fixes/improvements have already been incorporated, with more to come!  We are in the final approach to the 6.1 landing, so get your vote in now to get your favorite issues resolved.
  • Speaking of epic books: Manning has finally begun printing copies (and making e-books available) of Rich Sezov's Liferay In Action - THE definitive guide to Liferay Portal, covering all of the latest features of the Liferay 6.1 platform.  I saw a couple of copies floating around at WCS, and Rich was kind enough to autograph mine, and you can get yours today! 
  • Looking to put your newfound skills from WCS and Liferay In Action to good use?  Liferay Hungary has initiated an awesome program for University students: Liferay Code Camp 2011!  Meet professionals and other students, Write some code, get some real-world experience on a large software engineering project, and win a trip to Los Angeles or Madrid.
  • More greatness from (and for) the community: Radio Liferay!  A podcast series from Olaf Kock, covering the world of Liferay in human terms - a conversation with interesting people from in and around Liferay. Check out and subscribe to the feed.  Radio Liferay Episodes [3] [2], and [1(me!!)] are available now.
  • Liferay's Community Leadership Team had its second meetup in July, with the next one in mid-October.  You can find the minutes here.  Attended by 11 of the community's leaders, we had discussions ranging from community programs, to user groups, and an interesting discussion around "What Motivates the Community?".  The follow-up discussion thread is very interesting.
  • Harpreet from Cloudbees shows us how to deploy Liferay onto a cloud in 15 minutes.  If you saw his presentation at WCS, then this is old news, but still awesome!  
  • Dynamic Data Lists is a marquee feature in 6.1 - Mark shows us why.
  • Going to Oracle OpenWorld this year?  Liferay will be there (Moscone South - Booth 110 - behind the MySQL Demo Grounds), and is proud to be a Gold Sponsor at the first Jenkins User Conference.  Liferay uses Jenkins extensively for it's Continuous Integration process, and we look forward to meeting others who want to find out more about Jenkins and Liferay! Read more about our use of Jenkins in Harpreet's blog entry.
  • If you've browsed liferay.com on your mobile, you may have noticed the layout is different.  In fact, in a desktop browser, if you squeeze the website thinner and thinner, the layout will dynamically adjust (and images are changed, and so on).  This is a mobile responsive layout, and now you can use it, by grabbing it from here!
  • I don't normally re-post job listings, but this one caught my attention.  You simply show up (walk in) between 9a and 6p, work on a Liferay project, and get paid.  That's like fast food for Liferay developers.  Only reversed...  Sort of.  Interesting concept nonetheless!  Nice to see a proliferation of Liferay jobs.  Heh.. get it?  Pro-life-eration -- Liferay.  *groan*
  • Liferay Developers who want to use Maven with the Plugins SDK - Over at DZone, Kamesh explains how to do it.
  • jQuery is a nice toolkit for javascript developers - see how to easily integrate it into your portlets by reading Alexey's blog post.
  • I've seen How to Change Company Logo Liferay Portal 6.x on Twitter about a zillion times.  Occasionally the profile picture of the person re-tweeting this is a famous inventor or some celebrity.  Perhaps it is a cry for help, or to get on Community Roundup.  So here it is.  Now we can all change our company logos!  I just changed mine.  There, I did it again!
  • Liferay Community User Group activity continues to rise.  We are up to 10 groups, with a few more in the pipeline.  New groups include Austin, Netherlands, and Italy.  Spain already held a first meetup (minutes here, en Español, and excelente tee shirt pic here).  Contact community@liferay.com if you wish to start your own!
  • Ever wanted to open your portlet up in a modal (or non-modal) dialog in Liferay?  It's pretty easy!  And it's extensively used in Liferay 6.1 so that you don't lose context when visiting the various administrative interfaces.  
  • If you need direct database access from your Liferay plugin, look no further.  Daniel shows us the (easy) way.
  • Easy integration of OpenDJ and Liferay by profiq. I particularly like Liferay's UI for configuring LDAP and several other integration options.  *cough* database configuration *cough*.
  • A lot of the administrative interfaces in Liferay are driven by the Search Container API - simply showing a list of something and allowing actions on the individual items or in bulk.  If you've created new users, sites, or virtually any other portal entity in Liferay, you've used a Search Container.  If you want to create one for your custom entities, look no further than Daniel's excellent blog, and this post in particular.
  • OpenXava 4.2.3 is now available!  OpenXava allows you to define business applications with simple POJOs, JPA and Java 5 annotations.  Here's a nice (though dated) wiki article on how to integrate OpenXava into Liferay.
  • Liferay working with Java 7!  Though Java 7 might not be ready for primetime, Liferay works well (with a few caveats).
  • Liferay only good for intranets?  Not so, says Szymon (and I happen to agree :) ).  This site runs Liferay.
  • Soft Shake 2011 will take place October 3-4, in Geneva.  This year, Liferay will get some coverage via Vaadin's Sami Ekblad, with a discussion on Rich Portlets in Liferay 6 with Vaadin.  Stop by and say Hi if you're there!
  • A Liferay event hosted by Smile in Paris a couple of weeks ago attracted a lot of community members to learn more, and Guillaume provides a nice wrap-up of the event (en français).
  • Uh oh.. here comes the Android crowd!  Aritz shows us how to easily talk to Liferay from your Android device.
  • Well, this is interesting.  A PortletBuilder tool (akin to ServiceBuilder) - building Portlets based on XML definitions!  THanks Mark Polly!
  • Liferay does not discriminate in its support for various UI frameworks.  Stack traces from all are equally long :-)  But GWT is a popular one (Vaadin in fact is built on it).  Here's a great tutorial on using GWT and making instanceable portlets using it.
  • Wow.. a great contribution by Holistic Security!  Calling Alfresco Webscripts from Liferay w/AJAX.  A complete solution, including downloadable binaries!

New Blog Posts: Ehcache Configuration, Spain Symposium Details, Radio Liferay Episodes [3] [2], and [1(me!!)], Spring Contexts, DL Record Plugin, Liferay & OSGi, Utilizing Instance Storage on EC2, Adding Workflow Portlets to a Page, Community Activity Map (me!!), Activity Map Explained, Themes Settings and Advanced Controls, Liferay WCS and Jenkins User Conference, The Myth of Free Open Source Portals, Theme JSP Overrides, JSP Include Buffer, Changing Context PathApoyando a Unicef en la campaña del Cuerno de África, Test JARs, Controlling Content, Changing Default Column Target for Drag 'n Drop, Community Stats (me again!!).  Dang.  Once we enable blogging for the entire community, I may have to stop this bit :)

Recent Wiki Updates: Introduction to Liferay, JSON Web Services, Non-Standard Timezones, JBPM, AlloyUI Forms, Websphere 6.1 Tips, Application Servers, Custom Queries in 5.2, Liferay IDE 1.4 Plan, Liferay Hooks, Translation Team, Solr, Web Content Velocity Variables, URL Parameters, Contributing, Database Configuration.

Looking forward to meeting many of you in Frankfurt and Madrid!  I will leave you with two teaser videos produced for this year's symposium.  First, a recap of where we are at Liferay:

Finally, a recap of day 1 from WCS:

Enjoy!

 

Community Activity Map Explained

Company Blogs August 30, 2011 By James Falkner Staff

By popular demand, below is a technical description and source code of the new Community Activity Map that I posted a couple of weeks ago.

Overview

I started on this back in December 2010 when we were refreshing the community landing page.  Having a list of recent issues, and announcements is all well and good, but there is so much more happening in our worldwide community than the static content I can create, so I wanted a good way to dynamically visualize all of the activity.  Liferay has a built-in activity stream framework, and indeed activities can already be seen in a listing (for example, on my profile page on liferay.com).  However, it is a static display and only relates to my activities.  Wouldn't it be great to see everyone's activities, and the global location at which these activities occur?  Liferay and Google Maps to the rescue!

Portlet or Web Content

Initially, I created a simple MVC-based portlet with a single JSP page.  On the server side, the JSP contained a scriptlet that fetched activities using Liferay's SocialActivity service, and created a giant javascript object in the resulting markup, containing around 20 activities.  The markup is then shipped to the client side, where some more javascript rendered the activities on a google map.  This was all well and good, but unless you refreshed the page, you'd always see the same 20 activities, in a loop, forever.  It also meant that render time was slower because all of the action occured on the server side, and the client didn't see anything until the activities were already fetched and processed.

The other problem was I didn't test it on IE, and when I showed it to Brian Chan, he tried it on IE and predictably it didn't work.  

I abandoned this work due to other priorities, but then about a month ago I found some spare time and resurrected the code.  This time, I decided to make it closer to realtime, and update the page dynamically, so I switched to an AJAX mechanism, which fetched activities using the portlet's serveResource API in an AJAX call (using AlloyUI's built-in AJAX IO library).  I made sure it worked on IE, and submitted the code for review to Peter Shin.  Peter, being the awesome developer he is, thought this would be simpler to maintain if it were purely web content using Liferay's powerful Web Content Management system.  He was able to convert my portlet into pure Velocity-based web content (similar to Ray's example from a few months ago).  This means that its much easier to make updates, and the liferay.com maintainers don't have to be bothered every time I wanted to re-deploy a new version.  Nice, and immense thanks goes to Peter for this!

Web Content Framework

To create apps using Liferay WCM, you create web content structures and templates and articles (this should be familiar to anyone that has used Liferay WCM).  In this case, our structure is simple, and contains a single configurable element called height.  This is then referenced in the template using $height.data.  You could add other configurable parameters (for example, configurable timeouts, or configurable Google Map options, etc).  The web content template has the following framework:

#if ($request.lifecycle == "RENDER_PHASE")
  ## Client-side initial render call will render the result of this code
#elseif ($request.lifecycle == "RESOURCE_PHASE")
  ## Client-side AJAX call will fetch the result of this code
#end
That's it!  The RENDER_PHASE code is akin to a portlet's "View Mode" code (typically a JSP called view.jsp), and the RESOURCE_PHASE is akin to a portlet's serveResource code.

In this case, the RENDER_PHASE contains the javascript for fetching activities via an AlloyUI AJAX call, and then rendering the Google Map, and placing "bubbles" on the map at varying intervals.

The RESOURCE_PHASE results in a JSON object, which is read by code in the RENDER_PHASE, which contains the activities.

When this is all put together, the client side gets the RENDER_PHASE javascript, and executes it, which causes the AJAX call back to Liferay, and returns the result of the RESOURCE_PHASE code.  It's important to NOT make the template cachable, such that every AJAX call results in a new execution of the RESOURCE_PHASE call (otherwise, the same activities would be returned, time and time again).

Fetching and Rendering Activities

The important part of the RESOURCE_PHASE code is here:

#set ($socialActivities = $socialActivityLocalService.getGroupActivities($scopeGroupId, 0, 50))
#foreach ($socialActivity in $socialActivities)
  #set ($socialActivityFeedEntry = $socialActivityInterpreterLocalService.interpret($socialActivity, $themeDisplay))
  ##  do stuff with it
#end
This code retrieves 50 activities from the current group (in our case, the "liferay.com" community on liferay.com), and creates a JSON array, which is returned as a result of this call.
 
Each activity is placed in the array and consists of the following items:
  • body - The rendered content for the activity (rendered via the socialActivityInterpreterLocalService.interpret() call)
  • description - The description of the time of the activity (e.g. "A few seconds ago", "Yesterday", or a date/time)
  • geocoderAddress - The location information for the user who did the activity (more on this below)
  • title - The title of the activity
  • userDisplayURL - The user's profile URL
  • userFullName - The user's name
  • userPortraitURL - The URL to the user's profile picture
The resulting JSON object output by the RESOURCE_PHASE looks like
{
  "jsonArray": [{"body":"....", "geocoderAddress", "France", ...}, {...more activities here...}]
}

 

Geocoder Details

This app uses Google's Geocoder functionality to turn a human-readable address into a Latitude/Longitude, for placing on the map.  The address is taken from your profile, either your full address (if you entered it), or your country (if you entered it), and failing that, defaults to Liferay's headquarters near Los Angeles :)  We do NOT take your full street address.  Don't want anyone visiting you and telling you your wiki edit was wrong :)
 
To fetch the user's address, we make a call to Liferay's AddressService and look for the user's "Primary" address:
#foreach ($address in $user.getAddresses())
  #if ($address.isPrimary())
    #set ($city = $address.getCity())
    #set ($region = $address.getRegion().getName())
    #set ($country = $address.getCountry().getName())
    ## more stuff 
  #end
#end
 
If the user has not entered their address, we then look at a custom User Attribute called country that we have on liferay.com:
#set ($country = $user.getExpandoBridge().getAttribute("country"))
Once the user's location is known, it is stored in the resulting JSON object and shipped to the client during the AJAX call, where we pass the address to Google's geocoder API (or fetch a cached result if the address has already been geocoded).
 

Time Description

A simple way to make a more human-readable timestamp, by looking at how long ago an activity occurred, and generating a nice looking string to represent it.
#set ($now = $dateUtil.newDate())
#set ($millisecondsBetween = $now.getTime() - $socialActivity.getCreateDate())
#if ($millisecondsBetween < 60000)
  #set ($description = $languageUtil.get($locale, "a-few-seconds-ago"))
#elseif ....
  ## more strings for different time differences
#end
 

Making the AJAX call

Using AlloyUI, a simple call is made back to the server, to fetch the results of the RESOURCE_PHASE code, using the below javascript:
function ${portletNamespace}getSocialActivities() {
  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}socialActivityCache = responseData.jsonArray || [];
              ${portletNamespace}renderSocialActivity();
            },
          failure: function(event, id, obj) {
          }
        }
      }
    );
  }
);

}
The resulting JSON object is stored, and the renderSocialActivity() function is called to loop through the activities.
 

Rendering the map

You can see the javascript that is used to render the map in the source code below.
var ${portletNamespace}googleMap = new google.maps.Map(
  document.getElementById("${portletNamespace}map"),
  {
    center: new google.maps.LatLng(35, 0),
    mapTypeControl: false,
    mapTypeId: google.maps.MapTypeId.ROADMAP,
    navigationControl: false,
    scaleControl: false,
    streetViewControl: false,
    zoom: 2
  }
The (35,0) is arbitrarily in Northern Algeria.  Don't ask where I came up with that :)  You can turn on additional map controls (e.g. the ability to switch to sattelite view vs. road vew), and configure the default zoom level, with the above options object (you could even make these part of your web content structure, to make it easier to tweak).
 
The javascript code that actually places the bubbles on the map:
function ${portletNamespace}openInfoWindow(position, content, zIndex) {
	var infoWindow = new google.maps.InfoWindow(
		{
			content: content,
			position: position
		}
	);

	infoWindow.setOptions(
		{
			disableAutoPan: false,
			zIndex: zIndex
		}
	);

	infoWindow.open(${portletNamespace}googleMap);

	setTimeout(
		function() {
			infoWindow.close();
		},
		15000
	);
}

Notice the hard-coded values for timeouts. This could be improved by making it part of the web content's structure (like height), so that it could be configured easier. Also notice the zIndex argument - this makes sure that newer bubbles appear on top of older bubbles.

The geocoderAddress returned as part of the JSON object (from the RESOURCE_PHASE) is geocoded using Google's geocoder API:

 

geocoder.geocode(
  {"address": geocoderAddress},
  function(results, status) {
    if (status == google.maps.GeocoderStatus.OK) {
      ${portletNamespace}openInfoWindow(results[0].geometry.location, content, ${portletNamespace}index);
      ${portletNamespace}geocoderAddressCache[geocoderAddress] = results[0].geometry.location;
    }
  }
);
Notice we place the resulting address in a cache object, so we don't have to geocode the same address again (Google places a limit on the number of geocoding calls a given IP address can make in a given day).
 

Timeouts

This app is not truly realtime.  For a truly realtime result (which would be rather boring, since only one or two activities occur every minute), one would need to install some kind of ModelListener hook into Liferay, listening for new activities, and maintain a long-running comet-style open connection (similar to how liferay.com's chat functionality works) to feed new activities to the client side.  Instead, there are various timeouts that occur in the javascript which place the most recent block of activities on the map at random intervals.  Each activity's Time To Live is hard-coded at 15 seconds (again, this could be configured via a web content structure element), and the period between events is random between 10 and 20 seconds using this call:
setTimeout("${portletNamespace}renderSocialActivity()", 10000 + (Math.floor(Math.random() * 10000)));
After all of the activities in a given block are rendered, a new set of activities is fetched using the same AJAX call as before, and the loop begins all over again.
 

Random Notes and Improvements

There is a lot of noisy #set directives in the Velocity code in the RESOURCE_PHASE, because of the nuances of Liferay's WCM (in particular, because you don't have access to a "live" ThemeDisplay object, we have to create one and populate it with necessary content from the "sparse" name/value theme-display property available from the request object, which is available to WCM templates).  Don't let it distract you!
 
I am hoping some of you may be interested in making improvements, such as:
  • Making more things configurable, like the timeouts, map options, etc
  • Making it truly real-time using a ModelListener Hook as described above
  • Fetching more kinds of activities (for example, right now, the map won't show blog entries, since these occur in the user's "group", not the liferay.com group)
  • Accessing activities from outside of liferay.com!  Perhaps integrating with your Facebook profile, the Liferay twitter stream, or some other activity feed
  • Others?

 

The full Source Code

If you want to use this for your own purposes, below is the source code.  Here are the steps to use this:
  1. Create a web content structure, and cut and paste the below structure code into it.
  2. Create a web content template, associate it with the above newly-created structure, and cut and paste the below Velocity code into your template.
  3. Create a new web content article, selecting the above structure and template.
  4. Add a "Web Content Display" portlet to a page, and configure it to show the newly created article created in step 3.
#set ($portletNamespace = $request.portlet-namespace)
#set ($scopeGroupId = $getterUtil.getLong($request.theme-display.scope-group-id))
#set ($timeZone = $timeZoneUtil.getTimeZone($request.theme-display.time-zone))
#set ($userId = $getterUtil.getLong($request.theme-display.user-id))

#set ($height = $getterUtil.getString($height.data, "300"))

#if ($request.lifecycle == "RENDER_PHASE")
  <link href="//code.google.com/apis/maps/documentation/javascript/examples/standard.css" rel="stylesheet" type="text/css" />

  <script type="text/javascript" src="//maps.google.com/maps/api/js?sensor=false"></script>

  <div id="${portletNamespace}map" style="height: ${height}px; margin-bottom: 1.5em; width: 100%;"><!-- --></div>

  <script type="text/javascript">
    var ${portletNamespace}geocoderAddressCache = new Object();

    var ${portletNamespace}googleMap = new google.maps.Map(
      document.getElementById("${portletNamespace}map"),
      {
        center: new google.maps.LatLng(35, 0),
        mapTypeControl: false,
        mapTypeId: google.maps.MapTypeId.ROADMAP,
        navigationControl: false,
        scaleControl: false,
        streetViewControl: false,
        zoom: 2
      }
    );

    google.maps.event.addDomListener(window, "load", ${portletNamespace}getSocialActivities);

    var ${portletNamespace}index = 0;
    var ${portletNamespace}socialActivityCache = [];

    function ${portletNamespace}getSocialActivities() {
      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}socialActivityCache = responseData.jsonArray || [];

                  ${portletNamespace}renderSocialActivity();
                },
                failure: function(event, id, obj) {
                }
              }
            }
          );
        }
      );
    }

    function ${portletNamespace}openInfoWindow(position, content, zIndex) {
      var infoWindow = new google.maps.InfoWindow(
        {
          content: content,
          position: position
        }
      );

      infoWindow.setOptions(
        {
          disableAutoPan: false,
          zIndex: zIndex
        }
      );

      infoWindow.open(${portletNamespace}googleMap);

      setTimeout(
        function() {
          infoWindow.close();
        },
        15000
      );
    }

    function ${portletNamespace}renderSocialActivity() {
      if (${portletNamespace}socialActivityCache.length <= 0) {
        ${portletNamespace}openInfoWindow(new google.maps.LatLng(35, 0), Liferay.Language.get("there-are-no-recent-activities"), 1);

        return;
      }

      if (${portletNamespace}index >= ${portletNamespace}socialActivityCache.length) {
        ${portletNamespace}index = 0;

        setTimeout("${portletNamespace}getSocialActivities()", 1000);

        return;
      }

      var content =  '<div>' +
              '  <a href="' + ${portletNamespace}socialActivityCache[${portletNamespace}index].userDisplayURL + '">' +
              '    <img alt="' + ${portletNamespace}socialActivityCache[${portletNamespace}index].userFullName + '" style="float: left;" height="44" hspace="4" vspace="4" src="' + ${portletNamespace}socialActivityCache[${portletNamespace}index].userPortraitURL + '" />' +
              '  </a>' +
              '  <div>' +
                  ${portletNamespace}socialActivityCache[${portletNamespace}index].description +
              '  </div>' +
              '  <div>' +
                  ${portletNamespace}socialActivityCache[${portletNamespace}index].title +
              '  </div>' +
              '  <div>' +
                  ${portletNamespace}socialActivityCache[${portletNamespace}index].body +
              '  </div>' +
              '</div>';

      var geocoderAddress = ${portletNamespace}geocoderAddressCache[${portletNamespace}socialActivityCache[${portletNamespace}index].geocoderAddress];

      if (geocoderAddress) {
        ${portletNamespace}openInfoWindow(geocoderAddress, content, ${portletNamespace}index);
      }
      else {
        var geocoder = new google.maps.Geocoder();

        geocoderAddress = ${portletNamespace}socialActivityCache[${portletNamespace}index].geocoderAddress;

        geocoder.geocode(
          {"address": geocoderAddress},
          function(results, status) {
            if (status == google.maps.GeocoderStatus.OK) {
              ${portletNamespace}openInfoWindow(results[0].geometry.location, content, ${portletNamespace}index);

              ${portletNamespace}geocoderAddressCache[geocoderAddress] = results[0].geometry.location;
            }
          }
        );
      }

      ${portletNamespace}index = ${portletNamespace}index + 1;

      setTimeout("${portletNamespace}renderSocialActivity()", 10000 + (Math.floor(Math.random() * 10000)));
    }
  </script>
#elseif ($request.lifecycle == "RESOURCE_PHASE")
  #set ($logFactory = $portal.getClass().forName("com.liferay.portal.kernel.log.LogFactoryUtil"))
  #set ($log = $logFactory.getLog("com.liferay.portal.util.PortalImpl"))
  #set ($portalBeanLocator = $portal.getClass().forName("com.liferay.portal.kernel.bean.PortalBeanLocatorUtil"))
  #set ($portletBeanLocator = $portal.getClass().forName("com.liferay.portal.kernel.bean.PortletBeanLocatorUtil"))

  #set ($fastDateFormatFactoryUtil = $portal.getClass().forName("com.liferay.portal.kernel.util.FastDateFormatFactoryUtil"))
  #set ($jsonFactory = $portalBeanLocator.locate("com.liferay.portal.kernel.json.JSONFactoryUtil"))
  #set ($permissionThreadLocal = $portal.getClass().forName("com.liferay.portal.security.permission.PermissionThreadLocal"))
  #set ($socialActivityInterpreterLocalService = $portalBeanLocator.locate("com.liferay.portlet.social.service.SocialActivityInterpreterLocalService.velocity"))
  #set ($socialActivityLocalService = $portalBeanLocator.locate("com.liferay.portlet.social.service.SocialActivityLocalService.velocity"))
  #set ($userLocalService = $portalBeanLocator.locate("com.liferay.portal.service.UserLocalService.velocity"))

  #set ($dateFormatDateTime = $fastDateFormatFactoryUtil.getDateTime(1, 3, $locale, $timeZone))
  #set ($portalURL = $httpUtil.getProtocol($request.attributes.CURRENT_URL) + "://" + $getterUtil.getString($request.theme-display.portal-url))

  #set ($themeDisplay = $portal.getClass().forName("com.liferay.portal.theme.ThemeDisplay").newInstance())

  #set ($V = $themeDisplay.setLocale($locale))
  #set ($V = $themeDisplay.setPathImage($getterUtil.getString($request.theme-display.path-image)))
  #set ($V = $themeDisplay.setPathMain($getterUtil.getString($request.theme-display.path-main)))
  #set ($V = $themeDisplay.setPermissionChecker($permissionThreadLocal.getPermissionChecker()))
  #set ($V = $themeDisplay.setPortalURL($portalURL))
  #set ($V = $themeDisplay.setScopeGroupId($scopeGroupId))
  #set ($V = $themeDisplay.setTimeZone($request.theme-display.time-zone))
  #set ($V = $themeDisplay.setUser($userLocalService.getUserById($userId)))

  #set ($socialActivities = $socialActivityLocalService.getGroupActivities($scopeGroupId, 0, 50))

  #set ($jsonArray = $jsonFactory.createJSONArray())

  #foreach ($socialActivity in $socialActivities)
    #set ($socialActivityFeedEntry = $socialActivityInterpreterLocalService.interpret($socialActivity, $themeDisplay))

    #if ($validator.isNotNull($socialActivityFeedEntry))
      #set ($user = $userLocalService.getUserById($socialActivity.getUserId()))

      #set ($geocoderAddress = "")

      #foreach ($address in $user.getAddresses())
        #if ($address.isPrimary())
          #set ($city = $address.getCity())
          #set ($region = $address.getRegion().getName())
          #set ($country = $address.getCountry().getName())

          #set ($s = "")

          #if ($validator.isNotNull($city))
            #set ($s = $s + $city + ",")
          #end

          #if ($validator.isNotNull($region))
            #set ($s = $s + $region + ",")
          #end

          #if ($validator.isNotNull($country))
            #set ($s = $s + $country)
          #end

          #if ($validator.isNotNull($s))
            #set ($geocoderAddress = $s)
          #end
        #end
      #end

      #if ($validator.isNull($geocoderAddress))
        #set ($country = $user.getExpandoBridge().getAttribute("country"))

        #if ($validator.isNotNull($country))
          #set ($geocoderAddress = $languageUtil.get($locale, $stringUtil.merge($country)))
        #end
      #end

      #if ($validator.isNull($geocoderAddress))
        #set ($geocoderAddress = "Walnut, CA, United States of America")
      #end

      #set ($now = $dateUtil.newDate())

      #set ($millisecondsBetween = $now.getTime() - $socialActivity.getCreateDate())
      #set ($description = $dateFormatDateTime.format($socialActivity.getCreateDate()))

      #if ($millisecondsBetween < 60000)
        #set ($description = $languageUtil.get($locale, "a-few-seconds-ago"))

        #if ($validator.equals($description, "a-few-seconds-ago"))
          #set ($description = "A few seconds ago.")
        #end
      #elseif ($millisecondsBetween < 3600000)
        #set ($minutes = $millisecondsBetween / 60000)

        #set ($description = $languageUtil.format($locale, "about-x-minutes-ago", $stringUtil.merge([$minutes]), false))

        #if ($validator.equals($description, "about-x-minutes-ago"))
          #set ($description = "About " + $minutes + " minute(s) ago.")
        #end
      #elseif ($millisecondsBetween < 86400000)
        #set ($hours = $millisecondsBetween / 3600000)

        #set ($description = $languageUtil.format($locale, "about-x-hours-ago", $stringUtil.merge([$hours]), false))

        #if ($validator.equals($description, "about-x-hours-ago"))
          #set ($description = "About " + $hours + " hour(s) ago.")
        #end
      #elseif ($millisecondsBetween < 604800000)
        #set ($days = $dateUtil.getDaysBetween($dateUtil.newDate($socialActivity.getCreateDate()), $now, $timeZone))

        #set ($description = $languageUtil.format($locale, "about-x-days-ago", $stringUtil.merge([$days]), false))

        #if ($validator.equals($description, "about-x-days-ago"))
          #set ($description = "About " + $days + " day(s) ago.")
        #end
      #end

      #set ($jsonObject = $jsonFactory.createJSONObject())

      #set ($V = $jsonObject.put("body", $socialActivityFeedEntry.getBody()))
      #set ($V = $jsonObject.put("description", $description))
      #set ($V = $jsonObject.put("geocoderAddress", $geocoderAddress))
      #set ($V = $jsonObject.put("title", $socialActivityFeedEntry.getTitle()))
      #set ($V = $jsonObject.put("userDisplayURL", $user.getDisplayURL($themeDisplay)))
      #set ($V = $jsonObject.put("userFullName", $htmlUtil.escape($user.getFullName())))
      #set ($V = $jsonObject.put("userPortraitURL", $user.getPortraitURL($themeDisplay)))

      #set ($V = $jsonArray.put($jsonObject))
    #end
  #end

  {
    "jsonArray": $jsonArray
  }
#end

The WCM structure is pretty darn simple:

<root>
  <dynamic-element name='height' type='text' index-type='' repeatable='false'/>
</root>

That's all folks!

New Community Activity Map

Company Blogs August 17, 2011 By James Falkner Staff

You may have noticed a new feature on our community homepage, http://liferay.org - the Recent Activity Map is a dynamic Google Map that displays activities from our community, as they happen.  The map itself is a standard Google Map - and therefore, you can scroll around (using the hand tool), zoom in and out, and use the standard keyboard shortcuts (e.g. arrow keys, +/- keys).

The location information is taken from your liferay.com profile - either your actual address (city, region, and country only, not your street address), or your country, if you entered it.  Your profile picture is also used, along with the clickable activity information in the bubbles that pop up.

Entering your Location

If you want your location on the map to be correct, you can either enter your country, or your full address.

To enter your country, go to "My Account", and select a country from the "Country" drop-down list.  Note that when only entering country information, then all of your activities will appear to emanate from the geographical center of your country, which may be in the middle of a lake or the top of a mountain :-)

To enter a more accurate address, click on the "Address" tab on the right, and enter a complete address.  Make sure to mark it as your "Primary" address, as this is the address used for the map.

From then on, your activitiy bubbles will be shown in their proper location.  

Technical Notes

This is the best part: the map is implemented entirely as a Web Content Template!  Thanks to the power of Liferay's WCM system, and Peter Shin, who noticed my portlet was basically doing an AJAX call to the portlet's serveResource facility, Peter converted this to a velocity-based web content template (similar to Ray's earlier example).  Much easier to maintain and make updates.

The client side part is very simple, it just uses javascript and the Google Maps API to put up a map, and loop through the list of SocialActivity objects returned from Liferay's SocialActivity service.  User addresses are similarly looked up using Liferay's Address service.  If a user does not enter a full address, they can also just enter their Country through a drop-down on the user profile, implemented as an Expando attribute on the User entity.  We then access this through the ExpandoBridge for the User entity.

Once we have a set of activities, and associated usernames, profile URL and picture, timestamp (formatted to read nicer, such as "a few seconds ago" or "yesterday", also localized), then we use simple javascript to loop through them and place each of them on the map using the Google Maps API.  Once all entries are shown, we re-fetch a new set of activities. 

I think this gives our community a more lively, vibrant feel, especially for newcomers who happen upon this page early in their Liferay experience.  And it's fun seeing your name and activities "up in lights" every so often.  

Community Stats: Part 1

Company Blogs August 11, 2011 By James Falkner Staff

We all know that Liferay is a great social content and collaboration platform, which is why we at Liferay use it to implement our liferay.com website.  There are tons of interesting potential statistics to glean from this, to introspect into our community a bit more than the standard "50,000 registered users, 170,000 forum posts" kinds of stats that often find their way into slide decks at conferences.  I'd like to start a series of posts for interesting information about our community, that goes a bit deeper than typical stats.  Also, I welcome ideas for other stats, and I encourage everyone to use the community tools to their fullest extent.  For example, marking forum posts as questions when appropriate, and circling back to mark answers, and perhaps adding tags as appropriate.  

As part of a recent podcast which you'll be seeing/hearing in the near future, I wanted to generate some statistics on our activity level.  We often claim number like the above, but are they really indicative of the quality and usefulness of our community?  I think diving a bit deeper is useful, so here is the first in a series of stats I'll be looking at.  We are also planning a larger-scale survey (thanks to the awesome input from some of our valued community members in this thread), so look for that soon.

Registered User by Geography

First, a quick survey of registered liferay.com users by geography:

Ok, that's interesting, but what about by region?

This is not surprising to me - I've seen a lot of activity from our friends where Liferay enjoys a lot of mindshare.  

Recent Activity

Now, these first two charts are "all time" - that is, since the beginning of time (around 2002).  I thought it might be interesting to look at the numbers from more recent history, looking at the 5 year, 2 year, 1 year, 6 month, 2 month, and 1 month time periods.  I guess not surprisingly I didn't see much variation in the above.  When you get down to the last 2 months, the US starts to ramp up, perhaps we need to take more summer breaks or something.

 

Active, Unique Logins

Lastly, I often qualify the "50,000 registered users" stat with "well, of course, not all of them are active" and I usually guesstimate that 10% are active.  A basic measure of activity is when a user logs in.  You can use liferay.com anonymously all you wish, but to contribute, you generally have to log in.  So let's look at the lastLoginDate field for our user database and graph it out for unique users.

I was pleasently surprised to see that over 10,000 unique users have actually logged in at least once since February alone (of course many of these were 1-timers but still).

Finally, I found this graph rather amusing given my recent bad luck with IE (courtesy of "William" and GraphJam):

Have an idea of the kinds of stats you may be interested in?  Leave comments here or in the thread mentioned above and what we can dig up.

Community Roundup

Company Blogs August 4, 2011 By James Falkner Staff

Hi all, it's time for another installment of your linky-linky Community Roundup!  It's been quite busy in the community and at Liferay.  Community activity is buzzing and we are gearing up for various Fall events and activities, starting off with our annual West Coast Symposium in California, where there will be several key announcements, including the unveiling of Liferay 6.1, availability of the Liferay Marketplace, and several new and innovative event features that I guarantee you'll love (more on this later), so put down your ice cream and get registered! You will not want to miss this one.

Now, on to the links.

Whew, I have learned my lesson about waiting too long to post roundups.  Hope everyone is having a safe and enjoyable summer (in the northern hemisphere) or winter (down undah), see you all soon in LA at the West Coast Symposium!

Community BugSquad Begins

Company Blogs July 13, 2011 By James Falkner Staff

You may have seen a previous mention of the Community BugSquad program in my last Community Roundup.  I'm excited to announce that the program has officially kicked off today!  The amazing volunteers of this program are contributing their time an expertise to review features in the upcoming Liferay 6.1 release, identify any new or regressed bugs (of course, Liferay is 100% bug-free, but it never hurts to make sure wink ), and improve their Liferay knowledge in a fun and educational environment.  My goal is to do this for every release.  

The first phase of the program involves reviewing the new Liferay 6.1 CE Features (such as Staging w/Branching, Personalizable Pages, Dynamic Site Templates, and others), with the goal of identifying any usability gaps, or other issues that would be encountered if this were released as-is, and recording in the form:

The second phase of the program involves downloading and trying our upcoming Release Candidate builds for Liferay 6.1, and finding (but not necessarily fixing) bugs.  Again, not that there would be any, but if there are, and they are known about before the final release, they have a good chance of getting fixed!  A win-win situation all around, and the team gets some hands-on Liferay experience, and valuable networking with other community members and Liferay staff.  If you're interested in participating, just leave a note in the comments below, and you'll be signed up!

 

Community Roundup

Company Blogs June 13, 2011 By James Falkner Staff

8 Days to the official start of summer in the northern hemisphere.  2 days until the France Symposium.  That sounds like a good reason for another community roundup!  Hope everyone has a safe and fun summer season.  I have a love/hate relationship with it.  I love the summer season, but it means that from now on, the days will grow increasingly shorter and colder.  Luckily the Liferay community keeps me energized and warm.  On to the links!

  • As you may know, Liferay is looking to release the next version of Liferay Portal, version 6.1, later this year. We have found that engaging the community early on in the release cycle makes for higher quality and less migration issues, so I am organizing two special programs: Liferay Community Verifiers, and The Liferay BugSquad. Check out this forum thread, and respond if interested in participating!
  • There has been a lot of interest recently around Liferay User Groups.  Liferay has maintained a list of community events for our user groups in the past, however we are going to take it a step further with the introduction of user group "homes" on liferay.org!  Groups will get a set of collaboration applications (forums, wikis, calendars, etc) and a presence on the site.  Watch for this in the next couple of weeks.
  • Have a SCORM-based LMS and wish to surface its learning content in Liferay?  Arcusys has just released their SCORM module for Liferay to the community under LGPL.  Very nice work!  More background here, and here.
  • The 100 PaperCuts team is taking a short breather in advance of the 6.1 release.  Many of the same volunteers have signed up for the Community Verifier and 6.1 BugSquad program (see above).  It's been great getting to know everyone, and all of your hard work is definitely appreciated by the wider community!
  • The Liferay France Symposium is only days away, in beautiful Paris.  Featuring a host of Liferay experts and partners, the symposium will be a full day of discussion, demo, and use case studies.  If you are in the area, onsite registration is available!
  • The Liferay Hungary Symposium was held last month, and also featured a great set of speakers.  Steve provides some detail in his blog. If you missed the event, slides are available, and pics are up! [set 1, set 2].
  • On June 21, at the University of Szeged, Szeged Tech Meetup will be held, featuring discussions about Liferay.
  • Interesting.  Liferay is very often used to.. well.. integrate stuff.  So Steffen's very useful blog is well-titled. 
  • Swisher International designed a great corporate site on Liferay - and won an ADDY!
  • Alexey and EmForge continue to give back to the community with the release of Liferay 6.0.6 AMI images for Amazon EC2.  An easy and cheap way to quickly get a manageable Liferay instance up and running!
  • Alexey also provided a nice overview to Model Listener hooks for Liferay.  I'll see if I can't find time to update the wiki page.
  • Speaking of giving back, it's always nice to see ideas pop up and be driven by the community.  Case in point, providing Liferay's Document Library with anti-virus scanning ability.  This idea is well on its way to completion by those involved!  Great work by all!
  • Liferay LIVE continues its excellent series on all things Liferay.  This week Neil Griffin presented on Developing JSF 2 Portlets with ICEfaces, AlloyFaces, and LiferayFaces [slides now, video coming soon].  In the coming weeks we plan on doing encore presentations of several of  the East Coast Symposium presentations, so watch for those!
  • In a previous life I used to work on Accessibility, so I know how much attention to detail is required to make a site fully accessible.  Liferay makes it easy, as these sites can testify.  It's way more than alt attributes :)
  • Ben provides a nice overview of Liferay Layout Templates.
  • translate.liferay.com continues to enjoy tremendous success, giving our community a much-needed refresh of many of the translations provided out of box.  120 users and 41 languages, and the list keeps growing!  Top translations last week included German, Croation, Basque, Japanese, and Portugese.  And now, the core Liferay Portal translations are being managed through translate.liferay.com.  Wow!
  • Wondering about the benefits of an Alfresco + Liferay solution?  An interesting discussion has broken out on LinkedIn about the relative merits.  Take a look!
  • Couple of JIRA updates: we turned on rich text editing last week, so now your descriptions and comments can include all sorts of cool features.  Also, you may notice two new ticket types: "Epics" and "Stories".  We are going to begin using these to track Roadmap items and finally get rid of the always-outdated-the-moment-it-is-edited Roadmap wiki page.  This way the community can see, at a glance, what's coming down the pipe!
  • Creating new services using Liferay's Service Builder framework is now possible, graphically, using Liferay IDE.  This is now part of the nightly update, and slated for inclusion into the upcoming 1.3 release.  Nice work Greg et al!  [Also, a nice excerpt from Rich Sezov's Liferay in Action discusses Service Builder in detail].
  • The Holy Grail of Liferay Services would be a giant book that had a chapter for each Liferay service, along with descriptions of the underlying data model used to link everything together, java, javascript, and web services APIs to access the content, descriptions of all those esoteric formal parameters, etc.  While this isn't that, it's a neat start and a nice contribution.
  • Kristof provides us a fully-fleshed out example of embedded Web Content in your Liferay theme.  This keeps your editors from ever having to mess with your finely-crafted theme files!
  • Our very own Juan Fernández has released his latest beast of a contribution, the Wordpress Importer!  Bring all your content from Wordpress into Liferay with one click.  Very useful!
  • Interesting comparison of Liferay vs. Drupal.  Some of the items are questionable in their accuracy and even relevancy (why do we get an X next to PHP?). 
  • Recent Liferay Blog Posts:  Yet Another Liferay JSON Service Example, Liferay Portal Translation Project, Igor on JSON web service improvements, Liferay Is Not A Sausage Factory, and finally Liferay.com Mobile Sites and Responsive Layouts.
  • Recent Wiki Updates: Translation Team, WebDAV, JBoss Tips, Custom Logging, Accessible Sites, 100 PaperCuts, Liferay Development Style, JSON Serialization, JSON Web Services, and finally CMS Template (Velocity).

Ok, I said last time I'd do this every 2 weeks.  It's been 3 weeks.  That's an improvement from 4 weeks.  Baby steps, y'know? Finally, I'll leave you with my new desktop background pic.

Showing 61 - 80 of 103 results.
Items 20
of 6