The Conflict Resolution framework helps Liferay users identify and solve conflicts in the Recycle Bin. The most common conflict for the Recycle Bin is duplicate naming. For instance, say you create a file called myfile.txt in a folder called Documents. Then you decide you should delete it, because you already have that file on your file system, so you recycle it and upload the myfile.txt from your file system. You would get a naming conflict because, although myfile.txt has a Recycle Bin entry, it’s actually still in the original folder but with its status changed and its visibility turned off.

This tutorial covers how to implement the Conflict Resolution framework so you can avoid Recycle Bin conflicts. (Note that you’re on your own when it comes to conflicts outside the Recycle Bin.)

Follow these steps and you’ll be resolving conflicts in no time!

  1. Rename Entities sent to the Recycle Bin
  2. Restore the Entity’s Original Name When Restoring From Recycle Bin
  3. Implement Conflict Resolution Trash Handler Methods

Step 1: Rename Entities Sent to the Recycle Bin

Entities are never actually created in the Recycle Bin. Instead, the entity is kept in its original location and a trash entry that points to it is created in the Recycle Bin. When viewing the entry in the Recycle Bin, the name appears the same as it does in the original entry. But the portal needs a way to distinguish between the recycled pointer and the real entity, which can be restored. This requires adding logic to your entity’s service and leveraging trash utilities to generate new names for Recycle Bin entries.

When an entry is sent to the Recycle Bin, it’s still in its original location, but with a different status. Users may create an entity with a name, move it to the Recycle Bin, and then create another entity with the same name. Since both entities are in the same place, a naming conflict could occur. Some applications only allow one entity to have a particular property value; for example, names for documents, titles for songs in an album, friendly URLs for pages, etc. On moving an entity to the Recycle Bin, you must rename properties that could generate conflicts. For example Liferay’s Jukebox Portlet uses a UnicodeProperties instance to store a mapping of each song’s title:

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()));

The mapping is stored with the trash entry. TrashUtil.getTrashTitle(trashEntry.getEntryId()) is invoked from SongLocalServiceImpl’s moveSongToTrash method to set the name of the original entity. In this case, a song is renamed to a unique value that can be used to look up the trash entry associated with the entity. Invoking TrashUtil.getTrashTitle(trashEntry.getEntryId()) resets the name of the original entity to a unique value–a slash followed by the trash entry’s ID. Since the entity is now in the Recycle Bin, it’s hidden from view in its original location; so users never see this mane. As you’ll see shortly, the unique names generated by TrashUtil.getTrashTitle(...) are used to get the names of the original entities when restoring those entities.

trash-entries-with-same-name.png

Figure 1: The Recycle Bin allows you to manage trash entries, even if they share the same name.

Next, you’ll see how to restore a trashed entity’s original name in your restore process.

Step 2: Restore the Entity’s Original Name When Restoring From Recycle Bin

Since recycled entities are renamed, you need to retrieve the original name when restoring them. The code snippet below, from the Jukebox portlet’s restoreSongFromTrash(long userId, long songId) method in SongLocalServiceImpl, demonstrates how to retrieve the song’s old name to restore it:

Song song = songPersistence.findByPrimaryKey(songId);

song.setName(TrashUtil.getOriginalTitle(song.getName()));

The original entity is retrieved by its ID. Then the TrashUtil.getOriginalTitle method is called to look up the entity’s original name. Remember that the entity’s current name is based on the trash entry’s ID. TrashUtil’s getTrashTitle method looks up the trash entry and returns the title value (the song’s original name, in this case) previously mapped in the entry’s type settings properties. The original song name is set back to the song entity.

Lastly, whether an entity is in the Recycle Bin or not, you should always render an entity with its original name. As an example of rendering a song’s original title based on a locale, the Jukebox portlet’s SongAssetRenderer uses the following method:

@Override
public String getTitle(Locale locale) {
    if (!_song.isInTrash()) {
        return _song.getName();
    }

    return TrashUtil.getOriginalTitle(_song.getName());
}

If the song isn’t in the Recycle Bin, the song’s current name is returned. Otherwise, the method invokes TrashUtil.getOriginalTitle(_song.getName()) to return the song’s original name.

Next, you need to finish satisfying the interface contract, and completely implement the Conflicts Resolution functionality.

Step 3: Implement Conflict Resolution Trash Handler Methods

Your app can now rename entries when they’re removed and reinstate their original names when they’re restored. What happens when the original entry is restored to its original location, and a new entry with the same name also is there? This causes a naming conflict that needs to be resolved by the user.

The Recycle Bin framework provides a UI for users to decide whether to overwrite the existing entry or to keep both entries by renaming the title of the entry they’re restoring. The figure below shows a conflict resolution pop-up that’s displayed in the Jukebox portlet on trying to restore a song for which an identically named song is already present.

resolved-conflict-rb.png

Figure 2: The Recycle Bin enables you to handle conflicts by notifying the user with a pop-up message and options for solving the problem. Clearly, if you recycled Kashmir, someone may have tried to fix that both by re-uploading it and by restoring it.

Two methods need to be implemented in the trash handler to allow for the necessary checks and updates. The first method must check for duplicate trash entries. If an entry with the same name is detected in a directory, it must throw an exception. To learn how to do this, you can reference the checkDuplicateTrashEntry method from SongTrashHandler. Here’s the code from that method:

public void checkDuplicateTrashEntry(
       TrashEntry trashEntry, long containerModelId, String newName)
    throws PortalException {

    Song song = SongLocalServiceUtil.getSong(trashEntry.getClassPK());

    if (containerModelId == TrashEntryConstants.DEFAULT_CONTAINER_ID) {
       containerModelId = song.getAlbumId();
    }

    String originalName = trashEntry.getTypeSettingsProperty("title");

    if (Validator.isNotNull(newName)) {
       originalName = newName;
    }

    Song duplicateSong = SongLocalServiceUtil.getSong(
       song.getGroupId(), song.getArtistId(), containerModelId,
       originalName);

    if (duplicateSong != null) {
       RestoreEntryException ree = new RestoreEntryException();

       ree.setDuplicateEntryId(duplicateSong.getSongId());
       ree.setOldName(duplicateSong.getName());
       ree.setTrashEntryId(trashEntry.getEntryId());

       throw ree;
    }
}

Lastly, implement a method that updates the entry title name. For instance, the Jukebox portlet updates a song title by calling this updateTitle method:

public void updateTitle(long classPK, String name) throws PortalException {
    Song song = SongLocalServiceUtil.getSong(classPK);

    song.setName(name);

    SongLocalServiceUtil.updateSong(song);
}

This method is also from the SongTrashHandler class. It is called when the entry you’re restoring needs its title renamed to resolve a conflict with a preexisting entry that has the same name. After implementing these methods, your app’s users should always be able to resolve naming conflicts involving trashed entries.

Fantastic! By leveraging the Conflicts Resolution framework in your app, you’re able to provide a smarter Recycle Bin that handles potential conflicts with ease.

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)
Moving and Restoring Parent Entities Previous