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.
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.
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 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();
}
In your object type class, you signal support for comments by implementing the CommentableType interface.
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:
// 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;
}
<!-- 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>
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);
}
}
Now that your content type is hooked into the basic Comment Model, let us focus our attention on the View.
<!-- 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.)