Widget Templates are ways to customize how a widget looks. You can create templates for a widget’s display and then choose which template is active.
Figure 1: By using a custom display template, your portlet's display can be customized.
To add Widget Template support to your portlet, follow the steps below.
-
Create and register a custom
*PortletDisplayTemplateHandler
component. Liferay provides theBasePortletDisplayTemplateHandler
as a base implementation for you to extend. You can check theTemplateHandler
interface Javadoc to learn about each template handler method.The
@Component
annotation ties your handler to a specific portlet by setting the propertyjavax.portlet.name
to your portlet’s name. The same property should be found in your portlet class. For example,@Component( immediate = true, property = { "javax.portlet.name="+ AssetCategoriesNavigationPortletKeys.ASSET_CATEGORIES_NAVIGATION }, service = TemplateHandler.class )
The Site Map widget sets the
@Component
annotation like this:@Component( immediate = true, property = "javax.portlet.name=" + SiteNavigationSiteMapPortletKeys.SITE_NAVIGATION_SITE_MAP, service = TemplateHandler.class ) public class SiteNavigationSiteMapPortletDisplayTemplateHandler extends BasePortletDisplayTemplateHandler { }
You’ll continue stepping through the Site map widget’s
TemplateHandler
implementation next. -
Override the base class’
getClassName()
,getName(...)
, andgetResourceName()
methods:@Override public String getClassName() { return LayoutSet.class.getName(); } @Override public String getName(Locale locale) { String portletTitle = _portal.getPortletTitle( SiteNavigationSiteMapPortletKeys.SITE_NAVIGATION_SITE_MAP, ResourceBundleUtil.getBundle(locale, getClass())); return LanguageUtil.format(locale, "x-template", portletTitle, false); } @Override public String getResourceName() { return SiteNavigationSiteMapPortletKeys.SITE_NAVIGATION_SITE_MAP; }
These methods return the template handler’s class name, the template handler’s name (via resource bundle), and the resource name associated with the Widget Template, respectively.
-
Override the
getTemplateVariableGroups(...)
method to return your widget template’s script variable groups. These are used to display hints in the template editor palette.@Override public Map<String, TemplateVariableGroup> getTemplateVariableGroups( long classPK, String language, Locale locale) throws Exception { Map<String, TemplateVariableGroup> templateVariableGroups = super.getTemplateVariableGroups(classPK, language, locale); TemplateVariableGroup templateVariableGroup = templateVariableGroups.get("fields"); templateVariableGroup.empty(); templateVariableGroup.addCollectionVariable( "pages", List.class, PortletDisplayTemplateConstants.ENTRIES, "page", Layout.class, "curPage", "getName(locale)"); templateVariableGroup.addVariable( "site-map-display-context", SiteNavigationSiteMapDisplayContext.class, "siteMapDisplayContext"); return templateVariableGroups; }
For this example, the Pages and Site Map Display Context fields are added to the default variables in the template editor palette.
Figure 2: You can click a variable to add it to the template editor.
-
Set your display template configuration file path:
@Override protected String getTemplatesConfigPath() { return "com/liferay/site/navigation/site/map/web/portlet/template" + "/dependencies/portlet-display-templates.xml"; }
This method returns the XML file containing the display template definitions available for your portlet. You’ll create this file next.
-
Create your
portlet-display-templates.xml
file to define your display template definitions. For example,<?xml version="1.0"?> <root> <template> <template-key>site-map-multi-column-layout-ftl</template-key> <name>portlet-display-template-name-multi-column-layout</name> <description>portlet-display-template-description-multi-column-layout-sitemap</description> <language>ftl</language> <script-file>com/liferay/site/navigation/site/map/web/portlet/template/dependencies/portlet_display_template_multi_column_layout.ftl</script-file> <cacheable>false</cacheable> </template> </root>
This defined template option is read and presented to the user through the widget’s Configuration menu. Navigate to the Site Map widget’s Configuration menu and you can confirm the Multi Column Layout option is available.
Figure 3: You can choose the Widget Template you want to apply from the widget's Configuration menu.
This template is created using FreeMarker. You’ll create this template option next.
-
Create your template script file that you specified in the previous step. For the Site Map widget, its Multi Column Layout option is configured in a FreeMarker template like this:
<#if entries?has_content> <@liferay_aui.row> <#list entries as entry> <#if layoutPermission.containsWithoutViewableGroup(permissionChecker, entry, "VIEW")> <@liferay_aui.col width=25> <div class="results-header"> <h3> <a <#assign layoutType = entry.getLayoutType() /> <#if layoutType.isBrowsable()> href="${portalUtil.getLayoutURL(entry, themeDisplay)}" </#if> >${entry.getName(locale)}</a> </h3> </div> <@displayPages depth=1 pages=entry.getChildren(permissionChecker) /> </@liferay_aui.col> </#if> </#list> </@liferay_aui.row> </#if> <#macro displayPages depth pages > <#if pages?has_content && ((depth < displayDepth?number) || (displayDepth?number == 0))> <ul class="child-pages"> <#list pages as page> <li> <a <#assign pageType = page.getLayoutType() /> <#if pageType.isBrowsable()> href="${portalUtil.getLayoutURL(page, themeDisplay)}" </#if> >${page.getName(locale)}</a> <@displayPages depth=depth + 1 pages=page.getChildren(permissionChecker) /> </li> </#list> </ul> </#if> </#macro>
This template definition enforces page permissions, formats how the pages are displayed (multi column), and provides clickable links for each page.
-
Your widget must define permissions for creating and managing display templates. Add the action key
ADD_PORTLET_DISPLAY_TEMPLATE
to your portlet’s/src/main/resources/resource-actions/default.xml
file:<?xml version="1.0"?> <!DOCTYPE resource-action-mapping PUBLIC "-//Liferay//DTD Resource Action Mapping 7.2.0//EN" "http://www.liferay.com/dtd/liferay-resource-action-mapping_7_2_0.dtd"> <resource-action-mapping> ... <portlet-resource> <portlet-name>yourportlet</portlet-name> <permissions> <supports> <action-key>ADD_PORTLET_DISPLAY_TEMPLATE</action-key> <action-key>ADD_TO_PAGE</action-key> <action-key>CONFIGURATION</action-key> <action-key>VIEW</action-key> </supports> ... </permissions> </portlet-resource> ... </resource-action-mapping>
-
If your widget hasn’t defined Liferay permissions before, create a file named
portlet.properties
in the/resources
folder and add the following contents providing the path to yourdefault.xml
:include-and-override=portlet-ext.properties resource.actions.configs=resource-actions/default.xml
-
Now expose the Widget Template selector to your users. Include the
<liferay-ddm:template-selector>
tag in the JSP file you’re using to control your portlet’s configuration.For example, it may be helpful for you to insert a
<liferay-frontend:fieldset>
in your configuration JSP file like this:<liferay-frontend:fieldset collapsible="<%= true %>" label="templates" > <div class="display-template"> <liferay-ddm:template-selector classNameId="<%= YourEntity.class.getName() %>" displayStyle="<%= displayStyle %>" displayStyleGroupId="<%= displayStyleGroupId %>" refreshURL="<%= PortalUtil.getCurrentURL(request) %>" showEmptyOption="<%= true %>" /> </div> </liferay-frontend:fieldset>
In this JSP, the
<liferay-ddm:template-selector>
tag specifies the Display Template drop-down menu to be used in the widget’s Configuration menu. -
You must now extend your view code to render your portlet using the selected Widget Template.
First, initialize the Java variables needed for the Widget Template:
<% String displayStyle = GetterUtil.getString(portletPreferences.getValue("displayStyle", StringPool.BLANK)); long displayStyleGroupId = GetterUtil.getLong(portletPreferences.getValue("displayStyleGroupId", null), scopeGroupId); %>
Next, you can test if the Widget Template is configured, grab the entities to be rendered, and render them using the Widget Template. The tag
<liferay-ddm:template-renderer>
aids with this process. It automatically uses the selected template or renders its body if no template is selected.Here’s some example code that demonstrates implementing this:
<liferay-ddm:template-renderer className="<%= YourEntity.class.getName() %>" contextObjects="<%= contextObjects %>" displayStyle="<%= displayStyle %>" displayStyleGroupId="<%= displayStyleGroupId %>" entries="<%= yourEntities %>" > <%-- The code that renders the default view should be inserted here. --%> </liferay-ddm:template-renderer>
In this step, you initialized variables dealing with the display settings (
displayStyle
anddisplayStyleGroupId
) and passed them to the tag along with other parameters.As an example, the Site Map widget implements the
<liferay-ddm:template-renderer>
tag in itsview.jsp
like this:<liferay-ddm:template-renderer className="<%= LayoutSet.class.getName() %>" contextObjects="<%= contextObjects %>" displayStyle="<%= siteNavigationSiteMapPortletInstanceConfiguration.displayStyle() %>" displayStyleGroupId="<%= siteNavigationSiteMapDisplayContext.getDisplayStyleGroupId() %>" entries="<%= siteNavigationSiteMapDisplayContext.getRootLayouts() %>" > <%= siteNavigationSiteMapDisplayContext.buildSiteMap() %> </liferay-ddm:template-renderer>
This logic builds the site’s navigation map when the widget is added to a page.
Awesome! Your portlet now supports Widget Templates! Once your script is uploaded and saved, Users with the specified Roles can select the template when they’re configuring the display settings of your portlet on a page. You can visit the Styling Widgets with Widget Templates section for more details on using Widget Templates.