Bloggers recientes

Nate Cavanaugh

Staff
37 Mensajes
17 de septiembre de 2014

Vicki Lea Tsang

Staff
5 Mensajes
11 de septiembre de 2014

Martin Yan

Staff
6 Mensajes
10 de septiembre de 2014

Angela Wu

Staff
3 Mensajes
8 de septiembre de 2014

Máté Thurzó

Staff
3 Mensajes
8 de septiembre de 2014

Mohit Soni

Staff
2 Mensajes
4 de septiembre de 2014

Matti Tahvonen

4 Mensajes
2 de septiembre de 2014

Jorge Ferrer

Staff
57 Mensajes
29 de agosto de 2014

Olaf Kock

Staff
78 Mensajes
26 de agosto de 2014

Ronald Sarayudej

Staff
151 Mensajes
25 de agosto de 2014

Liferay Portal 6.2 Release Candidate 1

General Blogs 24 de septiembre de 2013 Por James Falkner Staff

You may have noticed over the weekend that Liferay released a first "Release Candidate" build named RC1. Download it here:

This is a build that could potentially be promoted to the first GA release! From here on out, Liferay's release and QA teams will be testing each RC, and if issues are found that need to be fixed (characterized as a "Show Stopper" bug), another RC will be done the following week, and repeat. Once a suitable RC is produced, it will simply be renamed GA1 and the party can begin. So we are officially on "Release Watch".

Around 220 fixes and 37 "Stories" (minor improvements) have been committed to this RC since the last development release. And, as usual, the community has been a huge help with finding, fixing, and writing regression tests for all those issues that Liferay can never find - issues that occur in your specific environments and upgrade scenarios that will help us improve the product and make your transition to the release (or the first impression for newcomers) that much better! So kudos to the many of you that have contributed to this release - it is a testament to open source and to the enthusiasm and passion shared by those in our community (if you contributed, be sure to add your name to the Community Contributor Hall of Fame!).

In this post, I'd like to briefly share some statistics from the community about this release, as well as some stats from our various community programs that offered a diverse and focused way to give something back.

Overall 6.2 Contributions by non-Liferay staff

  • 1025 New Bugs filed (885 Bugs, 114 Feature Request Ideas, 690 total Resolved)
  • 224 Community Contributions (!)
  • 10,000+ Issues resolved (since Jan 6, 2012, the day 6.1 was released, and including all ticket types like bugs, features, stories, tasks, etc, regardless of origin)

Community Verifiers in 6.2

  • 691 Bugs Verified
  • 645 Resolved
  • 46 Unresolved

Bugsquad in 6.2

6.2 Translations (for Portal and Plugins)

  • A sum total of over 100,000 combined individual new submissions, suggested modifications, and approval actions (The version of Pootle used by the Translation Team is not good at generating statistics, so this is a sum total of all users and their actions, including contributions of string translations, as well as "approvals", via a screen-scrape!).

6.2 Community Beta Program

  • 72 Participants
  • 130 Issues reported
  • 81 Issues Resolved

As you can see, a lot of work went into this release, both from Liferay staff and our generous and ambitious community, and I would like to personally congratulate and thank each and every person who contributed to this release. I've been using the new release for a couple of months for day-to-day tasks and have to say it is well worth the upgrade.

If you're curious what the new release is all about, check out the RC build, and read the official docs to learn more about the release. A lot of blood, sweat, and tears has gone into this release, and I hope you find that it meets or exceeds your needs! As always, keep an eye on the Release Dashboard for up-to-the-minute information about the final builds and ultimate release.

2013 Liferay Marketplace App Contest Results

General Blogs 30 de agosto de 2013 Por James Falkner Staff

Today Liferay announced the winners of the 2013 Liferay Marketplace App Contest, and I'm particularly happy to see so many great entries and apps in the contest! The contest entries covered a wide range of use cases and employed some pretty cool technology above and beyond Liferay itself, and is a testament to the flexibility and power of Liferay. But it's more than that. Seeing the passion from our community and the innovations in these apps underscores the fact that Liferay is a great platform, but the true power comes from the ideas and applications from you, our community of customers, partners, and enthusiasts.

As of today, there are about 100 community, non-Liferay apps on the Marketplace, with another 100 in the pipeline in various stages of approval. All of them bring new value and ideas to our community, and many of them are open source (including our two grand prize winners). These apps and their developers bring huge value to our community and to your Liferay deployments. Of those entries submitted, I personally found 10 or so apps I will be regularly using in my daily work from now on, and I can't wait to see what other gems you come up with in the months and years ahead.

Below are our winners and runners-up, along with some interesting facts about the app and/or the developers. You should check these apps out, and while you're at it, check out the rest of the really cool apps on the marketplace. It was really tough to choose the best of the best, because there were so many awesome entries! Great job, everyone!

Note that due to several issues in the previous Liferay release (6.1 CE GA2 and EE GA2), several of these apps would not work out of the box with GA2 without workarounds, so couldn't be on the Marketplace, but were still able to participate in the contest anyway (it wouldn't have been fair otherwise!). So some of the below apps do not have links, as they are still in the process of being vetted against the new release.

Valamis (grand prize winner)

Valamis is a social learning environment for sharing and receiving knowledge. You can use it as your organization's social learning environment. You can also use it to encourage individuals to collaboratively share tacit knowledge which cannot be found from books, but which is a combination of experience and wisdom.

Valamis was formerly known as Project Learn, one of our first Liferay Community projects! It's awesome to see this project graduate into a commercial open source tool. Also, they can make sweet videos! Janne and Jari have done a fantastic job and we're lucky to have them.

Props 'n' Prefs (grand prize winner)

This application allows to search and view system properties, portal properties, portal preferences and server preferences. It also allows you to delete portal preferences or server preferences, with full search of properties and their values, full content popups, one-click resetting of portal and server settings, and more!

Sébastien actually submitted around 5 or so apps to the contest. One prize per person! But pretty much every app is useful, and the scripting apps are in my go-to list now, but this one provides a huge help to admins everywhere that get stuck with some of Liferay's quirks when it comes to configuration, and integrates nicely with the platform.

Mercado

Mercado is a marketplace for software applications for an IT enterprise to create a user centric collaborative interface within a controlled environment. Create a knowledge repository of products, reports, videos, photos, code, and other downloadable items, with included workflow, dashboards, moderation, search, ratings, and other features.

Cignex Datamatics needs no introduction, and has been a long-time community and professional partner. If you're attending the Liferay North America Symposium this year, you'll have a chance to talk to them about this and other apps.

Remote jBPM Workflow

A drop-in workflow engine which features REST services exposed by a GWT-based console, and integration with JBPM 5.4. Other features include form management, programmatic access to form values, and more!

Liferay is a well-known integration platform, and this app gives a nice turn-key solution for your JBPM needs.

BTop

BTop is an online payment app which will help you optimize transactions and facilitate online payments services management. By using an easy administration board you will be able to manage payment plugins, sellers configuration, payment methods and get a report for each transaction. Other features include a customizable rules engine, secure payments, geolocalization of payments, and integration with Liferay's shopping portlet.

This is an app that is currently in the pipeline for the Marketplace, so not yet available, but will be very soon! 

Workflow Selection by Web Content Structure Type

This handy app allows you to assign different Liferay workflows based on the type (structure) of web content articles. Simple, yet powerful addition to your workflow arsenal. Why didn't I think of that! :)

Permeance is also a heavy contributor to our various community initiatives like the Bugsquad, Community Verifier, and most recently (and still in progress) the Liferay 6.2 Community Beta Program.

Webtown Web Content Display

This app is an enhanced web content display portlet. It allows you to filter the web content you want to display in several added ways, including by scope, structure, tags, and categories. It also lets you specify the listing and detailed view templates you want to use for each of the structures, and has additional extra features like paging, social bookmarks, ordering, and more.

PDF Viewer

An enhanced PDF viewer, supporting features such as browsing the document structure (chapters, subchapters), zoom, and searching within the PDF. It also integrates with LIferay's global search capabilities, and a friendly URL scheme for documents.

Alexey was also recognized as a Contributor of the Year last year.

OpenScape Web Collaboration

OpenScape embeds a real-time document sharing capability into your team collaboration space. With a simple click from your Liferay Portal, you can select contacts, friends, or team members that need to participate in a web collaboration session, and start up a collaborative session between participants.

The team at Siemens did a great job integrating their web collab product with LIferay. It's not just an IFrame!

Ajaxable search container paginator

This handy hook turns every use of Liferay's Search Container taglibs and APIs into an ajax-powered, no-page-refresh-needed pagination tool (this includes even Liferay's own use of the Search Container).

Omar did an awesome job recognizing a shortcoming of typical portals (and the portlet 2.0 spec), and provided a simple and elegant solution. It just works.

Liferay Instant Messaging System

Do you like to chat with your friends on your favorite social network? Have you been missing the same functionality in Liferay? Not anymore! Brand new Liferay Instant Messaging System brings you all you ever wanted packed in a single plugin.

Shout

A simple yet powerful app to exchange short messages on a Liferay site. Another really simple yet cool way to engage audiences on a Liferay site. It breaks down barriers to communication in a nice looking and performant way.

AjaxQuote Portlet

Real-time market quote data is streamed to one or more portlets on the page by Ajax and JavaScript technology. Watch the tickers move in real-time on the screen (with no stress on the back-end; all client side services). Supports anonymous and personalized tickers.

Popups

