Adding Asset Features to Your User Interface

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:

asset-publisher-full-content.png

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.

guestbook-tags-related-assets.png

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 ActionsEdit 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.)

portlet-breadcrumb.png

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.

Next Steps

Using AlloyUI in Your Application

0 (0 Votes)
Implementing Asset Renderers Previous