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