This app lets you write custom popups (using Liferay's WCM system), and target those popups using a flexible, rules-based interface. Target based on URL, site role (with autocomplete), and select the frequency for the popups.

BonitaBPM Integration

This app provides a complete integration with BonitaBPM. Manage BonitaBPM processes, cases and user tasks from Liferay Portal & Synchronize Bonita users with Liferay.

Jack Rider was also featured on Radio LIferay recently, and has also been an avid supporter of Liferay and our community. Thanks, Jack!

Custom Landing Page Hook

After a user logs into Liferay, it is a common requirement to be able to redirect them to a different page based on their Organization or Site membership. This hook allows you to specify the desired redirection behavior using a properties file setting.

Tejas was also recognized as a Top Contributor, and this app is one of the most often requested on our forums. Now I have a place to point people.

Categories Multi Select

This app provides a new search facet, integrated with Liferay's Faceted Search, allowing one to select multiple categories to "drill down" into, while searching for assets, as well as a handy portlet that can be used to filter existing asset publisher apps on a given page.

stickyNotes

stickyNotes allow you to have a dashboard to post sticky notes, and it can be located anywhere on the portal. With stickyNotes you can post ads, events, reminders, or anything you can think of, in a visual and simple way, using multiple layouts and colors.

Random Content Rotator

This portlet allows a random piece of web content (text, image, document, video, or any combination) to be randomly displayed on page load. This is great for rotating banner images, side bar text/images, quotes, or testimonials.

Ask IRC

A handy way to integrate your portal with Liferay's official IRC channel! With this app, administrators and users have an easy and foolproof way to contact other community members in the official Liferay chat channel on FreeNode (#liferay). About a month ago I started noticing new users like "LRAdminInNeed824" join the Liferay chat channel and begin asking intelligent questions. Now I know why they are showing up! Thanks Bijan!

...In closing

A big thanks goes to each of the 70+ people who participated in our first ever app contest. It's great to see so many of you willing to showcase your awesome talents and ideas! Liferay would not be where it is today without the contributions, innovations, enthusiasm from our community. I can't wait for the next one!

New Release - Liferay Portal 6.1 CE GA3

General Blogs 23 de agosto de 2013 Por James Falkner Staff

I’m happy to announce that today, Liferay released the much anticipated Liferay Portal 6.1 CE GA3 release! DOWNLOADS: [Tomcat Bundle | GlassFish Bundle | JBoss Bundle | Maven Artifacts and Info | Source Bundle | Github Repoother bundles and support files].

This update contains over 500 fixes, most importantly those fixes related to Liferay’s Security Manager (née PACL) and Spring MVC, which prevented many of you from successfully publishing your apps to the Marketplace. These issues should now be fixed.

Version Name

Following Liferay's versioning scheme established in 2010, this release is called Liferay Portal 6.1 CE GA3. The internal version number is 6.1.2. See below for upgrade instructions from previous releases.

PACL Fixes

Earlier this year, Liferay made the use of its Security Manager optional, for those that wish to publish free apps. This will continue to be optional, though you are highly encouraged to make use of PACL in your apps - this is an important step for securing your apps, especially paid apps (once this feature is added to Marketplace).

In support of PACL, a new quasi-feature has also been added in this release - the PACL Policy Generator. This feature will automatically generate the necessary PACL policy declarations for you, saving you from having to manually test, modify, and re-test your apps. See the PACL Policy Generator's official documentation for details on how to use the generator.

If you’ve been waiting for this release to publish your app to the Marketplace, be sure to test your app with this release, and indicate that your apps are compatible with this release (and the associated EE release) by using liferay-versions=6.1.2+,6.1.30+ in your liferay-plugin-package.properties file. If you’ve already successfully published apps with liferay-versions=6.1.1+ or liferay-versions=6.1.1+,6.1.20+ then you don’t need to re-publish, but you should test against the release to ensure compatibility (see the compatibility note below).

For more detail, see:

  • LPS-33047 - PACL - As a developer I would like reasonable java operations such as classloading, reflection, native library access within libraries I include to not prevent me from developing plugins for the marketplace
  • LPS-32200 As a Liferay Marketplace Developer, it should be less time consuming and less error prone to identify and declare necessary PACL declarations

Security Fixes

Liferay is committed to producing high quality and secure products. As with all of Liferay’s CE update releases, many security bugs have been fixed, including all of the security issues reported to, and fixed in the context of, the Liferay Community Security Team. The security of our products is very important to our customers and the wider Liferay community, and we have processes in place to ensure that any security-related issues are promptly addressed and that our customers' data is kept secure. For more detail, check out Liferay's “/security” URL.

JDK 7 Support

Another addition to this release is support for Java 7. Typically, Liferay will add support for new underlying platforms in a minor release, but this one was too important to pass up. See LPS-29538 Java 7 Support for details.

Spring MVC Fixes

6.1 CE GA2 had a bug that prevented many Spring MVC-based apps from properly initializing. See LPS-29103 Custom Spring MVC -portlets broken after upgrading to Liferay 6.1 GA2 (web.xml listener order) for more detail, but rest assured it's no longer an issue!

Compatibility

Liferay aims to make releases within a given “release family” compatible, and are continually improving the development process to catch compatibility issues early and go through the proper deprecation process, and it is no different in this release. A handful of APIs have been deprecated in this release as a result of bugfixing. If you are using them, you should strongly consider modifying your apps to use their documented replacement APIs.

LPS-28713

  • com.liferay.portlet.documentlibrary.util.AudioProcessorUtil::setAudioProcessor()
  • com.liferay.portlet.documentlibrary.util.ImageProcessorUtil::setImageProcessor()
  • com.liferay.portlet.documentlibrary.util.PDFProcessorUtil::setPDFProcessor()
  • com.liferay.portlet.documentlibrary.util.RawMetadataProcessorUtil::setRawMetadataProcessor()
  • com.liferay.portlet.documentlibrary.util.VideoProcessorUtil::setVideoProcessor()

LPS-24411

  • com.liferay.portal.util.Portal::getAlternateURL()
  • com.liferay.portal.util.Portal::getCanonicalURL()
  • com.liferay.portal.util.PortalUtil::getAlternateURL()
  • com.liferay.portal.util.PortalUtil::getCanonicalURL()

Upgrade

As a general rule, you can upgrade from one major release to the next major release. For example, you can upgrade directly from Liferay 6.0.x to 6.1.2, but not from 5.2.x to 6.1.2. If you need to upgrade over several major releases, you'll need to run the upgrade procedure for each major release until you reach the release you want. See the official upgrade documentation for more detail and the explicit steps for upgrading. In particular, you can upgrade from 6.0.x, 6.1.0, and 6.1.1 to this release (6.1.2).

Known Issues

1. The GlassFish bundle has a known first-start issue (LPS-39095) with the following workarounds:

  • Stop and restart the domain
  • Add portal.security.manager.strategy=default to your portal-ext.properties file (this causes Liferay to use the default security manager configured by the application server. A security manager will not be used if the application server did not configure one) 

2. Due to a last minute JSP compilation build failure, the Liferay+Resin bundle is not yet available. Engineers are working on the fix as I type and it should be available soon! 

In closing

A special community shout-out goes to Rotterdam CS and Emeldi for helping us with the PACL and Spring MVC testing - their apps and staff helped us find several important issues before the release, which is an awesome by-product of all the great apps people are making! Thanks, ya'll!

This is most likely the last update for the 6.1 CE vintage - 6.2 CE is right around the corner, so if you’re interested in seeing what’s coming in the next release, be sure to get involved in the 6.2 Beta program!

Calling All Stations: Liferay Community Beta Program

General Blogs 6 de agosto de 2013 Por James Falkner Staff

We need your help!

The next release of Liferay Portal - version 6.2 - has been under development for over a year, and many of you have been instrumental in getting new features in, fixing old bugs (and new bugs), and generally improving the quality of each of the prior Milestone releases, and for that you should all be proud (and add your name to the Community Hall of Fame while you're at it!). We've made a lot of progress in terms of release quality and development process, and have exposed it through various avenues, such as the JIRA Development Board, Release Dashboard, Ideas Site, and the ongoing effort to improve the responsiveness to contributions (which we really, really value).

I am excited to announce the latest initiative in our community - the Community Beta Program! This program is kicking off in concert with the first release of Liferay 6.2 Beta, and is continues to serve as yet another way to improve quality. What better way to ensure the next release is suitable for you, than to give it a go and report the good, bad, and the ugly?

The Process

It's simple: sign up for the program, pick the 'tracks' you wish to follow (based on functional areas of Liferay), and then try out the Beta release, making note (in the form of JIRA tickets) of the bugs or confusing aspects of Liferay that you find.

Each track will have a dedicated written guide, and one or more members of Liferay's engineering team dedicated to your success.  The written guide will explain in more detail what is involved in the particular track, so you're not just left to your own devices (though you're free to explore anything you wish!) We will also have an area on liferay.com with a dedicated forum and supporting FAQ on which to collaborate by next week.

Liferay (the company) has a dedicated and awesome QA team that will be heavily involved in this program, and are going to be available for questions, collaboration, and will also be paying particular attention to the Beta team and its findings. If you're willing to try out the Beta, you'll get the attention you deserve.

How to get involved

The program is open to the entire community, all we ask is that you fill out the Beta Program Signup Form, so that we can ensure we get enough coverage in all of the functional areas of the release. We are asking for anyone interested to sign up by August 11 (next Sunday), so that we can kick off the program the following week.

Why should I participate?

Now the good part! Besides the tremendous gratitude you'll get from the Liferay Community (and the Liferay QA, Engineering, and Release teams), there are other benefits:

  • Get a sneak peek at new stuff in Liferay
  • Help us help you ensure that your future migration to the new version will be as smooth as possible
  • Meet and engage with others in our community, including our awesome QA and Engineering teams
  • Get enshrined in the annals of Liferay history (ok, it's just a README embedded in the release, but still..)
  • Get a leg up on the competition for Top Contributor awards
  • SWAG: Everyone loves SWAG. We have some special gifts for those that participate
  • The knowledge that you helped make a difference, gave a little back, and hopefully learn more about yourself and Liferay

So please, if you have some time in the next month to help us make this release the best ever, sign up and be on the lookout for additional communication in the days to come!

For more information about the Beta program, please see the Beta Program Wiki which has additional information about the tracks, and what to expect.

 

Community Roundup

General Blogs 11 de junio de 2013 Por James Falkner Staff

What's the time? It's time to get ill, with another Community Roundup! The Liferay Community has done some amazing things these last few months since the last roundup, and so here we go with another collection of things our community has been up to. On with the links!

  • Mobile apps and responsive design are all the rage these days, but how does that translate to Liferay? Visit these Liferay-powered sites on your mobile and see what all the fuss is about. Also, check out this video on Mobile Apps with Liferay from last year's EU Symposium.
  • As you know, last year we began the Community Verifier team, to help verify existing issues in Liferay, and clean up our JIRA database of backlogged issues. Earlier this year the team embarked on a competition to cut down the number of unverified bugs. I'm happy to report the team was able to verify over 360 of the 480 unverified bugs! The team will get free goodies and recognition for their efforts, and deserve a hearty thanks!
  • Jelastic has done an excellent write-up of how to deploy a highly available Liferay Cluster to their cloud, including configuration of Nginx, Tomcat, Liferay, and MySQL (with a sharded Liferay database).
  • Contributions from our community continue to roll in, with over 100 individual contributions for the upcoming 6.2 release. Edward Gonzales from Liferay's project management team has been instrumental in directing and encouraging community contributions, so if you see his name on your JIRA tickets from time to time, know that he's working to make sure contributions are treated with the respect and priority they deserve.
  • Speaking of contributors, our quarterly Top Contributor awards for Q1 2013 were awarded to Oli BayerGnaniyar Zubair, and Alexey Kakunin.  These three did some great work in Q1, and are examples of what our community is all about. Q2 is rapidly coming to a close, so keep those contributions coming! Bug fixes, features, translations, Marketplace apps, documentation, helping out on the forms, it all counts!
  • Want to test-drive Liferay in the cloud, for free? Check out BitNami's one-click Liferay Launcher and get an hour of a Liferay server to play with (of course with options to upgrade).
  • Although we get a ton of community traffic on the liferay.com forums, it is not the only place where one can get some awesome Liferay action. Check out the Liferay leaders on stackoverflow.com, who tirelessly help our community whereever they may be needed.
  • Zhao made an impressive 3D Liferay rendering, suitable for desktops, and drawing the ire of branding gurus everywhere :)
  • Anyone up for some LAR-gery (that's pronounced like "large-erry"). Ok, bad joke. Anyway, Terry shows you how to perform some surgery on LAR files to filter out junk and get clean data for a new Liferay instance.
  • I am enjoying the random tweets mentioning Liferay that have absolutely nothing to do with Liferay. I'm sure it's some sort of scam or SEO play, but they are enjoyable for a moment. Take this one: An quotation squander temporary so that java trust in there with liferay approximation. Come again?
  • Radio Liferay is back in action, with some really awesome episodes from Ville Ingman (Vaadin), Eduardo and Zeno on AlloyUI 2.0, and Zsolt Balogh on LESA. Future episodes will include myself, Jorge Ferrer, and Juan Gonzalez. If you have pressing questions you'd like answered, head over to the Radio Liferay Google+ Community and ask away!
  • KL shows us some awesome Advanced Navigation techniques for your Liferay Themes, to make it easier to navigate.
  • Back in April, we embarked on an interesting experiment: a 24 hour marathon tour around the world and visited Liferay Community members from almost every continent on the earth. The complete replay (along with person-by-person indexes) can be found on the Day Of Liferay page. One day I'll find time to cut and paste these into a nice summary. Also, I was very impressed with the Google Hangouts peformance, and will be using this in an upcoming regular virtual meetup (details coming soon!).
  • One of our first Liferay Community Projects has graduated! Check out Project Learn on the Liferay Marketplace. Hopefully we'll see more and more community projects exposed in this way. It's a great testament to the power of open source and open collaboration.
  • Getting involved with a local Liferay User Group is one of the best ways to grow your Liferay chops. New user groups in Birmingham (Alabama) and Cuba have started, and are looking for new members! If you are interested in starting your own User Group, there are new resources (and a nice video intro from Savoir-faire Linux) available on the User Group site. And don't miss our user group's upcoming events, such as India, or Hamburg.
  • From the releases desk: If you're watching the releases dashboard, you'll know that there have been recent releases of Liferay Faces, Social Office, Liferay IDE, and AlloyUI! New Liferay Portal CE releases expected later this year include Liferay Portal 6.1 CE GA3 and Liferay Portal 6.2.
  • Liferay Portal 6.2 is slated for release later this year, and it is packed with updates and new features. If you're interested in checking out the latest, Grab the latest Milestone build and dig in. We are quickly approaching the first Beta build of 6.2, and will once again rely on our awesome community to ferret out issues before GA.
  • Standards form the backbone of today's interoperable web, and Liferay also believes strongly in their advancement. Though at times they may be slow, they are critical to our industry. Don't miss the Java EE 7 Launch broadcast tomorrow, and be on the lookout for Liferay's involvement in this important standard.
  • If you're an Ubuntu fan, you may be aware of Ubutu Juju, a quick and easy way to deploy Ubuntu and its apps to public and private clouds. One such app is Liferay, whose Juju Charm is under development and will enable quick and easy deploying of Liferay, but more importantly, of wiring up dependent services like databases, app servers, load balancers, clustering support, etc. Contributions are always welcome :)
  • The Liferay conference season is officially underway, with the first symposium concluding on June 6 in Paris. If you've invested time and resources into Liferay, attending a Liferay conference is the absolute best way to strengthen that investment and ensure future dividends. Look for conferences in Berlin, Frankfurt, Madrid, San Francisco, London, and Florence this fall. And don't forget to grab the companion app [iOS, Android] on the way to the conference!
  • The Liferay Marketplace has relaxed its publication requirements, and the use of Liferay's Security Manager (aka PACL) is now optional, while usability issues are worked out. In the meantime, we've seen a lot of activity on the Marketplace, including over 50 community apps available now, with another 70 in the pipeline.  Keep 'em coming! And don't forget, amost every app1 on the Marketplace is eligible to enter the Liferay Marketplace App Contest, and win a free trip to a conference or an iPad Mini. See the official rules for details (you know there had to be some, right?).
  • Ideas: everyone has them, and they drive innovation into many things in life, including Software. With Liferay's Community Ideas Dashboard, we now have an easy to use tool to let our creativity loose, and socialize them throughout the community, in the hope that enterprising developers out there (including Liferay, Inc.) will run with them. Check it out, and let's hear your best ideas! Since its inception, there have been 135 ideas submitted, and the two highest vote-getting ideas have either been implemented (as a Marketplace app, thanks Rotterdam CS!) or are under development.
  • You may have noticed a subtle change at liferay.com - the web team has rolled out with the first phase of a website refresh, including better performance on mobile devices (no more menus taking up half the page, and a nice slide out navigator), and improved (and localizable) headers/footers. More to come!
  • Usually in this space I would list recent blog entries from Liferay staff members, since historically they were the only ones who could blog on liferay.com. That's no longer true, everyone can now blog, so check out the recent blog entries by our wonderful community, and learn something new each day!
  • There have been many awesome blog posts and wiki updates, and we try to keep everyone up to date monthly via the Community Pulse newsletter. If you wish to receive this, sign up here.
  • Liferay is in the running again this year for @cmscritic's 2013 Critic's Choice CMS Award! Please, take a moment to nominate Liferay for this prestigous award, and see if we can repeat again this year!
  • If you're attending CMSDay, Mitarbeiterportale, Jenkins User Conference, Community Leadership Summit, OSCON, or JavaOne this year, please.. stop by and say hi!

Well, that's all I have for now. Hope everyone has a safe and fun summer (or winter, for our southern hemisphere friends)! See you on the interwebs...

Liferay, WebSockets, and Node: Good Times!

General Blogs 8 de mayo de 2013 Por James Falkner Staff

Lately I've been tinkering a lot with lightweight, asynchronous, event-driven apps on Liferay using a variety of established techniques and frameworks. It's a nice way to build apps! After working it for a while, I wanted to share what I've learned, so put together a talk and was fortunate to be selected to attend and speak at this year's KCDC, and presented my learnings and a demo via a presentation on Event-driven Programming with Java and HTML5 WebSockets.
 
You can find the slides on SlideShare, and the demo code on GitHub.
 
At the end of the presentation I showed a demo, and I wanted to go into a bit more detail here. It is essentially a WebSockets demo using a Google Heatmap to visualize the location of bloggers who are blogging on a Liferay site. I used Tomcat 8.x for the JSR-356 WebSockets implementation, partly because I am comfortable it, but also because it has a bug that I wanted to use to demonstrate a point I made during the talk :) I'm pretty sure GlassFish doesn't suffer from this!
 

Social Driver

To simulate lots of people doing activity on Liferay (and therefore generating activities for which I can listen), I re-used my Social Driver, resurrected from 7Cogs. This code spawns theads that programmatically create users and create blog entries, wiki pages, forum posts, and do other activities like vote and comment on content. It does this in separate threads, which simulates lots of people doing things on your Liferay site.
 
I have covered the basics of the SocialDriver in my series of "7Cogs is Dead! Long Live 7Cogs!" posts (here, and here), which I hope to finish off in the next couple of weeks. 
 

Demo Part 1

In the first part of the demo, I have a Google Heatmap which visualizes the location of the fake users, based on their registered address (which is also faked). When content (blogs, wikis, forums) is created, a SocialActivity is generated. My hook listens for these events, and sends them to my WebSocket endpoint, which ships them to my client webpage with the Google map.
It all works great when you have a single thread generating events.  The map builds nicely, and all is well. However, a few seconds after you start up more threads, things get weird, and WebSocket messages emitted from the server get jumbled and mixed together, causing the browser to immediately fail the websocket connection, and the client comes to a grinding halt (Liferay happily continues to create activities, though).
 

Demo Part 2

In part 2 we used Wireshark to inspect the network traffic, in an attempt to debug the problem.  Looking at the network traffic reveals that in the end, the last few WebSockets frames are mixed up / jumbled up, causing the browser to misinterpret the bytes, and fail quickly (which is nice!).
 
The problem appears to be that the code in Tomcat for sending messages down the pipe isn't multithread-safe.  Two or more threads can get into the same area of code, and send content at the same time, and this is exactly what happened here: my blog thread and my wiki thread tried to send messages to the client at the same time, and one's message was mixed in with the other, causing the browser to issue a cryptic Could not decode a text frame as UTF-8.  Looking at the offending packet in Wireshark:
You can see the complete message of {"address":"Sudan"} but then some more bits that is the beginning of the next message, which the browser tries to interpret as text, and fails (it's actually the beginning of the next websocket frame).
 
Synchronizing the code that calls into Tomcat does the trick (e.g. via synchronized), but a) I shouldn't have to do that because it's part of the spec (and I think this is a bug in Tomcat), and b) Tomcat might not be the best place to scale out, especially because it's hosting Liferay already.  Node.js to the rescue!
 

Demo Part 3

Here, we let Node handle the websockets broadcasts to the clients, while Tomcat and Liferay handle the portal itself.
The code in my tiny node server (which requires websockets.js, via npm install websockets) does the trick.  It listens for messages over HTML (this could have, and probably should have been done with redis pub/sub but I was out of time), and then broadcasts them to all clients listening on the websocket. In this demo there's only one client, 
 
With node in place, and click the switch on the portlet, to switch over to it and then happily start up many threads and watch our heatmap build nicely:
 

Lessons learned

  • Coding event-based, asynchronous web program is fun and exciting! There are many frameworks and technologies to make it easy, both on client and server, and if I can do it, anyone can do it :)
  • It's really easy to integrate awesome apps with Liferay, due to its Java heritage, rich APIs and lightweight JSON or RESTful web services.
  • Java EE features (like JSR-356, aka WebSockets) and other upcoming technologies in Java EE 7 will lower the Java EE learning curve even further.
Enjoy!

Marketplace, PACL, and Community Plugins

General Blogs 1 de mayo de 2013 Por James Falkner Staff

If you've been developing plugins for the Liferay Marketplace, you're undoubtedly aware of the issues surrounding the development and publication process for apps.  The main issues are around the required use of the Security Manager (aka PACL), which has proven challenging to get right (or not even possible to make work, depending on the nature of the app, and the use of certain frameworks/libraries). This, plus other issues in the most recent release (such as LPS-29103) has meant that many of our most valued community members have been prevented from publishing to the Marketplace through no fault of their own, and they have not been shy about letting us know of their concerns (see here, and here). I personally find it very encouraging to see such passion and constructive criticism from our community, and I'm happy to report that we're making significant changes to the Marketplace to address these concerns and make Marketplace the high quality, go-to place for Liferay apps.

Here's what the team is working on right now:
  • Remove the requirement for Security Manager (aka PACL) to be enabled
  • Improve the "Denial Reasons" given when apps are rejected (usually because they fail a test case)
  • Document the environments in which apps are tested and more clearly specify requirements for metadata
  • Improve the Security Manager developer experience
The first item in particular is intended to bring back the kind of functionality we had in the legacy community plugins repository, but still ensure that the Marketplace contains quality apps that actually work as advertised (since apps will still be run through anti-virus checks and basic smoke tests). App developers will have a choice to publish their app with or without the use of PACL, and apps will be marked as such when viewed on the Marketplace. This will also make it less ambiguous for developers looking to enter the Marketplace App Contest with apps that otherwise work without PACL. PACL will still be required for apps offered for sale (once that feature is available). 
 
The other items relate to improving the developer experience of developing for Marketplace. We now have hundreds of apps and registered developers (not including Liferay itself) who are publishing to the Marketplace, and it's critical that the development and publishing process be as smooth, intuitive, and informative as possible. This is foremost on the team's mind, and if you have any additional feedback (besides those mentioned above), don't hesitate to make yourself heard either in the comments below, or in the Marketplace Developer forums.  We are hoping to implement these changes in the next couple of months, to resolve the difficulties with Marketplace development.
 
As far as the PACL experience, in the next Liferay releases (for both 6.1 and 6.2) will be a new PACL policy generator tool (LPS-32200) which will vastly simplify the creation of a PACL-enabled app. If you want to test-drive it now, go grab the 6.2 Milestone 5 build - it's fully implemented and ready for you to try out (here's how).
 
