001    /**
002     * Copyright (c) 2000-2013 Liferay, Inc. All rights reserved.
003     *
004     * This library is free software; you can redistribute it and/or modify it under
005     * the terms of the GNU Lesser General Public License as published by the Free
006     * Software Foundation; either version 2.1 of the License, or (at your option)
007     * any later version.
008     *
009     * This library is distributed in the hope that it will be useful, but WITHOUT
010     * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
011     * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
012     * details.
013     */
014    
015    package com.liferay.portlet.journal.action;
016    
017    import com.liferay.portal.NoSuchLayoutException;
018    import com.liferay.portal.kernel.exception.SystemException;
019    import com.liferay.portal.kernel.language.LanguageUtil;
020    import com.liferay.portal.kernel.log.Log;
021    import com.liferay.portal.kernel.log.LogFactoryUtil;
022    import com.liferay.portal.kernel.util.ContentTypes;
023    import com.liferay.portal.kernel.util.HtmlUtil;
024    import com.liferay.portal.kernel.util.ParamUtil;
025    import com.liferay.portal.kernel.util.StringPool;
026    import com.liferay.portal.kernel.util.StringUtil;
027    import com.liferay.portal.kernel.util.Validator;
028    import com.liferay.portal.kernel.xml.Document;
029    import com.liferay.portal.kernel.xml.Element;
030    import com.liferay.portal.kernel.xml.Node;
031    import com.liferay.portal.kernel.xml.SAXReaderUtil;
032    import com.liferay.portal.kernel.xml.XPath;
033    import com.liferay.portal.model.Layout;
034    import com.liferay.portal.service.LayoutLocalServiceUtil;
035    import com.liferay.portal.struts.PortletAction;
036    import com.liferay.portal.theme.ThemeDisplay;
037    import com.liferay.portal.util.PortalUtil;
038    import com.liferay.portal.util.PortletKeys;
039    import com.liferay.portal.util.WebKeys;
040    import com.liferay.portlet.PortletURLImpl;
041    import com.liferay.portlet.journal.model.JournalArticle;
042    import com.liferay.portlet.journal.model.JournalArticleDisplay;
043    import com.liferay.portlet.journal.model.JournalFeed;
044    import com.liferay.portlet.journal.model.JournalFeedConstants;
045    import com.liferay.portlet.journal.service.JournalContentSearchLocalServiceUtil;
046    import com.liferay.portlet.journal.service.JournalFeedLocalServiceUtil;
047    import com.liferay.portlet.journal.util.JournalRSSUtil;
048    import com.liferay.portlet.journalcontent.util.JournalContentUtil;
049    import com.liferay.util.RSSUtil;
050    
051    import com.sun.syndication.feed.synd.SyndContent;
052    import com.sun.syndication.feed.synd.SyndContentImpl;
053    import com.sun.syndication.feed.synd.SyndEnclosure;
054    import com.sun.syndication.feed.synd.SyndEntry;
055    import com.sun.syndication.feed.synd.SyndEntryImpl;
056    import com.sun.syndication.feed.synd.SyndFeed;
057    import com.sun.syndication.feed.synd.SyndFeedImpl;
058    import com.sun.syndication.feed.synd.SyndLink;
059    import com.sun.syndication.feed.synd.SyndLinkImpl;
060    import com.sun.syndication.io.FeedException;
061    
062    import java.io.OutputStream;
063    
064    import java.util.ArrayList;
065    import java.util.Date;
066    import java.util.List;
067    
068    import javax.portlet.PortletConfig;
069    import javax.portlet.PortletRequest;
070    import javax.portlet.PortletURL;
071    import javax.portlet.ResourceRequest;
072    import javax.portlet.ResourceResponse;
073    import javax.portlet.ResourceURL;
074    
075    import org.apache.struts.action.ActionForm;
076    import org.apache.struts.action.ActionMapping;
077    
078    /**
079     * @author Raymond Aug??
080     */
081    public class RSSAction extends PortletAction {
082    
083            @Override
084            public void serveResource(
085                            ActionMapping actionMapping, ActionForm actionForm,
086                            PortletConfig portletConfig, ResourceRequest resourceRequest,
087                            ResourceResponse resourceResponse)
088                    throws Exception {
089    
090                    resourceResponse.setContentType(ContentTypes.TEXT_XML_UTF8);
091    
092                    OutputStream outputStream = resourceResponse.getPortletOutputStream();
093    
094                    try {
095                            byte[] bytes = getRSS(resourceRequest, resourceResponse);
096    
097                            outputStream.write(bytes);
098                    }
099                    finally {
100                            outputStream.close();
101                    }
102            }
103    
104            protected String exportToRSS(
105                            ResourceRequest resourceRequest, ResourceResponse resourceResponse,
106                            JournalFeed feed, String languageId, Layout layout,
107                            ThemeDisplay themeDisplay)
108                    throws Exception {
109    
110                    SyndFeed syndFeed = new SyndFeedImpl();
111    
112                    syndFeed.setDescription(feed.getDescription());
113    
114                    List<SyndEntry> syndEntries = new ArrayList<SyndEntry>();
115    
116                    syndFeed.setEntries(syndEntries);
117    
118                    List<JournalArticle> articles = JournalRSSUtil.getArticles(feed);
119    
120                    if (_log.isDebugEnabled()) {
121                            _log.debug("Syndicating " + articles.size() + " articles");
122                    }
123    
124                    for (JournalArticle article : articles) {
125                            SyndEntry syndEntry = new SyndEntryImpl();
126    
127                            String author = PortalUtil.getUserName(article);
128    
129                            syndEntry.setAuthor(author);
130    
131                            SyndContent syndContent = new SyndContentImpl();
132    
133                            syndContent.setType(RSSUtil.ENTRY_TYPE_DEFAULT);
134    
135                            String value = article.getDescription(languageId);
136    
137                            try {
138                                    value = processContent(
139                                            feed, article, languageId, themeDisplay, syndEntry,
140                                            syndContent);
141                            }
142                            catch (Exception e) {
143                                    if (_log.isWarnEnabled()) {
144                                            _log.warn(e, e);
145                                    }
146                            }
147    
148                            syndContent.setValue(value);
149    
150                            syndEntry.setDescription(syndContent);
151    
152                            String link = getEntryURL(
153                                    resourceRequest, feed, article, layout, themeDisplay);
154    
155                            syndEntry.setLink(link);
156    
157                            syndEntry.setPublishedDate(article.getDisplayDate());
158                            syndEntry.setTitle(article.getTitle(languageId));
159                            syndEntry.setUpdatedDate(article.getModifiedDate());
160                            syndEntry.setUri(link);
161    
162                            syndEntries.add(syndEntry);
163                    }
164    
165                    syndFeed.setFeedType(feed.getFeedType() + "_" + feed.getFeedVersion());
166    
167                    List<SyndLink> syndLinks = new ArrayList<SyndLink>();
168    
169                    syndFeed.setLinks(syndLinks);
170    
171                    SyndLink selfSyndLink = new SyndLinkImpl();
172    
173                    syndLinks.add(selfSyndLink);
174    
175                    ResourceURL feedURL = resourceResponse.createResourceURL();
176    
177                    feedURL.setCacheability(ResourceURL.FULL);
178                    feedURL.setParameter("struts_action", "/journal/rss");
179                    feedURL.setParameter("groupId", String.valueOf(feed.getGroupId()));
180                    feedURL.setParameter("feedId", String.valueOf(feed.getFeedId()));
181    
182                    String link = feedURL.toString();
183    
184                    selfSyndLink.setHref(link);
185    
186                    selfSyndLink.setRel("self");
187    
188                    syndFeed.setTitle(feed.getName());
189                    syndFeed.setPublishedDate(new Date());
190                    syndFeed.setUri(feedURL.toString());
191    
192                    try {
193                            return RSSUtil.export(syndFeed);
194                    }
195                    catch (FeedException fe) {
196                            throw new SystemException(fe);
197                    }
198            }
199    
200            protected String getEntryURL(
201                            ResourceRequest resourceRequest, JournalFeed feed,
202                            JournalArticle article, Layout layout, ThemeDisplay themeDisplay)
203                    throws Exception {
204    
205                    List<Long> hitLayoutIds =
206                            JournalContentSearchLocalServiceUtil.getLayoutIds(
207                                    layout.getGroupId(), layout.isPrivateLayout(),
208                                    article.getArticleId());
209    
210                    if (hitLayoutIds.size() > 0) {
211                            Long hitLayoutId = hitLayoutIds.get(0);
212    
213                            Layout hitLayout = LayoutLocalServiceUtil.getLayout(
214                                    layout.getGroupId(), layout.isPrivateLayout(),
215                                    hitLayoutId.longValue());
216    
217                            return PortalUtil.getLayoutFriendlyURL(hitLayout, themeDisplay);
218                    }
219                    else {
220                            long plid = PortalUtil.getPlidFromFriendlyURL(
221                                    feed.getCompanyId(), feed.getTargetLayoutFriendlyUrl());
222    
223                            String portletId = PortletKeys.JOURNAL_CONTENT;
224    
225                            if (Validator.isNotNull(feed.getTargetPortletId())) {
226                                    portletId = feed.getTargetPortletId();
227                            }
228    
229                            PortletURL entryURL = new PortletURLImpl(
230                                    resourceRequest, portletId, plid, PortletRequest.RENDER_PHASE);
231    
232                            entryURL.setParameter("struts_action", "/journal_content/view");
233                            entryURL.setParameter(
234                                    "groupId", String.valueOf(article.getGroupId()));
235                            entryURL.setParameter("articleId", article.getArticleId());
236    
237                            return entryURL.toString();
238                    }
239            }
240    
241            protected byte[] getRSS(
242                            ResourceRequest resourceRequest, ResourceResponse resourceResponse)
243                    throws Exception {
244    
245                    ThemeDisplay themeDisplay = (ThemeDisplay)resourceRequest.getAttribute(
246                            WebKeys.THEME_DISPLAY);
247    
248                    JournalFeed feed = null;
249    
250                    long id = ParamUtil.getLong(resourceRequest, "id");
251    
252                    long groupId = ParamUtil.getLong(resourceRequest, "groupId");
253                    String feedId = ParamUtil.getString(resourceRequest, "feedId");
254    
255                    if (id > 0) {
256                            feed = JournalFeedLocalServiceUtil.getFeed(id);
257                    }
258                    else {
259                            feed = JournalFeedLocalServiceUtil.getFeed(groupId, feedId);
260                    }
261    
262                    String languageId = LanguageUtil.getLanguageId(resourceRequest);
263    
264                    long plid = PortalUtil.getPlidFromFriendlyURL(
265                            themeDisplay.getCompanyId(), feed.getTargetLayoutFriendlyUrl());
266    
267                    Layout layout = themeDisplay.getLayout();
268    
269                    if (plid > 0) {
270                            try {
271                                    layout = LayoutLocalServiceUtil.getLayout(plid);
272                            }
273                            catch (NoSuchLayoutException nsle) {
274                            }
275                    }
276    
277                    String rss = exportToRSS(
278                            resourceRequest, resourceResponse, feed, languageId, layout,
279                            themeDisplay);
280    
281                    return rss.getBytes(StringPool.UTF8);
282            }
283    
284            protected String processContent(
285                            JournalFeed feed, JournalArticle article, String languageId,
286                            ThemeDisplay themeDisplay, SyndEntry syndEntry,
287                            SyndContent syndContent)
288                    throws Exception {
289    
290                    String content = article.getDescription(languageId);
291    
292                    String contentField = feed.getContentField();
293    
294                    if (contentField.equals(JournalFeedConstants.RENDERED_WEB_CONTENT)) {
295                            String rendererTemplateId = article.getTemplateId();
296    
297                            if (Validator.isNotNull(feed.getRendererTemplateId())) {
298                                    rendererTemplateId = feed.getRendererTemplateId();
299                            }
300    
301                            JournalArticleDisplay articleDisplay =
302                                    JournalContentUtil.getDisplay(
303                                            feed.getGroupId(), article.getArticleId(),
304                                            rendererTemplateId, null, languageId, themeDisplay, 1,
305                                            _XML_REQUUEST);
306    
307                            if (articleDisplay != null) {
308                                    content = articleDisplay.getContent();
309                            }
310                    }
311                    else if (!contentField.equals(
312                                            JournalFeedConstants.WEB_CONTENT_DESCRIPTION)) {
313    
314                            Document document = SAXReaderUtil.read(
315                                    article.getContentByLocale(languageId));
316    
317                            contentField = HtmlUtil.escapeXPathAttribute(contentField);
318    
319                            XPath xPathSelector = SAXReaderUtil.createXPath(
320                                    "//dynamic-element[@name=" + contentField + "]");
321    
322                            List<Node> results = xPathSelector.selectNodes(document);
323    
324                            if (results.size() == 0) {
325                                    return content;
326                            }
327    
328                            Element element = (Element)results.get(0);
329    
330                            String elType = element.attributeValue("type");
331    
332                            if (elType.equals("document_library")) {
333                                    String url = element.elementText("dynamic-content");
334    
335                                    url = processURL(feed, url, themeDisplay, syndEntry);
336                            }
337                            else if (elType.equals("image") || elType.equals("image_gallery")) {
338                                    String url = element.elementText("dynamic-content");
339    
340                                    url = processURL(feed, url, themeDisplay, syndEntry);
341    
342                                    content =
343                                            content + "<br /><br /><img alt='' src='" +
344                                                    themeDisplay.getURLPortal() + url + "' />";
345                            }
346                            else if (elType.equals("text_box")) {
347                                    syndContent.setType("text");
348    
349                                    content = element.elementText("dynamic-content");
350                            }
351                            else {
352                                    content = element.elementText("dynamic-content");
353                            }
354                    }
355    
356                    return content;
357            }
358    
359            protected String processURL(
360                    JournalFeed feed, String url, ThemeDisplay themeDisplay,
361                    SyndEntry syndEntry) {
362    
363                    url = StringUtil.replace(
364                            url,
365                            new String[] {
366                                    "@group_id@", "@image_path@", "@main_path@"
367                            },
368                            new String[] {
369                                    String.valueOf(feed.getGroupId()), themeDisplay.getPathImage(),
370                                    themeDisplay.getPathMain()
371                            }
372                    );
373    
374                    List<SyndEnclosure> syndEnclosures = JournalRSSUtil.getDLEnclosures(
375                            themeDisplay.getURLPortal(), url);
376    
377                    syndEnclosures.addAll(
378                            JournalRSSUtil.getIGEnclosures(themeDisplay.getURLPortal(), url));
379    
380                    syndEntry.setEnclosures(syndEnclosures);
381    
382                    List<SyndLink> syndLinks = JournalRSSUtil.getDLLinks(
383                            themeDisplay.getURLPortal(), url);
384    
385                    syndLinks.addAll(
386                            JournalRSSUtil.getIGLinks(themeDisplay.getURLPortal(), url));
387    
388                    syndEntry.setLinks(syndLinks);
389    
390                    return url;
391            }
392    
393            private static final String _XML_REQUUEST =
394                    "<request><parameters><parameter><name>rss</name><value>true</value>" +
395                            "</parameter></parameters></request>";
396    
397            private static Log _log = LogFactoryUtil.getLog(RSSAction.class);
398    
399    }