AnswersFeedGenerator.java

/*
 * Copyright (C) 2003-2009 eXo Platform SAS.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Affero General Public License
 * as published by the Free Software Foundation; either version 3
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, see<http://www.gnu.org/licenses/>.
 */
package org.exoplatform.forum.rss;

import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;

import javax.jcr.Node;
import javax.jcr.NodeIterator;
import javax.jcr.Session;

import org.exoplatform.forum.common.jcr.JCRTask;
import org.exoplatform.forum.common.jcr.KSDataLocation;
import org.exoplatform.forum.common.jcr.PropertyReader;
import org.exoplatform.forum.common.jcr.VoidReturn;
import org.exoplatform.services.log.ExoLogger;
import org.exoplatform.services.log.Log;

import com.sun.syndication.feed.synd.SyndContent;
import com.sun.syndication.feed.synd.SyndContentImpl;
import com.sun.syndication.feed.synd.SyndEntry;
import com.sun.syndication.feed.synd.SyndFeed;

/**
 * @author <a href="mailto:patrice.lamarque@exoplatform.com">Patrice Lamarque</a>
 * @version $Revision$
 */
public final class AnswersFeedGenerator extends RSSProcess implements FeedContentProvider, FeedListener {

  private static final Log   LOG          = ExoLogger.getLogger(AnswersFeedGenerator.class);

  public static final String FAQ_RSS_TYPE = "exo:faqRSS".intern();

  public static final String KS_FAQ       = "faq".intern();

  public static final String FAQ_APP      = "faqApp".intern();
  
  public AnswersFeedGenerator() {
    super();
  }
  
  public AnswersFeedGenerator(KSDataLocation dataLocator) {
    super(dataLocator);
  }

  
  public void itemAdded(String path) {
    try {
      ItemSavedTask task = new ItemSavedTask(path,false);
      dataLocator.getSessionManager().executeAndSave(task);
    } catch (Exception e) {
      LOG.error("itemAdded failed for" + path, e);
    }
  }
  
  public void itemUpdated(String path) {
    try {
      ItemSavedTask task = new ItemSavedTask(path,true);
      dataLocator.getSessionManager().executeAndSave(task);
    } catch (Exception e) {
      LOG.error("itemUpdated failed for" + path, e);
    }
  }
  
  class ItemSavedTask implements JCRTask<VoidReturn> {
    private String path;
    private boolean updated;
    
   public ItemSavedTask(String path, boolean updated) {
      this.path = path;
      this.updated = updated;
   }

   public VoidReturn execute(Session session) throws Exception {
     itemSaved(path,updated);
     return VoidReturn.VALUE;
   }
    
  }
  
  protected void itemSaved(String path, boolean updated) throws Exception {
    // find the node corresponding to this event
    Node questionNode = findQuestionNode(path);
    if (questionNode == null) {
      LOG.debug("generate Feed event was ignored on " + path);
      return;
    }

    final String questionNodeName = questionNode.getName();
    PropertyReader question = new PropertyReader(questionNode);
        
    Node categoryNode = questionNode.getParent().getParent();
    PropertyReader category = new PropertyReader(categoryNode);
    String categoryDescription = category.string("exo:description", "eXo link:" + RSS.DEFAULT_FEED_LINK);
    String categoryTitle = category.string("exo:name", "Home");

    // only approved or activated questions
    if ((!question.bool("exo:isActivated") || !question.bool("exo:isApproved"))) {
      RSS rss = new RSS(categoryNode);
      SyndFeed feed = rss.removeEntry(questionNodeName);
      feed.setTitle(categoryTitle);
      feed.setDescription(categoryDescription);
      rss.saveFeed(feed, FAQ_RSS_TYPE);
      return;
    }
    
    SyndEntry entry = createQuestionEntry(questionNode);
    RSS data = new RSS(categoryNode);
    if (data.feedExists()) {
      SyndFeed feed = null;

      if (updated) {
        feed = data.removeEntry(questionNodeName);
      }
      feed = data.addEntry(entry);
      feed.setDescription(categoryDescription);
      feed.setTitle(categoryTitle);
      data.saveFeed(feed, FAQ_RSS_TYPE);
    } else {
      SyndFeed feed = RSS.createNewFeed(categoryTitle, category.date("exo:createdDate", new Date()));
      feed.setDescription(categoryDescription);
      String categoryLink = entry.getLink()
      + "?portal:componentId=faq&portal:type=action&portal:isSecure=false&uicomponent=UICategories&op=OpenCategory&"
      + "objectId=" + question.string("exo:categoryId");
      feed.setLink(categoryLink);
      feed.setEntries(Arrays.asList(new SyndEntry[] { entry }));
      data.saveFeed(feed, FAQ_RSS_TYPE);     
    }

  }