Finally, I want to let you know that we (as a company) make every effort to listen and respond to the open source community. We don't always get everything perfect right out of the gate, and sometimes it takes a while to make a change, but please know that Liferay depends on its community to point out the good and the bad, and make corrections as necessary. This is another example of why open source and open development processes are vastly superior to the alternative.

Community Blogging - Now Available

General Blogs 16 de abril de 2013 Por James Falkner Staff

I am very excited to announce that as of today, all Liferay community members can now create and maintain their own blogs on liferay.com! Wooo!! This was announced near the end of the A Day of Liferay, and has been on the TODO list for a while. The problem has always been how to maintain the quality of content on liferay.com, as blogs are generally considered to be a more reliable source of information about Liferay, and are generally expected to be of higher quality than your typical stream-of-conciousness found in other publishing areas like the community forums.

Blog Streams

Every community member's blog is maintained in their Profile (more on this later). What follows is a description of how all of our blog posts are aggregated and visualized on the community site:

The first important point is that we will start with two separate (visually) blog streams - the Staff Blog and the Community Blog. As you guessed, the Community Blog is for topics by and for the community. Anyone can post here, including Liferay staffers, and will do so more often than not. The Staff Blog is for posts by Liferay staffers that may have a more general target audience than just our community. As an example, Ray Auge would post his most recent work on OSGi to the Community Blog. Caris Chan would likely post her blog about Liferay's new offices in France to the Staff Blog.

There are three areas where these streams are evident:

  1. On the Community Dashboard"Recent Bloggers" remains on the dashboard, but each post is further decorated with a gray Staff tag for Liferay staffers, and each post is categorized according to which stream it appears in.
  2. On the Community Bloggers PageEach stream is separately shown. In addition, a list of individual bloggers appears on the right, for bloggers both on and off of liferay.com, sorted by total number of posts, with Liferay staffers receiving the Staff tag. If you maintain an external blog centered around Liferay and wish to have it listed, fill out the form!
  3. On an individual blog post - The stream in which the post appears is tagged, along with a tag for Liferay staffers.

Creating your Blog

At Liferay, we drink our own champagne, so you'll be using the Liferay Blogs Portlet to enter your blogs.

To start off, we are going to ask that if you wish to blog that you manually request it. We will never refuse to enable blogging for anyone (unless you don't appear to be a human), but do not want a flood of spammy posts clogging up the system. To get things started, we have enabled blogging for the vast majority of you already (over 500 community members), that have done even a very small amount of activity in the last 12 months, so chances are it's already turned on for you. So go check your profile to see if you have a Blog link, before requesting it, by checking if you have the "Blog" link on your liferay.com profile.

To create a new blog, navigate to Places -> User Profile:

you will land on your personal (private) "Home" page.  Next, click Profile:

and then click Blog (if you do not see a Blog link, request to get upgraded!):

To add a new blog entry, click on (yep, you guessed it):  You will be presented with a WYSIWYG editor in which to enter content. Each post is sanitized to remove potentially dangerous content that clever folks might try to enter via the "source" mode, such as javascript, IFrames to other sites (however, embedded gists are allowed!), and other questionable CSS tricks.

You can save your blog as a Draft, and come back later to work on it, or just click Publish to publish!  Once you publish, your post should immediately appear in the Community Stream as described above. You can always come back and edit your entries, or delete them if you wish.

External Blogs

I know that many of you actively maintain your own blog off of liferay.com. If you wish to have your blog listed on the Community Blogs page, you can also request this via the external blog listing request page. That list is constructed by looking at the top 50 liferay.com bloggers (ranked by # of posts), and then external blogs are randomly inserted.

Tips and Tricks

  • You can include uploaded images in your blogs by clicking the Image button, and uploading to your personal space on liferay.com.
  • You can use certain CSS classes like callout to make images have rounded corners and shadows, etc. Go look at old blogs and use your favorite web page inspector to discover items.
  • You can include gists (code snippets) using the 'Embed' option, and cutting/pasting the necessary javascript. This is the only kind of js allowed in your blog.

Blogger Policies

Blogging has been around forever, as have the written and unwritten rules, so this should not be new to any of you. Here are some of the written rules:

  • Be courtous and respectful. Don't use foul language, don't be mean, don't disrespect, don't hate, don't pre-judge, etc.
  • Don't post advertisements for your business or post job offers.
  • Check your syntax and spelling. No one will take u srsly if u write like a txt msg to your bf.
  • Don't use your blog to post questions about Liferay that belong in the forums, unless you already have an answer and wish to educate. Blogs are for well-formed thoughts about a particular subject, not for getting help connecting Liferay to your corporate LDAP server.
  • Cite your sources, don't use copyrighted content without permission, give credit where credit is due.
  • Be sure to read and respond to follow-up comments.
  • If you have not yet added a profile picture, please do so before blogging. Seeing a list of anonymous faces in the blog stream is just sad :)
  • No tricks! (Such as continuously updating your entry's Display Date so that your blog is always at the top of the list)

There are more, and I'm sure I left a few out. I really hope this can be a great, free, open resource for all of us, so please play nice :) All blogs and posts are subject to deletion and with no warning if you violate these rules, but you'll probably get a warning first.

Have fun, I know it's been a long time coming, and I can't wait to see what kinds of interesting posts our community has in store!

Acknowledgements

Giving credit where credit is due! Amos Fong led the implementation team, and David Miyabe did the design. I acted as Chief Pestering Officer on behalf of our community.

A Day of Liferay - That's a Wrap!

General Blogs 12 de abril de 2013 Por James Falkner Staff

Liferay.com Web Team SingingEarlier this week we wrapped up A Day of Liferay - a 24 hour epic  trip around the globe to meet our amazing community and get to know more about their interests and work they do on and around Liferay and other open source projects. I want to personally thank everyone who took time out of their day to participate - it was great to see the diversity of people we have in our little corner of open source, and hear about what they're up to! I had a lot of fun doing it, and learned a lot, and hope you did to!

On the A Day of Liferay page, I have added a complete index of links for each of the significant parts of the broadcast (or you can spend the next 24 hours watching the broadcast in its entirety :) ).

Also, for those that won stuff (shirts, pi's, etc). Rest assured you will be notified next week to arrange for you to recieve your hard-earned prizes! Now, where's my 10 million-gram mega-sugary Cachaça

Liferay Community Ideas (Beta)

Company Blogs 12 de marzo de 2013 Por James Falkner Staff

The Liferay open source project relies on its community for many of the evolutionary and revolutionary ideas for features that eventually find their way into a release. Liferay strongly believes in empowering the community not only to be able to create change through feature requests but also to feel responsible for socializing, communicating, recommending, and even implementing their own and others' ideas.
 
Today we are opening up a new community process centered around the concept of Ideation. This process brings a new emphasis on teasing out and promoting the best ideas from the community, for the community. It gives all of us new and easy ways to submit, understand, and promote ideas within the community. The most sought-after feature ideas will float to the top, from which anyone can choose the best, and implement!
 
This is for the YOU. While Liferay staff, partners, customers, and ISVs are all a part of our community, this process is going to be very organic. Our community is the key to its success, generating and implementing innovative ideas, pointing out the duplicates, providing important cross-ticket references, and helping to promote Feature Requests that they wish to see completed. Liferay is a BIG project and we know that there are many different perspectives in our community as to what features are most important but we also have a large number of capable developers in our community that can contribute these features (to either the core platform or as a Marketplace App). 
 
Along with this process, we have implemented a new Liferay Community Ideas Dashboard, on which you can see feature requests as they flow through the community, and can comment/vote/promote your favorites. This dashboard brings some needed visibility and simplification to JIRA for working with Feature Requests. You can read more about the concepts, workflow, and the dashboard on the Feature Request wiki page.
 
As a corporate sponsor of the Liferay project, Liferay Inc. will also pick up Feature Requests to be implemented by Liferay staff, but this is not always going to happen!  All community members are encouraged to consider implementing and contributing the ideas most important to them. Liferay Inc. can probably get to .1%-1% of the amazing ideas coming out of the community, so if a feature is super-important to you, get as many people talking about it as you can, and let the community decide whether it gets implemented, or break out your IDE and take a crack at it!
 
There are a few important changes as a result of this which are detailed below:
  • Though most of the process changes (e.g. JIRA workflows, etc) are complete, it's still a Beta (or Beta++) as there are some remaining items to do, like localization of the dashboard, and we want your feedback on the feature request process sooner rather than later.
  • This process continues to use our JIRA system at issues.liferay.com. The New Feature and Improvement ticket types have been consolidated into a single ticket type of Feature Request.  The dashboard pulls data from JIRA for display.
  • The process for contributing to Liferay remains the same: Submit your patches and git pull requests against the appropriate Feature Request ticket by clicking the "Contribute Solution" button.
  • Creating new Feature Requests is now simplified, and the form only requires a summary, description, and component with which the request is associated. No more weird fields with funny names.
  • Feature Requests remain Open until they are implemented, either in the core Liferay platform or as a Marketplace App, at which time they'll be Closed with one of 4 possibilities.
  • The Proposals Wiki will be deprecated as part of this process, but the Suggestions and Feature Requests forum will remain for additional discussion avenues related to Feature Requests. Authors who have entered ideas into either of these will be encouraged to "port" their ideas over to the new Ideation process, using a Feature Request JIRA ticket.

What's Next?

Go to the Liferay Community Ideas dashboard (or peruse the entire list), and read over a few of the ideas generated by the community. Vote on a few that you like, and click the social icons     next to the feature to generate some social buzz. If you have an idea of your own related to a potential Liferay feature, click  next to the appropriate area and let the community know what you're thinking! This system relies on the community's willingness to share ideas and promote others, so get started!

A Day of Liferay

Company Blogs 11 de marzo de 2013 Por James Falkner Staff

[EDIT: The A Day Of Liferay page has a detailed guest schedule. Tune into the webcast on April 9 by visiting the same!].

The best ideas often start with "Hey ya'll, watch this!!" or "Oh boy, this is probably a bad idea".  I hope this is one of them. One of the good ones. In this case I literally invite you to "watch this!".

On April 9, 2013 starting at 1300 GMT, you are invited to join myself and a cadre of many others in our awesome community as we take a virtual trip around the globe, stopping along the way to visit those in our community that make Liferay a great destination for open source technology. This worldwide trip will last for 24 continuous hours, and will make stops in every single continent on the planet save for Antarctica (though there have been downloads of Liferay from The Ice!).
 
It's the people behind the term "Liferay Community" that make it great! But we are all stuck behind keyboards and monitors and are geographically separated for most of the time. So, for this day I hope we can come together to meet interesting people in our community, and learn more about their culture, passions, personalities, and Liferay work that goes on behind the anonymous forum posts and blogs. We'll see screen shares of interesting new ideas, technical and non-technical interviews with prominent community members, Q&A from participants, artistic performances, and much more!
 
We will be using Google+ Hangouts On Air, with full audio and video, and with a lot of different and exciting guests throughout the day. We'll post a full schedule of guests and the times on which they will be on soon (I'm still trying to lure a few more victimsguests, and then I'll update this blog post with a pointer), but you can expect community members from your neighboring timezones to be on during typical working hours for you. Anyone with a device and browser will be able to watch the webcast and interact with the group using various methods.
 
We'll also have a few surprise guests, and will be randomly giving away prizes to participants, so be sure to set aside some time during the webcast to join in! It should be a lot of fun. Plus, you get to see what 24 continuous hours of webcasting does to people. All in the name of community cheeky
 
Don't worry, if you cannot join in the fun on April 9, the entire 24 hours will be recorded. We'll also create a highlight reel to summarize any extra special magic moments that may occur. And thanks in advance to all of the brave guests who will join us on our epic journey. Hope to see you all online April 9-10!

Community Roundup

Company Blogs 25 de febrero de 2013 Por James Falkner Staff

Time for another community roundup!  It's been far too long since the last one, and as usual a lot has been happening in and around our community.  I hope everyone has had a good beginning to the new year, and will find some interesting and useful items below.  Warm up your clickers, and read on!

  • Slides and recordings from last year's autumn Liferay Symposiums can be found on the landing pages for the North America, Europe, Spain, and Italy symposiums.  You may also be interested in the news reported from the symposia, or the awards (my favorite part - recognizing awesome community contributions!).
  • If you rely on Customizable Pages within Liferay, you may find this tidbit from Bin very useful - how to allow users to customize pages, but not too much :)
  • As you know, last year we began the Community Verifier team, to help verify existing issues in Liferay, and clean up our JIRA database of backlogged issues. Once again we are taking a run at the backlog, with a leaderboard and some prizes! If interested, join up in this thread.
  • Thank-yous go to our awesome 2012 Q3 and Q4 top contributorsPrakash Khanchandani, Victor Zorin, Jignesh Vachhani, Jan Geißler, Aniceto P Madrid, and David Kubitza!  These members gave a lot of virtual sweat over the last 6 months!  If you see them about, be sure to give them some gratitude!
  • After a brief hiatus, Radio Liferay has continued, with episodes featuring Jim Hinkey from the Liferay Knowledge Management team, Sam Kong on Security, and most recently the man himself (Olaf) on Liferay's well hidden features.  Spice up your daily commute with gems from our community rock stars!
  • We have had a lot of interest in new user groups recently.  Since the last roundup, we now have groups in Southern California, Colorado Springs, Australia, Japan, Montreal, Poland, and Sweden.  In addition, several user group founders contributed to a Presentation Template, providing a set of nice-looking and relevant slides to use in creating and giving presentations at community meetups.
  • Notable user groups with recent activity include India, Montreal, Netherlands, Japan, and SoCal. Also, check out the UK User Group's recent meetup reports.  They are using Social Office for the group site, and it's looking good!
  • A community Ruby Gem to create and manage Liferay projects?  Yes, please!
  • The Liferay Marketplace is now fully armed and operational. Several notable apps have been published, such as Xtivia's Carousel Portlet, Permeance's Documents and Media Downloader, and Neurones' Property Search Portlet.  Here's a list of all of them, including Liferay's apps.
  • From the releases desk: Liferay Portal 6.1 CE GA3 continues to bake in the oven. Several issues related to PACL have been fixed, hopefully making it possible to publish even more apps on the Marketplace.  Liferay Portal 6.2 is also under active development.  The latest Milestone release (Milestone 4) is undergoing community testing. Watch the Release Dashboard for updates!
  • The Liferay Faces team has been busy since the acquisition of portletfaces.org - check out the latest release, and Neil's interview with Kito Mann, and don't miss the latest release!
  • Nice tip from Snig on how to get rid of Alfresco Share navigation headers when using it with Liferay. Thanks, Snig!
  • Tomas shows us how to run Liferay's built-in unit and integration tests. Of course these are run whenever someone commits, but it's nice to run it for your own projects as well.
  • You may have noticed that when you are filing issues at issues.liferay.com, the ticket type New Feature and Improvement are gone, replaced by a single Feature Request ticket type.  More to come on this, but this is the start of a cool new Community Ideation process we've wanted for a long time, allowing requests to bubble up via socializing of ideas, voting, commenting, etc.  Good things ahead!
  • Accessibility is not trivial, and Liferay has gone to great lengths to make the platform accessible.  Watch Julio navigate a Liferay site blindfolded!
  • AlloyUI has had a storied past with Liferay.  With the launch of version 2.0, it has a new look and feel, a fantastic website, improved documentation, and much more. There's even a new #alloyui IRC channel on FreeNode!
  • You probably saw this, but Milen made some really cool eye candy and shows you the activity in Liferay's source base.  
  • Fun fact of the day: "Liferay, the most popular Java-based content management system, has increased its user base by 44.3% in the last year."  Link.
  • Bitergia has done an interesting analysis of our open source community, looking at Git and JIRA data over the last few years. Of course, the total activity is ramping up as Liferay grows and is used more and more, but more interesting are the quantiles graphs - showing roughly how long it took to resolve tickets opened in a given month.  These and other kinds of metrics are constantly being monitored and used to improve the effectiveness of our open source community.  Look for additional efforts in the future!
  • A Liferay and OSGi combination is a quasi-nirvana state that we are steadily approaching.  Check out Miguel's slides from the Spain Symposium for a brief glimpse into this promising future for modularity in Liferay!  As an example, check out shell-level access with Gogo. I can't wait to ssh into my running Liferay and fool around!
  • The Liferay website underwent an upgrade to 6.1 EE last month.  The site itself didn't change much, but the web team can now concentrate on an information redesign (including our beloved community site!).  Don't worry, your favorite stuff will continue to work as usual!  But better!
  • XMLPortletFactory continues to add awesomeness thanks to its community of developers, and now developers can easily integrate their custom actions with Liferay's Activity Framework.  I continue to bang on the "unappreciated feature drum" when discussing Social activities within Liferay!
  • Tabbed and paginated search results with no javascript.  Thanks Amit!
  • IRC traffic on #liferay has been steadily increasing - at one point last week, we had over 40 people in the chatroom, a record for Liferay. Kudos to all of you who are contributing to our little group!
  • Nick shows us how to use the popular Twitter Bootstrap framework to build responsive layouts within a Liferay site.  Well done!
  • Jorge from Liferay has a remarkable series of posts on Liferay's internal architecture.  The first 5 parts are:  OverviewServices LayerWeb Services, Caching, and Service Builder.  This is a great reference for any new or experienced Liferay developer.
  • NightHacking on Liferay, with Sampsa.  Enough said!
  • We in the community are serious about security.  With the formation of the Community Security Team last year, we have a better way of educating developers and providing source and binary patches for security issues.  In addition, there seems to be a renewed interest in documenting all the ways of hardening Liferay.  Here's a nice post describing how to test for vulnerabilities, and how to protect against them.  There's also a wiki page that shows you additional security best practices.
  • There have been many awesome blog posts and wiki updates, and we try to keep everyone up to date monthly via the Community Pulse newsletter.  If you wish to receive this, sign up here.
  • If you're attending EclipseCon, Portal Tech Days, Congres Intranet, JAX, Gartner PCC, KCDC, Community Leadership Summit, OSCON, or JavaOne this year, please.. stop by and say hi!

That's all I have for you today. Until next time (and hopefully such a time isn't that far out in the future next time!).

