/* * JBoss, Home of Professional Open Source * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.seam.wiki.core.action; import static org.jboss.seam.international.StatusMessage.Severity.INFO; import java.util.Date; import org.jboss.seam.Component; import org.jboss.seam.ScopeType; import org.jboss.seam.annotations.Begin; import org.jboss.seam.annotations.FlushModeType; import org.jboss.seam.annotations.In; import org.jboss.seam.annotations.Name; import org.jboss.seam.annotations.Scope; import org.jboss.seam.annotations.web.RequestParameter; import org.jboss.seam.core.Conversation; import org.jboss.seam.core.Events; import org.jboss.seam.international.Messages; import org.jboss.seam.international.StatusMessages; import org.jboss.seam.security.AuthorizationException; import org.jboss.seam.security.Identity; import org.jboss.seam.wiki.core.action.prefs.CommentsPreferences; import org.jboss.seam.wiki.core.dao.SpamReportDAO; import org.jboss.seam.wiki.core.exception.InvalidWikiRequestException; import org.jboss.seam.wiki.core.feeds.FeedDAO; import org.jboss.seam.wiki.core.feeds.FeedEntryManager; import org.jboss.seam.wiki.core.model.FeedEntry; import org.jboss.seam.wiki.core.model.WikiComment; import org.jboss.seam.wiki.core.model.WikiNode; import org.jboss.seam.wiki.core.model.WikiSpamReport; import org.jboss.seam.wiki.core.ui.WikiRedirect; import org.jboss.seam.wiki.core.wikitext.editor.WikiTextEditor; import org.jboss.seam.wiki.util.WikiUtil; @Name("commentHome") @Scope(ScopeType.CONVERSATION) public class CommentHome extends NodeHome<WikiComment, WikiNode>{ public static final String REPLY_PREFIX = "Re: "; /* -------------------------- Context Wiring ------------------------------ */ @In(required = false) protected DocumentHome documentHome; @In protected FeedDAO feedDAO; @In protected SpamReportDAO spamReportDAO; @In("#{preferences.get('Comments')}") protected CommentsPreferences commentsPreferences; /* -------------------------- Internal State ------------------------------ */ @RequestParameter protected Long parentCommentId; protected boolean showForm = false; protected WikiTextEditor textEditor = new WikiTextEditor("comment", 32767, true, true); /* -------------------------- Basic Overrides ------------------------------ */ @Override protected boolean isPageRootController() { return false; } @Override protected String getEditorWorkspaceDescription(boolean create) { return null; } @Override public Class<WikiComment> getEntityClass() { return WikiComment.class; } @Override public void create() { super.create(); if (documentHome != null) { setParentNodeId(documentHome.getInstance().getId()); } } @Override public WikiComment findInstance() { return getWikiNodeDAO().findWikiComment((Long)getId()); } @Override protected WikiNode findParentNode(Long parentNodeId) { return getEntityManager().find(WikiNode.class, parentNodeId); } @Override public WikiComment afterNodeCreated(WikiComment comment) { comment = super.afterNodeCreated(comment); comment.setAreaNumber(documentHome.getInstance().getAreaNumber()); comment.setDerivedName(documentHome.getInstance()); comment.setWikiname(WikiUtil.convertToWikiName(comment.getName())); comment.setCreatedBy(getCurrentUser()); // TODO: This means that when a user is deleted, his name will still be preserved. However, it's an old name. if (comment.isOwnedByRegularUser()) { comment.setFromUserName(getCurrentUser().getFullname()); } // Default to title of document as subject comment.setSubject(documentHome.getInstance().getName()); // Default to help text comment.setContent(Messages.instance().get("lacewiki.msg.wikiTextEditor.EditThisText")); return comment; } @Override public boolean isPersistAllowed(WikiComment node, WikiNode parent) { getLog().trace("checking comment persist permissions"); return Identity.instance().hasPermission("Comment", "create", documentHome.getInstance()); } /* -------------------------- Custom CUD ------------------------------ */ @Override public boolean beforePersist() { getInstance().setContent( textEditor.getValueAndConvertLinks(getParentNode().getAreaNumber()) ); if (textEditor.isValuePlaintext()) { getInstance().setUseWikiText(false); } return true; } @Override public String persist() { // TODO: This is not pretty but this has really not been designed for non-threaded comments WikiNode oldParent = getParentNode(); // Remember old parent setParentNode(documentHome.getInstance()); // Set the "real" parent, which is the document String outcome = super.persist(); setParentNode(oldParent); // Reset old parent afterwards if (outcome != null) { if (documentHome.getInstance().isEnableCommentsOnFeeds()) { FeedEntry feedEntry = ((FeedEntryManager) Component.getInstance(getFeedEntryManagerName())).createFeedEntry(getInstance()); feedDAO.createFeedEntry(documentHome.getParentNode(), documentHome.getInstance(), feedEntry, false); getEntityManager().flush(); } Events.instance().raiseEvent("Comment.persisted"); endConversation(); WikiRedirect.instance() .setWikiDocument(documentHome.getInstance()) .setPropagateConversation(false) .setFragment("comment" + getInstance().getId()) .execute(); } return null; // No navigation } public String remove(Long commentId) { setNodeId(commentId); initEditor(false); if (isManaged()) { // Additional permission required besides NodeHome.remove() if (!Identity.instance().hasPermission("Comment", "delete", getInstance().getParent()) ) { throw new AuthorizationException("You don't have permission for this operation"); } // Remove feed entry before removing comment feedDAO.removeFeedEntry( feedDAO.findFeeds(getInstance()), feedDAO.findFeedEntry(getInstance()) ); spamReportDAO.removeReports(spamReportDAO.findReports(getInstance())); remove(); getEntityManager().clear(); Events.instance().raiseEvent("Comment.commentListRefresh"); } return null; // Prevent navigation } /** * Creates a spam report for the selected comment * * @param commentId * @return */ public String reportAsSpam(Long commentId) { WikiSpamReport report = new WikiSpamReport(); setNodeId(commentId); report.setComment(getInstance()); report.setReporter(currentUser); getEntityManager().persist(report); StatusMessages.instance().addFromResourceBundleOrDefault( INFO, "lacewiki.label.ReportThankYou", "Thank you for your assistance." ); return "success"; } @Override protected NodeRemover getNodeRemover() { return (CommentNodeRemover)Component.getInstance(CommentNodeRemover.class); } @Override protected Validatable[] getUpdateValidations() { return new Validatable[] { textEditor }; } @Override protected Validatable[] getPersistValidations() { return new Validatable[] { textEditor }; } /* -------------------------- Messages ------------------------------ */ @Override protected void createdMessage() { StatusMessages.instance().addFromResourceBundleOrDefault( INFO, "lacewiki.msg.Comment.Persist", "Comment '{0}' has been saved.", getInstance().getSubject() ); } @Override protected void updatedMessage() { StatusMessages.instance().addFromResourceBundleOrDefault( INFO, "lacewiki.msg.Comment.Update", "Comment '{0}' has been updated.", getInstance().getSubject() ); } @Override protected void deletedMessage() { StatusMessages.instance().addFromResourceBundleOrDefault( INFO, "lacewiki.msg.Comment.Delete", "Comment '{0}' has been deleted.", getInstance().getSubject() ); } /* -------------------------- Internal Methods ------------------------------ */ protected void endConversation() { showForm = false; Conversation.instance().end(); getEntityManager().clear(); // Need to force re-read in the topic list refresh Events.instance().raiseEvent("Comment.commentListRefresh"); } protected String getFeedEntryManagerName() { return "wikiCommentFeedEntryManager"; } protected void setReplySubject(WikiComment parentComment) { if (!parentComment.getSubject().equals(documentHome.getInstance().getName())) { if (parentComment.getSubject().startsWith(REPLY_PREFIX)) { getInstance().setSubject(parentComment.getSubject()); } else { getInstance().setSubject(REPLY_PREFIX + parentComment.getSubject()); } } } protected void setQuotedContent(WikiComment parentComment) { getInstance().setContent(quote( parentComment.getContent(), parentComment.getCreatedOn(), parentComment.isOwnedByRegularUser() ? parentComment.getCreatedBy().getFullname() : parentComment.getFromUserName() )); } protected String quote(String text, Date date, String authorName) { StringBuilder quoted = new StringBuilder(); quoted.append("<blockquote>").append("\n"); quoted.append("_").append(authorName); quoted.append(" ").append(Messages.instance().get("forum.label.WroteOn")).append(" "); quoted.append(WikiUtil.formatDate(date)).append(":").append("_").append("<br/>\n\n"); quoted.append(text); quoted.append("\n").append("</blockquote>").append("\n\n"); quoted.append(Messages.instance().get("lacewiki.msg.wikiTextEditor.EditThisText")); return quoted.toString(); } /* -------------------------- Public Features ------------------------------ */ public boolean isShowForm() { return showForm; } public void setShowForm(boolean showForm) { this.showForm = showForm; } @Begin(flushMode = FlushModeType.MANUAL, join = true) public void newComment() { initEditor(false); showForm = true; textEditor.setValue(getInstance().getContent()); } @Begin(flushMode = FlushModeType.MANUAL, join = true) public void replyTo() { prepareReply(); textEditor.setValue(getInstance().getContent()); WikiRedirect.instance() .setWikiDocument(documentHome.getInstance()) .setPropagateConversation(true) .execute(); } @Begin(flushMode = FlushModeType.MANUAL, join = true) public void quote() { prepareReply(); setQuotedContent((WikiComment)getParentNode()); textEditor.setValue(getInstance().getContent()); WikiRedirect.instance() .setWikiDocument(documentHome.getInstance()) .setPropagateConversation(true) .execute(); } private void prepareReply() { if (parentCommentId == null || parentCommentId.equals(0l)) throw new InvalidWikiRequestException("Missing parentCommentId request parameter"); getLog().debug("reply to comment id: " + parentCommentId); initEditor(false); showForm = true; setParentNodeId(parentCommentId); getInstance(); // Init the parent, has to happen here setReplySubject((WikiComment)getParentNode()); } public void rate(Long commentId, int rating) { getLog().debug("rating comment with id: " + commentId + " as " + rating); // Only the owner of the document can rate comments of that document if ( !currentUser.getId().equals(documentHome.getInstance().getCreatedBy().getId()) ) { throw new AuthorizationException("You don't have permission for this operation"); } // Guest can't rate if (currentUser.isGuest()) { throw new IllegalStateException("User interface bug, guests can't rate comments"); } setId(commentId); if (isManaged()) { if (getInstance().getRating() != 0) { throw new IllegalStateException("User interface bug, can't rate comment that was already rated"); } if (getInstance().getCreatedBy().getId().equals(currentUser.getId())) { throw new IllegalStateException("User interface bug, a user can't rate his/her own comments"); } getInstance().setRating(rating); Events.instance().raiseEvent("Comment.rated"); } } public void cancel() { endConversation(); WikiRedirect.instance() .setWikiDocument(documentHome.getInstance()) .setPropagateConversation(false) .execute(); } @RequestParameter("showCommentForm") public void showCommentForm(Boolean requestParam) { if (requestParam != null && requestParam && !showForm) { getLog().debug("request parameter sets comment form visible, starts conversation"); Conversation.instance().begin(true, false); Conversation.instance().changeFlushMode(FlushModeType.MANUAL); setParentNodeId(documentHome.getInstance().getId()); newComment(); } } public WikiTextEditor getTextEditor() { return textEditor; } }