/*
* ConcourseConnect
* Copyright 2009 Concursive Corporation
* http://www.concursive.com
*
* This file is part of ConcourseConnect, an open source social business
* software and community platform.
*
* Concursive ConcourseConnect 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, version 3 of the License.
*
* Under the terms of the GNU Affero General Public License you must release the
* complete source code for any application that uses any part of ConcourseConnect
* (system header files and libraries used by the operating system are excluded).
* These terms must be included in any work that has ConcourseConnect components.
* If you are developing and distributing open source applications under the
* GNU Affero General Public License, then you are free to use ConcourseConnect
* under the GNU Affero General Public License.
*
* If you are deploying a web site in which users interact with any portion of
* ConcourseConnect over a network, the complete source code changes must be made
* available. For example, include a link to the source archive directly from
* your web site.
*
* For OEMs, ISVs, SIs and VARs who distribute ConcourseConnect with their
* products, and do not license and distribute their source code under the GNU
* Affero General Public License, Concursive provides a flexible commercial
* license.
*
* To anyone in doubt, we recommend the commercial license. Our commercial license
* is competitively priced and will eliminate any confusion about how
* ConcourseConnect can be used and distributed.
*
* ConcourseConnect 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 Affero General Public License for more
* details.
*
* You should have received a copy of the GNU Affero General Public License
* along with ConcourseConnect. If not, see <http://www.gnu.org/licenses/>.
*
* Attribution Notice: ConcourseConnect is an Original Work of software created
* by Concursive Corporation
*/
package com.concursive.connect.web.rss.servlets;
import com.concursive.commons.db.ConnectionElement;
import com.concursive.commons.db.ConnectionPool;
import com.concursive.commons.http.RequestUtils;
import com.concursive.commons.text.StringUtils;
import com.concursive.connect.Constants;
import com.concursive.connect.config.ApplicationPrefs;
import com.concursive.connect.web.modules.blog.dao.BlogPost;
import com.concursive.connect.web.modules.blog.dao.BlogPostList;
import com.concursive.connect.web.modules.discussion.dao.Topic;
import com.concursive.connect.web.modules.discussion.dao.TopicList;
import com.concursive.connect.web.modules.documents.dao.FileItem;
import com.concursive.connect.web.modules.documents.dao.FileItemList;
import com.concursive.connect.web.modules.login.utils.UserUtils;
import com.concursive.connect.web.modules.profile.dao.Project;
import com.concursive.connect.web.modules.profile.dao.ProjectCategory;
import com.concursive.connect.web.modules.profile.dao.ProjectCategoryList;
import com.concursive.connect.web.modules.profile.utils.ProjectUtils;
import com.concursive.connect.web.modules.wiki.dao.Wiki;
import com.concursive.connect.web.modules.wiki.dao.WikiList;
import com.concursive.connect.web.rss.utils.FeedUtils;
import com.concursive.connect.web.utils.PagedListInfo;
import com.concursive.connect.web.webdav.WebdavManager;
import com.sun.syndication.feed.synd.*;
import com.sun.syndication.io.SyndFeedOutput;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
/**
* Description
*
* @author matt rajkowski
* @version $Id$
* @created Sep 9, 2005
*/
public class FeedServlet extends HttpServlet {
private static final Log LOG = LogFactory.getLog(FeedServlet.class);
private static final String MIME_TYPE = "application/xml; charset=UTF-8";
private static final String COULD_NOT_GENERATE_FEED_ERROR = "Could not generate feed";
public void init() {
}
public void service(HttpServletRequest request, HttpServletResponse response) throws IOException {
try {
request.setCharacterEncoding("UTF-8");
} catch (Exception e) {
LOG.warn("Unsupported encoding");
}
Connection db = null;
try {
response.setContentType(MIME_TYPE);
ApplicationPrefs prefs = (ApplicationPrefs) getServletContext().getAttribute(Constants.APPLICATION_PREFS);
String url = "http://" + RequestUtils.getServerUrl(request);
List entries = new ArrayList();
SyndEntry entry;
SyndContent description;
// Everything is ready, now check the username/password
db = getConnection(getServletContext());
java.util.Date publishDate = null;
// Can pull from a cache or rebuild the news list...
String path = request.getRequestURI();
// Generate the RSS and replace the cache for all public projects
SyndFeed feed = new SyndFeedImpl();
if (path.endsWith("/rss.xml")) {
// @todo /feed/products/name_of_product/rss.xml
// Use the purpose of the site for determining what is included in the feed
String purpose = prefs.get(ApplicationPrefs.PURPOSE);
// The intranet feed is not a public feed and requires login
int userId = -1;
if ("intranet".equals(purpose) && "true".equals(prefs.get(ApplicationPrefs.INFORMATION_IS_SENSITIVE))) {
// Check credentials
boolean authenticated = WebdavManager.checkAuthentication(request);
if (!authenticated) {
WebdavManager.askForAuthentication(response);
return;
}
userId = WebdavManager.validateUser(db, request);
if (userId == -1) {
WebdavManager.askForAuthentication(response);
return;
}
}
// Check to see which public feed is requested
if (path.endsWith("/feed/rss.xml")) {
// This is the main website feed
feed = FeedUtils.loadFeed("/rss.xml," + purpose, url);
feed.setTitle(prefs.get("TITLE"));
feed.setDescription(prefs.get("TITLE") + " feed");
} else {
// Check to see which website category feed is requested
ProjectCategoryList projectCategoryList = new ProjectCategoryList();
projectCategoryList.setTopLevelOnly(true);
projectCategoryList.setEnabled(true);
if (userId == -1) {
projectCategoryList.setSensitive(Constants.FALSE);
}
projectCategoryList.buildList(db);
for (ProjectCategory projectCategory : projectCategoryList) {
String normalizedCategoryName = projectCategory.getNormalizedCategoryName();
if (path.indexOf(normalizedCategoryName) != -1) {
String requestedFeed = path.substring(path.indexOf("/feed/") + "/feed/".length(), path.indexOf(".xml")) + ".xml";
LOG.debug("Requested feed: " + requestedFeed);
feed = FeedUtils.loadFeed(requestedFeed + "," + purpose, url);
feed.setTitle(prefs.get("TITLE") + " - " + projectCategory.getLabel());
feed.setDescription(prefs.get("TITLE") + " - " + projectCategory.getLabel() + " feed");
break;
}
}
}
// No feed was found
if (feed == null) {
return;
}
} else {
boolean authenticated = WebdavManager.checkAuthentication(request);
if (!authenticated) {
WebdavManager.askForAuthentication(response);
return;
}
int userId = WebdavManager.validateUser(db, request);
if (userId == -1) {
WebdavManager.askForAuthentication(response);
return;
}
if (path.endsWith("/news.xml") || path.endsWith("/blog.xml")) {
feed.setTitle(prefs.get("TITLE") + " - Blog Rollup");
feed.setDescription(prefs.get("TITLE") + " personalized blog rollup");
BlogPostList newsList = new BlogPostList();
newsList.setLastNews(5);
newsList.setForUser(userId);
//Calendar cal = Calendar.getInstance();
// 14 Days
//cal.add(Calendar.DAY_OF_MONTH, -30);
//Timestamp alertRangeStart = new Timestamp(cal.getTimeInMillis());
//newsList.setAlertRangeStart(alertRangeStart);
newsList.setCurrentNews(Constants.TRUE);
newsList.buildList(db);
Iterator i = newsList.iterator();
while (i.hasNext()) {
BlogPost thisArticle = (BlogPost) i.next();
Project thisProject = ProjectUtils.loadProject(thisArticle.getProjectId());
entry = new SyndEntryImpl();
entry.setTitle(thisArticle.getSubject());
entry.setPublishedDate(thisArticle.getStartDate());
if (publishDate == null || thisArticle.getStartDate().after(publishDate)) {
publishDate = thisArticle.getStartDate();
}
entry.setAuthor(UserUtils.getUserName(thisArticle.getEnteredBy()));
description = new SyndContentImpl();
description.setType("text/html");
description.setValue(thisArticle.getIntro());
entry.setLink(url + "/show/" + thisProject.getUniqueId() + "/post/" + thisArticle.getId());
entry.setDescription(description);
entries.add(entry);
}
}
if (path.endsWith("/discussion.xml")) {
feed.setTitle(prefs.get("TITLE") + " - Discussion Rollup");
feed.setDescription(prefs.get("TITLE") + " personalized discussion rollup");
TopicList topicList = new TopicList();
topicList.setForUser(userId);
PagedListInfo pagedList = new PagedListInfo();
pagedList.setColumnToSortBy("i.last_reply_date");
pagedList.setSortOrder("desc");
pagedList.setItemsPerPage(10);
topicList.setPagedListInfo(pagedList);
topicList.buildList(db);
Iterator i = topicList.iterator();
while (i.hasNext()) {
Topic thisTopic = (Topic) i.next();
Project thisProject = ProjectUtils.loadProject(thisTopic.getProjectId());
entry = new SyndEntryImpl();
entry.setTitle(thisTopic.getSubject());
entry.setPublishedDate(thisTopic.getReplyDate());
if (publishDate == null || thisTopic.getReplyDate().after(publishDate)) {
publishDate = thisTopic.getReplyDate();
}
if (thisTopic.getReplyBy() > -1) {
entry.setAuthor(UserUtils.getUserName(thisTopic.getReplyBy()));
} else if (thisTopic.getReplyBy() == -1) {
entry.setAuthor(UserUtils.getUserName(thisTopic.getEnteredBy()));
}
description = new SyndContentImpl();
description.setType("text/html");
if (thisTopic.getReplyBy() == -1) {
// This is the first posting
description.setValue(StringUtils.toHtml(thisTopic.getBody()));
} else {
// This is a reply
// TODO: Load the reply
description.setValue(StringUtils.toHtml("A reply has been posted."));
}
entry.setLink(url + "/show/" + thisProject.getUniqueId() + "/topic/" + thisTopic.getId() + "?resetList=true&modified=" + thisTopic.getReplyDate().getTime());
entry.setDescription(description);
entries.add(entry);
}
}
if (path.endsWith("/documents.xml")) {
feed.setTitle(prefs.get("TITLE") + " - Documents Rollup");
feed.setDescription(prefs.get("TITLE") + " personalized document rollup");
FileItemList fileItemList = new FileItemList();
fileItemList.setLinkModuleId(Constants.PROJECTS_FILES);
fileItemList.setForProjectUser(userId);
PagedListInfo pagedList = new PagedListInfo();
pagedList.setColumnToSortBy("f.modified");
pagedList.setSortOrder("desc");
pagedList.setItemsPerPage(10);
fileItemList.setPagedListInfo(pagedList);
fileItemList.buildList(db);
Iterator i = fileItemList.iterator();
while (i.hasNext()) {
FileItem fileItem = (FileItem) i.next();
entry = new SyndEntryImpl();
entry.setTitle(fileItem.getSubject());
entry.setPublishedDate(fileItem.getModified());
if (publishDate == null || fileItem.getModified().after(publishDate)) {
publishDate = fileItem.getModified();
}
entry.setAuthor(UserUtils.getUserName(fileItem.getModifiedBy()));
description = new SyndContentImpl();
description.setType("text/html");
description.setValue(
"<table border=\"0\" cellpadding=\"2\" cellspacing=\"0\">\n" +
" <tr>\n" +
" <td valign=\"top\">\n" +
" " + fileItem.getImageTag("-23", url) + "\n" +
" </td>\n" +
" <td>\n" +
" " + StringUtils.toHtml(fileItem.getClientFilename()) + " " + fileItem.getVersion() + ", " + fileItem.getRelativeSize() + "k\n" +
" </td>\n" +
" </tr>\n" +
" </table>");
entry.setLink(url + "/ProjectManagementFiles.do?command=Details&pid=" + fileItem.getLinkItemId() + "&fid=" + fileItem.getId() + "&folderId=" + fileItem.getFolderId());
entry.setDescription(description);
entries.add(entry);
}
}
if (path.endsWith("/wiki.xml")) {
feed.setTitle(prefs.get("TITLE") + " - Wiki Rollup");
feed.setDescription(prefs.get("TITLE") + " personalized wiki entry rollup");
WikiList wikiList = new WikiList();
wikiList.setForUser(userId);
PagedListInfo pagedList = new PagedListInfo();
pagedList.setColumnToSortBy("w.modified");
pagedList.setSortOrder("desc");
pagedList.setItemsPerPage(10);
wikiList.setPagedListInfo(pagedList);
wikiList.buildList(db);
Iterator i = wikiList.iterator();
while (i.hasNext()) {
Wiki thisWiki = (Wiki) i.next();
entry = new SyndEntryImpl();
Project thisProject = ProjectUtils.loadProject(thisWiki.getProjectId());
if (thisWiki.getSubjectLink() == null || "".equals(thisWiki.getSubjectLink().trim())) {
entry.setTitle(thisProject.getTitle() + ": Home");
} else {
entry.setTitle(thisWiki.getSubject());
}
entry.setPublishedDate(thisWiki.getModified());
if (publishDate == null || thisWiki.getModified().after(publishDate)) {
publishDate = thisWiki.getModified();
}
entry.setAuthor(UserUtils.getUserName(thisWiki.getModifiedBy()));
description = new SyndContentImpl();
description.setType("text/html");
entry.setLink(url + "/show/" + thisProject.getUniqueId() + "/wiki" + (StringUtils.hasText(thisWiki.getSubjectLink()) ? "/" + thisWiki.getSubjectLink() : "") + "?modified=" + thisWiki.getModified().getTime());
entry.setDescription(description);
entries.add(entry);
}
}
// Prepare the feed classes before using the database connection...
//rss_0.90, rss_0.91, rss_0.92, rss_0.93, rss_0.94, rss_1.0 rss_2.0 or atom_0.3
if (publishDate == null) {
publishDate = new java.util.Date();
}
feed.setEntries(entries);
}
// Output the feed
SyndFeedOutput output = new SyndFeedOutput();
feed.setFeedType("rss_2.0");
feed.setPublishedDate(publishDate);
feed.setLink(url);
output.output(feed, response.getWriter());
} catch (Exception ex) {
String msg = COULD_NOT_GENERATE_FEED_ERROR;
log(msg, ex);
ex.printStackTrace(System.out);
response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, msg);
} finally {
freeConnection(db, getServletContext());
}
}
protected static ConnectionElement getConnectionElement(ServletContext context) {
ApplicationPrefs prefs = (ApplicationPrefs) context.getAttribute("applicationPrefs");
ConnectionElement ce = new ConnectionElement();
ce.setDriver(prefs.get("SITE.DRIVER"));
ce.setUrl(prefs.get("SITE.URL"));
ce.setUsername(prefs.get("SITE.USER"));
ce.setPassword(prefs.get("SITE.PASSWORD"));
return ce;
}
protected static Connection getConnection(ServletContext context) throws SQLException {
ConnectionElement ce = getConnectionElement(context);
ConnectionPool sqlDriver = (ConnectionPool) context.getAttribute("ConnectionPoolRSS");
return sqlDriver.getConnection(ce, false);
}
protected static void freeConnection(Connection db, ServletContext context) {
if (db != null) {
ConnectionPool sqlDriver = (ConnectionPool) context.getAttribute("ConnectionPoolRSS");
sqlDriver.free(db);
}
db = null;
}
}