Part II: 7Cogs is Dead! Long Live 7Cogs!

Company Blogs 14 de febrero de 2013 Por James Falkner Staff

In the first installment of this series, I showed several (hopefully useful) code snippets demonstrating the programmatic creation of various things in Liferay - pages, portlets, and structured web content, which can be found in the smoldering ashes of the 7Cogs sample data included in Liferay until version 6.1.
 
I am using these snippets in an app that auto-populates a blank Liferay Site with interesting eye candy people and content, useful for showing demos and running tests that require a lot of user-generated content (you can see examples of such content in the third welcome video created for the 6.1 launch).
 
In this blog post, we'll look some of the code that generates the fake users and content.  These snippets build on, but are not not directly copied from, 7Cogs. So quality is most likely decreasing :)  But most of the Liferay logic is derived from 7Cogs.
 

As a Reminder

  • This should all work in the latest Liferay 6.1 CE and EE releases.
  • Not a lot of clever coding. I'll post the full source to git as part of the final installment and invite you to fork and fix.
  • Not a lot of error checking here.  If it fails it doesn't end up blowing up, but also doesn't continue.  I leave it to your trusty development skills to fix this.
  • A lot of this is hard-coded or assumes the English language is being used.  You can undo this with basic development skills as well!
  • I don't go into excruciating detail about some of the Liferay APIs used here.  Javadoc is your friend.  Most of the time.

Get a random point in time

1
2
3
4
5
6
7
// generate a random calendar within the past year
public static Calendar getCal() {
    Calendar now = Calendar.getInstance();
    int rHours = (int) (Math.random() * 8640);
    now.add(Calendar.HOUR, 0 - rHours);
    return now;
}
 
This method is used when setting the creation date of user-generated content, to simulate that the content was created at some point in the past.  It makes time-based visualizations more interesting!
 

Create a Profile Avatar/Picture

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// A bunch of random gravatar IDs for use when creating new users
public static final String[] GRAVATAR_IDS = new String[]{
    "205e460b479e2e5b48aec07710c08d50", 
    "c63392ca320086522cf4d55cbf1d3808", 
    "4d346581a3340e32cf93703c9ce46bd4",
    // and so on
};

// fetch a random portrait from gravatar and return its bytes
public static byte[] getPortraitBytes() throws Exception {

    String id = SocialDriverConstants.GRAVATAR_IDS[(int) (Math.random() *
        (double) SocialDriverConstants.GRAVATAR_IDS.length)];
    String urlS = "http://2.gravatar.com/avatar/" + id + "?s=80&d=identicon";
    URL url = new URL(urlS);
    ByteArrayOutputStream bos = new ByteArrayOutputStream();
    InputStream in = url.openStream();
    byte[] buf = new byte[2048];
    int bytesRead;
    while ((bytesRead = in.read(buf)) != -1) {
        bos.write(buf, 0, bytesRead);
    }
    in.close();
    return bos.toByteArray();
}
 
Now the fun, fragility, and questionable coding begins!  The SocialDriverConstants class has a bunch of hard-coded user names, job titles, Gravatar picture IDs, industry keywords for Wikipedia searches, and such, used when generating the content.  This code fetches a random picture from Gravatar for use when creating new user profiles later on.
 

Generate Friend Requests

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
// generate a friend request and optionally confirm it
public static void addSocialRequest(
    User user, User receiverUser, boolean confirm)
    throws Exception {

    SocialRequest socialRequest = SocialRequestLocalServiceUtil.addRequest(
        user.getUserId(), 0, User.class.getName(), user.getUserId(),
        1, StringPool.BLANK,
        receiverUser.getUserId());

    if (confirm) {
        SocialRequestLocalServiceUtil.updateRequest(
            socialRequest.getRequestId(),
            SocialRequestConstants.STATUS_CONFIRM, new ThemeDisplay());
    }
}
 
This code links users together through Social Relations (aka Friending).  It will optionally confirm the friend request.  This is used to randomly friend users to make them less lonely and make the social graph appear more interesting.
 

Creating and assign fake Addresses

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
private static void assignAddressTo(User user)
    throws SystemException, PortalException {
    String street = "123 Anystreet";
    String city = "Doesnt Matter";
    String zip = "12342";
    List<Country> countrys = CountryServiceUtil.getCountries();
    Country country = countrys.get((int) (Math.random() * (double)
        countrys.size()));
    double rnd = Math.random();
    if (rnd < .1) {
        country = CountryServiceUtil.getCountryByName("United States");
    } else if (rnd < .2) {
        country = CountryServiceUtil.getCountryByName("Japan");
    }
    List<Region> regions = RegionServiceUtil.getRegions(country
        .getCountryId());
    long regionId = 0;
    if (regions != null && !regions.isEmpty()) {
        regionId = regions.get((int) (Math.random() * (double) regions
            .size())).getRegionId();
    }

    AddressLocalServiceUtil.addAddress(user.getUserId(),
        Contact.class.getName(), user.getContactId(),
        street, "", "", city, zip, regionId, country.getCountryId(),
        11002, true, true);
}
 
Here we are randomly creating an Address and assigning it to a user, for later visualization.  You can also see a slight bias for the United States and Japan (you can change this).  10% of the time, the user's address will be in the US.  10% of the time, it will be in Japan.  The other 80% of the time it'll be in a random country in the world (of the countries defined by Liferay).  This is used later when visualizing user's locations.
 
