Implementing Asset Renderers
Step 1 of 2
Liferay DXP’s asset renderers follow the factory pattern, so you must create a
GuestbookAssetRendererFactory
that instantiates the GuestbookAssetRenderer
’s
private guestbook object. Here, you’ll create both classes.
Get started by creating the Asset Renderer class first.
Creating the AssetRenderer Class
Follow these steps to create the GuestbookAssetRenderer
class:
-
Create a new package called
com.liferay.docs.guestbook.asset
in theguestbook-service
module’ssrc/main/java
folder. In this package, create aGuestbookAssetRenderer
class that extends Liferay DXP’sBaseJSPAssetRenderer
class. Extending this class gives you a head-start on implementing theAssetRenderer
interface. Start with this code:package com.liferay.docs.guestbook.asset; import com.liferay.asset.kernel.model.BaseJSPAssetRenderer; import com.liferay.portal.kernel.exception.PortalException; import com.liferay.portal.kernel.exception.SystemException; import com.liferay.portal.kernel.model.LayoutConstants; import com.liferay.portal.kernel.portlet.LiferayPortletRequest; import com.liferay.portal.kernel.portlet.LiferayPortletResponse; import com.liferay.portal.kernel.portlet.PortletURLFactoryUtil; import com.liferay.portal.kernel.security.permission.ActionKeys; import com.liferay.portal.kernel.security.permission.PermissionChecker; import com.liferay.portal.kernel.util.HtmlUtil; import com.liferay.portal.kernel.util.PortalUtil; import com.liferay.portal.kernel.util.StringUtil; import com.liferay.docs.guestbook.portlet.constants.GuestbookPortletKeys; import com.liferay.docs.guestbook.model.Guestbook; import com.liferay.docs.guestbook.service.permission.GuestbookPermission; import java.util.Locale; import javax.portlet.PortletRequest; import javax.portlet.PortletResponse; import javax.portlet.PortletURL; import javax.portlet.WindowState; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class GuestbookAssetRenderer extends BaseJSPAssetRenderer<Guestbook> { }
-
Add the constructor and the guestbook class variable next. Most of the methods in this class are simply getters that return fields from this private guestbook object:
public GuestbookAssetRenderer(Guestbook guestbook) { _guestbook = guestbook; } private Guestbook _guestbook;
-
The
BaseJSPAssetRenderer
abstract class that you’re extending contains dummy implementations of thehasEditPermission
andhasViewPermission
methods that you must override. Override these dummy implementations with actual permission checks using theGuestbookPermission
class that you created earlier:@Override public boolean hasEditPermission(PermissionChecker permissionChecker) throws PortalException { long guestbookId = _guestbook.getGuestbookId(); return GuestbookPermission.contains(permissionChecker, guestbookId, ActionKeys.UPDATE); } @Override public boolean hasViewPermission(PermissionChecker permissionChecker) throws PortalException { long guestbookId = _guestbook.getGuestbookId(); return GuestbookPermission.contains(permissionChecker, guestbookId, ActionKeys.VIEW); }
-
Add the following getter methods to retrieve information about the guestbook asset:
@Override public Guestbook getAssetObject() { return _guestbook; } @Override public long getGroupId() { return _guestbook.getGroupId(); } @Override public long getUserId() { return _guestbook.getUserId(); } @Override public String getUserName() { return _guestbook.getUserName(); } @Override public String getUuid() { return _guestbook.getUuid(); } @Override public String getClassName() { return Guestbook.class.getName(); } @Override public long getClassPK() { return _guestbook.getGuestbookId(); } @Override public String getSummary(PortletRequest portletRequest, PortletResponse portletResponse) { return "Name: " + _guestbook.getName(); } @Override public String getTitle(Locale locale) { return _guestbook.getName(); } @Override public boolean include(HttpServletRequest request, HttpServletResponse response, String template) throws Exception { request.setAttribute("GUESTBOOK", _guestbook); request.setAttribute("HtmlUtil", HtmlUtil.getHtml()); request.setAttribute("StringUtil", new StringUtil()); return super.include(request, response, template); }
The final method makes several utilities, as well as the
Guestbook
entity, available to Liferay DXP in theHttpServletRequest
object. -
Override the
getJspPath
method. This method returns a string that represents the path to the JSP that renders the guestbook asset. When the Asset Publisher displays an asset’s full content, it invokes the asset renderer class’sgetJspPath
method and passes atemplate
string parameter that equals"full_content"
. This returns/asset/guestbook/full_content.jsp
when thefull_content
template string is passed as a parameter. You’ll create this JSP later when updating your application’s user interface:@Override public String getJspPath(HttpServletRequest request, String template) { if (template.equals(TEMPLATE_FULL_CONTENT)) { request.setAttribute("gb_guestbook", _guestbook); return "/asset/guestbook/" + template + ".jsp"; } else { return null; } }
-
Override the
getURLEdit
method. This method returns a URL for editing the asset:@Override public PortletURL getURLEdit(LiferayPortletRequest liferayPortletRequest, LiferayPortletResponse liferayPortletResponse) throws Exception { PortletURL portletURL = liferayPortletResponse.createLiferayPortletURL( getControlPanelPlid(liferayPortletRequest), GuestbookPortletKeys.GUESTBOOK, PortletRequest.RENDER_PHASE); portletURL.setParameter("mvcRenderCommandName", "/guestbookwebportlet/edit_guestbook"); portletURL.setParameter("guestbookId", String.valueOf(_guestbook.getGuestbookId())); portletURL.setParameter("showback", Boolean.FALSE.toString()); return portletURL; }
-
Override the
getURLViewInContext
method. This method returns a URL to view the asset in its native application:@Override public String getURLViewInContext(LiferayPortletRequest liferayPortletRequest, LiferayPortletResponse liferayPortletResponse, String noSuchEntryRedirect) throws Exception { try { long plid = PortalUtil.getPlidFromPortletId(_guestbook.getGroupId(), GuestbookPortletKeys.GUESTBOOK); PortletURL portletURL; if (plid == LayoutConstants.DEFAULT_PLID) { portletURL = liferayPortletResponse.createLiferayPortletURL(getControlPanelPlid(liferayPortletRequest), GuestbookPortletKeys.GUESTBOOK, PortletRequest.RENDER_PHASE); } else { portletURL = PortletURLFactoryUtil.create(liferayPortletRequest, GuestbookPortletKeys.GUESTBOOK, plid, PortletRequest.RENDER_PHASE); } portletURL.setParameter("mvcRenderCommandName", "/guestbookwebportlet/view"); portletURL.setParameter("guestbookId", String.valueOf(_guestbook.getGuestbookId())); String currentUrl = PortalUtil.getCurrentURL(liferayPortletRequest); portletURL.setParameter("redirect", currentUrl); return portletURL.toString(); } catch (PortalException e) { } catch (SystemException e) { } return noSuchEntryRedirect; }
-
Override the
getURLView
method. This method returns a URL to view the asset from within the Asset Publisher:@Override public String getURLView(LiferayPortletResponse liferayPortletResponse, WindowState windowState) throws Exception { return super.getURLView(liferayPortletResponse, windowState); }
-
Save the class.
-
You have an error in your class, because the
guestbook-service
project doesn’t have access to theGuestbookPortletKeys
object that’s in theguestbook-web
project.It is logical to think this could be corrected by including the project as a dependency in
guestbook-service
’sbuild.gradle
file, but that creates a circular dependency.guestbook-web
already depends onguestbook-service
, so you can’t makeguestbook-service
depend circularly onguestbook-web
.So now what do you do?
Make sure you’ve opened both
guestbook-api
andguestbook-web
projects. Drag thecom.liferay.docs.guestbook.portlet.constants
package from theguestbook-web
project and drop it on theguestbook-api
project’ssrc/main/java
folder. Blamo! You fixed the problem.guestbook-service
depends onguestbook-api
and implements its interfaces.guestbook-web
depends on both. Now you have only linear dependencies.
Next you can create the AssetRendererFactory
class.
Creating the GuestbookAssetRendererFactory Class
Follow these steps to create the GuestbookAssetRendererFactory
:
-
In the
com.liferay.docs.guestbook.asset
package, create a class calledGuestbookAssetRendererFactory
that extends Liferay DXP’sBaseAssetRendererFactory
class. Replace its code with this starter code:package com.liferay.docs.guestbook.asset; import com.liferay.asset.kernel.model.AssetRenderer; import com.liferay.asset.kernel.model.AssetRendererFactory; import com.liferay.asset.kernel.model.BaseAssetRendererFactory; import com.liferay.docs.guestbook.model.Guestbook; import com.liferay.docs.guestbook.service.EntryLocalService; import com.liferay.docs.guestbook.service.GuestbookLocalService; import com.liferay.docs.guestbook.service.permission.GuestbookPermission; import com.liferay.docs.guestbook.constants.GuestbookPortletKeys; import com.liferay.portal.kernel.util.WebKeys; import com.liferay.portal.kernel.exception.PortalException; import com.liferay.portal.kernel.portlet.LiferayPortletRequest; import com.liferay.portal.kernel.portlet.LiferayPortletResponse; import com.liferay.portal.kernel.security.permission.PermissionChecker; import com.liferay.portal.kernel.theme.ThemeDisplay; import javax.portlet.PortletRequest; import javax.portlet.PortletURL; import javax.servlet.ServletContext; import org.osgi.service.component.annotations.Component; import org.osgi.service.component.annotations.Reference; @Component(immediate = true, property = {"javax.portlet.name=" + GuestbookPortletKeys.GUESTBOOK}, service = AssetRendererFactory.class ) public class GuestbookAssetRendererFactory extends BaseAssetRendererFactory<Guestbook> { public GuestbookAssetRendererFactory() { setClassName(CLASS_NAME); setLinkable(_LINKABLE); setPortletId(GuestbookPortletKeys.GUESTBOOK); setSearchable(true); setSelectable(true); }
This code contains the class declaration and the constructor. It sets the class name it creates an
AssetRenderer
for, a portlet ID, and a boolean (_LINKABLE
) set totrue
. The boolean denotes the methods that provide URLs in the generatedAssetRenderer
are implemented. -
Implement the
getAssetRenderer
method, which constructs newGuestbookAssetRenderer
instances for specific guestbooks. It uses theclassPK
(primary key) parameter to retrieve the guestbook from the database. It then calls theGuestbookAssetRenderer
’s constructor, passing the retrieved guestbook as an argument:@Override public AssetRenderer<Guestbook> getAssetRenderer(long classPK, int type) throws PortalException { Guestbook guestbook = _guestbookLocalService.getGuestbook(classPK); GuestbookAssetRenderer guestbookAssetRenderer = new GuestbookAssetRenderer(guestbook); guestbookAssetRenderer.setAssetRendererType(type); guestbookAssetRenderer.setServletContext(_servletContext); return guestbookAssetRenderer; }
-
You’re extending
BaseAssetRendererFactory
, an abstract class that implements theAssetRendererFactory
interface. To ensure that your custom asset is associated with the correct entity, each asset renderer factory must implement thegetClassName
andgetType
methods (among others):@Override public String getClassName() { return CLASS_NAME; } @Override public String getType() { return TYPE; }
-
Implement the
hasPermission
method via theGuestbookPermission
class:@Override public boolean hasPermission(PermissionChecker permissionChecker, long classPK, String actionId) throws Exception { Guestbook guestbook = _guestbookLocalService.getGuestbook(classPK); return GuestbookPermission.contains(permissionChecker, guestbook, actionId); }
-
Add the remaining code to create the portlet URL for the asset and specify whether it’s linkable:
@Override public PortletURL getURLAdd(LiferayPortletRequest liferayPortletRequest, LiferayPortletResponse liferayPortletResponse, long classTypeId) { PortletURL portletURL = null; try { ThemeDisplay themeDisplay = (ThemeDisplay) liferayPortletRequest.getAttribute(WebKeys.THEME_DISPLAY); portletURL = liferayPortletResponse.createLiferayPortletURL(getControlPanelPlid(themeDisplay), GuestbookPortletKeys.GUESTBOOK, PortletRequest.RENDER_PHASE); portletURL.setParameter("mvcRenderCommandName", "/guestbookwebportlet/edit_guestbook"); portletURL.setParameter("showback", Boolean.FALSE.toString()); } catch (PortalException e) { } return portletURL; } @Override public boolean isLinkable() { return _LINKABLE; } @Override public String getIconCssClass() { return "bookmarks"; } @Reference(target = "(osgi.web.symbolicname=com.liferay.docs.guestbook.portlet)", unbind = "-") public void setServletContext(ServletContext servletContext) { _servletContext = servletContext; } private ServletContext _servletContext; @Reference(unbind = "-") protected void setGuestbookLocalService(GuestbookLocalService guestbookLocalService) { _guestbookLocalService = guestbookLocalService; } private GuestbookLocalService _guestbookLocalService; private static final boolean _LINKABLE = true; public static final String CLASS_NAME = Guestbook.class.getName(); public static final String TYPE = "guestbook"; }
-
Organize imports (Ctrl-Shift-O) and save the file.
Great! The guestbook asset renderer is complete. Next, you’ll create the entry asset renderer.