Sometimes a client needs to be able to modify web content that appears on multiple pages. In some occasions you just can’t put the web content in the right place using the “basic” WCM features like Layout Templates and/or Journal Templates.
Consider things like a short introduction text in a header, a doormat, a footer, static navigation menu’s that rarely need to be updated, places for banners for temporary promotions and so on. What all these things have in common is that they don’t fit in the scope of Layout Templates or Journal Templates. You can’t reach the places I mentioned before with those facilities, because they mostly reside somewhere in the theme itself.
In this blog post/tutorial I will demonstrate and explain 2 ways to embed web content in a Liferay theme — using a demo theme which you can download below!
Take a look at the screenshot below:
The layout (theme) is made up out of four important areas:
- header: the area in the top of the layout;
- footer: the area in the bottom of the layout;
- sidebar: the area in the right center of the layout;
- content: the area in the left center of the layout. The layout of this area is determined by the configured Layout Template.
The first three areas (header, footer and sidebar) are the ones that should contain embedded web content which can be edited by the client through the Liferay Control Panel.
Create a journal article (or web content) for every area first as I have done in the screenshot below:
Note that I have used alphanumeric ID’s for the journal articles listed above instead of generated (numerical) ID’s. I will explain later why I’ve done that.
The first way is based upon embedding the Web Content Display portlet: this portlet comes out the of box with Liferay.
Below is the configuration file (
liferay-look-and-feel.xml) of the demo theme.
I’ve added the journal article ID of every journal article I need to embed in the theme (they are added as theme settings).
There’s also an extra setting
portlet-setup-show-borders-default configured to hide the portlet wrapper of the embedded Web Content Display portlets.
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE look-and-feel PUBLIC "-//Liferay//DTD Look and Feel 5.2.0//EN" "http://www.liferay.com/dtd/liferay-look-and-feel_5_2_0.dtd"> <look-and-feel> <compatibility> <version>5.2.3</version> </compatibility> <company-limit> <company-includes /> <company-excludes /> </company-limit> <theme name="Demo" id="demo"> <template-extension>vm</template-extension> <settings> <setting key="portlet-setup-show-borders-default" value="false" /> <setting key="header-article-id" value="HEADER" /> <setting key="sidebar-article-id" value="SIDEBAR" /> <setting key="footer-article-id" value="FOOTER" /> </settings> </theme> </look-and-feel>
Next you need to add some Velocity code to the
init_custom.vm file in the templates directory.
In this example I will only embed the sidebar as a separate Web Content Display portlet.
#set ($portletSetupShowBordersDefault = $theme_settings.getProperty("portlet-setup-show-borders-default")) #set ($sidebarArticleId = $theme_settings.getProperty("sidebar-article-id"))
In this Velocity template the theme settings are loaded as Velocity variables.
When this file is set, we can proceed to the last step: modifying the
portal_normal.vm file. In this file the
init_custom.vm is executed thanks to the following statement:
The needed variables are injected in the theme and now we have all the information available that is needed to embed the web content.
You can now embed the web content with the following Velocity code:
$velocityPortletPreferences.setValue("portlet-setup-show-borders", $portletSetupShowBordersDefault) $velocityPortletPreferences.setValue("group-id", "$group_id") $velocityPortletPreferences.setValue("article-id", "$sidebarArticleId") $theme.runtime("56_INSTANCE_a0b1", "", $velocityPortletPreferences.toString()) #set ($VOID = $velocityPortletPreferences.reset())
First we need to configure the portlet preferences for the portlet we’re about to embed.
$portletSetupShowBordersDefault:injected setting from the XML configuration. This setting hides the portlet borders.
$sidebarArticleId:injected setting from the XML configuration. This variable contains the journal article ID of the sidebar.
$group_id:this variable is automatically set in the theme and contains the community ID.
Next, the portlet is embedded using the
runtime method of the variable
$theme. Notice that you have to provide this method with a portlet namespace, which in this case is
56_INSTANCE_a0b1. Make sure that this namespace always starts with
56_INSTANCE_ or else Liferay might embed a wrong portlet!
Notice the last line of code: the portlet preferences are reset. Always reset your portlet preferences after embedding the Web Content Display portlet or else you might get duplicate content in your theme! Also make sure that the portlet namespaces are unique within a page.
You might wonder why I’ve added all the variables as theme settings first. This approach makes the theme configurable. If something needs to be changed afterwards, you don’t need to modify the Velocity code, but just reconfigure the theme through the XML file
Also notice that I’ve used alphanumerical ID’s instead of numerical (generated) ID’s. I highly recommend using alphanumerical ID’s over the generated ones, simply because the generated ID’s might be different between other environments (development, integration, staging, production). If you use alphanumerical ID’s you don’t need to reconfigure the theme for every environment.
You can disable auto-generating ID’s with the following setting in
So far for the first way!
The second way makes use of the Liferay services. Instead of embedding a whole portlet in the theme we just load the web content to embed in the HTML of the theme.
The theme configuration was already discussed in the first way. In this explanation that same configuration is used again.
init_custom.vm file must be modified again. In this example I will embed the header and footer content through this alternative way.
Take a look at the new content of the
#set ($headerArticleId = $theme_settings.getProperty("header-article-id")) #set ($footerArticleId = $theme_settings.getProperty("footer-article-id")) #set ($headerContent = $journalContentUtil.getContent($group_id, $headerArticleId, null, "$locale", $theme_display)) #set ($footerContent = $journalContentUtil.getContent($group_id, $footerArticleId, null, "$locale", $theme_display))
Again, the theme settings are loaded as Velocity variables. Next, the web content service (
JournalContentUtil) is used to load the content through the
This method accepts 4 arguments:
$group_id:the community ID of the web content article.
$article_id:the web content article ID.
$template_id:the web content template ID. You are free to use any web content template that is associated with the web content structure used by the article.
$locale:the portal locale (which is set by default).
$theme_display:the theme display object (which is set by default).
This web content is immediately set as Velocity variables.
Now in the
portal_normal.vm file you just have to put these content variables in the right place and we’re done!
That’s it for the second (simpler) way!
Both ways are very efficient and clean ways to embed web content in Liferay themes.
The second way is simpler than the first way, but you will not be able to embed the web content with the portlet wrapper. If this wrapper is needed or useful for any reason, I suggest you to use the first way.
In all the other cases, I recommend using the second (simpler) way.
Feel free to ask questions and don’t hesitate to download the demo theme below! This theme contains all the code discussed above.
The two ways described above are applied in the demo theme which you can download below! Make sure to create three web content articles with ID’s
FOOTER. If these articles don’t exist, Liferay will tell you that the content is expired.