[jboss-cvs] jboss-seam/examples/wiki/src/main/org/jboss/seam/wiki/plugin/forum ...

Christian Bauer christian at hibernate.org
Fri Nov 9 10:00:28 EST 2007


  User: cbauer  
  Date: 07/11/09 10:00:28

  Added:       examples/wiki/src/main/org/jboss/seam/wiki/plugin/forum           
                        ForumQuery.java ReplyHome.java ForumInfo.java
                        TopicHome.java ForumTopic.java ForumListHome.java
                        ForumHome.java ForumDAO.java ForumQueries.hbm.xml
                        ForumSearchSupport.java ForumCookie.java
  Log:
  Initial import of forum plugin
  
  Revision  Changes    Path
  1.1      date: 2007/11/09 15:00:28;  author: cbauer;  state: Exp;jboss-seam/examples/wiki/src/main/org/jboss/seam/wiki/plugin/forum/ForumQuery.java
  
  Index: ForumQuery.java
  ===================================================================
  package org.jboss.seam.wiki.plugin.forum;
  
  import org.jboss.seam.wiki.core.model.Directory;
  import org.jboss.seam.wiki.core.model.User;
  import org.jboss.seam.wiki.core.action.Pager;
  import org.jboss.seam.annotations.Name;
  import org.jboss.seam.annotations.Scope;
  import org.jboss.seam.annotations.In;
  import org.jboss.seam.annotations.Observer;
  import org.jboss.seam.annotations.web.RequestParameter;
  import org.jboss.seam.ScopeType;
  import org.jboss.seam.Component;
  
  import java.util.List;
  import java.util.Map;
  import java.util.ArrayList;
  import java.io.Serializable;
  
  @Name("forumQuery")
  @Scope(ScopeType.CONVERSATION)
  public class ForumQuery implements Serializable {
  
      private Pager pager;
  
      @RequestParameter
      public void setPage(Integer page) {
          if (pager == null) pager = new Pager(2l);
          pager.setPage(page);
      }
  
      public Pager getPager() {
          return pager;
      }
  
      @In
      Directory currentDirectory;
  
      @In
      User currentUser;
  
      @In
      int currentAccessLevel;
  
      @In
      ForumDAO forumDAO;
  
      /* ####################### FORUMS ########################## */
  
      List<Directory> forums;
      public List<Directory> getForums() {
          if (forums == null) loadForums();
          return forums;
      }
  
      Map<Long, ForumInfo> forumInfo;
      public Map<Long, ForumInfo> getForumInfo() {
          return forumInfo;
      }
  
      @Observer(value = {"Forum.forumListRefresh", "org.jboss.seam.postAuthenticate"}, create = false)
      public void loadForums() {
          forums = forumDAO.findForums(currentDirectory);
          forumInfo = forumDAO.findForumInfo(currentDirectory);
  
          // Find unread postings
          User adminUser = (User)Component.getInstance("adminUser");
          User guestUser = (User)Component.getInstance("guestUser");
          if ( !(currentUser.getId().equals(guestUser.getId())) &&
               !(currentUser.getId().equals(adminUser.getId())) ) {
              List<ForumTopic> unreadTopics = forumDAO.findUnreadTopics(currentUser.getPreviousLastLoginOn());
              ForumCookie forumCookie = (ForumCookie)Component.getInstance("forumCookie");
              for (ForumTopic unreadTopic : unreadTopics) {
                  if (forumInfo.containsKey(unreadTopic.getParent().getId()) &&
                      !forumCookie.getCookieValues().containsKey(unreadTopic.getId().toString())) {
                      forumInfo.get(unreadTopic.getParent().getId()).setUnreadPostings(true);
                  }
              }
          }
      }
  
      /* ####################### TOPICS ########################## */
  
      private List<ForumTopic> topics;
  
      public List<ForumTopic> getTopics() {
          if (topics == null) loadTopics();
          return topics;
      }
  
      @Observer(value = {"Forum.topicPersisted", "org.jboss.seam.postAuthenticate"}, create = false)
      public void loadTopics() {
          pager.setNumOfRecords( forumDAO.findTopicCount(currentDirectory) );
          topics = pager.getNumOfRecords() > 0
              ? forumDAO.findTopics(currentDirectory, pager.getNextRecord(), pager.getPageSize())
              : new ArrayList<ForumTopic>();
  
          User adminUser = (User)Component.getInstance("adminUser");
          User guestUser = (User)Component.getInstance("guestUser");
          // Find unread postings
          if ( !(currentUser.getId().equals(guestUser.getId())) &&
               !(currentUser.getId().equals(adminUser.getId())) ) {
              List<ForumTopic> unreadTopics = forumDAO.findUnreadTopics(currentUser.getPreviousLastLoginOn());
              ForumCookie forumCookie = (ForumCookie)Component.getInstance("forumCookie");
              // TODO: This is nested interation but it's difficult to make this more efficient
              for (ForumTopic topic : topics) {
                  for (ForumTopic unreadTopic : unreadTopics) {
                      if (unreadTopic.getId().equals(topic.getId())&&
                          !forumCookie.getCookieValues().containsKey(topic.getId().toString())) {
                          topic.setUnread(true);
                      }
                  }
              }
          }
      }
  
  }
  
  
  
  1.1      date: 2007/11/09 15:00:28;  author: cbauer;  state: Exp;jboss-seam/examples/wiki/src/main/org/jboss/seam/wiki/plugin/forum/ReplyHome.java
  
  Index: ReplyHome.java
  ===================================================================
  package org.jboss.seam.wiki.plugin.forum;
  
  import org.jboss.seam.wiki.core.action.CommentHome;
  import org.jboss.seam.wiki.core.model.Comment;
  import org.jboss.seam.wiki.core.model.Document;
  import org.jboss.seam.wiki.core.model.User;
  import org.jboss.seam.wiki.core.dao.FeedDAO;
  import org.jboss.seam.wiki.util.WikiUtil;
  import org.jboss.seam.annotations.*;
  import org.jboss.seam.ScopeType;
  import org.jboss.seam.Component;
  import org.jboss.seam.international.Messages;
  
  import java.util.Date;
  
  @Name("replyHome")
  @Scope(ScopeType.CONVERSATION)
  public class ReplyHome extends CommentHome {
  
  
      @Create
      public void initialize() {
          super.initialize();
  
          // Add this document to the "read" list in the forum cookie
          // TODO: At some point, this adds up to the 4 kb cookie value limit, maybe we should only store unread ids?
          ForumCookie forumCookie = (ForumCookie) Component.getInstance("forumCookie");
          forumCookie.addCookieValue(currentDocument.getId().toString(), "r");
      }
  
      private boolean showForm = false;
  
      public boolean isShowForm() {
          return showForm;
      }
  
      public void setShowForm(boolean showForm) {
          this.showForm = showForm;
      }
  
      public void createComment() {
          comment = new Comment();
          comment.setFromUser(currentUser);
      }
  
      public void createComment(Comment replyTo, boolean quote) {
          createComment();
          comment.setSubject(replyTo.getSubject());
          if (quote) comment.setText(quote(replyTo.getText(), replyTo.getCreatedOn(), replyTo.getFromUser()));
      }
  
      public void createComment(ForumTopic replyTo, boolean quote) {
          createComment();
          comment.setSubject(replyTo.getName());
          if (quote) comment.setText(quote(replyTo.getContentWithoutMacros(), replyTo.getCreatedOn(), replyTo.getCreatedBy()));
      }
  
      @Begin(flushMode = FlushModeType.MANUAL)
      public void newReplyToComment(Long commentId, boolean quote) {
          showForm = true;
  
          // Take content from other comment
          Comment foundCommment = restrictedEntityManager.find(Comment.class, commentId);
          createComment(foundCommment, quote);
      }
  
      @Begin(flushMode = FlushModeType.MANUAL)
      public void newReplyToTopic(Long topicId, boolean quote) {
          showForm = true;
  
          // Take content from topic
          ForumTopic topic = restrictedEntityManager.find(ForumTopic.class, topicId);
          createComment(topic, quote);
      }
  
      @End
      public void cancel() {
          showForm = false;
      }
  
      @End
      @RaiseEvent("Forum.replyPersisted")
      public void persist() {
          Document doc = restrictedEntityManager.find(Document.class, currentDocument.getId());
          comment.setDocument(doc);
          // TODO: Break this, for performance reasons... doc.getComments().add(comment);
  
          restrictedEntityManager.persist(comment);
  
          StringBuilder feedEntryTitle = new StringBuilder();
          if (doc.getName().equals(comment.getSubject())) {
              feedEntryTitle.append("[").append(doc.getParent().getName()).append("] ");
              feedEntryTitle.append( Messages.instance().get("forum.label.reply.FeedEntryTitlePrefix") );
              feedEntryTitle.append(" ").append(comment.getSubject());
          } else {
              feedEntryTitle.append("[").append(doc.getParent().getName()).append("] ");
              feedEntryTitle.append("(");
              feedEntryTitle.append( Messages.instance().get("forum.label.reply.FeedEntryTitlePrefix") );
              feedEntryTitle.append(" ").append(WikiUtil.truncateString(doc.getName(), 20, "...")).append(") ");
              feedEntryTitle.append(comment.getSubject());
          }
  
          pushOnFeeds(doc, feedEntryTitle.toString());
  
          restrictedEntityManager.flush();
          restrictedEntityManager.clear();
  
          refreshComments();
  
          showForm = false;
      }
  
      private String quote(String text, Date date, User author) {
          StringBuilder quoted = new StringBuilder();
          quoted.append("<blockquote>").append("\n");
          quoted.append("_").append(author.getFullname());
          quoted.append(" ").append(Messages.instance().get("forum.label.WroteOn")).append(" ");
          quoted.append(WikiUtil.formatDate(date)).append(":").append("_").append("<br/>\n");
          quoted.append(text);
          quoted.append("\n").append("</blockquote>").append("\n\n");
          return quoted.toString();
      }
  
  }
  
  
  
  1.1      date: 2007/11/09 15:00:28;  author: cbauer;  state: Exp;jboss-seam/examples/wiki/src/main/org/jboss/seam/wiki/plugin/forum/ForumInfo.java
  
  Index: ForumInfo.java
  ===================================================================
  package org.jboss.seam.wiki.plugin.forum;
  
  import org.jboss.seam.wiki.core.model.Comment;
  
  public class ForumInfo {
  
      private boolean unreadPostings = false;
      private long totalNumOfTopics;
      private long totalNumOfPosts;
      private ForumTopic lastTopic;
      private Comment lastComment;
  
      public ForumInfo(long totalNumOfTopics, long totalNumOfPosts) {
          this.totalNumOfTopics = totalNumOfTopics;
          this.totalNumOfPosts = totalNumOfPosts;
      }
  
      public boolean isUnreadPostings() {
          return unreadPostings;
      }
  
      public void setUnreadPostings(boolean unreadPostings) {
          this.unreadPostings = unreadPostings;
      }
  
      public long getTotalNumOfTopics() {
          return totalNumOfTopics;
      }
  
      public void setTotalNumOfTopics(long totalNumOfTopics) {
          this.totalNumOfTopics = totalNumOfTopics;
      }
  
      public long getTotalNumOfPosts() {
          return totalNumOfPosts;
      }
  
      public void setTotalNumOfPosts(long totalNumOfPosts) {
          this.totalNumOfPosts = totalNumOfPosts;
      }
  
      public ForumTopic getLastTopic() {
          return lastTopic;
      }
  
      public void setLastTopic(ForumTopic lastTopic) {
          this.lastTopic = lastTopic;
      }
  
      public Comment getLastComment() {
          return lastComment;
      }
  
      public void setLastComment(Comment lastComment) {
          this.lastComment = lastComment;
      }
  
      // Was the last post made a topic or a comment/reply
      public boolean isLastPostLastTopic() {
          if (lastComment == null && lastTopic != null) return true;
          if (lastTopic != null && (lastTopic.getCreatedOn().getTime()>lastComment.getCreatedOn().getTime()) ) return true;
          return false;
      }
  
  }
  
  
  
  1.1      date: 2007/11/09 15:00:28;  author: cbauer;  state: Exp;jboss-seam/examples/wiki/src/main/org/jboss/seam/wiki/plugin/forum/TopicHome.java
  
  Index: TopicHome.java
  ===================================================================
  package org.jboss.seam.wiki.plugin.forum;
  
  import org.jboss.seam.Component;
  import org.jboss.seam.ScopeType;
  import org.jboss.seam.annotations.*;
  import org.jboss.seam.wiki.core.action.NodeHome;
  import org.jboss.seam.wiki.core.engine.WikiLinkResolver;
  import org.jboss.seam.wiki.core.model.Directory;
  import org.jboss.seam.wiki.core.dao.FeedDAO;
  
  import static javax.faces.application.FacesMessage.SEVERITY_INFO;
  import java.util.Date;
  
  @Name("topicHome")
  @Scope(ScopeType.CONVERSATION)
  public class TopicHome extends NodeHome<ForumTopic> {
  
      @In
      Directory currentDirectory;
  
      @In
      private FeedDAO feedDAO;
  
      private boolean showForm = false;
      private String formContent;
  
      /* -------------------------- Basic Overrides ------------------------------ */
  
      public void create() {
          super.create();
          super.setParentDirectory(currentDirectory);
      }
  
      protected boolean beforePersist() {
          // Sync topic content
          syncFormToInstance(getParentDirectory());
  
          // Macros
          getInstance().setDefaultMacros();
  
          // Set createdOn date _now_
          getInstance().setCreatedOn(new Date());
  
          return true;
      }
  
      /* -------------------------- Internal Methods ------------------------------ */
  
  
      private void syncFormToInstance(Directory dir) {
          WikiLinkResolver wikiLinkResolver = (WikiLinkResolver) Component.getInstance("wikiLinkResolver");
          getInstance().setContentWithoutMacros(
              wikiLinkResolver.convertToWikiProtocol(dir.getAreaNumber(), formContent)
          );
      }
  
      private void syncInstanceToForm(Directory dir) {
          WikiLinkResolver wikiLinkResolver = (WikiLinkResolver)Component.getInstance("wikiLinkResolver");
          formContent = wikiLinkResolver.convertFromWikiProtocol(dir.getAreaNumber(), getInstance().getContentWithoutMacros());
      }
  
      /* -------------------------- Messages ------------------------------ */
  
      protected void createdMessage() {
          getFacesMessages().addFromResourceBundleOrDefault(
                  SEVERITY_INFO,
                  "lacewiki.msg.Topic.Persist",
                  "Topic '{0}' has been saved.",
                  getInstance().getName()
          );
      }
  
      protected void updatedMessage() {
          getFacesMessages().addFromResourceBundleOrDefault(
                  SEVERITY_INFO,
                  "lacewiki.msg.Topic.Update",
                  "Topic '{0}' has been updated.",
                  getInstance().getName()
          );
      }
  
      protected void deletedMessage() {
          getFacesMessages().addFromResourceBundleOrDefault(
                  SEVERITY_INFO,
                  "lacewiki.msg.Topic.Delete",
                  "Topic '{0}' has been deleted.",
                  getInstance().getName()
          );
      }
  
      /* -------------------------- Public Features ------------------------------ */
  
      @Begin(flushMode = FlushModeType.MANUAL)
      public void newTopic() {
  
          showForm = true;
  
          // Start with a fresh instance
          setInstance(createInstance());
  
          // Get a fresh parent directory instance into the current persistence context
          setParentDirectory(loadParentDirectory(getParentDirectory().getId()));
      }
  
      @End
      public void cancel() {
          showForm = false;
      }
  
      @End
      @RaiseEvent("Forum.topicPersisted")
      public String persist() {
          String outcome = super.persist();
          showForm = outcome == null; // Keep showing the form if there was a validation error
  
          // Create feed entries (needs identifiers assigned, so we run after persist())
          if (outcome != null) {
              String feedEntryTitle = "[" + getParentDirectory().getName() + "] " + getInstance().getName();
              feedDAO.createFeedEntry(getInstance(), false, feedEntryTitle);
              getEntityManager().flush();
          }
  
          return outcome;
      }
  
      public boolean isShowForm() {
          return showForm;
      }
  
      public void setShowForm(boolean showForm) {
          this.showForm = showForm;
      }
  
      public String getFormContent() {
          // Load the topic content and resolve links
          if (formContent == null) syncInstanceToForm(getParentDirectory());
          return formContent;
      }
  
      public void setFormContent(String formContent) {
          this.formContent = formContent;
          if (formContent != null) syncFormToInstance(getParentDirectory());
      }
  
  }
  
  
  
  1.1      date: 2007/11/09 15:00:28;  author: cbauer;  state: Exp;jboss-seam/examples/wiki/src/main/org/jboss/seam/wiki/plugin/forum/ForumTopic.java
  
  Index: ForumTopic.java
  ===================================================================
  package org.jboss.seam.wiki.plugin.forum;
  
  import org.jboss.seam.wiki.core.model.Document;
  import org.jboss.seam.wiki.core.model.Comment;
  import org.jboss.seam.wiki.core.search.annotations.Searchable;
  import org.jboss.seam.wiki.util.WikiUtil;
  
  import javax.persistence.*;
  
  /**
   * Represents a forum topic (a particular thread of discussion).
   * <p>
   * We re-use the <tt>Document</tt> class and extend the node hierarchy further with
   * a new discriminator, <tt>FORUMTOPIC</tt>. In addition to normal document properties,
   * a forum topic has a few transient properties (sticky, unread, last comment, etc.) and
   * a few methods to handle macros that <i>must</i> appear before and after the topic
   * content (e.g. hideControls, hideCreatorHistory, forumReplies).
   * <p>
   * Although pragmatic, I'm not sure we'll handle macros that way (with string concatenation)
   * in the future. This should be made more generic and typesafe, I think we need macro metadata
   * - which really depends on how plugin metadata will be designed.
   *
   * @author Christian Bauer
   */
  @Entity
  @DiscriminatorValue("FORUMTOPIC")
  @org.hibernate.search.annotations.Indexed
  @Searchable(description = "Forum Topics")
  public class ForumTopic extends Document {
  
      @Transient
      protected final String[] MACROS_BEFORE_CONTENT =
              {"clearBackground", "forumPosting"};
      @Transient
      protected final String[] MACROS_AFTER_CONTENT =
              {"forumReplies", "hideControls", "hideComments", "hideTags", "hideCreatorHistory"};
  
      @Transient
      private long numOfComments;
      @Transient
      private boolean sticky;
      @Transient
      private boolean unread;
      @Transient
      private Comment lastComment;
  
      public ForumTopic() {
          super("New Topic");
          setContentWithoutMacros("Edit this *wiki* text, the preview will update automatically.");
          setNameAsTitle(false);
          setEnableComments(true);
          setEnableCommentForm(true);
          setEnableCommentsOnFeeds(true);
      }
  
      public static ForumTopic fromArray(Object[] propertyValues) {
          ForumTopic topic = (ForumTopic)propertyValues[0];
          topic.setNumOfComments(propertyValues[1] != null ? (Long)propertyValues[1] : 0);
          topic.setSticky(propertyValues[2] != null ? ((Integer) propertyValues[2] != 0) : false);
          topic.setLastComment(propertyValues[3] != null ? (Comment)propertyValues[3] : null);
          return topic;
      }
  
      public long getNumOfComments() {
          return numOfComments;
      }
  
      public void setNumOfComments(long numOfComments) {
          this.numOfComments = numOfComments;
      }
  
      public boolean isSticky() {
          return sticky;
      }
  
      public void setSticky(boolean sticky) {
          this.sticky = sticky;
      }
  
      public boolean isUnread() {
          return unread;
      }
  
      public void setUnread(boolean unread) {
          this.unread = unread;
      }
  
      public Comment getLastComment() {
          return lastComment;
      }
  
      public void setLastComment(Comment lastComment) {
          this.lastComment = lastComment;
      }
  
      public String getContentWithoutMacros() {
          String content = getContent();
          // Cut from first double newline to last double newline
          return  content.substring( content.indexOf("\n\n")+2, content.lastIndexOf("\n\n") );
      }
  
      public void setContentWithoutMacros(String content) {
          // First, remove any macros that the user might have put into the text
          content = WikiUtil.removeMacros(content);
          
          // Apply the macros before and after content, separated with double newlines
          StringBuilder contentWithMacros = new StringBuilder();
          for (String s : MACROS_BEFORE_CONTENT) contentWithMacros.append("[<=").append(s).append("]\n");
          contentWithMacros.append("\n").append(content).append("\n\n");
          for (String s : MACROS_AFTER_CONTENT) contentWithMacros.append("[<=").append(s).append("]\n");
          if (isSticky()) contentWithMacros.append("[<=forumStickyPosting]").append("]\n");
          setContent(contentWithMacros.toString());
      }
  
      public void setDefaultMacros() {
          StringBuilder macros = new StringBuilder();
          for (String s : MACROS_BEFORE_CONTENT) macros.append(s).append(" ");
          for (String s : MACROS_AFTER_CONTENT) macros.append(s).append(" ");
          if (isSticky()) macros.append("forumStickyPosting").append(" ");
          setMacros(macros.substring(0, macros.length()-1));
      }
  
      public String getIconName() {
          StringBuilder iconName = new StringBuilder();
          iconName.append("posting");
          if (isSticky()) {
              iconName.append("_sticky");
          } else if (!getEnableCommentForm()) {
              iconName.append("_locked");
          }
          if (isUnread()) iconName.append("_unread");
  
          return iconName.toString();
      }
  
  }
  
  
  
  1.1      date: 2007/11/09 15:00:28;  author: cbauer;  state: Exp;jboss-seam/examples/wiki/src/main/org/jboss/seam/wiki/plugin/forum/ForumListHome.java
  
  Index: ForumListHome.java
  ===================================================================
  package org.jboss.seam.wiki.plugin.forum;
  
  import org.jboss.seam.annotations.*;
  import org.jboss.seam.annotations.security.Restrict;
  import org.jboss.seam.ScopeType;
  import org.jboss.seam.Component;
  import org.jboss.seam.contexts.Contexts;
  import org.jboss.seam.wiki.core.model.Directory;
  import org.jboss.seam.wiki.core.model.Node;
  import org.jboss.seam.wiki.core.dao.NodeDAO;
  import org.jboss.seam.wiki.util.WikiUtil;
  
  import javax.persistence.EntityManager;
  import java.io.Serializable;
  
  @Name("forumListHome")
  @Scope(ScopeType.PAGE)
  public class ForumListHome implements Serializable {
  
      @In
      EntityManager restrictedEntityManager;
  
      @In
      ForumHome forumHome;
  
      private boolean managed;
  
      public boolean isManaged() {
          return managed;
      }
  
      public void setManaged(boolean managed) {
          this.managed = managed;
      }
  
      public void manage() {
          managed = true;
      }
  
      @Restrict("#{s:hasPermission('Node', 'editMenu', currentDirectory)}")
      @RaiseEvent("Forum.forumListRefresh")
      public void moveNode(int currentPosition, int newPosition) {
          Directory forumDirectory = (Directory)Component.getInstance("currentDirectory");
          forumDirectory = restrictedEntityManager.find(Directory.class, forumDirectory.getId());
          if (currentPosition != newPosition) {
              // Shift and refresh displayed list
              WikiUtil.shiftListElement(forumDirectory.getChildren(), currentPosition, newPosition);
  
              // Required update, this is only refreshed on database load
              for (Node node : forumDirectory.getChildren()) {
                  node.setDisplayPosition(forumDirectory.getChildren().indexOf(node));
              }
          }
          Contexts.getPageContext().set("currentDirectory", forumDirectory);
      }
  
  }
  
  
  
  1.1      date: 2007/11/09 15:00:28;  author: cbauer;  state: Exp;jboss-seam/examples/wiki/src/main/org/jboss/seam/wiki/plugin/forum/ForumHome.java
  
  Index: ForumHome.java
  ===================================================================
  package org.jboss.seam.wiki.plugin.forum;
  
  import static javax.faces.application.FacesMessage.SEVERITY_INFO;
  import javax.faces.application.FacesMessage;
  
  import org.jboss.seam.annotations.*;
  import org.jboss.seam.ScopeType;
  import org.jboss.seam.core.Conversation;
  import org.jboss.seam.core.Events;
  import org.jboss.seam.wiki.core.model.Directory;
  import org.jboss.seam.wiki.core.model.Document;
  import org.jboss.seam.wiki.core.action.NodeHome;
  import org.jboss.seam.wiki.core.dao.FeedDAO;
  import org.jboss.seam.wiki.util.WikiUtil;
  
  import java.util.Date;
  
  @Name("forumHome")
  @Scope(ScopeType.CONVERSATION)
  @AutoCreate
  public class ForumHome extends NodeHome<Directory> {
  
      @In
      Directory currentDirectory;
  
      @In
      FeedDAO feedDAO;
  
      private boolean showForm = false;
      private boolean hasFeed = true;
  
      /* -------------------------- Basic Overrides ------------------------------ */
  
      protected Directory createInstance() {
          Directory forum = super.createInstance();
          forum.setName("New Forum");
          hasFeed = true;
          return forum;
      }
  
      public Directory find() {
          Directory forum = super.find();
          hasFeed = forum.getFeed()!=null;
          return forum;
      }
  
      public void create() {
          super.create();
          setParentDirectoryId(currentDirectory.getId());
          init();
      }
  
      protected boolean beforePersist() {
          createOrRemoveFeed();
          return super.beforePersist();
      }
  
      public String persist() {
          // This is _always_ a subdirectory in an area
          getInstance().setAreaNumber(getParentDirectory().getAreaNumber());
  
          String outcome = super.persist();
          if (outcome != null) {
  
              // Default document is topic list
              Document topicList = createDefaultDocument();
              topicList.setAreaNumber(getInstance().getAreaNumber());
              topicList.setName(getInstance().getName() + " Forum");
              topicList.setWikiname(WikiUtil.convertToWikiName(topicList.getName()));
              topicList.setCreatedBy(getCurrentUser());
              topicList.setLastModifiedBy(getCurrentUser());
              topicList.setLastModifiedOn(new Date());
  
              getInstance().addChild(topicList);
              getInstance().setDefaultDocument(topicList);
  
              getEntityManager().persist(topicList);
              getEntityManager().flush();
  
              endConversation();
          }
          return null; // Prevent navigation
      }
  
      protected boolean beforeUpdate() {
          createOrRemoveFeed();
          return super.beforeUpdate();
      }
  
      public String update() {
          String outcome = super.update();
          if (outcome != null) endConversation();
          return null; // Prevent navigation
      }
  
      protected boolean beforeRemove() {
          // Remove all children (nested, recursively, udpates the second-level cache)
          getNodeDAO().removeChildren(getInstance());
  
          return true;
      }
  
      public String remove() {
          String outcome = super.remove();
          if (outcome != null) endConversation();
          return null; // Prevent navigation
      }
  
      /* -------------------------- Messages ------------------------------ */
  
      protected void createdMessage() {
          getFacesMessages().addFromResourceBundleOrDefault(
                  SEVERITY_INFO,
                  "forum.msg.Forum.Persist",
                  "Forum '{0}' has been saved.",
                  getInstance().getName()
          );
      }
  
      protected void updatedMessage() {
          getFacesMessages().addFromResourceBundleOrDefault(
                  SEVERITY_INFO,
                  "forum.msg.Forum.Update",
                  "Forum '{0}' has been updated.",
                  getInstance().getName()
          );
      }
  
      protected void deletedMessage() {
          getFacesMessages().addFromResourceBundleOrDefault(
                  SEVERITY_INFO,
                  "forum.msg.Forum.Delete",
                  "Forum '{0}' has been deleted.",
                  getInstance().getName()
          );
      }
      
      /* -------------------------- Internal Methods ------------------------------ */
  
      private void endConversation() {
          showForm = false;
          Conversation.instance().end();
          getEntityManager().clear(); // Need to force re-read in the forum list refresh
          Events.instance().raiseEvent("Forum.forumListRefresh");
      }
  
      private Document createDefaultDocument() {
          Document doc = new Document();
          doc.setNameAsTitle(true);
          doc.setReadAccessLevel(getInstance().getReadAccessLevel());
          doc.setWriteAccessLevel(getInstance().getWriteAccessLevel());
          doc.setEnableComments(false);
          doc.setEnableCommentForm(false);
  
          String[] defaultMacros = {"clearBackground", "hideControls", "hideComments", "hideTags", "hideCreatorHistory", "forumTopics"};
  
          StringBuilder contentWithMacros = new StringBuilder();
          StringBuilder macros = new StringBuilder();
          for (String s : defaultMacros) {
              contentWithMacros.append("[<=").append(s).append("]\n");
              macros.append(s).append(" ");
          }
          doc.setContent(contentWithMacros.toString());
          doc.setMacros(macros.substring(0, macros.length()-1));
  
          return doc;
      }
  
      public void createOrRemoveFeed() {
          if (hasFeed && getInstance().getFeed() == null) {
              // Does not have a feed but user wants one, create it
              feedDAO.createFeed(getInstance());
  
              getFacesMessages().addFromResourceBundleOrDefault(
                  FacesMessage.SEVERITY_INFO,
                  "forum.msg.Feed.Create",
                  "Created syndication feed for this forum");
  
          } else if (!hasFeed && getInstance().getFeed() != null) {
              // Does have feed but user doesn't want it anymore... delete it
              feedDAO.removeFeed(getInstance());
  
              getFacesMessages().addFromResourceBundleOrDefault(
                  FacesMessage.SEVERITY_INFO,
                  "forum.msg.Feed.Remove",
                  "Removed syndication feed of this forum");
  
          } else if (getInstance().getFeed() != null) {
              // Does have a feed and user still wants it, update the feed
              feedDAO.updateFeed(getInstance());
          }
      }
  
      /* -------------------------- Public Features ------------------------------ */
  
      public boolean isShowForm() {
          return showForm;
      }
  
      public void setShowForm(boolean showForm) {
          this.showForm = showForm;
      }
  
      public boolean isHasFeed() {
          return hasFeed;
      }
  
      public void setHasFeed(boolean hasFeed) {
          this.hasFeed = hasFeed;
      }
  
      @Begin(flushMode = FlushModeType.MANUAL)
      public void newForum() {
          init();
          showForm = true;
      }
  
      @Begin(flushMode = FlushModeType.MANUAL)
      public void edit(Long forumId) {
          setId(forumId);
          init();
          showForm = true;
      }
  
      public void cancel() {
          endConversation();
      }
  
      public void resetFeed() {
          if (getInstance().getFeed() != null) {
              getLog().debug("resetting feed of directory");
              getInstance().getFeed().getFeedEntries().clear();
              getInstance().getFeed().setPublishedDate(new Date());
              getFacesMessages().addFromResourceBundleOrDefault(
                  FacesMessage.SEVERITY_INFO,
                  "forum.msg.Feed.Reset",
                  "Queued removal of all feed entries from the syndication feed of this directory, please update to finalize");
          }
      }
  
  }
  
  
  
  1.1      date: 2007/11/09 15:00:28;  author: cbauer;  state: Exp;jboss-seam/examples/wiki/src/main/org/jboss/seam/wiki/plugin/forum/ForumDAO.java
  
  Index: ForumDAO.java
  ===================================================================
  package org.jboss.seam.wiki.plugin.forum;
  
  import org.jboss.seam.annotations.Name;
  import org.jboss.seam.annotations.In;
  import org.jboss.seam.annotations.AutoCreate;
  import org.jboss.seam.wiki.core.model.Directory;
  import org.jboss.seam.wiki.core.model.Comment;
  import org.hibernate.Session;
  import org.hibernate.ScrollableResults;
  import org.hibernate.transform.ResultTransformer;
  
  import javax.persistence.EntityManager;
  import java.util.List;
  import java.util.Map;
  import java.util.HashMap;
  import java.util.Date;
  
  @Name("forumDAO")
  @AutoCreate
  public class ForumDAO {
  
      @In
      EntityManager entityManager;
  
      @In
      EntityManager restrictedEntityManager;
  
      public List<Directory> findForums(Directory forumDirectory) {
          return getSession(true).getNamedQuery("forums")
                  .setParameter("parentDir", forumDirectory)
                  .list();
      }
      
      public Map<Long, ForumInfo> findForumInfo(Directory forumDirectory) {
          final Map<Long, ForumInfo> forumInfoMap = new HashMap<Long, ForumInfo>();
  
          // Append thread and posting count
          getSession(true).getNamedQuery("forumTopicPostCount")
              .setParameter("parentDir", forumDirectory)
              .setResultTransformer(
                  new ResultTransformer() {
                      public Object transformTuple(Object[] result, String[] strings) {
                          forumInfoMap.put(
                              (Long) result[0],
                              new ForumInfo( (Long)result[1], (Long)result[2])
                          );
                          return null;
                      }
                      public List transformList(List list) { return list; }
                  }
              )
              .list();
  
          // Append last topic Document
          getSession(true).getNamedQuery("forumLastTopic")
              .setParameter("parentDir", forumDirectory)
              .setResultTransformer(
                  new ResultTransformer() {
                      public Object transformTuple(Object[] result, String[] strings) {
                          if (forumInfoMap.containsKey((Long)result[0]))
                              forumInfoMap.get( (Long)result[0] ).setLastTopic( (ForumTopic)result[1] );
                          return null;
                      }
                      public List transformList(List list) { return list; }
                  }
              )
              .list();
  
          // Append last reply Comment
          getSession(true).getNamedQuery("forumLastComment")
              .setParameter("nsLeft", forumDirectory.getNsLeft())
              .setParameter("nsRight", forumDirectory.getNsRight())
              .setParameter("nsThread", forumDirectory.getNsThread())
              .setResultTransformer(
                  new ResultTransformer() {
                      public Object transformTuple(Object[] result, String[] strings) {
                          if (forumInfoMap.containsKey((Long)result[0]))
                              forumInfoMap.get( (Long)result[0] ).setLastComment( (Comment)result[1] );
                          return null;
                      }
                      public List transformList(List list) { return list; }
                  }
              )
              .list();
  
          return forumInfoMap;
      }
  
      public Long findTopicCount(Directory forum) {
          ScrollableResults cursor =
              getSession(true).getNamedQuery("forumTopics")
                  .setParameter("forum", forum)
                  .scroll();
          cursor.last();
          Long count = cursor.getRowNumber() + 1l;
          cursor.close();
  
          return count;
      }
     
      public List<ForumTopic> findTopics(Directory forum, long firstResult, long maxResults) {
          return getSession(true).getNamedQuery("forumTopics")
              .setParameter("forum", forum)
              .setResultTransformer(
                  new ResultTransformer() {
                      public Object transformTuple(Object[] result, String[] strings) {
                          return ForumTopic.fromArray(result);
                      }
                      public List transformList(List list) { return list; }
                  }
              )
              .setFirstResult(new Long(firstResult).intValue())
              .setMaxResults(new Long(maxResults).intValue())
              .list();
      }
  
      public List<ForumTopic> findUnreadTopics(Date lastLoginDate) {
          return getSession(true).getNamedQuery("forumUnreadTopics")
                  .setParameter("lastLoginDate", lastLoginDate)
                  .list();
      }
  
      private Session getSession(boolean restricted) {
          if (restricted) {
              return ((Session)((org.jboss.seam.persistence.EntityManagerProxy) restrictedEntityManager).getDelegate());
          } else {
              return ((Session)((org.jboss.seam.persistence.EntityManagerProxy) entityManager).getDelegate());
          }
      }
  }
  
  
  
  1.1      date: 2007/11/09 15:00:28;  author: cbauer;  state: Exp;jboss-seam/examples/wiki/src/main/org/jboss/seam/wiki/plugin/forum/ForumQueries.hbm.xml
  
  Index: ForumQueries.hbm.xml
  ===================================================================
  <?xml version="1.0"?>
  <!DOCTYPE hibernate-mapping PUBLIC
          "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
          "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
  
  <hibernate-mapping>
  
      <query name="forums">
          select
              f
          from
              Directory f left join fetch f.feed
          where
              f.parent = :parentDir
          order by f.displayPosition asc
      </query>
  
      <query name="forumTopicPostCount">
          select
              f.id, count(distinct child), count(distinct child) + count(distinct comment)
          from
              Directory f
                  left outer join f.children as child
                  left outer join child.comments as comment
          where
              f.parent = :parentDir
              and child != f.defaultDocument
          group
              by f.id
      </query>
  
      <query name="forumLastTopic">
          select
              f.id, lt
          from
              Directory f left outer join f.children as lt left join fetch lt.createdBy u join fetch u.profile
          where
              f.parent = :parentDir
              and lt != f.defaultDocument
              and lt.displayPosition = (select max(n.displayPosition) from Node n where n.parent = f)
      </query>
  
      <!-- This only uses the timestamp of all comments to identify the last comment - is that enough? -->
      <query name="forumLastComment"><![CDATA[
          select
              f.id, c
          from
              Comment c left join fetch c.fromUser u join fetch u.profile, Document d, Directory f join fetch c.document
          where
              c.createdOn =
                  (select max(com.createdOn) from Document doc, Comment com where
                  doc.nsLeft > :nsLeft and doc.nsRight < :nsRight and doc.nsThread = :nsThread
                  and com.document = doc)
              and c.document = d
              and d.parent = f
      ]]></query>
  
      <!-- Somewhat ugly workarounds for the missing case...when support in HQL select clause -->
      <query name="forumTopics"><![CDATA[
          select
              t,
              count(c),
              locate('forumStickyPosting', t.macros, 1),
              c2
          from
              Directory f,
              ForumTopic t
                  join fetch t.createdBy u join fetch u.profile up
                  left outer join t.comments c
                  left outer join t.comments c2 left join fetch c2.fromUser u2 left join fetch u2.profile u2p
          where
              f = :forum
              and t.parent = f
              and t != f.defaultDocument
              and (c2.createdOn = (select max(com.createdOn) from Comment com where com.document = t) or c2 is null)
              group by t.id, t.class, t.version, t.nsLeft, t.nsRight, t.nsThread, t.parent,
                       t.areaNumber, t.createdBy, t.createdOn, t.lastModifiedBy, t.lastModifiedOn, t.menuItem, t.name, t.displayPosition,
                       t.readAccessLevel, t.revision, t.wikiname, t.writeAccessLevel, t.tags,
                       t.enableComments, t.enableCommentForm, t.enableCommentsOnFeeds, t.nameAsTitle, t.macros,
                       locate('forumStickyPosting', t.macros, 1),
  
                       c2.id, c2.version, c2.document, c2.subject, c2.fromUser, c2.fromUserName, c2.fromUserEmail, c2.fromUserHomepage, c2.text, c2.useWikiText, c2.createdOn,
  
                       u.id, u.version, u.firstname, u.lastname, u.username, u.passwordHash, u.email, u.activated, u.activationCode, u.createdOn, u.lastLoginOn, u.memberHome, u.profile,
                       up.id, up.version, up.createdOn, up.bio, up.website, up.location, up.occupation, up.signature, up.imageContentType,
  
                       u2.id, u2.version, u2.firstname, u2.lastname, u2.username, u2.passwordHash, u2.email, u2.activated, u2.activationCode, u2.createdOn, u.lastLoginOn, u2.memberHome, u2.profile,
                       u2p.id, u2p.version, u2p.createdOn, u2p.bio, u2p.website, u2p.location, u2p.occupation, u2p.signature, u2p.imageContentType
  
              order by case locate('forumStickyPosting', t.macros, 1) when '0' then '0' else '1' end desc, t.createdOn desc, c2.createdOn desc
      ]]></query>
  
      <query name="forumUnreadTopics"><![CDATA[
          select
              distinct t
          from ForumTopic t left join fetch t.parent left outer join t.comments c
              where
                  t.createdOn > :lastLoginDate
                  or
                  c.createdOn > :lastLoginDate
  
      ]]></query>
  
  </hibernate-mapping>
  
  
  1.1      date: 2007/11/09 15:00:28;  author: cbauer;  state: Exp;jboss-seam/examples/wiki/src/main/org/jboss/seam/wiki/plugin/forum/ForumSearchSupport.java
  
  Index: ForumSearchSupport.java
  ===================================================================
  package org.jboss.seam.wiki.plugin.forum;
  
  import org.jboss.seam.annotations.Name;
  import org.jboss.seam.wiki.core.search.metamodel.SearchSupport;
  import org.jboss.seam.wiki.core.search.metamodel.SearchableEntityHandler;
  import org.jboss.seam.wiki.core.search.SearchHit;
  import org.jboss.seam.wiki.util.WikiUtil;
  import org.apache.lucene.search.Query;
  import org.apache.lucene.search.highlight.*;
  
  import java.util.HashSet;
  import java.util.Set;
  
  @Name("forumSearchSupport")
  public class ForumSearchSupport extends SearchSupport {
  
      public Set<SearchableEntityHandler> getSearchableEntityHandlers() {
  
          return new HashSet<SearchableEntityHandler>() {{
  
              add(
                  new SearchableEntityHandler<ForumTopic>() {
  
                      public boolean isReadAccessChecked() {
                          return true;
                      }
  
                      public SearchHit extractHit(Query query, ForumTopic forumTopic) throws Exception {
                          return new SearchHit(
                              ForumTopic.class.getSimpleName(),
                              "icon.posting.gif",
                              escapeBestFragments(query, new NullFragmenter(), forumTopic.getName(), 0, 0),
                              WikiUtil.renderURL(forumTopic),
                              escapeBestFragments(query, new SimpleFragmenter(100), forumTopic.getContent(), 5, 350)
                          );
                      }
                  }
              );
  
          }};
      }
  
  }
  
  
  
  1.1      date: 2007/11/09 15:00:28;  author: cbauer;  state: Exp;jboss-seam/examples/wiki/src/main/org/jboss/seam/wiki/plugin/forum/ForumCookie.java
  
  Index: ForumCookie.java
  ===================================================================
  package org.jboss.seam.wiki.plugin.forum;
  
  import org.jboss.seam.annotations.*;
  
  import javax.faces.context.FacesContext;
  import javax.servlet.http.HttpServletRequest;
  import javax.servlet.http.Cookie;
  import javax.servlet.http.HttpServletResponse;
  import java.util.Map;
  import java.util.HashMap;
  import java.net.URLEncoder;
  import java.net.URLDecoder;
  
  /**
   * Simplifes storing of cookie values in a temporary (browser-bound)
   * coookie. Use <tt>forumCookie.addCookieValue(key, value)</tt> to add a string
   * value to the cookie value map. Do not use the characters "=" and ";" in
   * either keys or values. The cookie is also bound to the current HTTP session
   * by appending the session identifier to the value. This means that the
   * maximum lifetime of cookie values is in fact the session, not the browser
   * instance.
   * <p>
   * One issue is that Tomcat apparently re-uses the session identifier if no
   * session exists but a JSESSIONID cookie was present in the request. That means
   * this code works only halfway, because we no longer can uniquely identify
   * sessions by their identifier.
   * <p>
   * TODO: This class could be made generic (customizable cookie settings) and added to Seam?
   *
   * @author Christian Bauer
   */
  @Name("forumCookie")
  @AutoCreate
  public class ForumCookie {
  
      public static final String COOKIE_NAME = "lacewiki_forum_topics";
      private static final String SESSION_KEY = "lacewiki_session_id";
  
      @In
      FacesContext facesContext;
  
      private Map<String, String> cookieValues;
  
      public Map<String, String> getCookieValues() {
          return cookieValues;
      }
  
      public void addCookieValue(String key, String value) {
          cookieValues.put(key, value);
          createCookie();
      }
  
      @Create
      public void create() {
          readCookie();
          if (getCookieValues() == null) {
              cookieValues = new HashMap<String, String>();
              cookieValues.put(SESSION_KEY, getSessionId());
              createCookie();
          }
      }
  
      private void readCookie() {
          if (getCookie() != null) {
              Map<String, String> values = decode(getCookie().getValue());
              if (values.get(SESSION_KEY).equals(getSessionId())) {
                  cookieValues = values;
              }
          }
      }
  
      private void createCookie() {
          FacesContext ctx = FacesContext.getCurrentInstance();
          if (ctx != null) {
              HttpServletResponse response = (HttpServletResponse) ctx.getExternalContext().getResponse();
              Cookie newCookie = new Cookie(COOKIE_NAME, encode(cookieValues));
              newCookie.setMaxAge(-1); // Delete when browser closes
              newCookie.setPath(ctx.getExternalContext().getRequestContextPath());
              response.addCookie(newCookie);
          }
      }
  
      private Cookie getCookie() {
          FacesContext ctx = FacesContext.getCurrentInstance();
          if (ctx != null) {
              for (Map.Entry<String, Object> entry : ctx.getExternalContext().getRequestCookieMap().entrySet()) {
              }
              return (Cookie) ctx.getExternalContext().getRequestCookieMap().get(COOKIE_NAME);
          } else {
              return null;
          }
      }
  
      @Observer(value = "org.jboss.seam.loggedOut", create = true)
      public void destroyCookie() {
          FacesContext ctx = FacesContext.getCurrentInstance();
          if (ctx != null) {
              HttpServletResponse response = (HttpServletResponse) ctx.getExternalContext().getResponse();
              Cookie newCookie = new Cookie(COOKIE_NAME, null);
              newCookie.setMaxAge(0);
              newCookie.setPath(ctx.getExternalContext().getRequestContextPath());
              response.addCookie(newCookie);
          }
      }
  
      private String getSessionId() {
          return ((HttpServletRequest)FacesContext.getCurrentInstance().getExternalContext().getRequest()).getSession().getId();
      }
  
      private String encode(Map<String, String> keyValuePairs) {
          StringBuilder cookieValue = new StringBuilder();
          for (Map.Entry<String, String> entry : keyValuePairs.entrySet()) {
              cookieValue.append(entry.getKey()).append("=").append(entry.getValue()).append(";");
          }
          String cookieValueEncoded;
          try {
              cookieValueEncoded = URLEncoder.encode(cookieValue.toString(), "UTF-8");
          } catch (Exception ex) {
              throw new RuntimeException(ex);
          }
          return cookieValueEncoded;
      }
  
      private Map<String, String> decode(String cookieValue) {
          Map<String, String> keyValuePairs = new HashMap<String, String>();
          String cookieValueDecoded;
          try {
              cookieValueDecoded = URLDecoder.decode(cookieValue, "UTF-8");
          } catch (Exception ex) {
              throw new RuntimeException(ex);
          }
          String[] keyValuePair = cookieValueDecoded.split(";");
          for (String keyValueString : keyValuePair) {
              String[] keyValue = keyValueString.split("=");
              keyValuePairs.put(keyValue[0], keyValue[1]);
          }
  
          return keyValuePairs;
      }
  
  }
  
  
  



More information about the jboss-cvs-commits mailing list