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, such as details about moderation, filtering, and deletion. 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 can 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 can find more about each in the sections below.
- Have your content type class give information about how comments to objects of your content type should be handled.
- Have your content object class give information about comments on a particular instance.
- Signal that your content type supports comments.
- Add support for user interface to display comments.
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:
CommentManager.COMMENTS_NONE
: No comments allowed.CommentManager.COMMENTS_OPEN
: Comments are permitted.CommentManager.COMMENTS_CLOSED
: Comments were permitted but are not allowed now.CommentManager.COMMENTS_MODERATED
: Comments must be approved by the owner.CommentManager.COMMENTS_DEFAULT
: The default status, which isOPEN
.
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.
isModeratedComment(CommentContentResource, Comment)
: Return true if moderation is enabled for the resource that is being commented on.
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;
}
getInterceptorManager
: Return the interceptor manager for the content object's container. The comments subsystem will use interceptors installed and active on the application instance to filter text in the content (such as for profanity).
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();
}
getParentObject
: Return anEntityDescriptor
for the parent object, if available. Return null if the your content type has no parent object (a common scenario).isDeletable
: Returntrue
if the comment can be deleted. This acts as a sanity check before deletion, ensuring the comment belongs to its comment content resource.
public boolean isDeleteable(CommentContentResource commentTarget, Comment comment) {
return commentTarget.getID() == comment.getCommentContentResource().getID();
}
getCommentStatus
: Return an int representing how commenting is currently configured for the content type instance. If you've implementedCommentContentResource.getCommentStatus
in your content object (above), you can call that method here for the return value.
public int getCommentStatus(CommentContentResource commentTarget)
throws NotFoundException {
Memo memo = (Memo) commentTarget;
return memo.getCommentStatus();
}
isCommentAttachedToCommentTarget
: Returntrue
if the comment is commenting a comment. This can happen when comments are nested. This method provides a sanity check, ensuring the comment is attached to the comment target.
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:
-
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;
} -
In your plugin's spring.xml file, add a bean definition for the info provider you want injected by Spring.
<bean id="memoObjectType" class="com.jivesoftware.clearspace.plugin.test_dynamic.MemoObjectType">
<property name="commentableTypeInfoProvider" ref="memoCommentableInfoProvider"/>
</bean>
<bean id="memoCommentableInfoProvider" class="com.jivesoftware.clearspace.plugin.test_dynamic.MemoCommentableInfoProvider">
</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. Thus, you can ensure that any interested sub-systems perform necessary operations on an as-needed basis.
Here is an example of firing a 'Deleting' ContentEvent
which coerces 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.
- Ensure that the Struts action that displays an instance of your content type extends
JiveActionSupport
. - 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 uses your instance to get and display comments using the template you're including.
<#include "/template/global/include/comment-macros.ftl" />
<@comments contentObject=memo />
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, for example, the form message for a successful comment post or message indicating a form error.