Moving Entries to the Recycle Bin

This tutorial covers how to add the ability to move your app’s entries to the Recycle Bin.

move-entities-rb.png

Figure 1: You can easily create a way to move your app’s entries to the Recycle Bin.

Here are the steps for implementing this Recycle Bin component:

  1. Enable Trash for Service Entities
  2. Implement a Trash Handler for each trash-enabled entity
  3. Create a service method to move entries to the Recycle Bin
  4. Create a portlet action to initiate moving entries to Recycle Bin
  5. Implement a Trash Renderer for each trash-enabled entity

Start off by configuring the app’s service.

Step 1: Enable Trash for Service Entities

For every entity you want to recycle bin-enable, you must enable the trash feature. To do this, insert insert the trash-enabled="true" attribute inside your entities’ <entity> tags in your service.xml file. Each trash-enabled entity element should look similar to this one:

<entity name="YourEntity" local-service="true" remote-service="true" uuid="true" trash-enabled="true">

Run Service Builder to generate back-end trash related classes for the entities.

You’ll implement trash handlers for these entities next.

Step 2: Implement a Trash Handler for Each Trash-Enabled Entity

As with many other Liferay frameworks–such as the workflow, assets, and indexing frameworks–you must implement a handler class for Recycle Bin. A Recycle Bin handler class manages moving entries to the Recycle Bin, viewing them in the Recycle Bin, restoring them, and permanently deleting them. You must implement the TrashHandler interface for each trash-enabled entity. As a convenience, Liferay provides the extensible abstract class BaseTrashHandler.

Consider the following TrashHandler methods as a minimal set of methods a trash handler must implement or override:

  • deleteTrashEntry
  • getClassName
  • getRestoreContainedModelLink
  • getRestoreMessage
  • hasTrashPermission
  • isInTrash
  • restoreTrashEntry

For example, the Jukebox portlet’s JukeBoxBaseTrashHandler class implements these methods and serves as a base implementation for the app’s song and album entities. As an example trash handler implementation, you can refer to the SongTrashHandler and its base class JukeBoxBaseTrashHandler.

After you’ve implemented trash handlers for your trash-enabled entities, specify the handlers in your app’s liferay-portlet.xml file. For example, the Jukebox’s Song portlet specifies the song trash handler like this:

<trash-handler>org.liferay.jukebox.trash.SongTrashHandler</trash-handler>

You can refer to the Jukebox portlet’s liferay-portlet.xml file to see the trash handlers it specifies.

Great! You have trash handlers ready to manage your trash entries, but you still need a way to get the entries to the Recycle Bin. You must create a service method for moving them there.

Step 3: Create a Service Method to Move Entries to the Recycle Bin

You’ll implement a local service method for moving them to the Recycle Bin. This service method must implement a trash service for the entity. Your -EntityNameLocalServiceImpl class’s trash method should look similar to the method moveSongToTrash in Jukebox Portlet’s SongLocalServiceImpl class:

@Indexable(type = IndexableType.REINDEX)
public Song moveSongToTrash(long userId, Song song)
    throws PortalException, SystemException {

    ServiceContext serviceContext = new ServiceContext();

    // Entry

    User user = userPersistence.findByPrimaryKey(userId);
    Date now = new Date();

    int oldStatus = song.getStatus();

    song.setModifiedDate(serviceContext.getModifiedDate(now));
    song.setStatus(WorkflowConstants.STATUS_IN_TRASH);
    song.setStatusByUserId(user.getUserId());
    song.setStatusByUserName(user.getFullName());
    song.setStatusDate(serviceContext.getModifiedDate(now));

    // Asset

    assetEntryLocalService.updateVisible(
       Song.class.getName(), song.getSongId(), false);

    // Trash

    UnicodeProperties typeSettingsProperties = new UnicodeProperties();

    typeSettingsProperties.put("title", song.getName());

    TrashEntry trashEntry = trashEntryLocalService.addTrashEntry(
       userId, song.getGroupId(), Song.class.getName(), song.getSongId(),
       song.getUuid(), null, oldStatus, null, typeSettingsProperties);

    song.setName(TrashUtil.getTrashTitle(trashEntry.getEntryId()));

    songPersistence.update(song);

    return song;
}

Notice that this method is annotated as @Indexable. That means that every time an entry is moved to the Recycle Bin, Liferay re-indexes the entities and their corresponding trash entries. This makes the trashed entries searchable only from the Recycle Bin, while regular entries aren’t searchable outside of the Recycle Bin.

There’s also a call to song.setStatus(WorkflowConstants.STATUS_IN_TRASH), which sets the song’s status, so that the workflow engine knows the song is in the trash. Note that a portlet need not leverage workflow to implement the Recycle Bin’s components.

Next, the asset’s visibility is updated so that it no longer appears outside the Recycle Bin. Its visibility is deactivated by the following call:

assetEntryLocalService.updateVisible(Song.class.getName(), song.getSongId(), false);

