« Back

Building simple applications with Liferay WCM

Staff Blogs October 3, 2009 By Bruno Farache Staff

Suppose that you, a portlet developer, need to develop a FAQ portlet that displays questions and answers and that allows certain users to create/update/delete FAQ entries, i.e., a simple CRUD application. Usually, the first thing that comes in mind is to implement a new Java-based portlet, actually, 2 portlets, one for displaying and a second for managing FAQ entries. Fortunately, Ray Augé showed me a different approach, there are some simple cases that can be solved only by using our Web Content framework, this way, each FAQ entry becomes a Web Content entry and for displaying them you can just create a Web Content Template containing a Velocity script.

The ability to create Velocity scripts has been in the portal for a long time but we made some enhancements for 5.3 that will make it easier to build such scripts. Note that some APIs used in this blog post are still in development and will be available only in 5.3. You can download all templates and structures used in this example here.

Portlet example built with Liferay WCM

Here's a screenshot of this portlet in action:
 


 

Each pair of question and answers are Web Content entries, and the template responsible for listing them is located at /templates/FAQ-PORTLET.vm in the zip file.

There are many advantages of building CRUD applications with Liferay WCM, as a developer you don't need to care implementing many features provided by Liferay WCM for free, such as:

1.  Creation, update and deletion of FAQ entries: since each FAQ entry is actually a Web Content entry, you can manage them from the Web Content portlet:




To edit an entry, you just need to fill the form, notice the category select box, FAQ-PORTLET.vm will filter entries accordingly:

 

 

FAQ entries could have other complex field types, such as images, files or any type available in Structures.

2. Versioning: you can have several versions of the same FAQ entry, you can see the history of changes and rollback to older versions if needed, just like any Web Content.

3. Approval workflow: content of each entry can be revised and approved by editors, FAQ entries will be displayed only when they are approved.

4. Tagging: you can aggregate FAQ entries with Asset Publisher portlet.

5. Export/import: you can backup or share FAQ entries with others by exporting and importing them to .lar files.

6. Staging and remote publishing: you can create FAQ entries in the staging community and publish them to the live community when they are ready, they can be also published to a remote Liferay instance.

7. Permissioning: you can define which FAQ entries an user will have permission to view.

7. Setup: Since the portlet that displays all FAQ entries is a Web Content itself with an associated Structure, you can take advantage of this to configure your portlet, in this example, there is a "show_index" option that will display or hide the questions index:


What's new in 5.3?

You might be thinking "OK, there is nothing new here, I know how to create Velocity templates and call Liferay services from them, what's new in 5.3?"

One of the problems we've found while creating these applications is that we can't query for Web Content fields separately. In the example, I needed to filter FAQ entries by their category field, however Web Contents are stored in the database like that:

<?xml version="1.0" encoding="UTF-8"?>

<root>
	<dynamic-element instance-id="uwYU33xF" name="question" type="text" index-type="text">
		<dynamic-content><![CDATA[What is Liferay Portal EE?]]></dynamic-content>
	</dynamic-element>
	<dynamic-element instance-id="rsTKdp5r" name="answer" type="text_area" index-type="text">
		<dynamic-content><![CDATA[<p>Liferay Portal Enterprise...</p>]]></dynamic-content>
	</dynamic-element>
	<dynamic-element instance-id="q7HOfdUk" name="category" type="list" index-type="keyword">
		<dynamic-content><![CDATA[ee_faq]]></dynamic-content>
	</dynamic-element>
</root>


As you can see, it would be hard to make a database query and fetch all FAQ entries associated to the "EE Edition" category, the table is not normalized. We fixed that by indexing Web Content fields separately in Lucene, now you have new options while creating a new Structure, you can define that a field will be indexed as a Keyword or Text by Lucene or won't be indexed at all:



In FAQ-PORTLET.vm we filter FAQ entries by the selected category:

#set ($clauses = [])
#set ($clause = $booleanClauseFactoryUtil.create("category", $selectedCategory, "MUST"))
#set ($void = $clauses.add($clause))
...
#set ($articleHits = $journalArticleService.search($companyId, ... $clauses, ...))

 