You may be wondering what that hard-coded 11002 is doing there.  Addresses can be of different types (e.g. Personal, Business, and Other. 11002 is the constant for Personal.  This is defined in Liferay's default SQL data, and not in Java, so I had to hard-code it here.
 

Setting User Password, bypassing TOU, Father's Middle Name

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// make it easy to login the first time
private static void setFirstLogin(String password, User user)
    throws Exception {
    UserLocalServiceUtil.updatePortrait(user.getUserId(),
        getPortraitBytes());

    UserLocalServiceUtil.updateLastLogin(
        user.getUserId(), user.getLoginIP());
    UserLocalServiceUtil.updateAgreedToTermsOfUse(user.getUserId(), true);
    UserLocalServiceUtil.updatePassword(user.getUserId(),
        password.toLowerCase(), password.toLowerCase(),
        false, true);
    UserLocalServiceUtil.updatePasswordReset(user.getUserId(), false);


    String[] questions = StringUtil.split(
        PropsUtil.get("users.reminder.queries.questions"));

    String question = questions[0];
    String answer = "1234";

    UserLocalServiceUtil.updateReminderQuery(
        user.getUserId(), question, answer);
}
 
This makes it such that you can log in with the newly created user without having to set password, agree to the terms of use, or set your reminder questions.  The password is set to the user's first name, using all lower-case letters.  So John Smith's password will be john.
 
It also sets a user's profile picture using a random Gravatar ID through the call to getPortraitBytes defined earlier.
 

Randomly assigning some Friends

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
// make a fake social network of friends by randomly friending some
// people
private static void assignRandomFriends(User user) throws Exception {
    int userCount = UserLocalServiceUtil.getUsersCount();
    int friendCount = (int) (Math.random() * (double) userCount);
    for (int i = 0; i < friendCount; i++) {
        int frienduser = (int) (Math.random() * (double) userCount);
        User randUser = UserLocalServiceUtil.getUsers(frienduser,
            frienduser + 1).get(0);
        if (randUser.getUserId() == user.getUserId()) continue;
        if (randUser.isDefaultUser()) continue;
        boolean confirm = (Math.random() > .4);
        addSocialRequest(user, randUser, confirm);
    }
}
 
This picks some random users with which a relationship is established.  It avoids friending the "default" (guest) user, and also avoids friending the user with themselves (which is weird, right?).  It uses the previously defined addSocialRequest from up above.
 

Setting up a User's Profile

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
// setup a user's public/private page theme, and construct some useful
// profile pages
private static void setupUserProfile(long companyId, String themeId,
                                     Group guestGroup, User user)
    throws Exception {
    Group group = user.getGroup();
    if (themeId != null && !themeId.isEmpty()) {
        LayoutSetLocalServiceUtil.updateLookAndFeel(
            group.getGroupId(), false, themeId, "01", "",
            false);
    }

    // Profile layout

    Layout layout = addLayout(
        group, user.getFullName(), false, "/profile", "2_columns_ii");
    JournalArticle cloudArticle, assetListArticle;

    try {
        cloudArticle = JournalArticleLocalServiceUtil.getLatestArticle
            (guestGroup.getGroupId(),
            SocialDriverConstants.CLOUD_ARTICLE_ID);
    } catch (NoSuchArticleException ex) {
        cloudArticle = addArticle("/cloud-structure.xml",
            "/cloud-template.vm", "cloud-article.xml",
            UserLocalServiceUtil.getDefaultUserId(companyId),
            guestGroup.getGroupId(),
            SocialDriverConstants.CLOUD_ARTICLE_ID);
    }

    try {
        assetListArticle = JournalArticleLocalServiceUtil
            .getLatestArticle(guestGroup.getGroupId(),
            SocialDriverConstants.ASSETLIST_ARTICLE_ID);
    } catch (NoSuchArticleException ex) {
        assetListArticle = addArticle("/assetlist-structure.xml",
            "/assetlist-template.vm", "assetlist-article.xml",
            UserLocalServiceUtil.getDefaultUserId(companyId),
            guestGroup.getGroupId(),
            SocialDriverConstants.ASSETLIST_ARTICLE_ID);
    }
    try {
        JournalArticleLocalServiceUtil.getLatestArticle(guestGroup
            .getGroupId(),
            SocialDriverConstants.EXPERTSLIST_ARTICLE_ID);
    } catch (NoSuchArticleException ex) {
        addArticle("/experts-structure.xml", "/experts-template.vm",
            "experts-article.xml",
            UserLocalServiceUtil.getDefaultUserId(companyId),
            guestGroup.getGroupId(),
            SocialDriverConstants.EXPERTSLIST_ARTICLE_ID);
    }

    addPortletId(layout, "1_WAR_socialnetworkingportlet", "column-1");
    addPortletId(layout, PortletKeys.REQUESTS, "column-1");
    String portletId = addPortletId(layout, PortletKeys.JOURNAL_CONTENT,
        "column-1");
    configureJournalContent(
        layout, guestGroup, portletId, cloudArticle.getArticleId());
    configurePortletTitle(layout, portletId, "Expertise");
    addPortletBorder(layout, portletId);

    addPortletBorder(layout, addPortletId(layout,
        "2_WAR_socialnetworkingportlet", "column-1"));

    addPortletBorder(layout, addPortletId(layout, PortletKeys.ACTIVITIES,
        "column-2"));
    addPortletBorder(layout, addPortletId(layout,
        "3_WAR_socialnetworkingportlet", "column-2"));

    // Expertise layout

    layout = addLayout(group, "Expertise", false, "/expertise",
        "2_columns_ii");

    addPortletId(layout, "1_WAR_socialnetworkingportlet", "column-1");
    addPortletId(layout, PortletKeys.REQUESTS, "column-1");
    portletId = addPortletId(layout, PortletKeys.JOURNAL_CONTENT,
        "column-1");
    configureJournalContent(
        layout, guestGroup, portletId, cloudArticle.getArticleId());
    configurePortletTitle(layout, portletId, "Expertise");
    addPortletBorder(layout, portletId);

    portletId = addPortletId(layout, PortletKeys.JOURNAL_CONTENT,
        "column-2");
    configureJournalContent(
        layout, guestGroup, portletId, assetListArticle.getArticleId());
    configurePortletTitle(layout, portletId, user.getFirstName() + "'s " +
        "Contributions");
    addPortletBorder(layout, portletId);

    // Social layout

    layout = addLayout(group, "Social", false, "/social", "2_columns_ii");

    addPortletId(layout, "1_WAR_socialnetworkingportlet", "column-1");
    addPortletId(layout, PortletKeys.REQUESTS, "column-1");
    addPortletBorder(layout, addPortletId(layout,
        "2_WAR_socialnetworkingportlet", "column-1"));
    addPortletBorder(layout, addPortletId(layout, PortletKeys.ACTIVITIES,
        "column-2"));
    addPortletBorder(layout, addPortletId(layout,
        "3_WAR_socialnetworkingportlet", "column-2"));

    // Blog layout

    layout = addLayout(group, "Blog", false, "/blog", "2_columns_ii");

    addPortletBorder(layout, addPortletId(layout,
        PortletKeys.RECENT_BLOGGERS, "column-1"));
    addPortletBorder(layout, addPortletId(layout, PortletKeys.BLOGS,
        "column-2"));

    // Workspace layout

    layout = addLayout(
        group, "Workspace", false, "/workspace", "2_columns_i");

    addPortletBorder(layout, addPortletId(layout,
        PortletKeys.RECENT_DOCUMENTS, "column-1"));
    addPortletBorder(layout, addPortletId(layout,
        PortletKeys.DOCUMENT_LIBRARY, "column-2"));

    addPortletId(layout, PortletKeys.CALENDAR, "column-2");
}
 
This rather long method is in the spirit of 7Cogs.  It first assigns the user's public pages a theme (lines 7-10) (if this option is chosen in the GUI to be seen later).  It then adds three Web Content Articles (lines 19-52) (each based on a Web Content Template and Structure).  These are special web content articles that I'll show you in the next installment in this series.  These articles, along with some other Liferay portlets (like a calendar, documents and media, recent bloggers, etc), are placed on the user's newly-created 4 profile pages (lines 54-126):
 
  • User's Name (Friendly URL: /profile)
  • Expertise (Friendly URL: /expertise)
  • Social (Friendly URL: /social)
  • Blog (Friendly URL: /blog)
  • Workspace (Friendly URL: /workspace)
 

Programmatically Creating a Liferay User

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
// add a new user to the DB, using random fake data,
// and set up their profile pages, and add
// them to the right sites, friend a couple of other random users.
public synchronized static User addUser(String firstName, long companyId,
                                        String themeId, boolean profileFlag)
    throws Exception {

    String lastName = getRndStr(SocialDriverConstants.LAST_NAMES);
    String job = getRndStr(SocialDriverConstants.JOB_TITLES);
    Group guestGroup = GroupLocalServiceUtil.getGroup(
        companyId, GroupConstants.GUEST);

    long[] groupIds;
    try {
        // see if there's a demo sales group, and add the user to the
        // group if so
        Group salesGroup = GroupLocalServiceUtil.getGroup(
            companyId, "Sales");
        groupIds = new long[]{guestGroup.getGroupId(),
            salesGroup.getGroupId()};
    } catch (Exception ex) {
        groupIds = new long[]{guestGroup.getGroupId()};
    }
    ServiceContext serviceContext = new ServiceContext();

    long[] roleIds;
    try {
        // see if we're using social office, and add SO's role to the new
        // user if so
        roleIds = new long[]{RoleLocalServiceUtil.getRole(companyId,
            "Social Office User").getRoleId()};
        serviceContext.setScopeGroupId(guestGroup.getGroupId());
    } catch (Exception ignored) {
        roleIds = new long[]{};
    }
    User user = UserLocalServiceUtil.addUser(UserLocalServiceUtil
        .getDefaultUserId(companyId), companyId, false,
        firstName,
        firstName,
        false, firstName.toLowerCase(), firstName.toLowerCase() +
        "@liferay.com", 0, "",
        LocaleUtil.getDefault(), firstName,
        "", lastName, 0, 0, true, 8, 15, 1974, job, groupIds,
        new long[]{}, roleIds, new long[]{},
        false, serviceContext);

    assignAddressTo(user);
    setFirstLogin(firstName, user);
    assignRandomFriends(user);


    if (profileFlag) {
        setupUserProfile(companyId, themeId, guestGroup, user);
    }
    return user;

}
 
This code creates a new Liferay User.  Some notes:
  • If it finds a Site called "Sales", it'll assign the newly created user to it, in addition to assigning them to the Guest Site.  This is because I use a demo group called "Sales" (mimics a typical enterprise sales organization) so I want to add users to it if such a site exists.
  • It will also imbue the new user with the "Social Office User" role if such a role exists (and it will, if you are using Social Office).
  • Note that Social Office comes with a nice looking theme, so in most cases if you are using SO, you'll not want the theme assignment to happen.  There is an option in the UI for this.
  • It finally assigns a random address to the user, configures their account to make it easy to login the first time, assigns random friends, and then sets up the new user's profile as defined above.
 

Get (or create) a random User

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
// retrieves a random user from the company.  If the randomly chosen user
// doesn't exist, create them first
public static long getUserId(long companyId, String themeId,
                             boolean profileFlag)
    throws Exception {

    User user;
    String first = getRndStr(SocialDriverConstants.FIRST_NAMES);
    try {
        user = UserLocalServiceUtil.getUserByScreenName(companyId,
            first.toLowerCase());
    } catch (NoSuchUserException ex) {
        user = addUser(first, companyId, themeId, profileFlag);
    }
    return user.getUserId();

}
 
This code will pick a random user from the set of defined users in our constants file, and if they don't exist yet, it'll create them (via prior code above).  It is used when creating fake content (blogs, wikis, ratings, comments, etc).
 

Removing Stop Words

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
// standard list of common english words to eliminate from tags
public static final List<String> ENGLISH_STOP_WORDS = Arrays.asList(
    "a", "an", "and", "are", "as", "at", "be", "but", "by",
    "for", "if", "in", "into", "is", "it",
    "no", "not", "of", "on", "or", "such",
    "that", "the", "their", "then", "there", "these",
    "they", "this", "to", "was", "will", "with");


// remove common tags from the array of passed-in tags
public static String[] removeEnglishStopWords(String[] a) {
    List<String> result = new ArrayList<String>();
    for (String as : a) {
        if (SocialDriverConstants.ENGLISH_STOP_WORDS.contains(as)) continue;
        if (as.length() < 3) continue;
        result.add(as);
    }

    return result.toArray(new String[result.size()]);
}
 
This removes common English Stop Words, those that are very common and are useless as tags, such as The or A, or any word that is 2 characters or less, which are pretty useless as tags too.  I'm pretty sure the Big-O performance of this is really bad.
 

User-Generated Content

As part of this app, we also generate a bunch of user content in the form of:
  • Blog Posts
  • Wiki Articles
  • Forum Posts
  • Blog, Wiki comments
  • Forum responses
  • Blog, Wiki, Forum ratings/votes
The content is generated by going out to Wikipedia and fetching articles based on pre-defined search terms.  The title of each article is used to generate a set of tags for the article (ignoring common words like a and the).  The Wikipedia JSON service is used and abused.  This content is cached for use while the generator is running.  Here's how to fetch it:
 

Picking terms for Wikipedia Searches

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
// hard-coded set of tags to use when searching wikipedia
public static final String[] FAVORED_TAGS = new String[]{
    "java",
    "liferay",
    "javaee",
    "ldap",
    "portal",
    "content management system",
    "social networking",
    "mobile",
    "android",
    "cloud computing",
    "j2ee",
    "enterprise software",
    "sharepoint",
    "single sign on",
    "gadgets",
    "open source",
    "collaboration"
};


public static String getRndStr(String[] ar) {
    return ar[(int) (Math.random() * (double) ar.length)];
}

// get some random terms to search for, with no duplicate terms
private String[] getRandomTerms(String[] allTerms) {

    int termCount = 1 + (int) (Math.random() *
        SocialDriverConstants.MAX_SEARCH_TERMS);
    List<String> result = new ArrayList<String>();
    String term;
    for (int j = 0; j < termCount; j++) {
        while (result.contains(term =
            SocialDriverUtil.getRndStr(allTerms))) {
            // do nothing
        }
        result.add(term);
    }
    return (String[]) result.toArray(new String[result.size()]);
}
 
Here we are just picking a random number of random terms from a pre-defined list.  Woo!  Note if you pick too many terms, you won't find anything on Wikipedia (since it's an AND search).  And if you pick too few, you'll get a LOT (which is great, but sometimes searching for java might get you coffee articles, or searching for mobile will get you information about a certain southern US city.  So it's good to have a set of related keywords.
 

Fetching articles from wikipedia

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
private static Set<ContentArticle> fetchWikipediaArticles(HttpClient
                                                              httpclient,
                                                          String[] terms)
    throws IOException, JSONException {
    int hitLength;
    Set<ContentArticle> result = new HashSet<ContentArticle>();

    StringBuilder sb = new StringBuilder();
    sb.append("http://en.wikipedia.org/w/api" +
        ".php?action=query&list=search&format=json&srsearch=");
    sb.append(HtmlUtil.escapeURL(StringUtil.merge(terms, ",")));
    sb.append("&srnamespace=0&srwhat=text&srprop=snippet&srlimit=");
    sb.append(SocialDriverConstants.WIKIPEDIA_MAX_ARTICLE_COUNT);

    GetMethod get = new GetMethod(sb.toString());
    httpclient.executeMethod(get);
    JSONObject wikis = JSONFactoryUtil.createJSONObject(get
        .getResponseBodyAsString());
    JSONArray qresult = wikis.getJSONObject("query").getJSONArray("search");
    hitLength = qresult.length();

    for (int k = 0; k < hitLength; k++) {
        JSONObject hit = qresult.getJSONObject(k);
        String title = hit.getString("title");
        String excerpt = hit.getString("snippet");
        if (excerpt == null || excerpt.isEmpty()) continue;
        String[] words = SocialDriverUtil.removeEnglishStopWords(title
            .replaceAll("[^A-Za-z0-9]", " ").split("\\s+"));
        result.add(new ContentArticle(title, excerpt, words));
    }
    return result;
}
 
Not much to do with Liferay, but this method queries Wikipedia via its JSON web services, using the supplied String, and established HTTPClient object (this uses Liferay's built-in, old version of the Commons HttpClient).
 
The result of a JSON web service call to Wikipedia is a JSON object (see their docs) containing the results of the search.  You can see what it looks like in your browser.  We grab the title, and break it apart (and remove stop words) and use the result as the tags for the article.  The body is represented by the article's snippet (you can't get the full content from Wikipedia due to usage restrictions).  The result of calling this method is a library of ContentArticle ojects that are very simple (just a container for the title, tags, and content of an article, used later on to make the content in Liferay).  You'll see how this all comes together in the next blog post in this series.
 
By the way, you can repeatedly call this to get more articles as needed.  It's a good idea to call it a several times (like 10 times with different search terms if you want to generate a LOT of Liferay content).
 

Creating a Blog Post

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
private void addEntry() throws Exception {
    Calendar rCal = SocialDriverUtil.getCal();


    ServiceContext context = new ServiceContext();
    context.setCreateDate(rCal.getTime());
    context.setModifiedDate(rCal.getTime());
    String cid = contentContainer.getRandomId();
    String title = contentContainer.getContentTitle(cid);
    String content = contentContainer.getContentBody(cid);
    String[] tags = contentContainer.getContentTags(cid);
    context.setWorkflowAction(WorkflowConstants.ACTION_PUBLISH);
    context.setAddGroupPermissions(true);
    context.setAddGuestPermissions(true);

    context.setAssetTagNames(tags);
    context.setCompanyId(companyId);
    context.setScopeGroupId(groupId);

    BlogsEntry newEntry = BlogsEntryLocalServiceUtil.addEntry
        (SocialDriverUtil.getUserId(companyId, themeId, profileFlag),
            title, "",
        content,
        rCal.get(Calendar.MONTH), rCal.get(Calendar.DAY_OF_MONTH),
        rCal.get(Calendar.YEAR),
        rCal.get(Calendar.HOUR_OF_DAY), rCal.get(Calendar.MINUTE), false,
            false, null, false, null,
        null, null, context);

    System.out.println("Added blog " + newEntry.getTitle() + " Tags: " +
        Arrays.asList(tags));
}
 
Now we are back to Liferay APIs.  Here we are randomly picking a date and a user and publishing a blog post using random content and tags from earlier Wikipedia fetches.  This has the side effect of making a new Activity Stream entry, and accumulating Social Activity points!  More on this in the next installment.  We are doing the ServiceBuilder dance here as well, just like before.
 