  private SyndEntry createQuestionEntry(Node questionNode) throws Exception  {
    String linkItem = getPageLink();
    linkItem += "?portal:componentId=faq&portal:type=action&portal:isSecure=false&uicomponent=UIQuestions&op=ViewQuestion&objectId=";
    linkItem += questionNode.getPath().substring(questionNode.getPath().indexOf("/categories/") + 1);   

    // Create new entry
    List<String> listContent = new ArrayList<String>();
    StringBuffer content = new StringBuffer();
    if (questionNode.hasNode("faqAnswerHome") && questionNode.getNode("faqAnswerHome").hasNodes()) {
      for (String answer : getAnswers(questionNode))
        content.append(" <b><u>Answer:</u></b> ").append(answer).append(". ");
    }
    if (questionNode.hasNode("faqCommentHome") && questionNode.getNode("faqCommentHome").hasNodes()) {
      for (String comment : getComments(questionNode))
        content.append(" <b><u>Comment:</u></b> ").append(comment).append(". ");
    }
    listContent.add(content.toString());
    
    
    SyndContent description = new SyndContentImpl();
    description.setType(RSS.PLAIN_TEXT);

    final String questionNodeName = questionNode.getName();
    PropertyReader question = new PropertyReader(questionNode);
    description.setValue(question.string("exo:name") + ". " + content); 
    final String title = question.string("exo:title");
    final Date created = question.date("exo:createdDate");
    final String owner = question.string("exo:author");
    
    SyndEntry entry = RSS.createNewEntry(questionNodeName, title, linkItem,  listContent, description, created, owner);
    entry.setLink(linkItem);
    return entry;
  }
  

  
  
  
  private Node findQuestionNode(String nodePath) {
    try {
    Node questionNode = (Node) getCurrentSession().getItem(nodePath);
    if (!questionNode.isNodeType("exo:faqQuestion") && !questionNode.isNodeType("exo:answer")
        && !questionNode.isNodeType("exo:answerHome") && !questionNode.isNodeType("exo:comment")
        && !questionNode.isNodeType("exo:commentHome"))
      return null;
    else if (questionNode.isNodeType("exo:answer") || questionNode.isNodeType("exo:comment")) {
      questionNode = questionNode.getParent().getParent();
      if (!questionNode.isNodeType("exo:faqQuestion"))
        return null; // Worked on other languages
    } else if (questionNode.isNodeType("exo:answerHome")
        || questionNode.isNodeType("exo:commentHome")) {
      questionNode = questionNode.getParent();
      if (!questionNode.isNodeType("exo:faqQuestion"))
        return null; // Worked on other languages
    }
    
    if (!questionNode.isNodeType("exo:faqQuestion")) {
      return null;
    }
    return questionNode;
    } catch(Exception e) {
      LOG.error("Failed to identify question Node for path" + nodePath, e);
      return null;
    }

  }
  
  List<String> getAnswers(Node questionNode) throws Exception{
    List<String> listAnswers = new ArrayList<String>();
    try{
      if(questionNode.hasNode("faqAnswerHome")){
        NodeIterator nodeIterator = questionNode.getNode("faqAnswerHome").getNodes();
        Node answerNode = null;
        int i = 0;
        while(nodeIterator.hasNext()){
          answerNode = nodeIterator.nextNode();
          if(answerNode.hasProperty("exo:responses") && answerNode.getProperty("exo:approveResponses").getBoolean() &&
              answerNode.getProperty("exo:activateResponses").getBoolean()) 
            listAnswers.add((answerNode.getProperty("exo:responses").getValue().getString())) ;
          i ++;
        }
      }
    } catch (Exception e){
      LOG.error("Failed to get answers for " + questionNode.getName(), e);
    }
    return listAnswers;
  }

  List<String> getComments(Node questionNode) throws Exception{
    List<String> listComment = new ArrayList<String>();
    try{
      if(questionNode.hasNode("faqCommentHome")){
        NodeIterator nodeIterator = questionNode.getNode("faqCommentHome").getNodes();
        Node commentNode = null;
        while(nodeIterator.hasNext()){
          commentNode = nodeIterator.nextNode();
          if(commentNode.hasProperty("exo:comments")) 
            listComment.add((commentNode.getProperty("exo:comments").getValue().getString())) ;
        }
      }
    } catch (Exception e){
      LOG.error("Failed to get comments for " + questionNode.getName(), e);
    }
    return listComment;
  }
  
  
  public InputStream getFeedContent(String targetId) {    
    return dataLocator.getSessionManager().executeAndSave(new GetFeedStreamTask(targetId));
  }
  
  class GetFeedStreamTask implements JCRTask<InputStream> {

    private String targetId;

    public GetFeedStreamTask(String targetId) {
      this.targetId = targetId;
    }

    public InputStream execute(Session session) throws Exception {
      Node parentNode = getAnswersServiceHome().getNode(targetId) ;
      return getFeedStream(parentNode, FAQ_RSS_TYPE, "ANSWERS RSS FEED");
    }
    
  }
  

  private Node getAnswersServiceHome() throws Exception {
    String path = dataLocator.getFaqHomeLocation();
    return dataLocator.getSessionManager().getCurrentSession().getRootNode().getNode(path);
  }

  public void itemRemoved(String path)  {
    dataLocator.getSessionManager().executeAndSave(new ItemRemovedTask(path));
  }

  class ItemRemovedTask implements JCRTask<VoidReturn> {

    private String path;

    public ItemRemovedTask(String path) {
      this.path = path;
    }

    public VoidReturn execute(Session session) throws Exception {
      try {
        String categoryPath = path.substring(0, path.indexOf("/questions/"));
        Node categoryNode = (Node) session.getItem(categoryPath);
        if(categoryNode == null ) categoryNode = (Node) getCurrentSession().getItem(categoryPath);
        while (!categoryNode.isNodeType("exo:faqCategory")) {
          categoryNode = categoryNode.getParent();
        }
        String itemId = path.substring(path.lastIndexOf("/") + 1);
        RSS rss = new RSS(categoryNode);
        SyndFeed feed = rss.removeEntry(itemId);
        String title = new PropertyReader(categoryNode).string("exo:name", "Root");
        feed.setTitle(title);
        rss.saveFeed(feed, FAQ_RSS_TYPE);
      } catch (Exception e) {
        LOG.debug("Failed to get RSS.", e);
      }
      return VoidReturn.VALUE;
    }
    
  }
  

}