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