package com.idega.block.article.business;
import java.io.IOException;
import java.rmi.RemoteException;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.servlet.ServletException;
import org.apache.webdav.lib.search.CompareOperator;
import org.apache.webdav.lib.search.SearchException;
import org.apache.webdav.lib.search.SearchExpression;
import org.apache.webdav.lib.search.SearchRequest;
import org.apache.webdav.lib.search.SearchScope;
import org.apache.webdav.lib.search.expressions.CompareExpression;
import com.idega.block.article.component.ArticleItemViewer;
import com.idega.block.article.component.ArticleListViewer;
import com.idega.block.rss.business.RSSAbstractProducer;
import com.idega.block.rss.business.RSSBusiness;
import com.idega.block.rss.business.RSSProducer;
import com.idega.block.rss.data.RSSRequest;
import com.idega.business.IBOLookup;
import com.idega.business.IBOLookupException;
import com.idega.content.business.ContentSearch;
import com.idega.content.business.ContentUtil;
import com.idega.core.builder.business.BuilderService;
import com.idega.core.builder.business.BuilderServiceFactory;
import com.idega.core.builder.data.ICPage;
import com.idega.core.search.business.Search;
import com.idega.core.search.business.SearchResult;
import com.idega.presentation.IWContext;
import com.idega.slide.business.IWContentEvent;
import com.idega.slide.business.IWSlideChangeListener;
import com.idega.slide.business.IWSlideService;
import com.idega.slide.business.IWSlideSession;
import com.idega.slide.util.IWSlideConstants;
import com.idega.util.CoreConstants;
import com.idega.util.IWTimestamp;
import com.idega.util.ListUtil;
import com.idega.util.StringUtil;
import com.sun.syndication.feed.synd.SyndEntry;
import com.sun.syndication.feed.synd.SyndFeed;
/**
* Generates 3 types of rss files for articles.
* 1. For all articles, /rss/article/
* 2. For all articles of a specific category, /rss/article/category/mycategory
* 3. For all articles on a specific page, /rss/article/page/mypageuri
* @author justinas
*
*/
public class ArticleRSSProducer extends RSSAbstractProducer implements RSSProducer, IWSlideChangeListener {
private static final Logger LOGGER = Logger.getLogger(ArticleRSSProducer.class.getName());
private static final String ARTICLE = ContentUtil.getContentBaseFolderPath() + "/article/";
private static final String ARTICLE_RSS = ARTICLE + "rss/";
protected static final String ARTICLE_SEARCH_KEY = "*.xml*";
public static final String RSS_FOLDER_NAME = "rss";
public static final String RSS_FILE_NAME = "articlefeed.xml";
public static final String PATH = CoreConstants.WEBDAV_SERVLET_URI + ARTICLE;
private List<String> rssFileURIsCacheList = new ArrayList<String>();
private int numberOfDaysDisplayed = 0;
public ArticleRSSProducer() {
super();
}
@Override
public void handleRSSRequest(RSSRequest rssRequest) throws IOException {
String feedParentFolder = null;
String feedFile = null;
String category = getCategory(rssRequest.getExtraUri());
String extraURI = rssRequest.getExtraUri();
if (extraURI == null) {
extraURI = CoreConstants.EMPTY;
}
if ((!extraURI.endsWith(CoreConstants.SLASH)) && (extraURI.length() != 0)) {
extraURI = extraURI.concat(CoreConstants.SLASH);
}
List<String> categories = new ArrayList<String>();
List<String> articles = new ArrayList<String>();
if (category != null)
categories.add(category);
IWContext iwc = getIWContext(rssRequest);
String language = iwc.getLocale().getLanguage();
if (StringUtil.isEmpty(extraURI)) {
feedParentFolder = ARTICLE_RSS;
feedFile = "all_".concat(language).concat(".xml");
} else if (category != null) {
feedParentFolder = ARTICLE_RSS.concat("category/").concat(category).concat(CoreConstants.SLASH);
feedFile = "feed_.".concat(language).concat(".xml");
} else {
// Have page URI
feedParentFolder = ARTICLE_RSS.concat("page/").concat(extraURI);
feedFile = "feed_".concat(language).concat(".xml");
categories = getCategoriesByURI(extraURI, iwc);
if (ListUtil.isEmpty(categories)) {
articles = getArticlesByURI(extraURI, iwc);
}
}
String realURI = CoreConstants.WEBDAV_SERVLET_URI+feedParentFolder+feedFile;
if (rssFileURIsCacheList.contains(feedFile)) {
try {
this.dispatch(realURI, rssRequest);
} catch (ServletException e) {
LOGGER.log(Level.WARNING, "Error dispatching: " + realURI, e);
}
} else {
// Generate RSS and store and the dispatch to it and add a listener to that directory
try {
//todo code the 3 different cases (see description)
searchForArticles(rssRequest, feedParentFolder, feedFile, categories, articles, extraURI);
rssFileURIsCacheList.add(feedFile);
this.dispatch(realURI, rssRequest);
} catch (Exception e) {
LOGGER.log(Level.WARNING, "Error while searching or dispatching: " + realURI, e);
throw new IOException(e.getMessage());
}
}
}
@SuppressWarnings("unchecked")
public void searchForArticles(RSSRequest rssRequest, String feedParentPath, String feedFileName, List<String> categories, List<String> articles,
String extraURI) {
IWContext iwc = getIWContext(rssRequest);
boolean getAllArticles = false;
if (StringUtil.isEmpty(extraURI)) {
getAllArticles = true;
}
String serverName = iwc.getServerURL();
serverName = serverName.substring(0, serverName.length()-1);
Collection<SearchResult> results = getArticleSearchResults(PATH, categories, iwc);
if (ListUtil.isEmpty(results)) {
LOGGER.warning("No results found in: " + PATH + " by the categories: " + categories);
return;
}
List<String> urisToArticles = new ArrayList<String>();
for (SearchResult result: results) {
urisToArticles.add(result.getSearchResultURI());
}
if (!ListUtil.isEmpty(articles)) {
if (ListUtil.isEmpty(categories)) {
urisToArticles = articles;
} else {
urisToArticles.addAll(articles);
}
}
if (!ListUtil.isEmpty(articles) && ListUtil.isEmpty(categories))
urisToArticles = articles;
RSSBusiness rss = null;
SyndFeed articleFeed = null;
long time = System.currentTimeMillis();
try {
rss = IBOLookup.getServiceInstance(iwc, RSSBusiness.class);
} catch (IBOLookupException e) {
LOGGER.log(Level.WARNING, "Error getting " + RSSBusiness.class, e);
}
String description = CoreConstants.EMPTY;
String title = CoreConstants.EMPTY;
if (ListUtil.isEmpty(results) && !getAllArticles) {
description = "No articles found. Empty feed";
} else {
description = "Article feed generated by IdegaWeb ePlatform, Idega Software, http://www.idega.com";
BuilderService bservice = null;
String pageKey = null;
try {
if (!StringUtil.isEmpty(extraURI)) {
bservice = BuilderServiceFactory.getBuilderService(iwc);
pageKey = bservice.getExistingPageKeyByURI(CoreConstants.SLASH + extraURI);
ICPage icpage = bservice.getICPage(pageKey);
title = icpage.getName();
}
} catch (Exception e) {
LOGGER.log(Level.WARNING, "Error getting page name from: " + extraURI, e);
}
String lang = iwc.getCurrentLocale().getLanguage();
SyndFeed allArticles = rss.createNewFeed(title, serverName , description, "atom_1.0", lang, new Timestamp(time));
List<SyndEntry> allEntries = new ArrayList<SyndEntry>();
for (int i = 0; i < urisToArticles.size(); i++) {
String articleURL = serverName.concat(urisToArticles.get(i)).concat(CoreConstants.SLASH).concat(lang).concat(".xml");
articleFeed = rss.getFeed(articleURL);
if (articleFeed != null)
allEntries.addAll(articleFeed.getEntries());
}
allArticles.setEntries(allEntries);
String allArticlesContent = null;
try {
allArticlesContent = rss.convertFeedToAtomXMLString(allArticles);
} catch (Exception e) {
LOGGER.log(Level.WARNING, "Error converting to Atom from: " + allArticles, e);
}
try {
IWSlideService service = this.getIWSlideService(rssRequest);
service.uploadFileAndCreateFoldersFromStringAsRoot(feedParentPath, feedFileName, allArticlesContent, RSSAbstractProducer.RSS_CONTENT_TYPE, true);
} catch (RemoteException e) {
LOGGER.log(Level.WARNING, "Error uploading to: " + feedParentPath + feedFileName + " file: " + allArticlesContent, e);
}
}
}
public void onSlideChange(IWContentEvent contentEvent) {
// On a file change this code checks if RSS file already exists and if so updates it (overwrites) with a new folder list
String uri = contentEvent.getContentEvent().getUri();
// Only do it for articles (whenever something changes in the articles folder)
if (!StringUtil.isEmpty(uri) && uri.indexOf("/cms/article/") >-1 ) {
// TODO don't remove cache on comments change, just check the URI for comments RSS.
getrssFileURIsCacheList().clear();
}
}
/**
* @param rssRequest
* @return
*/
protected String fixURI(RSSRequest rssRequest) {
String uri = CoreConstants.SLASH+rssRequest.getExtraUri();
if(!uri.endsWith(CoreConstants.SLASH)){
uri+=CoreConstants.SLASH;
}
if(!uri.startsWith(CoreConstants.PATH_FILES_ROOT)){
uri = CoreConstants.PATH_FILES_ROOT+uri;
}
return uri;
}
public Collection<SearchResult> getArticleSearchResults(String folder, List<String> categories, IWContext iwc) {
if (folder == null) {
return null;
}
if (iwc == null) {
iwc = IWContext.getInstance();
if (iwc == null) {
return null;
}
}
IWTimestamp oldest = null;
if (this.numberOfDaysDisplayed > 0) {
oldest = IWTimestamp.RightNow();
oldest.addDays(-this.numberOfDaysDisplayed);
}
IWSlideSession session = null;
try {
session = (IWSlideSession) IBOLookup.getSessionInstance(iwc,IWSlideSession.class);
} catch (IBOLookupException e) {
e.printStackTrace();
return null;
}
String webDavUri = null;
try {
webDavUri = session.getWebdavServerURI();
} catch (RemoteException e) {
e.printStackTrace();
}
if (webDavUri != null) {
if(folder.startsWith(webDavUri)){
folder = folder.substring(webDavUri.length());
}
if(folder.startsWith(CoreConstants.SLASH)){
folder = folder.substring(1);
}
}
SearchRequest articleSearch = null;
try {
articleSearch = getSearchRequest(folder, iwc.getCurrentLocale(), oldest, categories);
} catch (SearchException e) {
e.printStackTrace();
return null;
}
ContentSearch searchBusiness = new ContentSearch(iwc.getIWMainApplication());
searchBusiness.setToUseRootAccessForSearch(true);
searchBusiness.setToUseDescendingOrder(true);
Search search = searchBusiness.createSearch(articleSearch);
return search.getSearchResults();
}
public SearchRequest getSearchRequest(String scope, Locale locale, IWTimestamp oldest, List<String> categoryList) throws SearchException {
SearchRequest s = new SearchRequest();
s.addSelection(IWSlideConstants.PROPERTY_DISPLAY_NAME);
s.addSelection(IWSlideConstants.PROPERTY_CREATION_DATE);
s.addSelection(IWSlideConstants.PROPERTY_CATEGORY);
s.addScope(new SearchScope(scope));
SearchExpression expression = null;
String localeString = CoreConstants.EMPTY;
SearchExpression namePatternExpression = s.compare(CompareOperator.LIKE, IWSlideConstants.PROPERTY_DISPLAY_NAME,"%"+localeString+".article");
expression = namePatternExpression;
SearchExpression creationDateExpression = null;
if(oldest != null){
creationDateExpression = s.compare(CompareOperator.GTE, IWSlideConstants.PROPERTY_CREATION_DATE,oldest.getDate());
expression = s.and(expression,creationDateExpression);
}
List<CompareExpression> categoryExpressions = new ArrayList<CompareExpression>();
if (categoryList != null) {
for (Iterator<String> iter = categoryList.iterator(); iter.hasNext();) {
String categoryName = iter.next();
categoryExpressions.add(s.compare(CompareOperator.LIKE,IWSlideConstants.PROPERTY_CATEGORY,"%,"+categoryName+",%"));
}
Iterator<CompareExpression> expr = categoryExpressions.iterator();
if(expr.hasNext()){
SearchExpression categoryExpression = expr.next();
while (expr.hasNext()) {
categoryExpression = s.or(categoryExpression, expr.next());
}
expression = s.and(expression,categoryExpression);
}
}
s.setWhereExpression(expression);
return s;
}
/**
* @return Returns the rssFileURIsCacheList.
*/
protected List<String> getrssFileURIsCacheList() {
return rssFileURIsCacheList;
}
public String getCategory(String extraURI){
String category = null;
if(extraURI == null)
return null;
if(extraURI.length() == 0)
return null;
if(extraURI.startsWith("category/"))
category = extraURI.substring("category/".length(), extraURI.length());
else return null;
if(category.endsWith(CoreConstants.SLASH))
category = category.substring(0, category.length()-1);
return category;
}
public String getPageURI(String extraURI){
if(extraURI.endsWith(CoreConstants.SLASH))
return extraURI.substring(0, extraURI.length()-1);
else
return extraURI;
}
public List<String> getCategoriesByURI(String URI, IWContext iwc){
List<String> categories = new ArrayList<String>();
BuilderService bservice = null;
String property = null;
try {
bservice = BuilderServiceFactory.getBuilderService(iwc);
} catch (RemoteException e) {
e.printStackTrace();
}
String pageKey = bservice.getExistingPageKeyByURI(CoreConstants.SLASH+URI);
List<String> moduleId = bservice.getModuleId(pageKey, ArticleListViewer.class.getName());
if (moduleId != null){
for (int i = 0; i < moduleId.size(); i++) {
property = bservice.getProperty(pageKey, moduleId.get(i), "categories");
if (property != null) {
if (property.indexOf(",") != -1) {
Collection<String> strings = ListUtil.convertCommaSeparatedStringToList(property);
for (String string : strings) {
categories.add(string);
}
}
else {
categories.add(property);
}
}
else {
//Article list viewer without property - displaying all pages
categories = null;
}
}
}
return categories;
}
public List<String> getArticlesByURI(String URI, IWContext iwc){
List<String> articles = new ArrayList<String>();
BuilderService bservice = null;
try {
bservice = BuilderServiceFactory.getBuilderService(iwc);
} catch (RemoteException e) {
e.printStackTrace();
}
String pageKey = bservice.getExistingPageKeyByURI(CoreConstants.SLASH+URI);
List<String> moduleId = bservice.getModuleId(pageKey, ArticleItemViewer.class.getName());
if (moduleId != null){
for (int i = 0; i < moduleId.size(); i++) {
String articleURI = bservice.getProperty(pageKey, moduleId.get(i), "resourcePath");
articleURI = articleURI.substring(0, articleURI.length());
articles.add(CoreConstants.WEBDAV_SERVLET_URI+ articleURI);
}
}
return articles;
}
}