1   /**
2    * Copyright (c) 2000-2009 Liferay, Inc. All rights reserved.
3    *
4    * Permission is hereby granted, free of charge, to any person obtaining a copy
5    * of this software and associated documentation files (the "Software"), to deal
6    * in the Software without restriction, including without limitation the rights
7    * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8    * copies of the Software, and to permit persons to whom the Software is
9    * furnished to do so, subject to the following conditions:
10   *
11   * The above copyright notice and this permission notice shall be included in
12   * all copies or substantial portions of the Software.
13   *
14   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15   * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16   * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17   * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18   * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19   * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20   * SOFTWARE.
21   */
22  
23  package com.liferay.portlet.journal.action;
24  
25  import com.liferay.portal.NoSuchLayoutException;
26  import com.liferay.portal.SystemException;
27  import com.liferay.portal.kernel.language.LanguageUtil;
28  import com.liferay.portal.kernel.log.Log;
29  import com.liferay.portal.kernel.log.LogFactoryUtil;
30  import com.liferay.portal.kernel.util.ContentTypes;
31  import com.liferay.portal.kernel.util.ParamUtil;
32  import com.liferay.portal.kernel.util.StringPool;
33  import com.liferay.portal.kernel.util.StringUtil;
34  import com.liferay.portal.kernel.util.Validator;
35  import com.liferay.portal.kernel.xml.Document;
36  import com.liferay.portal.kernel.xml.Element;
37  import com.liferay.portal.kernel.xml.Node;
38  import com.liferay.portal.kernel.xml.SAXReaderUtil;
39  import com.liferay.portal.kernel.xml.XPath;
40  import com.liferay.portal.model.Layout;
41  import com.liferay.portal.service.LayoutLocalServiceUtil;
42  import com.liferay.portal.struts.PortletAction;
43  import com.liferay.portal.theme.ThemeDisplay;
44  import com.liferay.portal.util.PortalUtil;
45  import com.liferay.portal.util.PortletKeys;
46  import com.liferay.portal.util.WebKeys;
47  import com.liferay.portlet.PortletRequestImpl;
48  import com.liferay.portlet.PortletURLImpl;
49  import com.liferay.portlet.journal.model.JournalArticle;
50  import com.liferay.portlet.journal.model.JournalArticleDisplay;
51  import com.liferay.portlet.journal.model.JournalFeed;
52  import com.liferay.portlet.journal.model.impl.JournalFeedImpl;
53  import com.liferay.portlet.journal.service.JournalContentSearchLocalServiceUtil;
54  import com.liferay.portlet.journal.service.JournalFeedLocalServiceUtil;
55  import com.liferay.portlet.journal.util.JournalRSSUtil;
56  import com.liferay.portlet.journalcontent.util.JournalContentUtil;
57  import com.liferay.util.RSSUtil;
58  
59  import com.sun.syndication.feed.synd.SyndContent;
60  import com.sun.syndication.feed.synd.SyndContentImpl;
61  import com.sun.syndication.feed.synd.SyndEnclosure;
62  import com.sun.syndication.feed.synd.SyndEntry;
63  import com.sun.syndication.feed.synd.SyndEntryImpl;
64  import com.sun.syndication.feed.synd.SyndFeed;
65  import com.sun.syndication.feed.synd.SyndFeedImpl;
66  import com.sun.syndication.feed.synd.SyndLink;
67  import com.sun.syndication.io.FeedException;
68  
69  import java.io.OutputStream;
70  
71  import java.util.ArrayList;
72  import java.util.Iterator;
73  import java.util.List;
74  
75  import javax.portlet.PortletConfig;
76  import javax.portlet.PortletRequest;
77  import javax.portlet.PortletURL;
78  import javax.portlet.ResourceRequest;
79  import javax.portlet.ResourceResponse;
80  import javax.portlet.ResourceURL;
81  
82  import org.apache.struts.action.ActionForm;
83  import org.apache.struts.action.ActionMapping;
84  
85  /**
86   * <a href="RSSAction.java.html"><b><i>View Source</i></b></a>
87   *
88   * @author Raymond Augé
89   *
90   */
91  public class RSSAction extends PortletAction {
92  
93      public void serveResource(
94              ActionMapping mapping, ActionForm form, PortletConfig portletConfig,
95              ResourceRequest resourceRequest, ResourceResponse resourceResponse)
96          throws Exception {
97  
98          resourceResponse.setContentType(ContentTypes.TEXT_XML_UTF8);
99  
100         OutputStream os = resourceResponse.getPortletOutputStream();
101 
102         try {
103             os.write(getRSS(resourceRequest, resourceResponse));
104         }
105         finally {
106             os.close();
107         }
108     }
109 
110     protected String exportToRSS(
111             ResourceRequest resourceRequest, ResourceResponse resourceResponse,
112             JournalFeed feed, String languageId, Layout layout,
113             ThemeDisplay themeDisplay)
114         throws Exception {
115 
116         ResourceURL feedURL = resourceResponse.createResourceURL();
117 
118         feedURL.setCacheability(ResourceURL.FULL);
119 
120         feedURL.setParameter("struts_action", "/journal/rss");
121         feedURL.setParameter("groupId", String.valueOf(feed.getGroupId()));
122         feedURL.setParameter("feedId", String.valueOf(feed.getFeedId()));
123 
124         SyndFeed syndFeed = new SyndFeedImpl();
125 
126         syndFeed.setFeedType(feed.getFeedType() + "_" + feed.getFeedVersion());
127         syndFeed.setTitle(feed.getName());
128         syndFeed.setLink(feedURL.toString());
129         syndFeed.setDescription(feed.getDescription());
130 
131         List<SyndEntry> entries = new ArrayList<SyndEntry>();
132 
133         syndFeed.setEntries(entries);
134 
135         List<JournalArticle> articles = JournalRSSUtil.getArticles(feed);
136 
137         if (_log.isDebugEnabled()) {
138             _log.debug("Syndicating " + articles.size() + " articles");
139         }
140 
141         Iterator<JournalArticle> itr = articles.iterator();
142 
143         while (itr.hasNext()) {
144             JournalArticle article = itr.next();
145 
146             String author = PortalUtil.getUserName(
147                 article.getUserId(), article.getUserName());
148             String link = getEntryURL(
149                 resourceRequest, feed, article, layout, themeDisplay);
150 
151             SyndEntry syndEntry = new SyndEntryImpl();
152 
153             syndEntry.setAuthor(author);
154             syndEntry.setTitle(article.getTitle());
155             syndEntry.setLink(link);
156             syndEntry.setUri(syndEntry.getLink());
157             syndEntry.setPublishedDate(article.getDisplayDate());
158             syndEntry.setUpdatedDate(article.getModifiedDate());
159 
160             SyndContent syndContent = new SyndContentImpl();
161 
162             String value = article.getDescription();
163 
164             try {
165                 value = processContent(
166                     feed, article, languageId, themeDisplay, syndEntry,
167                     syndContent);
168             }
169             catch (Exception e) {
170                 if (_log.isWarnEnabled()) {
171                     _log.warn(e, e);
172                 }
173             }
174 
175             syndContent.setType(RSSUtil.DEFAULT_ENTRY_TYPE);
176             syndContent.setValue(value);
177 
178             syndEntry.setDescription(syndContent);
179 
180             entries.add(syndEntry);
181         }
182 
183         try {
184             return RSSUtil.export(syndFeed);
185         }
186         catch (FeedException fe) {
187             throw new SystemException(fe);
188         }
189     }
190 
191     protected String getEntryURL(
192             ResourceRequest resourceRequest, JournalFeed feed,
193             JournalArticle article, Layout layout, ThemeDisplay themeDisplay)
194         throws Exception {
195 
196         List<Long> hitLayoutIds =
197             JournalContentSearchLocalServiceUtil.getLayoutIds(
198                 layout.getGroupId(), layout.isPrivateLayout(),
199                 article.getArticleId());
200 
201         if (hitLayoutIds.size() > 0) {
202             Long hitLayoutId = hitLayoutIds.get(0);
203 
204             Layout hitLayout = LayoutLocalServiceUtil.getLayout(
205                 layout.getGroupId(), layout.isPrivateLayout(),
206                 hitLayoutId.longValue());
207 
208             return PortalUtil.getLayoutFriendlyURL(hitLayout, themeDisplay);
209         }
210         else {
211             long plid = PortalUtil.getPlidFromFriendlyURL(
212                 feed.getCompanyId(), feed.getTargetLayoutFriendlyUrl());
213 
214             String portletId = PortletKeys.JOURNAL_CONTENT;
215 
216             if (Validator.isNotNull(feed.getTargetPortletId())) {
217                 portletId = feed.getTargetPortletId();
218             }
219 
220             PortletURL entryURL = new PortletURLImpl(
221                 (PortletRequestImpl)resourceRequest, portletId, plid,
222                 PortletRequest.RENDER_PHASE);
223 
224             entryURL.setParameter("struts_action", "/journal_content/view");
225             entryURL.setParameter(
226                 "groupId", String.valueOf(article.getGroupId()));
227             entryURL.setParameter("articleId", article.getArticleId());
228 
229             return entryURL.toString();
230         }
231     }
232 
233     protected byte[] getRSS(
234             ResourceRequest resourceRequest, ResourceResponse resourceResponse)
235         throws Exception {
236 
237         ThemeDisplay themeDisplay = (ThemeDisplay)resourceRequest.getAttribute(
238             WebKeys.THEME_DISPLAY);
239 
240         JournalFeed feed = null;
241 
242         long id = ParamUtil.getLong(resourceRequest, "id");
243 
244         long groupId = ParamUtil.getLong(resourceRequest, "groupId");
245         String feedId = ParamUtil.getString(resourceRequest, "feedId");
246 
247         if (id > 0) {
248             feed = JournalFeedLocalServiceUtil.getFeed(id);
249         }
250         else {
251             feed = JournalFeedLocalServiceUtil.getFeed(groupId, feedId);
252         }
253 
254         String languageId = LanguageUtil.getLanguageId(resourceRequest);
255 
256         long plid = PortalUtil.getPlidFromFriendlyURL(
257             themeDisplay.getCompanyId(), feed.getTargetLayoutFriendlyUrl());
258 
259         Layout layout = themeDisplay.getLayout();
260 
261         if (plid > 0) {
262             try {
263                 layout = LayoutLocalServiceUtil.getLayout(plid);
264             }
265             catch (NoSuchLayoutException nsle) {
266             }
267         }
268 
269         String rss = exportToRSS(
270             resourceRequest, resourceResponse, feed, languageId, layout,
271             themeDisplay);
272 
273         return rss.getBytes(StringPool.UTF8);
274     }
275 
276     protected String processContent(
277             JournalFeed feed, JournalArticle article, String languageId,
278             ThemeDisplay themeDisplay, SyndEntry syndEntry,
279             SyndContent syndContent)
280         throws Exception {
281 
282         String content = article.getDescription();
283 
284         String contentField = feed.getContentField();
285 
286         if (contentField.equals(JournalFeedImpl.RENDERED_WEB_CONTENT)) {
287             String rendererTemplateId = article.getTemplateId();
288 
289             if (Validator.isNotNull(feed.getRendererTemplateId())) {
290                 rendererTemplateId = feed.getRendererTemplateId();
291             }
292 
293             JournalArticleDisplay articleDisplay =
294                 JournalContentUtil.getDisplay(
295                     feed.getGroupId(), article.getArticleId(),
296                     rendererTemplateId, null, languageId, themeDisplay, 1,
297                     _XML_REQUUEST);
298 
299             if (articleDisplay != null) {
300                 content = articleDisplay.getContent();
301             }
302         }
303         else if (
304             !contentField.equals(JournalFeedImpl.WEB_CONTENT_DESCRIPTION)) {
305 
306             Document doc = SAXReaderUtil.read(article.getContent());
307 
308             XPath xpathSelector = SAXReaderUtil.createXPath(
309                 "//dynamic-element[@name='" + contentField + "']");
310 
311             List<Node> results = xpathSelector.selectNodes(doc);
312 
313             if (results.size() == 0) {
314                 return content;
315             }
316 
317             Element el = (Element)results.get(0);
318 
319             String elType = el.attributeValue("type");
320 
321             if (elType.equals("document_library")) {
322                 String url = el.elementText("dynamic-content");
323 
324                 url = processURL(feed, url, themeDisplay, syndEntry);
325             }
326             else if (elType.equals("image") || elType.equals("image_gallery")) {
327                 String url = el.elementText("dynamic-content");
328 
329                 url = processURL(feed, url, themeDisplay, syndEntry);
330 
331                 content =
332                     content + "<br /><br /><img src='" +
333                         themeDisplay.getURLPortal() + url + "' />";
334             }
335             else if (elType.equals("text_box")) {
336                 syndContent.setType("text");
337 
338                 content = el.elementText("dynamic-content");
339             }
340             else {
341                 content = el.elementText("dynamic-content");
342             }
343         }
344 
345         return content;
346     }
347 
348     protected String processURL(
349         JournalFeed feed, String url, ThemeDisplay themeDisplay,
350         SyndEntry syndEntry) {
351 
352         url = StringUtil.replace(
353             url,
354             new String[] {
355                 "@group_id@",
356                 "@image_path@",
357                 "@main_path@"
358             },
359             new String[] {
360                 String.valueOf(feed.getGroupId()),
361                 themeDisplay.getPathImage(),
362                 themeDisplay.getPathMain()
363             }
364         );
365 
366         List<SyndLink> links = JournalRSSUtil.getDLLinks(
367             themeDisplay.getURLPortal(), url);
368         List<SyndEnclosure> enclosures = JournalRSSUtil.getDLEnclosures(
369             themeDisplay.getURLPortal(), url);
370 
371         syndEntry.setLinks(links);
372         syndEntry.setEnclosures(enclosures);
373 
374         return url;
375     }
376 
377     private static final String _XML_REQUUEST =
378         "<request><parameters><parameter><name>rss</name><value>true</value>" +
379             "</parameter></parameters></request>";
380 
381     private static Log _log = LogFactoryUtil.getLog(RSSAction.class);
382 
383 }