Supporting Comments in a Content Type

Supporting Comments in a Content Type

You can add support that allows people to add comments to instances of your content type. Along the way, you define the details of how comments should be handled by the application, including details about moderation, filtering, deletion, and more. The content in this topic assumes you've implemented a manager class to locate content type instances based on their ID in the system.

Note: The code in this topic is from the Memo sample content type. You'll find the sample in the Jive public sample Subversion repository.

Note: The content type API is still a new feature that might change as developers provide feedback about it.

Here are the high-level steps. You'll find more about each in the sections below.

Defining Comment Behavior

You define how comments should be handled on instances of your content type by implementing certain interfaces. In your Jive object class, you implement CommentContentResource to indicate whether and how comments should be allowed. You also implement an type info provider that defines the relationship between instances of your content type and the comment subsystem.

Implement CommentContentResource

Your content object interface should extend this one in addition to extending JiveContentObject. The interface has two methods. You can implement the getCommentDelegator method to return your own implementation of CommentDelegator if you want special comment-handling behavior. But in most cases it's easier to simply return null and use the default implementation instead (by referencing that default in the FTL file you use to display your content type).

For the interface's other method, getCommentStatus, return the CommentManager constant that indicate whether and how this particular instance of the content type allows comments. Here are those values:

Here's a brief example.

private int commentStatus = CommentManager.COMMENTS_DEFAULT;


// Code omitted.

/**
* Provides the comment delegator that the application can use to get information and perform
* actions on comments on a content instance. Returning null here provokes the application to
* use a default implementation. That implementation is referenced in the FTL file that
* presents the content.
*/
public CommentDelegator getCommentDelegator() {
    return null;
}

/**
* Gets status indicating the conditions under which comments are permitted on this memo
* instance.
*/
public int getCommentStatus() {
    return commentStatus;
}

/**
* Set by the application to indicate comment status (conditions for commenting) as set by a user.
*/
public void setCommentStatus(int status) throws UnauthorizedException {
    commentStatus = status;
}

Implement CommentableTypeInfoProvider

Implement the CommentableTypeInfoProvider interface to define how comments to objects of your content type should be handled. Your implementation (probably in a separate class) will include methods that tell the application how you want to handle features such as moderation, text filtering (through an interceptor), deletion and so on.

Here's a list of the key methods. In these methods, the CommentContentResource is an instance of your content type. As described below, your content object class should implement the CommentContentResource interface. The interface is implemented by any content object that can be commented.

public boolean isModeratedComment(CommentContentResource commentTarget, Comment comment) {
    try {
        Memo memo = memoManager.getMemo(commentTarget.getID());
            return memo.isCommentModerationEnabled();
    }
    catch (Exception e) {
        Log.error("Could not load memo with ID: " + commentTarget.getID(), e);
    }
    return false;
}

public InterceptorManager getInterceptorManager(CommentContentResource commentTarget)

    throws NotFoundException {
    Memo memo = null;
    try {
        memo = memoManager.getMemo(commentTarget.getID());
    }
    catch (Exception e) {
        Log.error("Could not load memo with ID: " + commentTarget.getID(), e);
        return null;
    }
    JiveContainer container = objectLoader.getJiveContainer(memo.getContainerType(),

        memo.getContainerID());
    if (container == null) {
        throw new NotFoundException();
    }
    return container.getInterceptorManager();
}

public boolean isDeleteable(CommentContentResource commentTarget, Comment comment) {
    return commentTarget.getID() == comment.getCommentContentResource().getID();
}

public int getCommentStatus(CommentContentResource commentTarget)

    throws NotFoundException {
    Memo memo = (Memo) commentTarget;
    return memo.getCommentStatus();
}

public boolean isCommentAttachedToCommentTarget(EntityDescriptor commentTarget,
    EntityDescriptor comment) {
    return comment.getID() == commentTarget.getID();
}

Signaling Support for Comments

In your object type class, you signal support for comments by implementing the CommentableType interface.