On first thought, this may seem a bit odd. Why do you have to make the entry invisible in its original location? I thought I was moving it to the Recycle Bin? Importantly, entries that are moved to the Recycle Bin are actually left in their original location; they’re just not visible.

Next, notice that the service method adds a new trash entry to the Recycle Bin:

TrashEntry trashEntry = trashEntryLocalService.addTrashEntry(
        userId, song.getGroupId(), Song.class.getName(), song.getSongId(),
        song.getUuid(), null, oldStatus, null, typeSettingsProperties);

Lastly, the moveSongToTrash service method invokes TrashUtil’s [getTrashTitle](http://docs.liferay.com/portal/6.2/javadocs/com/liferay/portlet/trash/util/TrashUtil.html#getTrashTitle(long)) method to set the entry’s trash title. The trash title is an alternative reference to the entry. The trash title prevents duplicate entry name conflicts, discussed in the tutorial Resolving Recycling Conflicts.

Step 4: Create a Portlet Action to Initiate Moving Entries to Recycle Bin

Great! You must now provide the means of invoking the service method from your portlet. You implement this using a portlet action that you can trigger from a JSP.

The JukeboxPortlet class, for example, extends MVCPortlet and implements portlet action method deleteSong to invoke the SongLocalServiceImpl’s moveSongToTrash method. Here’s an abbreviated version of the deleteSong method:

public void deleteSong(ActionRequest request, ActionResponse response)
    throws Exception {

    long songId = ParamUtil.getLong(request, "songId");

    boolean moveToTrash = ParamUtil.getBoolean(request, "moveToTrash");

    ServiceContext serviceContext = ServiceContextFactory.getInstance(
        Song.class.getName(), request);

    try {
        if (moveToTrash) {
            Song song = SongServiceUtil.moveSongToTrash(songId);
            ...
            SessionMessages.add(request, PortalUtil.getPortletId(request) +
                SessionMessages.KEY_SUFFIX_DELETE_SUCCESS_DATA, data);
            ...
        }
        else {
            SongServiceUtil.deleteSong(songId, serviceContext);

            SessionMessages.add(request, "songDeleted");
        }
        ...
    }
    ...
}

Note that the logic in the try block handles moving entries to the Recycle Bin and permanently deleting entries. You can write similar portlet action methods for deleting your app’s entries.

Now that you’ve written your portlet action class, you can use a JSP to invoke your portlet action. For example, the Jukebox portlet’s view_song.jsp implements buttons named Move to the Recycle Bin and Delete to trash or permanently delete a song:

<aui:nav>
    <portlet:actionURL name="deleteSong" var="deleteSongURL">
        <portlet:param name="songId" value="<%= String.valueOf(song.getSongId()) %>" />
        <portlet:param name="moveToTrash" value="<%= String.valueOf(trashEnabled) %>" />
        <portlet:param name="redirect" value="<%= redirect %>" />
    </portlet:actionURL>

    <c:choose>
        <c:when test="<%= trashEnabled %>">
            <aui:nav-item href="<%= deleteSongURL %>" iconCssClass="icon-trash" label="move-to-the-recycle-bin" />
        </c:when>
        <c:otherwise>
            <aui:nav-item href="<%= deleteSongURL %>" iconCssClass="icon-key" label="delete" useDialog="<%= true %>" />
        </c:otherwise>
    </c:choose>
</aui:nav>

Notice this JSP code specifies the JukeboxPortlet’s deleteSong action method. It also displays an appropriate button for recycling or deleting the entry, depending on whether the portlet class found the entity to be trash-enabled.

Now that you laid all the ground work for moving your entries to the Recycle Bin, you’re ready to set up the framework for rendering the trashed entries in the Recycle Bin. You do this by implementing trash renderers for the trash-enabled entities.

Step 5: Implement a Trash Renderer for Each Trash-Enabled Entity

In a similar way to creating a trash handler, you create a class to render trash entries in the Recycle Bin. If you’re already using an asset renderer, you can reuse it, as long as it also implements the TrashRenderer interface.

As an example of a combined asset renderer and trash renderer implementation, consider the Jukebox portlet’s SongAssetRenderer class.

If you don’t already have an asset renderer, you must create a trash renderer. In it, implement a getTrashRenderer method to instantiate and return a trash renderer based on the trash entry’s primary key. For an example of accessing the trash renderer from a trash handler, consider the getTrashRenderer method from the Document Library class DLFileShortcutTrashHandler:

@Override
public TrashRenderer getTrashRenderer(long classPK)
    throws PortalException, SystemException {

    DLFileShortcut fileShortcut = getDLFileShortcut(classPK);

    return new DLFileShortcutTrashRenderer(fileShortcut);
}

It creates a new trash renderer class, DLFileShortcutTrashRenderer, based on the file shortcut instance. You can similarly create a getTrashRenderer method in your trash renderer class.

Congratulations! You now know how to implement moving your app’s entries to the Recycle Bin.

Related Topics

Recycling Assets with the Recycle Bin

Service Builder and Services

Enabling Search and Indexing

Asset Framework

Asset Enabling Custom Entities

0 (0 Votes)
Recycle Bin Previous