Commenting on a Blog Post

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
private void commentEntry() throws Exception {

    if (BlogsEntryLocalServiceUtil.getBlogsEntriesCount() <= 0) return;
    int rand = (int) (Math.random() * (double) BlogsEntryLocalServiceUtil
        .getBlogsEntriesCount());
    BlogsEntry entry = BlogsEntryLocalServiceUtil.getBlogsEntries(rand,
        rand + 1).get(0);
    Calendar rCal = SocialDriverUtil.getCal();

    long userId = SocialDriverUtil.getUserId(companyId, themeId,
        profileFlag);
    ServiceContext context = new ServiceContext();
    context.setCreateDate(rCal.getTime());
    context.setModifiedDate(rCal.getTime());
    context.setCompanyId(companyId);
    context.setWorkflowAction(WorkflowConstants.ACTION_PUBLISH);
    context.setAddGroupPermissions(true);
    context.setAddGuestPermissions(true);

    context.setScopeGroupId(groupId);

    MBDiscussion disc = MBDiscussionLocalServiceUtil.getDiscussion
        (BlogsEntry.class.getName(),
        entry.getPrimaryKey());
    MBMessageLocalServiceUtil.addDiscussionMessage(userId, "Joe Schmoe",
        context.getScopeGroupId(), BlogsEntry.class.getName(),
        entry.getPrimaryKey(),
        disc.getThreadId(), MBThreadLocalServiceUtil.getMBThread(disc
        .getThreadId()).getRootMessageId(),
        "Subject of comment", "This is great", context);

    System.out.println("Commented on Blog " + entry.getTitle());
}
 
This is where things get even more interesting.  Comments in Liferay are created and managed through the MB (Message Board) functionality.  So, you'll see some of those APIs here.  Discussions are associated with content (like blog posts) through the standard className/classPK linkage pattern found throughout Liferay's Asset Framework.  So this code simply picks a random, existing blog post to comment on, and adds the comment to the thread associated with the blog post.  Other content types are very similar (e.g. Wikis, Documents, etc).
 

Voting on a Blog Post

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
private void voteEntry() throws Exception {

    if (BlogsEntryLocalServiceUtil.getBlogsEntriesCount() <= 0) return;
    int rand = (int) (Math.random() * (double) BlogsEntryLocalServiceUtil.getBlogsEntriesCount());
    BlogsEntry entry = BlogsEntryLocalServiceUtil.getBlogsEntries(rand, rand + 1).get(0);
    Calendar rCal = SocialDriverUtil.getCal();

    long userId = SocialDriverUtil.getUserId(companyId, themeId, profileFlag);
    ServiceContext context = new ServiceContext();
    context.setCreateDate(rCal.getTime());
    context.setModifiedDate(rCal.getTime());
    context.setCompanyId(companyId);
    context.setWorkflowAction(WorkflowConstants.ACTION_PUBLISH);
    context.setAddGroupPermissions(true);
    context.setAddGuestPermissions(true);

    context.setScopeGroupId(groupId);

    PrincipalThreadLocal.setName(userId);
    RatingsEntryServiceUtil.updateEntry(BlogsEntry.class.getName(), entry.getEntryId(),
        (int) (Math.random() * 5.0) + 1);
    System.out.println("Voted on Blog " + entry.getTitle());
}
 
Here is how to programmatically find and randomly rate  a random blog post (1 to 5 stars), using the RatingsEntry service.  The deal with the PrincipalThreadLocal on line 19 is that this is how Liferay knows "who" is voting (since voting is typically done in the context of a request/response).
 

Creating, Commenting, and Rating other Content Types

Looks at watch - Wow, where did the time go?  This will have to wait for the next installment, and I will show you how to wrap all of this into a simple Liferay portlet with a horrible yet usable UI for controlling the creation of content and social data.

7Cogs is Dead! Long Live 7Cogs!

Company Blogs 5 de febrero de 2013 Por James Falkner Staff

Ding dong! The witch is dead! Which old witch? The 7Cogs witch!

[EDIT: Part II covers additional stuff beyond this post!).

This isn't news anymore - Starting with 6.1, Liferay replaced the sample data in the bundled Liferay download with a much leaner set of pages and web content that didn't overrun your database with data that was generally the first thing to be removed when starting a new Liferay project. It's not that the sample data was bad, it was just... demo data that shotgunned itself all over your database, and required a database reset to remove.

But there are many gems to be found within the ashes of the good ole 7Cogs. It demonstrated how to programmatically create a lot of useful things in Liferay, so in a series of posts starting with this one I'd like to take a look at some of them, and show how I revived the 7Cogs' corpse and re-used it for something a little more interesting. While I won't cover 100% of what 7Cogs did, I hope you find some of these code snippets useful for your Liferay projects, and the resulting application fun and instructive.  

What did 7Cogs do?

7Cogs consisted of a theme and a hook (along with dependencies on other Liferay plugins like some of the social networking portlets). The main guts of the interesting bits are in the hook. When you first started Liferay 6.0, the hook would determine if it had been run before, and if not, go about its business.

If you look at the hook code from which these snippets evolved, you can see that it did a lot of stuff:

  • Added some Web Content Structures and Templates to the Guest Site
  • Inject a bunch of images from the plugin into the Document Library
  • Configured a custom Theme
  • Add a bunch of pages and portlets to the Guest Site public pages, with friendly URLs
  • Added some Organizations
  • Added a bunch of vocabularies, categories, and tags
  • Added a Mobile theme
  • Added a bunch of Roles and associated Permissions
  • Added several users assigned to those Roles and configured their profile pages
  • Added several documents to each user's document library
  • Caused several of them to befriend each other, and others to have outstanding friend requests
  • Added some Message Board threads, wiki pages, blogs, etc
  • Configured Web Content to be workflow'd with Kaleo
  • Faked a bunch of Social Equity data
  • Link all of the above into the semblance of a "real" live site that you just happened upon during the install

The last bit was what got me interested in this in the first place. At the time, I was interested in showing how Liferay could be used to build a community, but most of my work started on a blank site, and I quickly tired of making users and roles and sites and social features and so forth. 7Cogs was busy being retired into the annals of Liferay history, so I decided to try and refresh it to work on the latest 6.1 release and wrote some helper code around it to give some GUI control over what it was doing. My Frankenstein demo was alive, and I could use it to populate a real site, develop some interesting visualizations around social data, and even watch in real time as things were being generated.

First Things First

Over the course of several blog posts, I will build on previous blog posts with additional snippets, eventually building a system that can create a nice looking site with real-looking users and user-generated content from the web, nicely linked with tags and social activity data, and a couple of interesting visualizations that you can use to drive community site participation in your projects.  A few notes before we get started:

  • This should all work in the latest Liferay 6.1 CE and EE releases.
  • Not a lot of error checking here. If it fails it doesn't end up blowing up, but also doesn't continue. I leave it to your trusty development skills to fix this.
  • A lot of this is hard-coded or assumes the English language is being used. You can undo this with basic development skills as well!
  • Several Liferay APIs have many arguments that I don't explain in excruciating detail.  For the major service calls I tried to link to the javadoc, which can be your friend. Most of the time.

Getting Contents of a File from a plugin

public static byte[] getBytes(String path) throws Exception {
    return FileUtil.getBytes(getInputStream(path));
}

public static InputStream getInputStream(String path) throws Exception {
    Class<?> clazz = SocialDriverUtil.class;

    return clazz.getResourceAsStream("/resources" + path);
}

public static String getString(String path) throws Exception {
    return new String(getBytes(path));
}

Pretty simple. It just grabs a resource as a stream under the /resources directory in the plugin, and gives you a stream of bytes (getBytes) and can also convert it to a String (getString). These are used later on to retrieve content from the plugin that are housed in external files (resources) within the plugin.

Adding Resources to a Layout

// add a resource to a layout with the portletId.  Yes, this comment is completely worthless.
public static void addResources(Layout layout, String portletId)
    throws Exception {

    String rootPortletId = PortletConstants.getRootPortletId(portletId);

    String portletPrimaryKey = PortletPermissionUtil.getPrimaryKey(
        layout.getPlid(), portletId);

    ResourceLocalServiceUtil.addResources(
        layout.getCompanyId(), layout.getGroupId(), 0, rootPortletId,
        portletPrimaryKey, true, true, true);
}

This method simply adds the resources associated with a portlet to a layout (presumably because you are adding the portlet to the layout, which we are doing later on). Read the section in the documentation regarding Resources and how they interact with the Permissioning system of Liferay.

Adding a Layout (Page) to a Site

// create a new (blank) page for the given group/s public or private layoutset
public static Layout addLayout(
    Group group, String name, boolean privateLayout, String friendlyURL,
    String layoutTemplateId)
    throws Exception {

    ServiceContext serviceContext = new ServiceContext();

    Layout layout = LayoutLocalServiceUtil.addLayout(
        group.getCreatorUserId(), group.getGroupId(), privateLayout,
        LayoutConstants.DEFAULT_PARENT_LAYOUT_ID, name, StringPool.BLANK,
        StringPool.BLANK, LayoutConstants.TYPE_PORTLET, false, friendlyURL,
        serviceContext);

    LayoutTypePortlet layoutTypePortlet =
        (LayoutTypePortlet) layout.getLayoutType();

    layoutTypePortlet.setLayoutTemplateId(0, layoutTemplateId, false);

    addResources(layout, PortletKeys.DOCKBAR);

    return layout;
}

