/* * Copyright 2015-Present Entando Inc. (http://www.entando.com) All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ package org.entando.entando.plugins.jprss.aps.system.services.rss; import com.agiletec.aps.system.RequestContext; import com.agiletec.aps.system.common.AbstractService; import com.agiletec.aps.system.common.entity.event.EntityTypesChangingEvent; import com.agiletec.aps.system.common.entity.event.EntityTypesChangingObserver; import com.agiletec.aps.system.common.entity.model.EntitySearchFilter; import com.agiletec.aps.system.common.entity.model.IApsEntity; import com.agiletec.aps.system.common.entity.model.attribute.AttributeInterface; import com.agiletec.aps.system.common.entity.model.attribute.ITextAttribute; import com.agiletec.aps.system.exception.ApsSystemException; import com.agiletec.aps.system.services.baseconfig.ConfigInterface; import com.agiletec.aps.system.services.group.Group; import com.agiletec.aps.system.services.keygenerator.IKeyGeneratorManager; import com.agiletec.aps.system.services.page.IPageManager; import com.agiletec.plugins.jacms.aps.system.JacmsSystemConstants; import com.agiletec.plugins.jacms.aps.system.services.content.IContentManager; import com.agiletec.plugins.jacms.aps.system.services.content.model.Content; import com.agiletec.plugins.jacms.aps.system.services.content.model.ContentRecordVO; import com.agiletec.plugins.jacms.aps.system.services.content.model.SmallContentType; import com.agiletec.plugins.jacms.aps.system.services.content.model.SymbolicLink; import com.agiletec.plugins.jacms.aps.system.services.content.widget.util.FilterUtils; import com.agiletec.plugins.jacms.aps.system.services.linkresolver.ILinkResolverManager; import org.entando.entando.plugins.jprss.aps.system.services.JpRssSystemConstants; import com.rometools.rome.feed.synd.SyndContent; import com.rometools.rome.feed.synd.SyndContentImpl; import com.rometools.rome.feed.synd.SyndEntry; import com.rometools.rome.feed.synd.SyndEntryImpl; import com.rometools.rome.feed.synd.SyndFeed; import com.rometools.rome.feed.synd.SyndFeedImpl; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Manager that handles the RSS Channels * @author S.Puddu - E.Santoboni */ public class RssManager extends AbstractService implements IRssManager, EntityTypesChangingObserver { private static final Logger _logger = LoggerFactory.getLogger(RssManager.class); @Override public void init() throws Exception { try { this.loadMappingConfig(); _logger.debug("{} ready", this.getClass().getName()); } catch (Throwable t) { _logger.error("{} Manager: Error on initialization", this.getClass().getName(), t); } } @Override public void updateFromEntityTypesChanging(EntityTypesChangingEvent event) { if (!event.getEntityManagerName().equals(JacmsSystemConstants.CONTENT_MANAGER)) { return; } try { this.loadMappingConfig(); } catch (Throwable t) { _logger.error("error loading Rss Content Config", t); } } private void loadMappingConfig() throws ApsSystemException { Map<String, RssContentMapping> mappings = new HashMap<String, RssContentMapping>(); try { Map<String, IApsEntity> contentTypes = this.getContentManager().getEntityPrototypes(); Iterator<IApsEntity> contentTypeIter = contentTypes.values().iterator(); while (contentTypeIter.hasNext()) { IApsEntity contentType = contentTypeIter.next(); AttributeInterface attributeTitle = contentType.getAttributeByRole(JpRssSystemConstants.ATTRIBUTE_ROLE_RSS_CONTENT_TITLE); if (null != attributeTitle) { RssContentMapping mapping = new RssContentMapping(); mapping.setContentType(contentType.getTypeCode()); mapping.setTitleAttributeName(attributeTitle.getName()); AttributeInterface attributeDescr = contentType.getAttributeByRole(JpRssSystemConstants.ATTRIBUTE_ROLE_RSS_CONTENT_DESCRIPTION); if (null != attributeDescr) { mapping.setDescriptionAttributeName(attributeDescr.getName()); } mappings.put(contentType.getTypeCode(), mapping); } } } catch (Throwable t) { _logger.error("Error loading rss content mapping", t); throw new ApsSystemException("Error loading rss content mapping", t); } this.setContentMapping(mappings); } @Override public void addChannel(Channel channel) throws ApsSystemException { try { int key = getKeyGeneratorManager().getUniqueKeyCurrentValue(); channel.setId(key); this.getRssDAO().addChannel(channel); } catch (Throwable t) { _logger.error("Error adding a new channel", t); throw new ApsSystemException("Error adding a new channel", t); } } @Override public void deleteChannel(int id) throws ApsSystemException { try { this.getRssDAO().deleteChannel(id); } catch (Throwable t) { _logger.error("Error deleting the channel with code ", id, t); throw new ApsSystemException("Error deleting the channel with code: " + id, t); } } @Override public void updateChannel(Channel channel) throws ApsSystemException { try { this.getRssDAO().updateChannel(channel); } catch (Throwable t) { _logger.error("Error updating a channel", t); throw new ApsSystemException("Error updating a channel", t); } } @Override public List<Channel> getChannels(int status) throws ApsSystemException { List<Channel> channels = null; try { channels = this.getRssDAO().getChannels(status); } catch (Throwable t) { _logger.error("Error getting the list of the channels by status {}", status, t); throw new ApsSystemException("Error getting the list of the channels", t); } return channels; } @Override public Channel getChannel(int id) throws ApsSystemException { Channel channel = null; try { channel = this.getRssDAO().getChannel(id); } catch (Throwable t) { _logger.error("Error loading channel with id {}", id, t); throw new ApsSystemException("Error loading channel with id" + id, t); } return channel; } private EntitySearchFilter[] getEntitySearchFilter(Channel channel, String langCode) { String contentTypeCode = channel.getContentType(); String widgetParam = channel.getFilters(); EntitySearchFilter[] entitySearchFilters = null; if (null != widgetParam && widgetParam.trim().length() > 0) { IApsEntity contentType = this.getContentManager().getEntityPrototype(contentTypeCode); FilterUtils filterUtils = new FilterUtils(); entitySearchFilters = filterUtils.getFilters(contentType, widgetParam, langCode); } else { entitySearchFilters = new EntitySearchFilter[0]; } return entitySearchFilters; } @Override public SyndFeed getSyndFeed(Channel channel, String lang, String feedLink, HttpServletRequest req, HttpServletResponse resp) throws ApsSystemException { SyndFeed feed = null; if (null == feed) { feed = new SyndFeedImpl(); feed.setFeedType(channel.getFeedType()); feed.setTitle(channel.getTitle()); feed.setLink(feedLink); feed.setDescription(channel.getDescription()); List<String> contentsId = this.getContentsId(channel, lang); feed.setEntries(this.getEntries(contentsId, lang, feedLink, req, resp)); } return feed; } private List<SyndEntry> getEntries(List<String> contentsId, String lang, String feedLink, HttpServletRequest req, HttpServletResponse resp) throws ApsSystemException { List<SyndEntry> entries = new ArrayList<SyndEntry>(); Iterator<String> idIterator = contentsId.iterator(); while (idIterator.hasNext()) { String id = (String) idIterator.next(); ContentRecordVO currentContent = this.getContentManager().loadContentVO(id); RssContentMapping mapping = (RssContentMapping) this.getContentMapping().get(currentContent.getTypeCode()); if (null == mapping) { _logger.error("Null content mapping by existed channel for content type {}", currentContent.getTypeCode()); continue; } entries.add(this.createEntry(currentContent, lang, feedLink, req, resp)); } return entries; } private SyndEntry createEntry(ContentRecordVO contentVO, String langCode, String feedLink, HttpServletRequest req, HttpServletResponse resp) throws ApsSystemException { SyndEntry entry = new SyndEntryImpl(); RssContentMapping mapping = this.getContentMapping().get(contentVO.getTypeCode()); try { Content content = (Content) this.getContentManager().loadContent(contentVO.getId(), true); ITextAttribute titleAttr = (ITextAttribute) content.getAttribute(mapping.getTitleAttributeName()); String title = (titleAttr.getTextForLang(langCode)); if (null == title || title.trim().length() == 0) { title = titleAttr.getText(); } entry.setTitle(title); String link = this.createLink(content, feedLink); entry.setLink(link); entry.setPublishedDate(contentVO.getModify()); ITextAttribute descrAttr = (ITextAttribute) content.getAttribute(mapping.getDescriptionAttributeName()); if (null != descrAttr) { SyndContent description = new SyndContentImpl(); description.setType(JpRssSystemConstants.SYNDCONTENT_TYPE_TEXTHTML); String inLang = descrAttr.getTextForLang(langCode); //TODO Ottimizzare! RequestContext requestContext = new RequestContext(); requestContext.setRequest(req); requestContext.setResponse(resp); if (null != inLang && inLang.length() > 0) { String textValue = this.getLinkResolver().resolveLinks(inLang, requestContext); if (null != textValue && textValue.trim().length() > 0) { description.setValue(textValue); } else { description.setValue(descrAttr.getText()); } } else { String textValue = this.getLinkResolver().resolveLinks(descrAttr.getText(), requestContext); description.setValue(textValue); } entry.setDescription(description); } } catch (Throwable t) { _logger.error("Error in createEntry", t); throw new ApsSystemException("Error in createEntry", t); } return entry; } private String createLink(Content content, String feedLink) { SymbolicLink symbolicLink = new SymbolicLink(); StringBuilder destination = new StringBuilder(feedLink); String viewPageCode = content.getViewPage(); if (null == viewPageCode || null == this.getPageManager().getPage(viewPageCode)) { viewPageCode = this.getPageManager().getRoot().getCode(); } destination.append(viewPageCode).append(".page").append("?contentId=").append(content.getId()); symbolicLink.setDestinationToUrl(destination.toString()); return symbolicLink.getUrlDest(); } @Override public Map<String, String> getAvailableContentTypes() { Map<String, String> availableContentTypes = new HashMap<String, String>(); Iterator it = this.getContentMapping().entrySet().iterator(); Map<String, SmallContentType> contentTypes = this.getContentManager().getSmallContentTypesMap(); while (it.hasNext()) { Map.Entry pairs = (Map.Entry) it.next(); String typeCode = (String) pairs.getKey(); SmallContentType smallContentType = (SmallContentType) contentTypes.get(typeCode); if (null != smallContentType) { availableContentTypes.put(typeCode, smallContentType.getDescr()); } } return availableContentTypes; } @Override public RssContentMapping getContentMapping(String typeCode) { RssContentMapping mapping = this.getContentMapping().get(typeCode); if (null != mapping) { return mapping.clone(); } return null; } protected EntitySearchFilter[] addFilter(EntitySearchFilter[] filters, EntitySearchFilter filterToAdd) { int len = filters.length; EntitySearchFilter[] newFilters = new EntitySearchFilter[len + 1]; for (int i = 0; i < len; i++) { newFilters[i] = filters[i]; } newFilters[len] = filterToAdd; return newFilters; } private List<String> getContentsId(Channel channel, String langCode) throws ApsSystemException { RssContentMapping mapping = (RssContentMapping) this.getContentMapping().get(channel.getContentType()); if (null == mapping) { _logger.error("Null content mapping by existed channel for content type {}", channel.getContentType()); return new ArrayList<String>(); } try { EntitySearchFilter[] searchFilters = this.getEntitySearchFilter(channel, langCode);//this.getEntitySearchFilterDOM().getFilters(channel.getContentType(), channel.getFilters()); EntitySearchFilter filterToAdd = new EntitySearchFilter(IContentManager.ENTITY_TYPE_CODE_FILTER_KEY, false, channel.getContentType(), false); EntitySearchFilter[] entitySearchFilters = addFilter(searchFilters, filterToAdd); String[] categories = null; if (null != channel.getCategory() && channel.getCategory().trim().length() > 0) { categories = new String[]{channel.getCategory()}; } Collection<String> userGroupCodes = new ArrayList<String>(); userGroupCodes.add(Group.FREE_GROUP_NAME); List<String> contentsId = this.getContentManager().loadPublicContentsId(channel.getContentType(), categories, entitySearchFilters, userGroupCodes); if (channel.getMaxContentsSize() > 0 && contentsId.size() > channel.getMaxContentsSize()) { return contentsId.subList(0, channel.getMaxContentsSize()); } else { return contentsId; } } catch (Throwable t) { _logger.error("Error in rss contents", t); throw new ApsSystemException("Error in rss contents", t); } } @Override public Map<String, String> getAvailableFeedTypes() { return _availableFeedTypes; } public void setAvailableFeedTypes(Map<String, String> availableFeedTypes) { this._availableFeedTypes = availableFeedTypes; } protected Map<String, RssContentMapping> getContentMapping() { return _contentMapping; } protected void setContentMapping(Map<String, RssContentMapping> contentMapping) { this._contentMapping = contentMapping; } protected ConfigInterface getConfigManager() { return _configManager; } public void setConfigManager(ConfigInterface configManager) { this._configManager = configManager; } protected ILinkResolverManager getLinkResolver() { return _linkResolver; } public void setLinkResolver(ILinkResolverManager linkResolver) { this._linkResolver = linkResolver; } protected IContentManager getContentManager() { return _contentManager; } public void setContentManager(IContentManager contentManager) { this._contentManager = contentManager; } protected IPageManager getPageManager() { return _pageManager; } public void setPageManager(IPageManager pageManager) { this._pageManager = pageManager; } protected IRssDAO getRssDAO() { return _rssDAO; } public void setRssDAO(IRssDAO rssDAO) { this._rssDAO = rssDAO; } protected IKeyGeneratorManager getKeyGeneratorManager() { return _keyGeneratorManager; } public void setKeyGeneratorManager(IKeyGeneratorManager keyGeneratorManager) { this._keyGeneratorManager = keyGeneratorManager; } private Map<String, RssContentMapping> _contentMapping; private IContentManager _contentManager; private IPageManager _pageManager; private ConfigInterface _configManager; private IKeyGeneratorManager _keyGeneratorManager; private Map<String, String> _availableFeedTypes; private ILinkResolverManager _linkResolver; private IRssDAO _rssDAO; }