Advantages of creating CRUD applications with Liferay WCM

  • Faster coding, no need to deploy .wars, you can dynamically edit and save your velocity script, you'll see your changes right away.
  • Small footprint, it's not a regular portlet with tons of jars consuming memory, it will take longer processing the script but this can be avoided by caching entries.
  • You get all Liferay WCM features for free as I mentioned above, you just need to code a velocity script for displaying entries.

 

Drawbacks

  • Velocity scripts are harder to debug.
  • You can't instantiate Java objects, creation of objects can only be made by calling factories and static methods.
  • If your application has many views or many actions can be performed, your script will become very big, making it harder to maintain. You can't add business logic, there is no data validation.

 

Plans

There are many other useful applications that can be built with Liferay WCM, one that I can think of is Jobs Listing app, we'd like to hear from you which other apps could be useful.

Templates can be easily shared, we are planning to create a new type of plugin, something like a "Webscript" plugin type, a bundle would be basically a .lar file containing templates and structures that we could publish through our plugin installer portlet.

Threaded Replies Author Date
Hey Bruno, Great write-up! Thanks for sharing... Jeffrey Handa October 3, 2009 6:25 PM
Nice article. Thank you, Bruno. Jonas Yuan October 3, 2009 7:28 PM
Nice write up Bruno :) Brian Chan October 3, 2009 7:51 PM
Good write up. For what it's worth, we did our... S L B October 5, 2009 7:32 AM
Hi Bruno, Very interesting post, indeed. Could... Bertrand Pinel October 5, 2009 7:39 AM
Glad you guys liked it! Bertrand, That's the... Bruno Farache October 5, 2009 11:08 AM
Thanx Bruno. Something to look forward to! ... Petros Giakouvakis October 14, 2009 3:15 AM
Petros, The Web Content List allows... Bruno Farache October 14, 2009 4:30 PM
if writing a custom portlet in any other... dennis monsewicz October 30, 2009 11:43 AM
Can i do validation of input fields of... ANU T January 17, 2010 11:04 PM
For 6.0+, use this FAQ-PORTLET.vm instead: ... Bruno Farache June 9, 2010 4:33 PM
Hi Bruno, Is the "Webscript" plugin you... danny chanyalew June 11, 2010 10:30 AM
Hi Bruno, Thanks for this post. I am using... Michael A Ikhane January 25, 2010 3:38 AM
Great post! Is there an easy way to add... jeff Leitman January 29, 2010 11:42 AM
Hi Leitman did you manage to get to add the... Achmed Tyrannus Albab January 30, 2011 10:46 PM
I took a look at his public page and didn't... Geoff Garcia May 20, 2011 10:35 AM
On Liferay 6.0.3, these structures and... Henry K June 14, 2010 2:54 PM
did you set property... Mitesh S Panchal June 15, 2010 12:46 PM
I am trying sample on Liferay 5.2.8 EE, it is... Mitesh S Panchal June 15, 2010 12:49 PM
Mitesh, i think you are not getting... Lakshminarayana Mummanedi July 8, 2010 4:19 AM
Hi Bruno, Great Article to reduce effort/time... DarshanKumar N Bhatia July 26, 2010 9:39 PM
Can someone please provide the link for the... Rodrick Borg August 27, 2010 6:13 AM
See my comment above, it has the correct link. ... Bruno Farache August 27, 2010 6:22 AM
Hi thanks for the quick reply. I am using the... Rodrick Borg August 27, 2010 6:59 AM
Hi Rodrick, I had the same issue and solved it... Jan Boonen September 3, 2010 3:29 AM
I am getting the following error when trying to... Aaron Daubman October 29, 2010 11:25 AM
Unfortunately, the link from "You can download... Aaron Daubman October 29, 2010 1:35 PM
I am also getting the following error while... Arun Kumar S December 21, 2010 2:25 AM
Arun/Aaron You can get the document by going to... Ibrahim Bakhit December 28, 2010 7:25 PM
[...] Hi Terry, All Liferay's services are... Anonymous June 3, 2011 5:09 AM
Like many windows applications having an... Richard Knight July 14, 2011 9:44 AM
Hey Bruno , good article but unfortunately I... KK rajput July 21, 2011 5:23 AM
Hi Bruno , can you please provide a valid link... KK rajput July 22, 2011 6:01 AM
Hi. I have the same problem as Mitas has. The... Balazs Pinter July 25, 2011 3:37 PM
[...] Hi , any body able to implement what is... Anonymous August 18, 2011 10:11 PM
Still clueless how to add maxlength via... Achmed Tyrannus Albab October 24, 2011 12:55 AM
Hi I am Fresher in liferay So i want to know... Sivachandran Nagendran April 10, 2012 11:45 PM
if any body having the FAQ portlet please i... Sivachandran Nagendran April 10, 2012 11:47 PM

