001    /**
002     * Copyright (c) 2000-2012 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.io.FeedException;
060    
061    import java.io.OutputStream;
062    
063    import java.util.ArrayList;
064    import java.util.Iterator;
065    import java.util.List;
066    
067    import javax.portlet.PortletConfig;
068    import javax.portlet.PortletRequest;
069    import javax.portlet.PortletURL;
070    import javax.portlet.ResourceRequest;
071    import javax.portlet.ResourceResponse;
072    import javax.portlet.ResourceURL;
073    
074    import org.apache.struts.action.ActionForm;
075    import org.apache.struts.action.ActionMapping;
076    
077    /**
078     * @author Raymond Augé
079     */
080    public class RSSAction extends PortletAction {
081    
082            @Override
083            public void serveResource(
084                            ActionMapping mapping, ActionForm form, PortletConfig portletConfig,
085                            ResourceRequest resourceRequest, ResourceResponse resourceResponse)
086                    throws Exception {
087    
088                    resourceResponse.setContentType(ContentTypes.TEXT_XML_UTF8);
089    
090                    OutputStream outputStream = resourceResponse.getPortletOutputStream();
091    
092                    try {
093                            byte[] bytes = getRSS(resourceRequest, resourceResponse);
094    
095                            outputStream.write(bytes);
096                    }
097                    finally {
098                            outputStream.close();
099                    }
100            }
101    
102            protected String exportToRSS(
103                            ResourceRequest resourceRequest, ResourceResponse resourceResponse,
104                            JournalFeed feed, String languageId, Layout layout,
105                            ThemeDisplay themeDisplay)
106                    throws Exception {
107    
108                    ResourceURL feedURL = resourceResponse.createResourceURL();
109    
110                    feedURL.setCacheability(ResourceURL.FULL);
111                    feedURL.setParameter("struts_action", "/journal/rss");
112                    feedURL.setParameter("groupId", String.valueOf(feed.getGroupId()));
113                    feedURL.setParameter("feedId", String.valueOf(feed.getFeedId()));
114    
115                    SyndFeed syndFeed = new SyndFeedImpl();
116    
117                    syndFeed.setDescription(feed.getDescription());
118                    syndFeed.setFeedType(feed.getFeedType() + "_" + feed.getFeedVersion());
119                    syndFeed.setLink(feedURL.toString());
120                    syndFeed.setTitle(feed.getName());
121    
122                    List<SyndEntry> syndEntries = new ArrayList<SyndEntry>();
123    
124                    syndFeed.setEntries(syndEntries);
125    
126                    List<JournalArticle> articles = JournalRSSUtil.getArticles(feed);
127    
128                    if (_log.isDebugEnabled()) {
129                            _log.debug("Syndicating " + articles.size() + " articles");
130                    }
131    
132                    Iterator<JournalArticle> itr = articles.iterator();
133    
134                    while (itr.hasNext()) {
135                            JournalArticle article = itr.next();
136    
137                            String author = HtmlUtil.escape(
138                                    PortalUtil.getUserName(
139                                            article.getUserId(), article.getUserName()));
140                            String link = getEntryURL(
141                                    resourceRequest, feed, article, layout, themeDisplay);
142    
143                            SyndEntry syndEntry = new SyndEntryImpl();
144    
145                            syndEntry.setAuthor(author);
146    
147                            SyndContent syndContent = new SyndContentImpl();
148    
149                            String value = article.getDescription(languageId);
150    
151                            try {
152                                    value = processContent(
153                                            feed, article, languageId, themeDisplay, syndEntry,
154                                            syndContent);
155                            }
156                            catch (Exception e) {
157                                    if (_log.isWarnEnabled()) {
158                                            _log.warn(e, e);
159                                    }
160                            }
161    
162                            syndContent.setType(RSSUtil.ENTRY_TYPE_DEFAULT);
163                            syndContent.setValue(value);
164    
165                            syndEntry.setDescription(syndContent);
166    
167                            syndEntry.setLink(link);
168                            syndEntry.setPublishedDate(article.getDisplayDate());
169                            syndEntry.setTitle(article.getTitle(languageId));
170                            syndEntry.setUpdatedDate(article.getModifiedDate());
171                            syndEntry.setUri(syndEntry.getLink());
172    
173                            syndEntries.add(syndEntry);
174                    }
175    
176                    try {
177                            return RSSUtil.export(syndFeed);
178                    }
179                    catch (FeedException fe) {
180                            throw new SystemException(fe);
181                    }
182            }
183    
184            protected String getEntryURL(
185                            ResourceRequest resourceRequest, JournalFeed feed,
186                            JournalArticle article, Layout layout, ThemeDisplay themeDisplay)
187                    throws Exception {
188    
189                    List<Long> hitLayoutIds =
190                            JournalContentSearchLocalServiceUtil.getLayoutIds(
191                                    layout.getGroupId(), layout.isPrivateLayout(),
192                                    article.getArticleId());
193    
194                    if (hitLayoutIds.size() > 0) {
195                            Long hitLayoutId = hitLayoutIds.get(0);
196    
197                            Layout hitLayout = LayoutLocalServiceUtil.getLayout(
198                                    layout.getGroupId(), layout.isPrivateLayout(),
199                                    hitLayoutId.longValue());
200    
201                            return PortalUtil.getLayoutFriendlyURL(hitLayout, themeDisplay);
202                    }
203                    else {
204                            long plid = PortalUtil.getPlidFromFriendlyURL(
205                                    feed.getCompanyId(), feed.getTargetLayoutFriendlyUrl());
206    
207                            String portletId = PortletKeys.JOURNAL_CONTENT;
208    
209                            if (Validator.isNotNull(feed.getTargetPortletId())) {
210                                    portletId = feed.getTargetPortletId();
211                            }
212    
213                            PortletURL entryURL = new PortletURLImpl(
214                                    resourceRequest, portletId, plid, PortletRequest.RENDER_PHASE);
215    
216                            entryURL.setParameter("struts_action", "/journal_content/view");
217                            entryURL.setParameter(
218                                    "groupId", String.valueOf(article.getGroupId()));
219                            entryURL.setParameter("articleId", article.getArticleId());
220    
221                            return entryURL.toString();
222                    }
223            }
224    
225            protected byte[] getRSS(
226                            ResourceRequest resourceRequest, ResourceResponse resourceResponse)
227                    throws Exception {
228    
229                    ThemeDisplay themeDisplay = (ThemeDisplay)resourceRequest.getAttribute(
230                            WebKeys.THEME_DISPLAY);
231    
232                    JournalFeed feed = null;
233    
234                    long id = ParamUtil.getLong(resourceRequest, "id");
235    
236                    long groupId = ParamUtil.getLong(resourceRequest, "groupId");
237                    String feedId = ParamUtil.getString(resourceRequest, "feedId");
238    
239                    if (id > 0) {
240                            feed = JournalFeedLocalServiceUtil.getFeed(id);
241                    }
242                    else {
243                            feed = JournalFeedLocalServiceUtil.getFeed(groupId, feedId);
244                    }
245    
246                    String languageId = LanguageUtil.getLanguageId(resourceRequest);
247    
248                    long plid = PortalUtil.getPlidFromFriendlyURL(
249                            themeDisplay.getCompanyId(), feed.getTargetLayoutFriendlyUrl());
250    
251                    Layout layout = themeDisplay.getLayout();
252    
253                    if (plid > 0) {
254                            try {
255                                    layout = LayoutLocalServiceUtil.getLayout(plid);
256                            }
257                            catch (NoSuchLayoutException nsle) {
258                            }
259                    }
260    
261                    String rss = exportToRSS(
262                            resourceRequest, resourceResponse, feed, languageId, layout,
263                            themeDisplay);
264    
265                    return rss.getBytes(StringPool.UTF8);
266            }
267    
268            protected String processContent(
269                            JournalFeed feed, JournalArticle article, String languageId,
270                            ThemeDisplay themeDisplay, SyndEntry syndEntry,
271                            SyndContent syndContent)
272                    throws Exception {
273    
274                    String content = article.getDescription(languageId);
275    
276                    String contentField = feed.getContentField();
277    
278                    if (contentField.equals(JournalFeedConstants.RENDERED_WEB_CONTENT)) {
279                            String rendererTemplateId = article.getTemplateId();
280    
281                            if (Validator.isNotNull(feed.getRendererTemplateId())) {
282                                    rendererTemplateId = feed.getRendererTemplateId();
283                            }
284    
285                            JournalArticleDisplay articleDisplay =
286                                    JournalContentUtil.getDisplay(
287                                            feed.getGroupId(), article.getArticleId(),
288                                            rendererTemplateId, null, languageId, themeDisplay, 1,
289                                            _XML_REQUUEST);
290    
291                            if (articleDisplay != null) {
292                                    content = articleDisplay.getContent();
293                            }
294                    }
295                    else if (!contentField.equals(
296                                            JournalFeedConstants.WEB_CONTENT_DESCRIPTION)) {
297    
298                            Document document = SAXReaderUtil.read(
299                                    article.getContentByLocale(languageId));
300    
301                            XPath xPathSelector = SAXReaderUtil.createXPath(
302                                    "//dynamic-element[@name='" + contentField + "']");
303    
304                            List<Node> results = xPathSelector.selectNodes(document);
305    
306                            if (results.size() == 0) {
307                                    return content;
308                            }
309    
310                            Element element = (Element)results.get(0);
311    
312                            String elType = element.attributeValue("type");
313    
314                            if (elType.equals("document_library")) {
315                                    String url = element.elementText("dynamic-content");
316    
317                                    url = processURL(feed, url, themeDisplay, syndEntry);
318                            }
319                            else if (elType.equals("image") || elType.equals("image_gallery")) {
320                                    String url = element.elementText("dynamic-content");
321    
322                                    url = processURL(feed, url, themeDisplay, syndEntry);
323    
324                                    content =
325                                            content + "<br /><br /><img alt='' src='" +
326                                                    themeDisplay.getURLPortal() + url + "' />";
327                            }
328                            else if (elType.equals("text_box")) {
329                                    syndContent.setType("text");
330    
331                                    content = element.elementText("dynamic-content");
332                            }
333                            else {
334                                    content = element.elementText("dynamic-content");
335                            }
336                    }
337    
338                    return content;
339            }
340    
341            protected String processURL(
342                    JournalFeed feed, String url, ThemeDisplay themeDisplay,
343                    SyndEntry syndEntry) {
344    
345                    url = StringUtil.replace(
346                            url,
347                            new String[] {
348                                    "@group_id@", "@image_path@", "@main_path@"
349                            },
350                            new String[] {
351                                    String.valueOf(feed.getGroupId()), themeDisplay.getPathImage(),
352                                    themeDisplay.getPathMain()
353                            }
354                    );
355    
356                    List<SyndEnclosure> syndEnclosures = JournalRSSUtil.getDLEnclosures(
357                            themeDisplay.getURLPortal(), url);
358    
359                    syndEnclosures.addAll(
360                            JournalRSSUtil.getIGEnclosures(themeDisplay.getURLPortal(), url));
361    
362                    syndEntry.setEnclosures(syndEnclosures);
363    
364                    List<SyndLink> syndLinks = JournalRSSUtil.getDLLinks(
365                            themeDisplay.getURLPortal(), url);
366    
367                    syndLinks.addAll(
368                            JournalRSSUtil.getIGLinks(themeDisplay.getURLPortal(), url));
369    
370                    syndEntry.setLinks(syndLinks);
371    
372                    return url;
373            }
374    
375            private static final String _XML_REQUUEST =
376                    "<request><parameters><parameter><name>rss</name><value>true</value>" +
377                            "</parameter></parameters></request>";
378    
379            private static Log _log = LogFactoryUtil.getLog(RSSAction.class);
380    
381    }