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.wiki.service.impl;
24  
25  import com.liferay.portal.PortalException;
26  import com.liferay.portal.SystemException;
27  import com.liferay.portal.kernel.language.LanguageUtil;
28  import com.liferay.portal.kernel.util.GetterUtil;
29  import com.liferay.portal.kernel.util.HtmlUtil;
30  import com.liferay.portal.kernel.util.ObjectValuePair;
31  import com.liferay.portal.kernel.util.StringPool;
32  import com.liferay.portal.kernel.util.StringUtil;
33  import com.liferay.portal.security.permission.ActionKeys;
34  import com.liferay.portal.theme.ThemeDisplay;
35  import com.liferay.portal.util.ContentUtil;
36  import com.liferay.portal.util.PortalUtil;
37  import com.liferay.portal.util.PortletKeys;
38  import com.liferay.portal.util.PropsKeys;
39  import com.liferay.portal.util.PropsUtil;
40  import com.liferay.portal.velocity.VelocityUtil;
41  import com.liferay.portlet.wiki.model.WikiNode;
42  import com.liferay.portlet.wiki.model.WikiPage;
43  import com.liferay.portlet.wiki.service.base.WikiPageServiceBaseImpl;
44  import com.liferay.portlet.wiki.service.permission.WikiNodePermission;
45  import com.liferay.portlet.wiki.service.permission.WikiPagePermission;
46  import com.liferay.portlet.wiki.util.WikiUtil;
47  import com.liferay.portlet.wiki.util.comparator.PageCreateDateComparator;
48  import com.liferay.util.RSSUtil;
49  import com.liferay.util.diff.DiffResult;
50  import com.liferay.util.diff.DiffUtil;
51  
52  import com.sun.syndication.feed.synd.SyndContent;
53  import com.sun.syndication.feed.synd.SyndContentImpl;
54  import com.sun.syndication.feed.synd.SyndEntry;
55  import com.sun.syndication.feed.synd.SyndEntryImpl;
56  import com.sun.syndication.feed.synd.SyndFeed;
57  import com.sun.syndication.feed.synd.SyndFeedImpl;
58  import com.sun.syndication.io.FeedException;
59  
60  import java.io.IOException;
61  import java.io.StringReader;
62  
63  import java.util.ArrayList;
64  import java.util.HashMap;
65  import java.util.Iterator;
66  import java.util.List;
67  import java.util.Locale;
68  import java.util.Map;
69  
70  import javax.portlet.PortletPreferences;
71  
72  /**
73   * <a href="WikiPageServiceImpl.java.html"><b><i>View Source</i></b></a>
74   *
75   * @author Brian Wing Shun Chan
76   * @author Jorge Ferrer
77   *
78   */
79  public class WikiPageServiceImpl extends WikiPageServiceBaseImpl {
80  
81      public WikiPage addPage(
82              long nodeId, String title, String content, String summary,
83              boolean minorEdit, PortletPreferences prefs,
84              ThemeDisplay themeDisplay)
85          throws PortalException, SystemException {
86  
87          WikiNodePermission.check(
88              getPermissionChecker(), nodeId, ActionKeys.ADD_PAGE);
89  
90          return wikiPageLocalService.addPage(
91              getUserId(), nodeId, title, content, summary, minorEdit, prefs,
92              themeDisplay);
93      }
94  
95      public void addPageAttachments(
96              long nodeId, String title,
97              List<ObjectValuePair<String, byte[]>> files)
98          throws PortalException, SystemException {
99  
100         WikiNodePermission.check(
101             getPermissionChecker(), nodeId, ActionKeys.ADD_ATTACHMENT);
102 
103         wikiPageLocalService.addPageAttachments(nodeId, title, files);
104     }
105 
106     public void changeParent(
107             long nodeId, String title, String newParentTitle,
108             PortletPreferences prefs, ThemeDisplay themeDisplay)
109         throws PortalException, SystemException {
110 
111         WikiNodePermission.check(
112             getPermissionChecker(), nodeId, ActionKeys.ADD_PAGE);
113 
114         WikiPagePermission.check(
115             getPermissionChecker(), nodeId, title, ActionKeys.UPDATE);
116 
117         wikiPageLocalService.changeParent(
118             getUserId(), nodeId, title, newParentTitle, prefs, themeDisplay);
119     }
120 
121     public void deletePage(long nodeId, String title)
122         throws PortalException, SystemException {
123 
124         WikiPagePermission.check(
125             getPermissionChecker(), nodeId, title, ActionKeys.DELETE);
126 
127         wikiPageLocalService.deletePage(nodeId, title);
128     }
129 
130     public void deletePageAttachment(long nodeId, String title, String fileName)
131         throws PortalException, SystemException {
132 
133         WikiPagePermission.check(
134             getPermissionChecker(), nodeId, title, ActionKeys.DELETE);
135 
136         wikiPageLocalService.deletePageAttachment(nodeId, title, fileName);
137     }
138 
139     public List<WikiPage> getNodePages(long nodeId, int max)
140         throws PortalException, SystemException {
141 
142         List<WikiPage> pages = new ArrayList<WikiPage>();
143 
144         int lastIntervalStart = 0;
145         boolean listNotExhausted = true;
146 
147         while ((pages.size() < max) && listNotExhausted) {
148             List<WikiPage> pageList = wikiPageLocalService.getPages(
149                 nodeId, true, lastIntervalStart, lastIntervalStart + max);
150 
151             Iterator<WikiPage> itr = pageList.iterator();
152 
153             lastIntervalStart += max;
154             listNotExhausted = (pageList.size() == max);
155 
156             while (itr.hasNext() && (pages.size() < max)) {
157                 WikiPage page = itr.next();
158 
159                 if (WikiPagePermission.contains(getPermissionChecker(), page,
160                         ActionKeys.VIEW)) {
161 
162                     pages.add(page);
163                 }
164             }
165         }
166 
167         return pages;
168     }
169 
170     public String getNodePagesRSS(
171             long nodeId, int max, String type, double version,
172             String displayStyle, String feedURL, String entryURL)
173         throws PortalException, SystemException {
174 
175         WikiNodePermission.check(
176             getPermissionChecker(), nodeId, ActionKeys.VIEW);
177 
178         WikiNode node = wikiNodePersistence.findByPrimaryKey(nodeId);
179 
180         long companyId = node.getCompanyId();
181         String name = node.getName();
182         String description = node.getDescription();
183         List<WikiPage> pages = getNodePages(nodeId, max);
184         boolean diff = false;
185         Locale locale = null;
186 
187         return exportToRSS(
188             companyId, name, description, type, version, displayStyle,
189             feedURL, entryURL, pages, diff, locale);
190     }
191 
192     public WikiPage getPage(long nodeId, String title)
193         throws PortalException, SystemException {
194 
195         WikiPagePermission.check(
196             getPermissionChecker(), nodeId, title, ActionKeys.VIEW);
197 
198         return wikiPageLocalService.getPage(nodeId, title);
199     }
200 
201     public WikiPage getPage(long nodeId, String title, double version)
202         throws PortalException, SystemException {
203 
204         WikiPagePermission.check(
205             getPermissionChecker(), nodeId, title, ActionKeys.VIEW);
206 
207         return wikiPageLocalService.getPage(nodeId, title, version);
208     }
209 
210     public String getPagesRSS(
211             long companyId, long nodeId, String title, int max, String type,
212             double version, String displayStyle, String feedURL,
213             String entryURL, Locale locale)
214         throws PortalException, SystemException {
215 
216         WikiPagePermission.check(
217             getPermissionChecker(), nodeId, title, ActionKeys.VIEW);
218 
219         String description = title;
220         List<WikiPage> pages = wikiPageLocalService.getPages(
221             nodeId, title, 0, max, new PageCreateDateComparator(true));
222         boolean diff = true;
223 
224         return exportToRSS(
225             companyId, title, description, type, version, displayStyle, feedURL,
226             entryURL, pages, diff, locale);
227     }
228 
229     public void movePage(
230             long nodeId, String title, String newTitle,
231             PortletPreferences prefs, ThemeDisplay themeDisplay)
232         throws PortalException, SystemException {
233 
234         WikiNodePermission.check(
235             getPermissionChecker(), nodeId, ActionKeys.ADD_PAGE);
236 
237         WikiPagePermission.check(
238             getPermissionChecker(), nodeId, title, ActionKeys.UPDATE);
239 
240         wikiPageLocalService.movePage(
241             getUserId(), nodeId, title, newTitle, prefs, themeDisplay);
242     }
243 
244     public WikiPage revertPage(
245             long nodeId, String title, double version, PortletPreferences prefs,
246             ThemeDisplay themeDisplay)
247         throws PortalException, SystemException {
248 
249         WikiPagePermission.check(
250             getPermissionChecker(), nodeId, title, ActionKeys.UPDATE);
251 
252         return wikiPageLocalService.revertPage(
253             getUserId(), nodeId, title, version, prefs, themeDisplay);
254     }
255 
256     public void subscribePage(long nodeId, String title)
257         throws PortalException, SystemException {
258 
259         WikiPagePermission.check(
260             getPermissionChecker(), nodeId, title, ActionKeys.SUBSCRIBE);
261 
262         wikiPageLocalService.subscribePage(getUserId(), nodeId, title);
263     }
264 
265     public void unsubscribePage(long nodeId, String title)
266         throws PortalException, SystemException {
267 
268         WikiPagePermission.check(
269             getPermissionChecker(), nodeId, title, ActionKeys.SUBSCRIBE);
270 
271         wikiPageLocalService.unsubscribePage(getUserId(), nodeId, title);
272     }
273 
274     public WikiPage updatePage(
275             long nodeId, String title, double version, String content,
276             String summary, boolean minorEdit, String format,
277             String parentTitle, String redirectTitle, String[] tagsEntries,
278             PortletPreferences prefs, ThemeDisplay themeDisplay)
279         throws PortalException, SystemException {
280 
281         WikiPagePermission.check(
282             getPermissionChecker(), nodeId, title, ActionKeys.UPDATE);
283 
284         return wikiPageLocalService.updatePage(
285             getUserId(), nodeId, title, version, content, summary, minorEdit,
286             format, parentTitle, redirectTitle, tagsEntries, prefs,
287             themeDisplay);
288     }
289 
290     protected String exportToRSS(
291             long companyId, String name, String description, String type,
292             double version, String displayStyle, String feedURL,
293             String entryURL, List<WikiPage> pages, boolean diff, Locale locale)
294         throws SystemException {
295 
296         SyndFeed syndFeed = new SyndFeedImpl();
297 
298         syndFeed.setFeedType(RSSUtil.getFeedType(type, version));
299         syndFeed.setTitle(name);
300         syndFeed.setLink(feedURL);
301         syndFeed.setDescription(description);
302 
303         List<SyndEntry> entries = new ArrayList<SyndEntry>();
304 
305         syndFeed.setEntries(entries);
306 
307         WikiPage latestPage = null;
308 
309         for (WikiPage page : pages) {
310             String author = PortalUtil.getUserName(
311                 page.getUserId(), page.getUserName());
312 
313             String link = entryURL;
314 
315             SyndEntry syndEntry = new SyndEntryImpl();
316 
317             syndEntry.setAuthor(author);
318             syndEntry.setTitle(
319                 page.getTitle() + StringPool.SPACE + page.getVersion());
320             syndEntry.setPublishedDate(page.getCreateDate());
321 
322             SyndContent syndContent = new SyndContentImpl();
323 
324             syndContent.setType(RSSUtil.DEFAULT_ENTRY_TYPE);
325 
326             if (diff) {
327                 if (latestPage != null) {
328                     link +=
329                         "?" + PortalUtil.getPortletNamespace(PortletKeys.WIKI) +
330                             "version=" + page.getVersion();
331 
332                     String value = getPageDiff(
333                         companyId, latestPage, page, locale);
334 
335                     syndContent.setValue(value);
336 
337                     syndEntry.setDescription(syndContent);
338 
339                     entries.add(syndEntry);
340                 }
341             }
342             else {
343                 String value = null;
344 
345                 if (displayStyle.equals(RSSUtil.DISPLAY_STYLE_ABSTRACT)) {
346                     value = StringUtil.shorten(
347                         HtmlUtil.extractText(page.getContent()),
348                         _RSS_ABSTRACT_LENGTH, StringPool.BLANK);
349                 }
350                 else if (displayStyle.equals(RSSUtil.DISPLAY_STYLE_TITLE)) {
351                     value = StringPool.BLANK;
352                 }
353                 else {
354                     value = page.getContent();
355                 }
356 
357                 syndContent.setValue(value);
358 
359                 syndEntry.setDescription(syndContent);
360 
361                 entries.add(syndEntry);
362             }
363 
364             syndEntry.setLink(link);
365 
366             latestPage = page;
367         }
368 
369         try {
370             return RSSUtil.export(syndFeed);
371         }
372         catch (FeedException fe) {
373             throw new SystemException(fe);
374         }
375         catch (IOException ioe) {
376             throw new SystemException(ioe);
377         }
378     }
379 
380     protected String getPageDiff(
381             long companyId, WikiPage latestPage, WikiPage page,
382             Locale locale)
383         throws SystemException {
384 
385         String sourceContent = WikiUtil.processContent(latestPage.getContent());
386         String targetContent = WikiUtil.processContent(page.getContent());
387 
388         sourceContent = HtmlUtil.escape(sourceContent);
389         targetContent = HtmlUtil.escape(targetContent);
390 
391         List<DiffResult>[] diffResults = DiffUtil.diff(
392             new StringReader(sourceContent), new StringReader(targetContent));
393 
394         String template = ContentUtil.get(
395             "com/liferay/portlet/wiki/dependencies/rss.vm");
396 
397         Map<String, Object> variables = new HashMap<String, Object>();
398 
399         variables.put("companyId", companyId);
400         variables.put("contextLine", DiffUtil.CONTEXT_LINE);
401         variables.put("diffUtil", new DiffUtil());
402         variables.put("languageUtil", LanguageUtil.getLanguage());
403         variables.put("locale", locale);
404         variables.put("sourceResults", diffResults[0]);
405         variables.put("targetResults", diffResults[1]);
406 
407         try {
408             return VelocityUtil.evaluate(template, variables);
409         }
410         catch (Exception e) {
411             throw new SystemException(e);
412         }
413     }
414 
415     private static final int _RSS_ABSTRACT_LENGTH = GetterUtil.getInteger(
416         PropsUtil.get(PropsKeys.WIKI_RSS_ABSTRACT_LENGTH));
417 
418 }