Hey Bruno,

Great write-up! Thanks for sharing this information.
Posted on 10/3/09 6:25 PM.
Nice article. Thank you, Bruno.
Posted on 10/3/09 7:28 PM.
Nice write up Bruno emoticon
Posted on 10/3/09 7:51 PM.
Good write up. For what it's worth, we did our FAQ as a Message Board Category, but this would probably run a lot faster.
Posted on 10/5/09 7:32 AM.
Hi Bruno,
Very interesting post, indeed.
Could you just elaborate a little more on what is specific to the 5.3 version and what could be done an 5.2.x ?
Thanks again.
Posted on 10/5/09 7:39 AM.
Glad you guys liked it!

Bertrand,

That's the only restriction that I can think of: if for some reason you need to search for web content fields separately (in my case I needed to filter them by the category field), other than that, scripting 5.2.x is very powerful and you may find workarounds for this specific issue. You should give a try.

Remember that in order to reference $serviceLocator from vm scripts you need to remove it from this setting in portal.properties:

journal.template.velocity.restricted.variables=
Posted on 10/5/09 11:08 AM in reply to Bertrand Pinel.
Thanx Bruno. Something to look forward to!

Something that is also useful is the ability to add "next and previous" buttons, total number of results... In other words: custom paging based on fields. This seems however more something that should be added as feature to the web content list display instead of adding it to the templates functionality. What is missing there is the ability to select content based on the values of custom fields.

A rules engine for more advanced content selection seems a natural step...

These indexing of the fields should also definitely be picked by the search. Perhaps also add "searchable" checkbox. By checking the checkbox, this field can be used from the search which would detect all searchable fields and allow to search on these fields.

Anyhow: that's my wishlist. :-)
Posted on 10/14/09 3:15 AM.
Petros,

The Web Content List allows pagination, I can see a preferences called "Display per page".

Paginating entries with VM templates is also easy, the search API allows us to pass start and end parameters, I have here some templates that paginates entries with "next" and "previous" links.

I personally prefer using VM templates for displaying entries because I have full control over the look and feel and there is no need to change jsps.

Perhaps I didn't understand your last comment... Check for the last screenshot in this blog post, you see the highlighted select box with "keyword"? Isn't it the same thing of the "searchable" checkbox you suggested?

These changes were committed today: http://issues.liferay.com/browse/LPS-5376
Posted on 10/14/09 4:30 PM in reply to Petros Giakouvakis.
if writing a custom portlet in any other language besides Java w/velocity, what is the best way to piggy back on the instantiated sql class? Say for instance I am creating a custom portlet in PHP
Posted on 10/30/09 11:43 AM in reply to Bruno Farache.
Can i do validation of input fields of structures like the below while adding some text in structure.(client side validation)

"
<root>
<dynamic-element name='header' type='text_box'repeatable='false'></dynamic-element>
</root>
"
Posted on 1/17/10 11:04 PM in reply to Bruno Farache.
Hi Bruno,

Thanks for this post. I am using v5.2 and I want to now how to display a list of web contents the way it appears on the blog aggregator portlet. Where the title and an intro text is displayed, with a link to 'read more'

