/* * 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("&quot;", "\"").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; } }