Now that your guestbook and guestbook entry entities have been asset-enabled, you’re ready to use Liferay’s asset functionality in your application. You’ll start by implementing comments, ratings, tags, categories, and related assets for guestbooks. Then you’ll circle back and implement this same functionality for guestbook entries. All of the back-end support for these features is provided by Liferay. Your only task is to update your applications’ user interfaces to use these features.
In this section, you’ll be creating several new JSPs that require new imports.
Add the following imports to your guestbook-portlet project’s
docroot/html/init.jsp
file:
<%@ page import="java.util.Map" %>
<%@ page import="java.util.HashMap" %>
<%@ page import="com.liferay.portlet.asset.service.AssetEntryLocalServiceUtil" %>
<%@ page import="com.liferay.portlet.asset.service.AssetTagLocalServiceUtil" %>
<%@ page import="com.liferay.portlet.asset.model.AssetEntry" %>
<%@ page import="com.liferay.portlet.asset.model.AssetTag" %>
<%@ page import="com.liferay.portal.kernel.util.ListUtil" %>
It’s simpler to add these imports now (rather than as you go) so that you don’t run into errors as you’re working through this section.
Creating JSPs for Displaying Custom Assets in the Asset Publisher
Before you proceed, you need to tie up one loose end from the previous section.
Remember that you implemented render
methods in your GuestbookAssetRenderer
and EntryAssetRenderer
classes. These classes return strings containing the
paths to the JSPs that the Asset Publisher should use for displaying the full
content of the assets. The render
method of GuestbookAssetRenderer
returns
"/html/guestbookadmin/full_content.jsp"
and the render
method of
EntryAssetRenderer
returns "/html/guestbook/full_content.jsp"
. It’s time to
create these JSPs.
In your guestbook-portlet project, create a new file called full_content.jsp
in the docroot/html/guestbookadmin
folder. This JSP will display the full
content of a guestbook asset. Add the following contents to this file:
<%@include file="/html/init.jsp"%>
<%
Guestbook guestbook = (Guestbook)request.getAttribute("gb_guestbook");
guestbook = guestbook.toEscapedModel();
%>
<dl>
<dt>Name</dt>
<dd><%= guestbook.getName() %></dd>
</dl>
In this simple JSP, you grab the guestbook object which was added as a request
attribute. Then you display the guestbook name. The render
method of
GuestbookAssetRenderer
added the gb_guestbook
request attribute with the
following line:
renderRequest.setAttribute("gb_guestbook", _guestbook);
The guestbook’s toEscapedModel
method belongs to the GuestbookModelImpl
class which was generated by Service Builder. This method returns a “safe”
guestbook object, i.e. a guestbook model for which each field has been
HTML-escaped. Calling guestbook = guestbook.toEscapedModel();
before
displaying the guestbook name ensures that your JSP won’t display malicious code
that’s masquerading as a guestbook name.
Next, you need to create a full_content.jsp
for for displaying the full
content of a guestbook entry asset. Create a new file called full_content.jsp
in the docroot/html/guestbook
folder. Add the following contents to this file:
<%@include file="/html/init.jsp"%>
<%
Entry entry = (Entry)request.getAttribute("gb_entry");
entry = entry.toEscapedModel();
%>
<dl>
<dt>Guestbook</dt>
<dd><%= GuestbookLocalServiceUtil.getGuestbook(entry.getGuestbookId()).getName() %></dd>
<dt>Name</dt>
<dd><%= entry.getName() %></dd>
<dt>Message</dt>
<dd><%= entry.getMessage() %></dd>
</dl>
This JSP is almost as simple as the one for guestbooks. The only difference is that you’re displaying three fields of the guestbook entry entity as opposed to one field of the guestbook entity.
Test out your new JSPs by clicking on one of the Read More links for a
guestbook or guestbook entry that’s being displayed by the Asset Publisher
portlet. Alternatively, you can click on the title of the guestbook or guestbook
entry asset. Your full_content.jsp
should be rendered by the Asset Publisher
portlet:
Figure 1: When you click on the *Read More* link for a guestbook or guestbook entry that's displayed by the Asset Publisher, your `full_content.jsp` should be displayed.
By default, when displaying the full view of an asset, the Asset Publisher displays additional links for Twitter, Facebook, and Google Plus. These links allow you to publicize your asset on social media. The Back icon and the View in Context link return you to the Asset Publisher’s default view.
Enabling Tags, Categories, and Related Assets for Guestbooks
Since you’ve already asset-enabled guestbooks at the service layer, your
guestbook entities are all set to take advantage of Liferay’s back-end support
for tags and categories. Your only remaining task is to update your user
interface allow access to these features. Recall that you’ve designed your
application to allow users to add guestbooks from two different portlets: the
Guestbook portlet and the Guestbook Admin portlet. In this section, you’ll
update the form on the Guestbook Admin portlet’s edit_guestbook.jsp
page to
allow users to add, edit, or remove tags and categories when adding or updating
a guestbook. For simplicity’s sake, you’ll leave the Guestbook portlet’s
edit_guestbook.jsp
page alone. (Of course, nothing is preventing you from
adding tags and categories functionality to the Guestbook portlet’s
edit_guestbook.jsp
except a design decision.)
Open your guestbook-portlet project’s
docroot/html/guestbookadmin/edit_guestbook.jsp
file. Replace the existing
contents with the following contents:
<%@include file = "/html/init.jsp" %>
<%
Guestbook guestbook = null;
long guestbookId = ParamUtil.getLong(request, "guestbookId");
if (guestbookId > 0) {
guestbook = GuestbookLocalServiceUtil.getGuestbook(guestbookId);
}
%>
<portlet:renderURL var="viewURL">
<portlet:param name="mvcPath" value="/html/guestbookadmin/view.jsp"></portlet:param>
</portlet:renderURL>
<portlet:actionURL name='<%= guestbook == null ? "addGuestbook" : "updateGuestbook" %>' var="editGuestbookURL" />
<aui:form action="<%= editGuestbookURL %>" name="<portlet:namespace />fm">
<aui:model-context bean="<%= guestbook %>" model="<%= Guestbook.class %>" />
<aui:fieldset>
<aui:input type="hidden" name="guestbookId"
value='<%= guestbook == null ? "" : guestbook.getGuestbookId() %>' />
<aui:input name="name" />
</aui:fieldset>
<liferay-ui:asset-categories-error />
<liferay-ui:asset-tags-error />
<liferay-ui:panel defaultState="closed" extended="<%= false %>" id="guestbookCategorizationPanel" persistState="<%= true %>" title="categorization">
<aui:fieldset>
<aui:input name="categories" type="assetCategories" />
<aui:input name="tags" type="assetTags" />
</aui:fieldset>
</liferay-ui:panel>
<liferay-ui:panel defaultState="closed" extended="<%= false %>" id="guestbookAssetLinksPanel" persistState="<%= true %>" title="related-assets">
<aui:fieldset>
<liferay-ui:input-asset-links
className="<%= Guestbook.class.getName() %>"
classPK="<%= guestbookId %>"
/>
</aui:fieldset>
</liferay-ui:panel>
<aui:button-row>
<aui:button type="submit"></aui:button>
<aui:button type="cancel" onClick="<%= viewURL %>"></aui:button>
</aui:button-row>
</aui:form>
Here, you’re using Liferay and AUI JSP tags to add tags, categories, and related
assets to the form for adding or updating a guestbook. First, you add the
<liferay-ui:asset-categories-error />
and <liferay-ui:asset-categories-error />
tags to the form. These tags are responsible for displaying custom error
messages that appear if an error occurs with the categories or tags that are
submitted on the form. Next comes a <liferay-ui:panel>
tag with several
attributes set. The <liferay-ui:panel>
tag generates a collapsible section
inside which you add the input fields for tags and categories.
When using AUI, you can group related input field together with an
<aui:fieldset>
tag. You add the following two tags inside of an
aui:fieldset>
tag:
<aui:input name="categories" type="assetCategories" />
<aui:input name="tags" type="assetTags" />
Specifying the assetCategories
and assetTags
types for these <aui:input />
tags tells Liferay that these input tags represent asset categories and asset
tags. Liferay shows the appropriate selectors for tags and categories and
displays the tags and categories that have already been added to the guestbook.
Inside of the second <liferay-ui:panel>
tag is an <aui:fieldset>
tag
containing a <liferay-ui:asset-links>
tag. You have to specify values for the
className
and classPK
attributes in order for the correct asset links (the
related assets corresponding to the selected guestbook) to be displayed.
Test your updated edit_guestbook.jsp
page by navigating to your Guestbook
Admin portlet in the Control Panel and clicking on Add Guestbook. You’ll see a
field for adding tags and a selector for selecting related assets.
Figure 2: Once you've updated your Guestbook Admin portlet's `edit_guestbook.jsp` page, you'll see forms for adding tags and selecting related assets.
Where is the field for selecting categories? It’s been enabled but it won’t appear until you create a vocabulary and add at least one category to it. Create a sample vocabulary and add a few sample categories to this vocabulary. Then go back to the Guestbook Admin portlet, click on Add Guestbook or Actions → Edit next to a guestbook and confirm that categories are selectable.
To further test your entities’ integration with Liferay’s asset framework, add
the Tags Navigation, Tag Cloud, and Categories Navigation portlets to the page
with the Asset Publisher portlet. All of tags that you’ve created appear in the
Tags Navigation and Tag Cloud portlet. All of the categories that you’ve created
appear in the Categories Navigation portlet. Click on a tag name or category
name in any of the portlets that you added. When you do so, check that the Asset
Publisher dynamically displays only assets with the selected tag or category.
This mechanism works by means of public render parameters. The Tags Navigation,
Tag Cloud, and Categories Navigation portlets publish a tag
or a categoryId
render parameter and the Asset Publisher reads the parameter and uses it to
dynamically determine which assets to display.
You should also test the Related Assets feature. To do so, create a guestbook and, say, a web content article. Then select one asset as a related asset of the other and click Save. Or create two guestbooks and add one as a related asset of the other.
Asset links represent a reciprocal relationship. If one asset is a related asset of a second, the second is a related asset of the first. Check this for the assets that you linked together.
Enabling Comments and Ratings for Guestbooks
Liferay’s asset framework allows users to comment on and rate assets. As with
tags, categories, and related assets, since you already asset-enabled guestbooks
in the service layer, your only remaining task is to update your user interface
to allow access to these features. It’s best to separate the page where users
comment on and rate assets from the page where users actually edit the assets
themselves. If you added the commenting and rating functionality to the
Guestbook Admin portlet’s edit_guestbook.jsp
page, users might confuse the
collaboration fields with the content fields. It’s easy to imagine scenarios
where users should be able to view, comment on, and rate assets without being
able to actually edit the assets.
Create a new file called view_guestbook.jsp
in your guestbook-portlet
project’s docroot/WEB-INF/html/guestbookadmin
folder. You’ll edit the
docroot/WEB-INF/html/guestbookadmin/view.jsp
file to make the guestbook names
into links pointing to the new view_guestbook.jsp
file. Add the following
contents to view_guestbook.jsp
:
<%@include file = "/html/init.jsp" %>
<portlet:renderURL var="viewURL">
<portlet:param name="mvcPath" value="/html/guestbookadmin/view.jsp"></portlet:param>
</portlet:renderURL>
<liferay-ui:header backURL="<%= viewURL %>" title="guestbook" />
<%
long guestbookId = ParamUtil.getLong(renderRequest, "guestbookId");
Guestbook guestbook = GuestbookLocalServiceUtil.getGuestbook(guestbookId);
guestbook = guestbook.toEscapedModel();
AssetEntry assetEntry = AssetEntryLocalServiceUtil.getEntry(
Guestbook.class.getName(), guestbook.getGuestbookId());
String currentURL = PortalUtil.getCurrentURL(request);
PortalUtil.addPortletBreadcrumbEntry(request, guestbook.getName(),
currentURL);
PortalUtil.setPageSubtitle(guestbook.getName(), request);
PortalUtil.setPageDescription(guestbook.getName(), request);
List<AssetTag> assetTags = AssetTagLocalServiceUtil.getTags(
Guestbook.class.getName(), guestbook.getGuestbookId());
PortalUtil.setPageKeywords(ListUtil.toString(assetTags, "name"),
request);
%>
<dl>
<dt>Name</dt>
<dd><%= guestbook.getName() %></dd>
</dl>
<c:if test="<%= themeDisplay.isSignedIn() %>">
<liferay-ui:panel-container extended="<%= false %>"
id="guestbookCollaborationPanelContainer" persistState="<%= true %>">
<liferay-ui:panel collapsible="<%= true %>" extended="<%= true %>"
id="guestbookCollaborationPanel" persistState="<%= true %>"
title='<%= LanguageUtil.get(pageContext, "collaboration") %>'>
<liferay-ui:ratings className="<%= Guestbook.class.getName() %>"
classPK="<%= guestbook.getGuestbookId() %>" type="stars" />
<br />
<portlet:actionURL name="invokeTaglibDiscussion" var="discussionURL" />
<liferay-ui:discussion className="<%= Guestbook.class.getName() %>"
classPK="<%= guestbook.getGuestbookId() %>"
formAction="<%= discussionURL %>" formName="fm2"
ratingsEnabled="<%= true %>" redirect="<%= currentURL %>"
subject="<%= guestbook.getName() %>"
userId="<%= guestbook.getUserId() %>" />
</liferay-ui:panel>
</liferay-ui:panel-container>
</c:if>
<liferay-ui:asset-links
assetEntryId="<%= (assetEntry != null) ? assetEntry.getEntryId() : 0 %>"
className="<%= Guestbook.class.getName() %>"
classPK="<%= guestbook.getGuestbookId() %>" />
Here, you start by creating a URL to the Guestbook portlet’s default view. You
use this URL for your page’s Back icon that appears in the header that’s
created by the <liferay-ui:header>
tag.
In the scriptlet, you use the guestbookId
request attribute to get a guestbook
object. You convert it to an escaped model for security reasons, as discussed
earlier. Next, you update your portlet’s breadcrumb entry with the name of the
current guestbook. Since the Guestbook Admin portlet lives in the Control Panel,
the portlet breadcrumb is not visible. However, it would be visible if you added
the portlet to a regular portal page. (The Breadcrumb portlet appears on regular
portal pages, by default.)
Figure 3: The Breadcrumb portlet appears on regular portal pages, by default. It appears just beneath the main page navigation menu and displays the path to the current page or portlet.
At the end of the scriptlet, you add the names of the existing asset tags for
the current guestbook as keywords to the portal page. These tag names appear in
a <meta content="[tag names here]" lang="en-US" name="keywords" />
element in
the <head>
section of your portal page. These keywords can help search engines
find more easily find and index your page.
After the scriptlet, you define the main content of your page. You’re simply
displaying your guestbook’s name with <dl>
, <dt>
, and <dd>
tags the same
way you did in docroot/WEB-INF/html/guestbookadmin/full_content.jsp
.
Next, you use <liferay-ui:panel-container>
tag to create a panel container.
Inside this tag, you use a <liferay-ui:panel>
tag to create a panel containing
the comments and ratings components. The ratings component is implemented via
the <liferay-ui:ratings>
tag. The comments tag is implemented via the
<liferay-ui:discussion>
tag. Note that the <liferay-ui:discussion>
tag
requires an action URL to be supplied for its formAction
attribute. The
invokeTaglibDiscussion
action URL is responsible for actually adding the
comment after the user clicks Add Comment, enters a comment, and clicks
Reply.
Note that the whole panel container is wrapped in a <c:if>
tag. You’re
restricting access to comments and ratings to users who have signed in with a
portal account. You’re checking this with the following expression:
<%= themeDisplay.isSignedIn() %>
At the end of the page, you’re displaying the related assets of the current
guestbook. Note that you’re using the <liferay-ui:asset-links>
tag to
displayed related assets. This tag is distinct from the
<liferay-ui:input-asset-links>
that you used in edit_guestbook.jsp
that
allowed the user to select related assets.
Your view_guestbook.jsp
page is currently orphaned. Fix this by making the
guestbook names that appear in the search container of the default view into
links. Open your docroot/WEB-INF/guestbookadmin/view.jsp
and find the
following line:
<liferay-ui:search-container-column-text property="name" />
Replace this line with the following lines:
<portlet:renderURL var="viewGuestbook">
<portlet:param name="mvcPath" value="/html/guestbookadmin/view_guestbook.jsp" />
<portlet:param name="guestbookId" value="<%= String.valueOf(guestbook.getGuestbookId()) %>" />
</portlet:renderURL>
<liferay-ui:search-container-column-text property="name" href="<%= viewGuestbook %>" />
Here, you’re simply creating a URL that points to the view_guestbook.jsp
that
you created and adding an href
attribute that points to this URL. Test this
link by clicking on an existing guestbook. Then test that comments and ratings
work as expected.
Enabling Tags, Categories, and Related Assets for Guestbook Entries
Enabling tags, categories, and related assets for guestbook entries is very similar to enabling them for guestbooks. As with guestbooks, you’ll separate the page where users comment on and rate guestbook entries from the page where users actually edit the guestbook entries.
Open your guestbook-portlet project’s docroot/html/guestbook/edit_entry.jsp
file. Replace the existing contents with the following contents:
<%@include file = "/html/init.jsp" %>
<portlet:renderURL var="viewURL">
<portlet:param name="mvcPath" value="/html/guestbook/view.jsp"></portlet:param>
</portlet:renderURL>
<portlet:actionURL name="addEntry" var="addEntryURL"></portlet:actionURL>
<%
long entryId = ParamUtil.getLong(renderRequest, "entryId");
Entry entry = null;
if (entryId > 0) {
entry = EntryLocalServiceUtil.getEntry(entryId);
}
long guestbookId = ParamUtil.getLong(request, "guestbookId");
%>
<aui:form action="<%= addEntryURL %>" name="<portlet:namespace />fm">
<aui:model-context bean="<%= entry %>" model="<%= Entry.class %>" />
<aui:fieldset>
<aui:input name="name" />
<aui:input name="email" />
<aui:input name="message" />
<aui:input name="guestbookId" type="hidden" value='<%= entry == null ? guestbookId : entry.getGuestbookId() %>'/>
<aui:input name="entryId" type="hidden" />
</aui:fieldset>
<liferay-ui:asset-categories-error />
<liferay-ui:asset-tags-error />
<liferay-ui:panel defaultState="closed" extended="<%= false %>" id="entryCategorizationPanel" persistState="<%= true %>" title="categorization">
<aui:fieldset>
<aui:input name="categories" type="assetCategories" />
<aui:input name="tags" type="assetTags" />
</aui:fieldset>
</liferay-ui:panel>
<liferay-ui:panel defaultState="closed" extended="<%= false %>" id="entryAssetLinksPanel" persistState="<%= true %>" title="related-assets">
<aui:fieldset>
<liferay-ui:input-asset-links
className="<%= Entry.class.getName() %>"
classPK="<%= entryId %>"
/>
</aui:fieldset>
</liferay-ui:panel>
<aui:button-row>
<aui:button type="submit"></aui:button>
<aui:button type="cancel" onClick="<%= viewURL %>"></aui:button>
</aui:button-row>
</aui:form>
Test your JSP by using the Guestbook portlet to add and update Guestbook entries. Try add and removing tags, categories, and related assets. All these operations should work.
Enabling Comments and Ratings for Guestbook Entries
Create a new file called view_entry.jsp
in your guestbook-portlet project’s
docroot/WEB-INF/html/guestbook
folder. Add the following contents to it:
<%@include file = "/html/init.jsp" %>
<portlet:renderURL var="viewURL">
<portlet:param name="mvcPath" value="/html/guestbook/view.jsp"></portlet:param>
</portlet:renderURL>
<liferay-ui:header backURL="<%= viewURL %>" title="entry" />
<%
long entryId = ParamUtil.getLong(renderRequest, "entryId");
Entry entry = EntryLocalServiceUtil.getEntry(entryId);
entry = entry.toEscapedModel();
AssetEntry assetEntry = AssetEntryLocalServiceUtil.getEntry(
Entry.class.getName(), entry.getEntryId());
String currentURL = PortalUtil.getCurrentURL(request);
PortalUtil.addPortletBreadcrumbEntry(request, entry.getMessage(),
currentURL);
PortalUtil.setPageSubtitle(entry.getMessage(), request);
PortalUtil.setPageDescription(entry.getMessage(), request);
List<AssetTag> assetTags = AssetTagLocalServiceUtil.getTags(
Entry.class.getName(), entry.getEntryId());
PortalUtil.setPageKeywords(ListUtil.toString(assetTags, "name"),
request);
%>
<dl>
<dt>Guestbook</dt>
<dd><%= GuestbookLocalServiceUtil.getGuestbook(entry.getGuestbookId()).getName() %></dd>
<dt>Name</dt>
<dd><%= entry.getName() %></dd>
<dt>Message</dt>
<dd><%= entry.getMessage() %></dd>
</dl>
<c:if test="<%= themeDisplay.isSignedIn() %>">
<liferay-ui:panel-container extended="<%= false %>"
id="entryCollaborationPanelContainer" persistState="<%= true %>">
<liferay-ui:panel collapsible="<%= true %>" extended="<%= true %>"
id="entryCollaborationPanel" persistState="<%= true %>"
title='<%= LanguageUtil.get(pageContext, "collaboration") %>'>
<liferay-ui:ratings className="<%= Entry.class.getName() %>"
classPK="<%= entry.getEntryId() %>" type="stars" />
<br />
<portlet:actionURL name="invokeTaglibDiscussion" var="discussionURL" />
<liferay-ui:discussion className="<%= Entry.class.getName() %>"
classPK="<%= entry.getEntryId() %>"
formAction="<%= discussionURL %>" formName="fm2"
ratingsEnabled="<%= true %>" redirect="<%= currentURL %>"
subject="<%= entry.getMessage() %>"
userId="<%= entry.getUserId() %>" />
</liferay-ui:panel>
</liferay-ui:panel-container>
</c:if>
<liferay-ui:asset-links
assetEntryId="<%= (assetEntry != null) ? assetEntry.getEntryId() : 0 %>"
className="<%= Entry.class.getName() %>"
classPK="<%= entry.getEntryId() %>" />
This JSP is currently orphaned. Open your project’s
docroot/WEB-INF/html/guestbook/view.jsp
file to create a link to it. Find the
following line:
<liferay-ui:search-container-column-text property="message" />
Replace it with the following line:
<portlet:renderURL var="viewEntry">
<portlet:param name="mvcPath" value="/html/guestbook/view_entry.jsp" />
<portlet:param name="entryId" value="<%= String.valueOf(entry.getEntryId()) %>" />
</portlet:renderURL>
<liferay-ui:search-container-column-text property="message" href="<%= viewEntry %>"/>
Test your Guestbook portlet and check that the links works correctly. Then test that you can add comments and ratings to guestbook entries. Excellent! You’ve asset-enabled your guestbook and guestbook entry entities! And you’ve enabled tags, categories, related assets, comments, and ratings for both entities.