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