Cheers
Posted on 1/25/10 3:38 AM.
Great post! Is there an easy way to add validation? it would be fantastic if we could add attributes for validation to the dynamic-element tag, like so:

<dynamic-element name="foo" type="text" minLength="10" maxLength="200" />

etc.

Also, where can I find the descriptor files that these xsd structure files are already validated against?

thanks! emoticon
Posted on 1/29/10 11:42 AM.
For 6.0+, use this FAQ-PORTLET.vm instead:

http://www.liferay.com/c/document_library/get_file?uuid=ba0bf122-9fe0-49­2a-8456-528445656b7f&groupId=11325
Posted on 6/9/10 4:33 PM in reply to Bruno Farache.
Hi Bruno,

Is the "Webscript" plugin you proposed to create as a repository for sample webcontents available now?

thx
Posted on 6/11/10 10:30 AM in reply to Bruno Farache.
On Liferay 6.0.3, these structures and templates do nothing. All that is displayed is "Enterprise Edition FAQ" in the h1 tag.
Posted on 6/14/10 2:54 PM.
did you set property journal.template.velocity.restricted.variables under portal-ext.properties?
Posted on 6/15/10 12:46 PM in reply to Henry K.
I am trying sample on Liferay 5.2.8 EE, it is not bringing Web Contents.

I did some investigation looks like following is not working

#set ($clause = $booleanClauseFactoryUtil.create("category", $selectedCategory, "MUST"))

here $clause is not initialized.

and due to that following statement is not bringing results

#set ($articleHits = $journalArticleService.search($companyId, $groupId, $userId, null, null, $clauses, null, -1, -1))

<p>$articleHits</p>


any suggestion?
Posted on 6/15/10 12:49 PM in reply to Mitesh S Panchal.
Mitesh, i think you are not getting #booleanClauseFactoryUtil. Can you try to load them from spring-ext.xml as below.

<bean id="com.liferay.portal.kernel.search.BooleanClauseFactory" class="com.liferay.portal.search.generic.BooleanClauseFactoryImpl" />
Posted on 7/8/10 4:19 AM in reply to Mitesh S Panchal.
Hi Bruno,

Great Article to reduce effort/time to create a CRUD portlet using Web Content+structure+template.
Where I get complete steps for Liferay 6.03 ,also I need to know can we manage pdf,doc,or xml conversion using this concept.

Thanks
Mr.Bhatia
Posted on 7/26/10 9:39 PM.
Can someone please provide the link for the structures and templates since the link provided here is not working.

Also I have the same problem as Henry K with the latest Liferay version (6.0.5) and only the header is being displayed.
Posted on 8/27/10 6:13 AM.
See my comment above, it has the correct link.

http://www.liferay.com/c/document_library/get_file?uuid=ba0bf122-9fe0-492a-­8456-528445656b7f&groupId=11325
Posted on 8/27/10 6:22 AM in reply to Rodrick Borg.
Hi thanks for the quick reply.

I am using the given VM but only the title "Enterprise Edition FAQ" is getting displayed even though I have 2 web contents assigned this template.

I think that I am not linking the web contents entries with the template correctly. Can you please specify how that is done.
Posted on 8/27/10 6:59 AM in reply to Bruno Farache.
Hi Rodrick,

I had the same issue and solved it as follows (using Liferay 6.0.5):
1. look up the structureId of the structure
2. put that in the macro where it says #set ($structure = $journalStructureService.getStructure($groupId, "FAQ")) > replace "FAQ" with your structureId

Hope that works for you as well.

Cheers,

Jan
Posted on 9/3/10 3:29 AM in reply to Rodrick Borg.
I am getting the following error when trying to access this link - is there an updated/current link?
"""
Forbidden
You do not have permission to access the requested resource.
http://www.liferay.com/c/document_library/get_file?uuid=ba0bf122-9fe0-492a-8456-­528445656b7f&groupId=11325
"""

