/*
* 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.wcm.connector.collaboration;
import java.io.ByteArrayInputStream;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import javax.jcr.Node;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.dom.DOMSource;
import org.exoplatform.services.jcr.util.Text;
import org.exoplatform.services.log.ExoLogger;
import org.exoplatform.services.log.Log;
import org.exoplatform.services.rest.resource.ResourceContainer;
import org.exoplatform.services.wcm.core.WCMConfigurationService;
import org.exoplatform.services.wcm.publication.WCMComposer;
import org.exoplatform.services.wcm.utils.WCMCoreUtils;
import org.exoplatform.wcm.connector.BaseConnector;
import org.w3c.dom.Document;
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.SyndEntryImpl;
import com.sun.syndication.feed.synd.SyndFeed;
import com.sun.syndication.feed.synd.SyndFeedImpl;
import com.sun.syndication.io.SyndFeedOutput;
/**
* Generates an RSS feed.
*
* @LevelAPI Provisional
* @anchor RssConnector
*/
@Path("/feed/")
public class RssConnector extends BaseConnector implements ResourceContainer {
/** The WORKSPACE. */
static private String WORKSPACE = "workspace" ;
/** The REPOSITORY. */
static private String REPOSITORY = "repository" ;
/** The RS s_ version. */
static private String RSS_VERSION = "rss_2.0" ;
/** The FEE d_ title. */
static private String FEED_TITLE = "exo:feedTitle" ;
/** The DESCRIPTION. */
static private String DESCRIPTION = "exo:description" ;
/** The TITLE. */
static private String TITLE = "exo:title";
/** The LINK. */
static private String LINK = "exo:link" ;
/** The SUMMARY. */
static private String SUMMARY = "exo:summary";
/** The detail page */
static private String DETAIL_PAGE = "detail-page";
/** The detail get param */
static private String DETAIL_PARAM = "detail-param";
/** The wcm composer service. */
private WCMComposer wcmComposer;
/** The wcm configuration service. */
private WCMConfigurationService wcmConfigurationService;
/** The log. */
private static final Log LOG = ExoLogger.getLogger(RssConnector.class.getName());
/**
* Instantiates a new RSS connector.
*/
public RssConnector() {
this.wcmConfigurationService = WCMCoreUtils.getService(WCMConfigurationService.class);
this.wcmComposer = WCMCoreUtils.getService(WCMComposer.class);
}
/**
* Generates an RSS feed.
*
* @param repository The repository name.
* @param workspace The workspace name.
* @param server The server.
* @param siteName The name of site.
* @param title The title of the feed.
* @param desc The description of the feed.
* @param folderPath The folder path of the feed.
* @param orderBy The criteria to order the content.
* @param orderType The descending or ascending order.
* @param lang The language of the feed.
* @param detailPage The page used to open the content.
* @param detailParam The parameters are the key in the URL to let CLV know which really is the path in the current URL.
* @param recursive This param is deprecated and will be moved soon.
* @return The response.
* @throws Exception The exception
*
* @anchor RssConnector.generate
*/
@GET
@Path("/rss/")
public Response generate(@QueryParam("repository") String repository,
@QueryParam("workspace") String workspace,
@QueryParam("server") String server,
@QueryParam("siteName") String siteName,
@QueryParam("title") String title,
@QueryParam("desc") String desc,
@QueryParam("folderPath") String folderPath,
@QueryParam("orderBy") String orderBy,
@QueryParam("orderType") String orderType,
@QueryParam("lang") String lang,
@QueryParam("detailPage") String detailPage,
@QueryParam("detailParam") String detailParam,
@QueryParam("recursive") String recursive) throws Exception {
Map<String, String> contextRss = new HashMap<String, String>();
contextRss.put(REPOSITORY, repository);
contextRss.put(WORKSPACE, workspace);
contextRss.put(RSS_VERSION, "rss_2.0");
contextRss.put(FEED_TITLE, title);
if (desc == null)
desc = "Powered by eXo WCM-" + WCMCoreUtils.getProjectVersion();
contextRss.put(DESCRIPTION, desc);
contextRss.put(LINK, server);
if (detailPage == null)
detailPage = wcmConfigurationService.getRuntimeContextParam(WCMConfigurationService.PARAMETERIZED_PAGE_URI);
contextRss.put(DETAIL_PAGE, detailPage);
if (detailParam == null) detailParam = "content-id";
contextRss.put(DETAIL_PARAM, detailParam);
HashMap<String, String> filters = new HashMap<String, String>();
filters.put(WCMComposer.FILTER_MODE, WCMComposer.MODE_LIVE);
if (orderType == null) orderType = "DESC";
if (orderBy == null) orderBy = "publication:liveDate";
if (lang == null) lang = "en";
filters.put(WCMComposer.FILTER_ORDER_BY, orderBy);
filters.put(WCMComposer.FILTER_ORDER_TYPE, orderType);
filters.put(WCMComposer.FILTER_LANGUAGE, lang);
filters.put(WCMComposer.FILTER_RECURSIVE, recursive);
String path=null;
if (folderPath!=null) {
path = folderPath;
}
List<Node> nodes = wcmComposer.getContents(workspace,
path,
filters,
WCMCoreUtils.getUserSessionProvider());
String feedXML = generateRSS(nodes, contextRss);
Document document = DocumentBuilderFactory.newInstance()
.newDocumentBuilder()
.parse(new ByteArrayInputStream(feedXML.getBytes()));
DateFormat dateFormat = new SimpleDateFormat(IF_MODIFIED_SINCE_DATE_FORMAT);
Response response = Response.ok(new DOMSource(document), MediaType.TEXT_XML)
.header(LAST_MODIFIED_PROPERTY, dateFormat.format(new Date()))
.build();
return response;
}
/**
* Generates RSS.
*
* @param context The context.
* @return the string
*/
private String generateRSS(List<Node> nodes , Map<String, String> context) {
String rssVersion = context.get(RSS_VERSION) ;
String feedTitle = context.get(FEED_TITLE) ;
String feedDescription = context.get(DESCRIPTION) ;
String feedLink = context.get(LINK) ;
String detailPage = context.get(DETAIL_PAGE) ;
String detailParam = context.get(DETAIL_PARAM) ;
String repository = context.get(REPOSITORY) ;
String workspace = context.get(WORKSPACE) ;
String contentUrl;
if (!feedLink.endsWith("/")) {
contentUrl = feedLink + "/" + detailPage + "?" + detailParam + "=/" + repository + "/"
+ workspace;
} else {
contentUrl = feedLink + detailPage + "?" + detailParam + "=/" + repository + "/" + workspace;
}
if(feedTitle == null || feedTitle.length() == 0) feedTitle = "" ;
try {
SyndFeed feed = new SyndFeedImpl();
feed.setFeedType(rssVersion);
feed.setTitle(feedTitle.replaceAll(" ", " "));
feed.setLink(feedLink);
feed.setDescription(feedDescription.replaceAll(" ", " "));
feed.setEncoding("UTF-8");
List<SyndEntry> entries = new ArrayList<SyndEntry>();
Iterator<Node> iter = nodes.iterator();
while (iter.hasNext()) {
Node node = iter.next();
StringBuilder url= new StringBuilder();
url.append(contentUrl);
if (node.isNodeType("nt:frozenNode")) {
String uuid = node.getProperty("jcr:frozenUuid").getString();
Node originalNode = node.getSession().getNodeByUUID(uuid);
url.append(originalNode.getPath());
url.append("&version=");
url.append(node.getParent().getName());
} else {
url.append(node.getPath());
}
SyndEntry entry = new SyndEntryImpl();
if (node.hasProperty(TITLE)) {
String nTitle = node.getProperty(TITLE).getString();
//encoding
nTitle = Text.unescapeIllegalJcrChars(new String(nTitle.getBytes("UTF-8")));
entry.setTitle(Text.encodeIllegalXMLCharacters(nTitle).replaceAll(""", "\"").replaceAll("'", "\'"));
}
else entry.setTitle("") ;
entry.setLink(url.toString());
SyndContent description = new SyndContentImpl();
description.setType("text/plain");
if (node.hasProperty(SUMMARY)){
String summary=Text.encodeIllegalXMLCharacters(node.getProperty(SUMMARY)
.getString())
.replaceAll(" ", " ").replaceAll(""", "\"").replaceAll("'", "\'");
description.setValue(summary);
}else
description.setValue("");
entry.setDescription(description);
entries.add(entry);
entry.getEnclosures() ;
}
feed.setEntries(entries);
SyndFeedOutput output = new SyndFeedOutput();
return output.outputString(feed);
} catch (Exception e) {
if (LOG.isErrorEnabled()) {
LOG.error("Error when perform generateRSS: ", e);
}
}
return null;
}
/* (non-Javadoc)
* @see org.exoplatform.wcm.connector.BaseConnector#getContentStorageType()
*/
protected String getContentStorageType() throws Exception {
return null;
}
/* (non-Javadoc)
* @see org.exoplatform.wcm.connector.BaseConnector#getRootContentStorage(javax.jcr.Node)
*/
protected Node getRootContentStorage(Node node) throws Exception {
return null;
}
}