This code creates new pages in a given Group, with default permissions such that everyone can view the page, and assigns them a layout template (such as "2_columns_ii" for the ii'th version of the standard 2-Column layout with a particular percentage width for each column). It also adds the necessary resources of the dockbar to the permissioning system so that you can see the dockbar when viewing the page.

Adding a Portlet to a Layout (Page)

// persist an updated layout entity
public static void updateLayout(Layout layout) throws Exception {
    LayoutLocalServiceUtil.updateLayout(
        layout.getGroupId(), layout.isPrivateLayout(), layout.getLayoutId(),
        layout.getTypeSettings());
}

// add a portlet to a layout
public static String addPortletId(
    Layout layout, String portletId, String columnId)
    throws Exception {

    LayoutTypePortlet layoutTypePortlet =
        (LayoutTypePortlet) layout.getLayoutType();

    portletId = layoutTypePortlet.addPortletId(
        0, portletId, columnId, -1, false);

    addResources(layout, portletId);
    updateLayout(layout);

    return portletId;
}

The updateLayout method simply flushes the in-memory Layout entity into the Database. If you don't do this, any updates to the layout won't be persisted.

The addPortletId method will add a new portlet to a Layout (page) based on its id (such as PortletKeys.RECENT_BLOGGERS), and place it at the bottom of the specified column (such as "column-1"). You'll see examples of how this is called in a bit.

Setting a custom Title for a Portlet

// set a custom title for a portlet
public static void configurePortletTitle(
    Layout layout, String portletId, String title)
    throws Exception {

    PortletPreferences portletSetup =
        PortletPreferencesFactoryUtil.getLayoutPortletSetup(
            layout, portletId);

    portletSetup.setValue("portletSetupUseCustomTitle", String.valueOf(Boolean.TRUE));
    portletSetup.setValue("portletSetupTitle_en_US", title);

    portletSetup.store();
}

This method updates the displayed title of the portlet. Before you will see the title, you'll need to ensure the portlet is showing its borders.

Showing a Portlet's Borders

// set a portlet to show its borders
public static void addPortletBorder(Layout layout, String portletId)
    throws Exception {

    PortletPreferences portletSetup =
        PortletPreferencesFactoryUtil.getLayoutPortletSetup(
            layout, portletId);

    portletSetup.setValue(
        "portletSetupShowBorders", String.valueOf(Boolean.TRUE));

    portletSetup.store();
}

This method simply turns on the "Show Borders" option for a potlet on a given layout, so that its title is shown, and the portlet gets a rectilinear border around it. Like its configurePortletTitle cousin, it does this by programmatically setting the Portlet's preferences.

That's it for the basics of constructing pages and adding portlets. Now, let's take a look at how we can programmatically create structured Web Content.

Working with Structured Web Content

My demo requires some web content based on structures and templates. The structure (written in xml) and template (Velocity template) are stored externally (hence the need for the previous methods to retrieve files from the plugin).  You'll see what these look like in a later post, but you can always take any of your own articles and download them via the Web Content Editor GUI to see what they look like.  Here are the relevant bits:

Populating a Localization Map

public static void setLocalizedValue(Map<Locale, String> map, String value) {
    Locale locale = LocaleUtil.getDefault();

    map.put(locale, value);

    if (!locale.equals(Locale.US)) {
        map.put(Locale.US, value);
    }
}

This adds a string into a map based on your default locale, and populates the Locale.US value as well if you're not running in that locale. These maps are used when setting the title of web content and other various places where something can take on different, localized values depending on the runtime locale.

Adding a Web Content Structure

Now we are getting somewhere!

public static JournalStructure addJournalStructure(
    long userId, long groupId, String title, String fileName)
    throws Exception {

    Map<Locale, String> nameMap = new HashMap<Locale, String>();

    setLocalizedValue(nameMap, title);

    Map<Locale, String> descriptionMap = new HashMap<Locale, String>();

    setLocalizedValue(descriptionMap, title);

    String xsd = getString(fileName);

    ServiceContext serviceContext = new ServiceContext();

    serviceContext.setAddGroupPermissions(true);
    serviceContext.setAddGuestPermissions(true);

    try {
        JournalStructureLocalServiceUtil.deleteStructure(groupId, title);
    } catch (Exception ex) {
        System.out.println("Ignoring " + ex.getMessage());
    }
    return JournalStructureLocalServiceUtil.addStructure(
        userId, groupId, title, false, StringPool.BLANK, nameMap,
        descriptionMap, xsd, serviceContext);
}

This adds a new Structure, scoped to the specified Group, created by the specified user, with the specified title, whose contents come from the specified fileName. Note the bit with ServiceContext -- this creates the structure with the default permissions (VIEW) so that you can see it as a guest user or as a member of the group. Also, this method first tries to delete the structure if it already exists (and ignores it if there is a failure in doing so, sorry!).

Adding a Web Content Template

public static JournalTemplate addJournalTemplate(
    long userId, long groupId, String title, String structureId, String fileName)
    throws Exception {

    Map<Locale, String> descriptionMap = new HashMap<Locale, String>();

    setLocalizedValue(descriptionMap, "ATemplate");

    Map<Locale, String> nameMap = new HashMap<Locale, String>();

    setLocalizedValue(nameMap, "ATemplate");

    String xsl = getString(fileName);

    ServiceContext serviceContext = new ServiceContext();

    serviceContext.setAddGroupPermissions(true);
    serviceContext.setAddGuestPermissions(true);

    try {
        JournalTemplateLocalServiceUtil.deleteTemplate(groupId, title);
    } catch (Exception ex) {
        System.out.println("Ignoring " + ex.getMessage());

    }
    return JournalTemplateLocalServiceUtil.addTemplate(
        userId, groupId, title, false, structureId, nameMap,
        descriptionMap, xsl, true, "vm", false, false, StringPool.BLANK,
        null, serviceContext);
}

This one is similar to the structure one. The names used here (like "ATemplate") don't really matter. We do the same ServiceContext shenanigans as in the code for adding structures. Note the "vm" - this specifies that the language of the template is velocity (you can use others, such as freemarker).

Adding a Web Content Article

public static JournalArticle addJournalArticle(
    long userId, long groupId, String title, String fileName,
    String structureId, String templateId,
    ServiceContext serviceContext)
    throws Exception {

    String content = getString(fileName);

    serviceContext.setAddGroupPermissions(true);
    serviceContext.setAddGuestPermissions(true);
    serviceContext.setWorkflowAction(WorkflowConstants.ACTION_PUBLISH);
    Map<Locale, String> titleMap = new HashMap<Locale, String>();

    setLocalizedValue(titleMap, title);
    try {
        JournalArticleLocalServiceUtil.deleteArticle(groupId, title, serviceContext);
    } catch (Exception ex) {
        System.out.println("Ignoring " + ex.getMessage());
    }

    return JournalArticleLocalServiceUtil.addArticle(
        userId, groupId, 0, 0, title, false,
        JournalArticleConstants.VERSION_DEFAULT, titleMap, null,
        content, "general", structureId, templateId, StringPool.BLANK,
        1, 1, 2008, 0, 0, 0, 0, 0, 0, 0, true, 0, 0, 0, 0, 0, true,
        true, false, StringPool.BLANK, null, null, StringPool.BLANK,
        serviceContext);
}

Here we are adding an article based on the specified structure and template. We also do the same "delete first" thing to avoid throwing exceptions. There are a lot of arguments to the addArticle method at the bottom. Please refrain from giggling!  You can read all about it in the javadoc.

Causing a given Web Content Display Portlet to display a Web Content Article

// configure a web content display to show a specific article
public static void configureJournalContent(
    Layout layout, Group group, String portletId, String articleId)
    throws Exception {

    PortletPreferences portletSetup =
        PortletPreferencesFactoryUtil.getLayoutPortletSetup(
            layout, portletId);

    if (group == null) {
        portletSetup.setValue("groupId", String.valueOf(layout.getGroupId()));
    } else {
        portletSetup.setValue("groupId", String.valueOf(group.getGroupId()));
    }
    portletSetup.setValue("articleId", articleId);

    portletSetup.store();
}

Pretty basic, it just set's the Web Content Portlets preferences to show the given article. No error checking to ensure that the portlet is actually a web content article either. Whee!

What's Next?

At this point, we have seen how to programmatically create pages, adding and configuring portlets on those pages, creating web content, and displaying web content. In the next installment of this 7Cogs post-mortem, we'll see how we can have fun creating fake users, great-looking profiles for those users, and very real-looking user-generated content based on actual web content from Wikipedia, and put a simple UI on top. Finally, we'll take a look at how to generate and some interesting visualizations of Liferay's social data and explain how these can be used to drive your own communities using Liferay. Stay tuned!

Liferay now available on Windows Azure

Company Blogs 24 de enero de 2013 Por James Falkner Staff

One of Liferay's key strengths has always been its flexibility.  This stems from Liferay starting out as a consultant-based, services-oriented company.  Every customer had different requirements that were never envisioned as part of the product.  Each time a new requirement was met, facilities were developed to ensure that other requirements in that same area could be met in the future.  This result can be seen both in the underlying frameworks included in the software, and in the myriad sites built on Liferay.

Deployment requirements are no different, and Liferay is built to work with virtually any mainstream operating system, database, application server, front-end framework, and other ancillary services required of a modern web platform.  However, fitting together and maintaining all of those components can be tricky at times, and so to continue the tradition of flexibility, Liferay has partnered with a number of hosting providers to make it easy to deploy and maintain Liferay on premises and in the cloud, using the infrastructure that makes the most sense for your needs.

One of the benefits to us as community members is that our hosting partners get to know Liferay rather well, when designing cloud deployment models around it.  They've even discovered bugs and suggested improvements for Liferay itself!  They also get to know cloud infrastucture providers, which brings me to the point of this post: to let you all know that BitNami, a Liferay Hosting Partner, is now producing and maintaining Liferay images for use on Microsoft's Azure cloud, through their alliance with Microsoft Open Technologies. With Microsoft's VM Depot ("a community-driven catalog of open source virtual machine images for Windows Azure", as Gianugo puts it), you can launch and maintain Liferay instances directly on Azure, and combine it with many other available apps.

It's pretty cool for a number of reasons:

  • The BitNami Liferay stack includes a pre-configured and pre-wired Liferay stack including Ubuntu, Apache Tomcat, MySQL, and more, directly deployable onto Windows Azure.
  • Microsoft's VM Depot also includes a bunch of other open technologies that could be combined with Liferay, through your ability to remix applications and publish the result as a new image on the depot (Microsoft embracing Open Source?  No way!).
  • VM Depot images can later be used by production deployments on the public Azure cloud, with little or no effort (but at that point presumably one would have to pay for that).  So the VM Depot is a great proving ground for Liferay and other open source.
  • It's free!  As in beer!

I am particularly excited to see BitNami taking the initiative to seed the depot with Liferay and other open source.  BitNami started with Liferay 4.3.0 over 5 years ago, and has been a champion of open source even longer.  Hopefully we can all benefit from this new addition to the Liferay deployment ecosystem.  To get started, check out the BitNami Liferay Azure stack!

Community Release Dashboard

Company Blogs 8 de enero de 2013 Por James Falkner Staff

Everyone knows of Liferay's flagship software product, Liferay Portal, but Liferay actually develops and releases many other products in the Liferay ecosystem. At any one time, development is occurring on all of these products in one way or another, but most people don't learn of these other products until they show up in a press release or are otherwise waved in the air when they are released.

One of my goals as your community manager is to give you the tools and guidance you need to stay up to date with the various happenings within our community, in particular those happenings related to development of Liferay software. There are many ways to do this, and today you have another tool in your arsenal. But before describing it, I wanted to quickly run down the ways in which you can get involved with the development of software in the community, from the bottom to the top.

Source Code

At the lowest level of any open source project is the source code itself, where the rubber meets the road, and where daily (even hourly) changes are exposed. Liferay's source code is managed using the Git source code control system system, and its home lies on Github.com. All of Liferay's open source projects are house here, including Liferay Portal, AlloyUI, Liferay IDE, Liferay Plugins, Liferay's Official Documentation, Liferay Faces, Maven Support, and others.

Developers wishing to get involved can visit the Development wiki pages to set up their own personal development environment, learn the code, and subscribe ("watch") the git repository to receive notifications of any source code change by other developers in the community. Liferay's engineering team strives to make source code commits and associated messages as easy to understand as possible, but it does require a fair amount of development experience to understand what goes on under the hood!

JIRA

Tracking a large software development project like Liferay requires additional tooling above the source code itself. This is where JIRA is used to track bugs, new features, and other program management-related tasks in the form of tickets (or issues). This happens on the aptly-named issues.liferay.com JIRA instance.

Liferay uses JIRA for many different things. Of course, Liferay users are always encouraged to report bugs, and submit new features via JIRA (more on this "idea generation" is coming, as discussed at a recent Liferay Community Leadership Team meetup). Community contributions also flow through JIRA, in the form of "Contributed Solution" and "Community Resolved" states for each contribution.

If you watch the activity stream of items being created and edited, you can see what's happening, in a more human-friendly fashion. Various ticket types are created (such as bugs or New Improvements or Stories), but that can also quickly get overwhelming (there are upwards of 200-300 activities per day here!).

Community Development Board

Of course, source code and ticket activity streams are a very low-level, fine-grained way to participate or observe an open source project, so recently Liferay's engineering and product management teams have begun using a higher-level way to manage and track project changes. For each release of Liferay there exists a Development Board which shows you a quick summary of upcoming (TODO) items, items in progress, and completed items, in terms of New Features and Stories. Here's the development board for the next release, Liferay Portal 6.2. You can read more about this board in my previous blog post to get an idea of the information one can glean from this way of looking at things.

Releases

This brings us to the latest addition in the array of tools you can use to see what's happening. Many of you have requested information on what exactly Liferay has released, or will release in the future, without getting bogged down in the details of individual features or bugs. In fact, Jorge Ferrer (our VP of Engineering) asked me for this soon after I started at Liferay. Paraphrased: "we need a quick, reverse-chronological listing of Liferay releases, with relevant links, so that people can see what's available and get details about each release!" The new Release Dashboard aims to fulfill this gap.

It's a pretty simple Asset Publisher-driven list of releases, grouped by "Past, Current, and Next" buckets. Most importantly, you can get at all of the relevant links (download, issue trackers, and more importantly source code for open source releases) without having to dig deep into liferay.org. In addition, each release has brief release notes, with information about installation, upgrade, support, and other important information that one cannot see by simply looking at a list of "issues fixed". You'll find the Releases link on the left side of the community homepage.

You can check this list regularly, or refer to it when you need quick links for a particular release. As new releases are planned, or released, or retired, this list will be updated. You can also subscribe to its RSS feed if you are into that sort of thing. As releases are "promoted" from "Next" to "Current", and later to "Past", this will be reflected in the list and the feed.

Release Dates

You will also notice that each release has an associated date. For releases that have not yet occurred, you'll find a best guess estimate based on Liferay's desire to release new Portal technology releases every 12-18 months. As usual, these dates are not guaranteed and subject to change. As we get closer to a release date, these dates will be updated to reflect the latest estimate, and if a date is later expected to be "missed", notes will be added to the details section explaining why, and the expected date will be updated.

Milestones1

Liferay has made a lot of changes to its release process over the last few years. New in this cycle is the release of what are called Milestone builds. A Milestone build is a full (pre)release of the Liferay Platform, giving us in the community a chance to try out new features, or discover new bugs before the general availability (GA) release. This is in stark contrast to prior releases, where you only got a Beta release, a month or so before the final release, leaving Liferay very little time to correct anything before release.

This is a really great way to get ahead of the release curve, and discover if the upcoming release is going to meet your needs, and if not, provide that feedback much earlier in the release cycle. In addition, you'll find responsible engineers for various areas of Liferay much more responsive, as they are not in super-crunch-before-release mode. Check out the latest Milestone 3 release and give Liferay your feedback, to help make future releases much better than in the past!

Enterprise Edition

You'll also notice in this list several Enterprise Editions (e.g. Liferay Portal EE, Social Office EE, and Liferay Developer Studio) - the purpose of doing this is to let people know that there are commercially-supported release offerings from Liferay, and if you are looking for that, you don't have to dig into the website to find them.

Plugins

With the advent of the Liferay Marketplace, plugins that are developed by Liferay have been decoupled from the main platform releases, and so you won't find each individual plugin listed on the dashboard. However, plugins continue to be improved, and you can stay informed by watching each plugin's Marketplace landing page (here's the one for Solr), where you'll find version history, release notes for each release, and other information about the plugins. As new platform releases are made, plugins will be revised to incorporate any needed changes, or new features, for the new platform release.

If you find this useful, or have any suggestions, let me know in comments below. We have a lot of other improvements coming this year for our awesome community, and I look forward to seeing what we as a community can do together!

 

1By the way, another goal of these Milestones is to refine the process by which Liferay executes a release. Each Milestone becomes easier to release, as we refine the pipeline from developer's fingers to your download. This will help Liferay meet its expected release dates much easier than before.

What Do You Think?

Company Blogs 19 de diciembre de 2012 Por James Falkner Staff

What do you think?

This refrain is commonly found in forum posts, emails, and other postings throughout our community, both from employees of Liferay and its wider open source community.  It's important to be a better listener than a better talker, and to that end we in the community (and specifically your Community Leadership Team) are always asking this question, in order to gauge how best to serve.
 
One important facet of any community is to encourage new members as much as possible, both in a technical sense and in a motivational sense.  If you know precisely what motivates one to participate and/or contribute, you can tailor the resources in the community to target that motivation and efficiently bring new blood into the fold.  To that end, last year, an interesting question was posed.  "What motivates our community?".  A long discussion ensued, and we came to the conclusion that we don't really know, but we should ask what people think.  Back in October we did just that: we launched the 2012 Liferay Community Survey, using the questions developed by some of our most active community members.
 
I thought you might be interested in the results from this survey, so have a look.  I posted the full results of each question, but what follows are some highlights and some of the actions we are taking to address what we found.  We also discussed this in detail at the most recent Community Leadership Team meeting, and several members also provided valuable insight which I included below.
 
One caveat: this is not a scientifically accurate survey.  There's probably some unintended bias in the questions, and some leading questions, but we tried hard to come up with good questions to address what we wanted to know most.  If you have any ideas for future surveys, feel free to share here or in the forums.  Also, as Ryan correctly points out, correlation does not imply causation.  The number of rings in a tree trunk grows each year, as does the S&P 500, but one does not cause the other!  So keep that in mind when drawing conclusions.  Now, on with the show!
 

Survey Results

We had 142 respondents this time around, which I was personally happy with [see full results]. Interestingly enough, there were almost 50 people who started the survey but did not finish.  Perhaps the survey was too long, or not interesting enough?  
 

The Basics

No surprises here.  We've seen a nice uptick in community activity in India (check out the India User Group if interested in getting involved) and we've a number of high quality partners active in India as well.  A diverse community is a healthy community, so I would like the see the gender gap closed as much as possible. I believe there is a lot of innovation and missed opportunity from women that are unfortunately locked away in cultural stereotypes.You can read more about Women in Computing, and if you have any input, let's hear it! My 7 year old daughter has already given me an earful from using Liferay :)
 

Discovery

A couple of interesting bits here.
  • How did you discover Liferay?  Note the small percentage of "Educational Institution".  Steve George has been doing some work here to develop a University Outreach package that can be used worldwide to help bring Liferay to university students, and it's a standing item on our quarterly community meetups.  The students and professors gain a new teaching tool, and we of course gain well qualified graduates who will hopefully champion Liferay once they reach the "real world". 
  • How Long have you used Liferay vs. How many years have you contributed to Liferay?  Note the difference (Ryan did).  People have known and/or used Liferay on average about 3-4 years, but only contributed for 1-2 years.  So, there is a 1-2 year time period during which (we hypothesize) people are using and getting to know Liferay, and only then do they start contributing.  1-2 years is a long time to wait for these, and it points at a lack of community contribution on-ramping for new developers (and not just coders).  We in the community are continuing to work on Liferay University, hopefully bringing easy to follow curriculums for new members to understand Liferay and be able to contribute their knowledge and expertise quicker.

Information Sources

Also not a surprise - most of us use the Forums, Wikis, Blogs, and Official Documentation to get information on Liferay.  I'm proud to also see 11% of you get information from the source code itself.  That's a huge (but not only) benefit of open source, right?  So, yay for open source!
 

Project Details

Here we begin to ask some motivational questions.  We started by asking - why do you use Liferay CE and/or EE?  The results are not really a surprise, but its interesting to see the numbers.  The main reason to choose CE over EE for production use is that a project is too small to justify the cost.  That makes sense.  Liferay is used in a wide variety of solutions, from small, one-person web shops to large companies, so not everyone needs the features and benefits of an enterprise edition.  It's also nice to see a nice distribution of answers to the question "Why would you use Liferay EE (vs. CE)?".  Typical reasons for enterprise use.
 
Following this we asked what Liferay's #1 priority should be going forward, and again no major revelation here - quality, quality, quality.  We in the community have made some good progress here with the many special projects (BugSquad, Community Verifier, etc) that directly contribute to quality and usability, and will continue to do so.  Within Liferay's engineering halls, our community-born engineering lead, Jorge Ferrer, has been making a lot of changes and improvements both internally and externally to how Liferay is produced.  On the testing front, check out Manuel's presentation (en Español) from the Spain Symposium on the Liferay test infrastructure.  For documentation (always a crowd favorite), Rich and team are hard at work here. Follow his blog for more detail.  And finally, in order to make more stable and predictable releases, Liferay has already started producing Milestone builds of the next version.  This will give everyone a chance to see the current state of the project and provide early feedback, and allow Liferay to improve its development and release process, to produce high quality, stable, and regular (predictable) builds with certainty, vs. waiting until the last minute to discover some fatal showstopping flaw.
 

Motivations

Ok, here's where it gets interesting.  The first question is "do you contribute to Liferay?".  And then depending on your answer, the survey went down one of two paths.  Path 1 was for people who do contribute (and thankfully the majority of you do!), and asked questions about why you contribute, how much, what kinds, and so forth. Path 2 was for those that said they did not contribute, and we asked why not, etc.
 
 
For those that contribute, it's nice to see that the main motivation is for sharing skills and knowledge, and to "give something back".  Very altruistic, which is also part of the company's mission.  In addition, most people think that they give more than they get, and that others give more than they do.  Most contributions are in the form of knowledge in our forums, community projects, translations, etc.  Some of the less popular items (such as 100 PaperCuts or Liferay University) need a little tender love and care, which I hope we can do going forward.  
 
\
 
On the other hand, for those that do not contribute, the main reasons are lack of time and knowledge. Both of these can be helped by having better documentation, better quality, less of a learning curve, etc, which makes it easier to learn and hopefully result in less time taken to contribute.  So that I think will be a major focus for 2013. 
 

Also interesting to note the "No expected benefit" response.  This is related to another question in the survey, "does your company reward OSS contributions".  
 
Most companies worldwide do not, according to these results. I believe that many companies see OSS participation as something that takes away from an employee's normal duties, and I'd like to encourage all of you working at companies with this outlook to think about the benefits (direct and indirect) of participation.  Not only from the perspective of Liferay and its application within your company, but with your longer term career goals.  Participation and collaborative development is a great way to not only learn new skills, but make new friends and contacts, and feel good about the work that you do.
 
And on a related note: "No expected benefit" and "Takes too long to see a result" go hand-in-hand - some tickets go a long time before eventually being addressed, so people naturally assume they are being ignored, and are not willing to contribute in the future since their prior issues either took forever to be addressed, or not addressed at all.  This is unacceptable for an open source project and we are addressing this with a new Community Contribution Coordination process (more on this in a separate blog post).
 
 
As far as what might motivate one to start (or increase) participating and contributing, not surprisingly "when I have more knowledge" and "when I have more time" are at the top of the list.  Of course, this mirrors the other question "why do you not contribute".  "when I get free (online) training" ranks high as well, and I expect that the coming Liferay University content will address this.  "When I get special liferay.com powers/privileges" was an interesting response.  What kind of powers/privileges do you think might be beneficial?  Some ideas mentioned by the leadership team included the ability to blog on liferay.com (this is in the works, still), and perhaps discounts on sales if you prove your worth by contributing.  These are all ideas that are being explored.  What say you?
 

Other

One other thing caught my eye: almost 75% of you believe contributing and learning Liferay enhances your job opportunities.  This is fantastic, as it aligns nicely with the company's mission of helping others through technology.  If knowing that ServiceLocalUtil bypasses permission checking (vs. ServiceUtil) helps you get a job, then our work is done!

Be sure to check out the full list of questions and results and see if you can draw any conclusions of your own, and feel free to share them here.
 

Conclusions

Overall, the results seem to confirm several things:
  • Most people find out about Liferay outside of academia
  • There is a time gap between finding Liferay and being able to contribute.
  • Most people contribute because they want to share knowledge and give back.
  • Most contributors feel they get more than they give (which is OK!)
  • The biggest hurdle to contribution is lack of time and knowledge.
  • Liferay should focus more on quality, not as much on features.
  • Most companies do not see benefit to OSS participation.
Earlier in this post I mentioned several initiatives already underway to address these (indeed many of these concerns have been brought up before).  Additional things that we could do:
  • Create an open source "beat sheet" - this is a tool that sales people use that give quick summaries of their product vs. their competitors.  In this case, our competitors include companies who rely upon yet do not understand open source and do not encourage employees to contribute, and active users of Liferay who may be apprehensive about contributions for whatever reason.  And BTW - this also applies to Liferay itself!  
  • Incentivize contributions through special privileges like those mentioned by the CLT
  • Make contribution even easier by linking the product to the community more explicitly.  For example, a Bonfire plugin for Liferay (not a browser plugin, or maybe a browser plugin tied to the Liferay server).
If you have additional ideas on how to make our community even better, let's hear them!
 
Finally, we might want to do this survey again next year and see how the numbers change as a result of work done over the year.  
 

Survey Prize Winners

Of course, we had to dangle a small carrot in front of you to take the 10-15 minute survey, and so here is the result:  Prizes! Bear in mind it's not our primary motivator in the community (hey, we're a small company and can't afford it anyway).  The fine folks at Liferay, Componence, and MyOffice24x7 all contributed to the making of this survey and have generously provided the following prizes to three lucky participants!
 

Spy Tank - Winner: Matt Burke

Rover 2.0 App-Controlled Wireless Spy Tank. Faster, smarter, better! Rover 2.0 goes where no app-controlled tank has gone before. Drive it with your iPad, iPhone or iPod touch device, or with Android smart phones and tablets. See in the dark using Rover's built-in night vision. Stream and record live video and stills. Upload videos and stills to Facebook, Youtube and Twitter sites right from the App Control the angle of the video camera remotely.
 

iPad Foosball Table - Winner: Daphné Bellemin

Sure it’s cool to live in an ultra small, super expensive space in a hip city. But you miss out on a lot of stuff, like king sized beds and giant table games. Take foosball for example. There’s no way the real-life version of Chandler and Joey’s apartment on Friends could house one. Unless it was stored in the bathtub and all the other furniture was moved out of the way during play. Luckily, the folks at New Potato Technologies have created this elaborate iPad housing that serves as a full-fledged foosball table, complete with stubby legs and eight two-axis control rods. It looks like it can support up to four players, but how you can cram four people around even the larger-sized iPad is a mystery left for you to figure out.
 

Raspberry Pi - Winner: Venka Reddy

 
The Raspberry Pi is a credit-card sized computer that plugs into your TV and a keyboard. It’s a capable little PC which can be used for many of the things that your desktop PC does, like spreadsheets, word-processing and games. It also plays high-definition video. We want to see it being used by kids all over the world to learn programming.
 

2012 Liferay Community Awards

Company Blogs 29 de octubre de 2012 Por James Falkner Staff

I believe it's really important in any community to give credit where credit is due, whenever possible. When you give, even if you don't expect monetary or other material reward, it's nice to be recognized for your selflessness in front of your peers. It makes you feel good to have others recognize your achievements, and it hopefully spurs others to think about what they are good at and how they could potentially help our community by donating a little time and energy.

Last year we began an annual worldwide recognition award called Community Excellence, which was awarded to several of our partners from the Liferay ecosystem that demonstrated their commitment to the community. The award was based not on revenue or "the bottom line" in any way - instead, it was (and continues to be) based on the community impact that the partners have through their participation and contributions (answering on forums, writing blogs, coding bugfixes or improvements, providing translations, evangelism inside their own sphere of influence, etc). Partners have been essential in Liferay's recent growth, and their employees are some of the most active and beneficial members of the open source community.

I received some feedback shortly after recognizing the partners: what about individuals not affiliated with Liferay (the company) or its partner companies? I had no good answer: it was "the way it was done" in the past (through our regional Partner of the Year designation, which does involve measuring financial impact). Of course that's no excuse, and in fact, if you take time out of your day to contribute to a community that isn't part of your "day job", that is in many ways even more valuable, both to the community and to you. These kinds of contributions must be cherished and rewarded heavily, and contributors of this type must similarly be recognized. They have no financial or legal obligation to use and contribute to Liferay, so these selfless individuals form deep bonds within our community that transcend money. They are also often our most vocal critics, which gives us a much-needed shot of unbiased, and usually constructive criticism in order to "see the forest through the trees" and I believe help advance Liferay in directions unforeseen by those "in the know".

Therefore, I am honored to present this year's community awards to both partners AND individuals!

What We Measured

The actual, super-secret formula must never be revealed (only Ray has the super-secret key), and has been tweaked slightly this year, 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, and consideration for external activity such as community evangelism. For the 2012 awards, we considered activity that occured between midnight, September 30th, 2011 and midnight, October 1st, 2012.

Individual Contributors of the Year

 

Hitoshi Ozawa

Hitoshi has been instrumental on our forums with over 6600 lifetime posts, and has been really helpful getting new folks up and over the typical learning curve one experiences, especially with our earlier versions. He also participates in BugSquad and Community Verifier, providing much-needed contributions to our issue database. Finally, earlier this year, Hitoshi was awarded a quarterly contributor award which you can read more about to learn more about his work and background.

Hitoshi will be recognized at this year's Liferay Japan launch event next month!

 

Alexey Kakunin

Alexey has been active in the community for quite some time and has done a lot of work contributing and maintaining the Liferay/Activiti integration, providing an open source choice when using workflow with Liferay. In addition, he has been very generous with code contributions (bugfixes and improvements) to Liferay Portal, over half the time providing fixes along with his bug reports. It's quite natural for him to give back to the community, and I encourage all of us to take Alexey's lead!

 

David Nebinger

David stumbled upon Liferay in 2006 while looking for an open source portal platform for an intranet, and over the last year, really stepped up his game in contributing a huge body of knowledge to our forums and community documentation. He has a solid understanding of Liferay internals and openly provides his unbiased opinions and stories from his Liferay experience. David was also a featured speaker at this year's North America Symposium and was awarded with a quarterly contributor award earlier this year.

 

Juan Gonzalez

Juan embodies the spirit of openness that any community desires. He's been quite busy this year on the forums, but has also contributed lots of code in the form of bug fixes, improvements, new features (Assisting in the implementation of things like Video/Audio preview), and most recently providing easy to use patch sets for Liferay Portal CE. Juan is also active in the Community Security Team, and helps to champion Liferay in his work in Spain and elsewhere.

 

We have all benefited from the work of these four individuals, but there are many others who make our community one of the most active and positive groups to ever be associated with! I sincerely wish to thank all of you for making it so!

Partner Contribution of the Year

JavaServer Faces is one of the most widely adopted and powerful user interface framework for web applications. This didn't happen by accident, as developers have chosen it for its powerful features and easy integration with web technologies like Liferay. portletfaces.org was established by Mimacom AG and Triton Services to meet the need for an open community built around the related standards JavaEE, JSF, and Portlets.

With the popularity of Liferay and its beneficial association with the JSF community, it was a win-win decision to contribute the projects under portletfaces.org to the Liferay community, and both Mimacom and Triton Services were awarded Partner Contribution of the Year awards for this act.  Neil Griffin has a great writeup of the projects, and you can also read the press release for more information.

Mimacom

Triton Services

 

Liferay Community Excellence

Our strong worldwide partner network continued to move Liferay technology forward this past year, providing world-class Liferay services and contributions to our growing community. Last year we recognized the top 5, but this year the number jumps to 7 - due to the quality of work and outstanding level of community contribution!

Acando

Acando is very active in Norway and elsewhere in Europe, and has single-handedly provided 97% of the Norwegian translations for Liferay technology. They are also a founding member of the Norway User Group and regularly organize events for the group and for the larger Liferay community. This is the first of hopefully many years of community excellence!

Acando had planned to attend the Europe Symposium to receive their award - but unfortunately (or fortunately!) they had a last minute business opportunity, so we'll see them next year!

 

B2B2000

As a Platinum sponsor of this year's Liferay Spain Symposium, B2B 2000 is no stranger to the community. They had a well-rounded contribution effort this year, providing over 4000 individual Basque translations, are very active in #LSUG (the Liferay Spain User Group), and are very helpful on our forums (not only in Spanish!).

 

Cignex Datamatics

Cignex Datamatics is a repeat winner this year, and have contributed to virtually every facet of our community, including very helpful forum activity, knowledge sharing via their many online Liferay webinars, and are also contributing in our community programs such as BugSquad and others. Several of Cignex' ranks have also been recognized individually this year, and are very passionate about Liferay, both online on liferay.com, and in person at industry events such as the recent North America Symposium.

 

Componence

Based in the Netherlands, but making their presence known throughout our worldwide community, Componence has continued its tradition of open source contribution for Liferay. They founded and continue to push the Netherlands User Group forward, contributed time and energy to execute the Liferay Community Survey, provided translations, and participate in community efforts like BugSquad, Community Verifier, and are a regular presence at the Europe Symposium (where they organized a Table Football tournament for the community!).

 

Permeance

Hailing from Australia, Permeance has been very active in our forward-looking programs, providing feedback in the Liferay Portal 6.1 BugSquad Feature Reviews late last year, and are very active in promoting Liferay in the region. Permeance also dedicates a large percentage of their time to our community, recognizing the essentially free training one gets by participation.  Their work on our issues database and forums has helped make our recent releases better for them, their clients, and our community as a whole.

 

Savoir-faire Linux

Another repeat winner, Savoir-faire Linux is committed to open source, clearly understanding the benefits of participation and contribution. They have contributed major features in the past (enjoying Faceted Search? Thank these guys!), and this year were also active on our forums, contributed to BugSquad, sponsored the North American Symposium, and provided over 2500 French translations of Liferay.

 

SMC

Finally, SMC is also a first-timer (but were in the top 10 last year!). SMC founded and continues to lead the Italy User Group, and was one of the first Liferay partners ever, so it is no surprise they are committed to our community. SMC also contributed to our Italian and English forums, and is the organizing sponsor of this year's Italy Symposium next month.

 

Final Thoughts

In closing, I'd like to sincerely thank all of the above community members, and everyone else in the community who choose to use Liferay, choose to help make it better, and choose to brighten everyone else's day a little bit each time they do.

Community Meetup at the North America Symposium

Company Blogs 26 de septiembre de 2012 Por James Falkner Staff

If you are attending this year's Liferay North America Symposium in two weeks in San Francisco, on Sunday evening (the evening before the event begins) Liferay is hosting a community meetup. This will be a free event, starting around 8pm, at Irish Times [Map and Directions]. This is very close to the symposium venue, about a 3 minute walk.  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 San Francisco!

2012 Liferay Community Survey

Company Blogs 25 de septiembre de 2012 Por James Falkner Staff

Hey all!  As your community manager, I am always trying to learn from you as much as possible, in order to make our community a better place to share ideas, meet new people, and generally grow the world of Liferay.  However, for such a large and active community, I alone cannot ever hope to conquer this task, so your Community Leadership Team has been exploring ways to solicit feedback, and not just ordinary feedback, but feedback that gets to the root motivations of participation (or not!). To that end, today we are opening the 2012 Liferay Community Survey - please take a few minutes (ok, about 10 of them) and share your feedback with us.  The results will help us further understand what motivates your participation, the improvements you wish to see, and how we can better attract and retain new community members in 2013 and beyond!

The survey will be open until October 23.  Your answers are completely anonymous, unless you wish to receive feedback, or more importantly have a chance to win cool prizes offered by our survey sponsors: MyOffice24x7, Componence, and Liferay!  Gift cards, Liferay books, and other fabulous prizes will be awarded to a few lucky survey respondants, so take the time to take the survey and make your voice heard!  Once the survey closes, the community leadership team will review the findings and present them to the community, and the results will be used to tune our efforts in the community and project going forward.

By the way: the survey questions were generated from the community, and the survey itself is implemented using MyOffice24x7 SmartForms, running on Liferay 6.1 CE GA2, configured by Componence.  So it truly was a Liferay Community effort, and we are happily "eating our own dogfood", thanks ya'll!

Mostrando el intervalo 21 - 40 de 95 resultados.
Elementos por página 20
de 5