Implement CommentableType

Your implementation of the interface's single method, getCommentableTypeInfoProvider, should return an instance of your CommentableTypeInfoProvider implementation. You should have that instance injected into the object type class by using Spring.

Here are the steps:

  1. Implement the interface.

    // An field to hold the instance injected by Spring.

    private CommentableTypeInfoProvider commentableTypeInfoProvider;

    public CommentableTypeInfoProvider getCommentableTypeInfoProvider() {
        return commentableTypeInfoProvider;
    }

    public void setCommentableTypeInfoProvider(CommentableTypeInfoProvider commentableTypeInfoProvider) {
        this.commentableTypeInfoProvider = commentableTypeInfoProvider;
    }

  2. In your plugin's spring.xml file, add a bean definition for the info provider you want injected by Spring.

    <!-- Tell Spring that your object type class should receive the info provider. -->

    <bean id="memoObjectType" class="com.jivesoftware.clearspace.plugin.test_dynamic.MemoObjectType">
        <property name="commentableTypeInfoProvider" ref="memoCommentableInfoProvider"/>
        <!-- Other injected instances as needed. -->
    </bean>

    <!-- Identify the type you want to inject as a bean. -->

    <bean id="memoCommentableInfoProvider" class="com.jivesoftware.clearspace.plugin.test_dynamic.MemoCommentableInfoProvider">
        <!-- Include properties for instances that the info provider itself needs to receive. -->
    </bean>

Events and the Comment sub-system

The manager for your object type should fire ContentEvents in certain situations which signal the comment sub-system to perform necessary operations on instances of your commentable object type. For example, when an instance of your commentable object type is being deleted, the manager should fire a ContentEvent.ModificationType.Deleting event to signal the CommentManagerDeleteContentListener event handler to delete the comments that were made on your object as well. In general, it is best practice to have your object manager fire the right ContentEvent for the right situation. In this way you can ensure that any interested sub-systems will perform necessary operations on an as-needed basis.

Here is an example of firing a 'Deleting' ContentEvent which will coerce the comment sub-system to delete comments on a Memo object (taken from MemoManagerImpl). The MemoEvent class implements the ContentEvent interface.

public void deleteMemo(Memo memo) {
    ContainerAwareEntityDescriptor memoDescriptor = new ContainerAwareEntityDescriptor(memo);
    EntityDescriptor containerDescriptor = new EntityDescriptor(memo.getContainerType(), memo.getContainerID());

    fireEvent(new MemoEvent(MemoEvent.Type.DELETING, memoDescriptor, containerDescriptor, null));
      
    try {
        memoDAO.delete(memo.getID());
        fireEvent(new MemoEvent(MemoEvent.Type.DELETED, memoDescriptor, containerDescriptor, null));
    }
    catch (DAOException e) {
        throw new DAOException(e);
    }
}

private void fireEvent(MemoEvent e) {
    Future f = eventDispatcher.fireAndWait(e);
    try {
        f.get();
    }
    catch (InterruptedException ex) {
        log.error(ex.getMessage(), ex);
    }
    catch (ExecutionException ex) {
        log.error(ex.getMessage(), ex);
    }
}

Adding UI to View Comments

Now that your content type is hooked into the basic Comment Model, let us focus our attention on the View.

  1. Ensure that the Struts action that displays an instance of your content type extends JiveActionSupport.
  2. In the FreeMarker template associated with the Struts action, include the Jive macro that displays a comment block (shown in the example below). Add a "comments" macro and assign to it the Jive object instance passed to the template by your action. The application will use your instance to get and display comments using the template you're including.

<!-- BEGIN Memo comments -->

<#include "/template/global/include/comment-macros.ftl" />

<@comments contentObject=memo />

<!-- END Memo comments -->

In the same FreeMarker template that you edited in step 2, also paste this include: "<#include "/template/global/include/form-message.ftl"/>" where you wish the form messages to appear. (eg. the form message for a successful comment post or message indicating a form error.)