Thanks!
Posted on 10/29/10 11:25 AM in reply to Bruno Farache.
Unfortunately, the link from "You can download all templates and structures used in this example here." generates the following error:
"""
Not Found
The requested resource was not found.
http://www.liferay.com/c/document_library/get_file?p_l_id=745616&folderId=409092­3&name=DLFE-7602.zip
"""

Please update the links and permissions as this could be a very useful post.
Posted on 10/29/10 1:35 PM.
I am also getting the following error while downloading the attachment.

Not Found
The requested resource was not found.
http://www.liferay.com/c/document_library/get_file?p_l_id=745616&folderId=­409092­3&name=DLFE-7602.zip
Posted on 12/21/10 2:25 AM in reply to Aaron Daubman.
Arun/Aaron
You can get the document by going to Bruno's public page, document link at top (next to profile, etc) and you will find it the zip file there.
Here is a link that might work as well
http://www.liferay.com/web/bruno.farache/documents?p_p_lifecycle=0&p_p_id=20­&p_p_col_count=2&p_p_col_id=column-2&p_p_state=maximized&_20_struts_action=%2Fdo­cument_library%2Fview&p_p_mode=view&_20_folderId=4090923
Ibrahim
Posted on 12/28/10 7:25 PM in reply to Arun Kumar S.
Hi Leitman did you manage to get to add the attributes? if yes would you mind providing me link to it? im on 5.2.3. Thanks.
Posted on 1/30/11 10:46 PM in reply to jeff leitman.
I took a look at his public page and didn't find a link there. The link posted on 12/28/10 doesn't work either for me.
Posted on 5/20/11 10:35 AM in reply to Achmed Tyrannus Albab.
[...] Hi Terry, All Liferay's services are available to a WCM template through the serviceLocator. Since accessing the full Liferay API is quite a powerful privilege, this is restricted out-of-the-box. In... [...] Read More
Posted on 6/3/11 5:09 AM.
Like many windows applications having an embedded FileBrowser to pick a file to use in the application, how would you create a custom portlet that uses the existing WCM portlet as a means to create and choose a wcm entry to embed in the custom portlet. Any pointers would be greatly appreciated, since I don't want to recreate the entire functionality of the WCM portlet from scratch.
Posted on 7/14/11 9:44 AM in reply to .
Hey Bruno ,
good article but unfortunately I am not able to make it work 100% some time I am only Q&A and some time only categories and the title "Enterprise Edition FAQ" is getting displayed.
What I have done is created 2 structures and 2 templates and added article. In this case I am just getting the title "Enterprise Edition FAQ" with categories.
Posted on 7/21/11 5:23 AM.
Hi Bruno ,
can you please provide a valid link for For 6.0.5, FAQ-PORTLET.vm since the link you have provide below is not working.
http://www.liferay.com/c/document_library/get_file?uuid=ba0bf122-9fe0-49­­2a-8456-528445656b7f&groupId=11325
Posted on 7/22/11 6:01 AM.
Hi.

I have the same problem as Mitas has. The $clause seems like not a valid object.
#set ($clause = $booleanClauseFactoryUtil.create("category", $selectedCategory, "MUST"))

here $clause is not initialized.

and due to that following statement is not bringing results

#set ($articleHits = $journalArticleService.search($companyId, $groupId, $userId, null, null, $clauses, null, -1, -1))

<p>$articleHits</p>
Posted on 7/25/11 3:37 PM in reply to KK rajput.
[...] Hi , any body able to implement what is provided in this blog Building simple applications with Liferay WCM in liferay 6.0.5 . Thanks [...] Read More
Posted on 8/18/11 10:11 PM.
Still clueless how to add maxlength via structure and tempalte in 6.0.5. Any help here?
Posted on 10/24/11 12:55 AM.
Hi I am Fresher in liferay So i want to know about velocity script using structure format please If you have any tutorials please send for this mail id
sivachandran@formativesolutions.co.in
Posted on 4/10/12 11:45 PM in reply to Achmed Tyrannus Albab.
if any body having the FAQ portlet please i want that code
Posted on 4/10/12 11:47 PM in reply to Sivachandran Nagendran.