1   /**
2    * Copyright (c) 2000-2008 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.portlet.LiferayWindowState;
29  import com.liferay.portal.kernel.util.ContentTypes;
30  import com.liferay.portal.kernel.util.ParamUtil;
31  import com.liferay.portal.kernel.util.StringMaker;
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.model.Layout;
36  import com.liferay.portal.service.LayoutLocalServiceUtil;
37  import com.liferay.portal.struts.ActionConstants;
38  import com.liferay.portal.struts.PortletAction;
39  import com.liferay.portal.theme.ThemeDisplay;
40  import com.liferay.portal.util.DocumentUtil;
41  import com.liferay.portal.util.PortalUtil;
42  import com.liferay.portal.util.PortletKeys;
43  import com.liferay.portal.util.WebKeys;
44  import com.liferay.portlet.journal.model.JournalArticle;
45  import com.liferay.portlet.journal.model.JournalArticleDisplay;
46  import com.liferay.portlet.journal.model.JournalFeed;
47  import com.liferay.portlet.journal.model.impl.JournalFeedImpl;
48  import com.liferay.portlet.journal.service.JournalContentSearchLocalServiceUtil;
49  import com.liferay.portlet.journal.service.JournalFeedLocalServiceUtil;
50  import com.liferay.portlet.journal.util.JournalRSSUtil;
51  import com.liferay.portlet.journalcontent.util.JournalContentUtil;
52  import com.liferay.util.RSSUtil;
53  
54  import com.sun.syndication.feed.synd.SyndContent;
55  import com.sun.syndication.feed.synd.SyndContentImpl;
56  import com.sun.syndication.feed.synd.SyndEnclosure;
57  import com.sun.syndication.feed.synd.SyndEntry;
58  import com.sun.syndication.feed.synd.SyndEntryImpl;
59  import com.sun.syndication.feed.synd.SyndFeed;
60  import com.sun.syndication.feed.synd.SyndFeedImpl;
61  import com.sun.syndication.feed.synd.SyndLink;
62  import com.sun.syndication.io.FeedException;
63  
64  import java.io.IOException;
65  import java.io.OutputStream;
66  
67  import java.util.ArrayList;
68  import java.util.Iterator;
69  import java.util.List;
70  
71  import javax.portlet.PortletConfig;
72  import javax.portlet.RenderRequest;
73  import javax.portlet.RenderResponse;
74  
75  import org.apache.commons.logging.Log;
76  import org.apache.commons.logging.LogFactory;
77  import org.apache.struts.action.ActionForm;
78  import org.apache.struts.action.ActionForward;
79  import org.apache.struts.action.ActionMapping;
80  
81  import org.dom4j.Document;
82  import org.dom4j.DocumentHelper;
83  import org.dom4j.Element;
84  import org.dom4j.XPath;
85  
86  /**
87   * <a href="RSSAction.java.html"><b><i>View Source</i></b></a>
88   *
89   * @author Raymond Aug�
90   *
91   */
92  public class RSSAction extends PortletAction {
93  
94      public ActionForward render(
95              ActionMapping mapping, ActionForm form, PortletConfig config,
96              RenderRequest req, RenderResponse res)
97          throws Exception {
98  
99          if (req.getWindowState() == LiferayWindowState.EXCLUSIVE) {
100             res.setContentType(ContentTypes.TEXT_XML_UTF8);
101 
102             OutputStream out = res.getPortletOutputStream();
103 
104             try {
105                 out.write(getRSS(req));
106             }
107             finally {
108                 out.close();
109             }
110         }
111 
112         return mapping.findForward(ActionConstants.COMMON_NULL);
113     }
114 
115     protected String exportToRSS(
116             JournalFeed feed, String languageId, Layout layout,
117             ThemeDisplay themeDisplay)
118         throws Exception {
119 
120         String feedURL =
121             PortalUtil.getLayoutFriendlyURL(layout, themeDisplay) +
122                 "/journal/rss/" + feed.getGroupId() + "/" + 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);
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(feed, article, layout, themeDisplay);
149 
150             SyndEntry syndEntry = new SyndEntryImpl();
151 
152             syndEntry.setAuthor(author);
153             syndEntry.setTitle(article.getTitle());
154             syndEntry.setLink(link);
155             syndEntry.setPublishedDate(article.getDisplayDate());
156 
157             SyndContent syndContent = new SyndContentImpl();
158 
159             String value = article.getDescription();
160 
161             try {
162                 value = processContent(
163                     feed, article, languageId, themeDisplay, syndEntry,
164                     syndContent);
165             }
166             catch (Exception e) {
167                 if (_log.isWarnEnabled()) {
168                     _log.warn(e, e);
169                 }
170             }
171 
172             syndContent.setType("html");
173             syndContent.setValue(value);
174 
175             syndEntry.setDescription(syndContent);
176 
177             entries.add(syndEntry);
178         }
179 
180         try {
181             return RSSUtil.export(syndFeed);
182         }
183         catch (FeedException fe) {
184             throw new SystemException(fe);
185         }
186         catch (IOException ioe) {
187             throw new SystemException(ioe);
188         }
189     }
190 
191     protected String getEntryURL(
192             JournalFeed feed, JournalArticle article, Layout layout,
193             ThemeDisplay themeDisplay)
194         throws Exception {
195 
196         StringMaker sm = new StringMaker();
197 
198         List<Long> hitLayoutIds =
199             JournalContentSearchLocalServiceUtil.getLayoutIds(
200                 layout.getGroupId(), layout.isPrivateLayout(),
201                 article.getArticleId());
202 
203         if (hitLayoutIds.size() > 0) {
204             Long hitLayoutId = hitLayoutIds.get(0);
205 
206             Layout hitLayout = LayoutLocalServiceUtil.getLayout(
207                 layout.getGroupId(), layout.isPrivateLayout(),
208                 hitLayoutId.longValue());
209 
210             return PortalUtil.getLayoutFriendlyURL(hitLayout, themeDisplay);
211         }
212         else if (Validator.isNotNull(feed.getTargetLayoutFriendlyUrl())) {
213             long plid = PortalUtil.getPlidIdFromFriendlyURL(
214                 feed.getCompanyId(), feed.getTargetLayoutFriendlyUrl());
215 
216             Layout targetLayout = LayoutLocalServiceUtil.getLayout(plid);
217 
218             sm.append(
219                 PortalUtil.getLayoutFriendlyURL(targetLayout, themeDisplay));
220         }
221         else {
222             sm.append(PortalUtil.getLayoutFriendlyURL(layout, themeDisplay));
223         }
224 
225         sm.append("/journal_content/");
226 
227         if (Validator.isNotNull(feed.getTargetPortletId())) {
228             sm.append(feed.getTargetPortletId());
229         }
230         else {
231             sm.append(PortletKeys.JOURNAL_CONTENT);
232         }
233 
234         sm.append(StringPool.SLASH);
235         sm.append(article.getGroupId());
236         sm.append(StringPool.SLASH);
237         sm.append(article.getArticleId());
238 
239         return sm.toString();
240     }
241 
242     protected byte[] getRSS(RenderRequest req) throws Exception {
243         ThemeDisplay themeDisplay = (ThemeDisplay)req.getAttribute(
244             WebKeys.THEME_DISPLAY);
245 
246         JournalFeed feed = null;
247 
248         long id = ParamUtil.getLong(req, "id");
249 
250         long groupId = ParamUtil.getLong(req, "groupId");
251         String feedId = ParamUtil.getString(req, "feedId");
252 
253         if (id > 0) {
254             feed = JournalFeedLocalServiceUtil.getFeed(id);
255         }
256         else {
257             feed = JournalFeedLocalServiceUtil.getFeed(groupId, feedId);
258         }
259 
260         String languageId = LanguageUtil.getLanguageId(req);
261 
262         long plid = PortalUtil.getPlidIdFromFriendlyURL(
263             themeDisplay.getCompanyId(), feed.getTargetLayoutFriendlyUrl());
264 
265         Layout layout = themeDisplay.getLayout();
266 
267         if (plid > 0) {
268             try {
269                 layout = LayoutLocalServiceUtil.getLayout(plid);
270             }
271             catch (NoSuchLayoutException nsle) {
272             }
273         }
274 
275         String rss = exportToRSS(feed, languageId, layout, themeDisplay);
276 
277         return rss.getBytes(StringPool.UTF8);
278     }
279 
280     protected String processContent(
281             JournalFeed feed, JournalArticle article, String languageId,
282             ThemeDisplay themeDisplay, SyndEntry syndEntry,
283             SyndContent syndContent)
284         throws Exception {
285 
286         String content = article.getDescription();
287 
288         String contentField = feed.getContentField();
289 
290         if (contentField.equals(JournalFeedImpl.RENDERED_ARTICLE)) {
291             String rendererTemplateId = article.getTemplateId();
292 
293             if (Validator.isNotNull(feed.getRendererTemplateId())) {
294                 rendererTemplateId = feed.getRendererTemplateId();
295             }
296 
297             JournalArticleDisplay articleDisplay =
298                 JournalContentUtil.getDisplay(
299                     feed.getGroupId(), article.getArticleId(),
300                     rendererTemplateId, languageId, themeDisplay, 1,
301                     _XML_REQUUEST);
302 
303             if (articleDisplay != null) {
304                 content = articleDisplay.getContent();
305             }
306         }
307         else if (!contentField.equals(JournalFeedImpl.ARTICLE_DESCRIPTION)) {
308             Document doc = DocumentUtil.readDocumentFromXML(
309                 article.getContent());
310 
311             XPath xpathSelector = DocumentHelper.createXPath(
312                 "//dynamic-element[@name='" + contentField + "']");
313 
314             List<Element> results = xpathSelector.selectNodes(doc);
315 
316             if (results.size() == 0) {
317                 return content;
318             }
319 
320             Element el = results.get(0);
321 
322             String elType = el.attributeValue("type");
323 
324             if (elType.equals("document_library")) {
325                 String url = el.elementText("dynamic-content");
326 
327                 url = processURL(feed, url, themeDisplay, syndEntry);
328             }
329             else if (elType.equals("image") || elType.equals("image_gallery")) {
330                 String url = el.elementText("dynamic-content");
331 
332                 url = processURL(feed, url, themeDisplay, syndEntry);
333 
334                 content =
335                     content + "<br /><br /><img src='" +
336                         themeDisplay.getURLPortal() + url + "' />";
337             }
338             else if (elType.equals("text_box")) {
339                 syndContent.setType("text");
340 
341                 content = el.elementText("dynamic-content");
342             }
343             else {
344                 content = el.elementText("dynamic-content");
345             }
346         }
347 
348         return content;
349     }
350 
351     protected String processURL(
352         JournalFeed feed, String url, ThemeDisplay themeDisplay,
353         SyndEntry syndEntry) {
354 
355         url = StringUtil.replace(
356             url,
357             new String[] {
358                 "@group_id@",
359                 "@image_path@",
360                 "@main_path@"
361             },
362             new String[] {
363                 String.valueOf(feed.getGroupId()),
364                 themeDisplay.getPathImage(),
365                 themeDisplay.getPathMain()
366             }
367         );
368 
369         List<SyndLink> links = JournalRSSUtil.getDLLinks(
370             themeDisplay.getURLPortal(), url);
371         List<SyndEnclosure> enclosures = JournalRSSUtil.getDLEnclosures(
372             themeDisplay.getURLPortal(), url);
373 
374         syndEntry.setLinks(links);
375         syndEntry.setEnclosures(enclosures);
376 
377         return url;
378     }
379 
380     private static final String _XML_REQUUEST =
381         "<request><parameters><parameter><name>rss</name><value>true</value>" +
382             "</parameter></parameters></request>";
383 
384     private static Log _log = LogFactory.getLog(RSSAction.class);
385 
386 }