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 WikiPage getDraftPage(long nodeId, String title)
146         throws PortalException, SystemException {
147 
148         WikiPagePermission.check(
149             getPermissionChecker(), nodeId, title, ActionKeys.VIEW);
150 
151         return wikiPageLocalService.getDraftPage(nodeId, title);
152     }
153 
154     public List<WikiPage> getNodePages(long nodeId, int max)
155         throws PortalException, SystemException {
156 
157         List<WikiPage> pages = new ArrayList<WikiPage>();
158 
159         int lastIntervalStart = 0;
160         boolean listNotExhausted = true;
161 
162         while ((pages.size() < max) && listNotExhausted) {
163             List<WikiPage> pageList = wikiPageLocalService.getPages(
164                 nodeId, true, lastIntervalStart, lastIntervalStart + max);
165 
166             Iterator<WikiPage> itr = pageList.iterator();
167 
168             lastIntervalStart += max;
169             listNotExhausted = (pageList.size() == max);
170 
171             while (itr.hasNext() && (pages.size() < max)) {
172                 WikiPage page = itr.next();
173 
174                 if (WikiPagePermission.contains(getPermissionChecker(), page,
175                         ActionKeys.VIEW)) {
176 
177                     pages.add(page);
178                 }
179             }
180         }
181 
182         return pages;
183     }
184 
185     public String getNodePagesRSS(
186             long nodeId, int max, String type, double version,
187             String displayStyle, String feedURL, String entryURL)
188         throws PortalException, SystemException {
189 
190         WikiNodePermission.check(
191             getPermissionChecker(), nodeId, ActionKeys.VIEW);
192 
193         WikiNode node = wikiNodePersistence.findByPrimaryKey(nodeId);
194 
195         long companyId = node.getCompanyId();
196         String name = node.getName();
197         String description = node.getDescription();
198         List<WikiPage> pages = getNodePages(nodeId, max);
199         boolean diff = false;
200         Locale locale = null;
201 
202         return exportToRSS(
203             companyId, name, description, type, version, displayStyle,
204             feedURL, entryURL, pages, diff, locale);
205     }
206 
207     public WikiPage getPage(long nodeId, String title)
208         throws PortalException, SystemException {
209 
210         WikiPagePermission.check(
211             getPermissionChecker(), nodeId, title, ActionKeys.VIEW);
212 
213         return wikiPageLocalService.getPage(nodeId, title);
214     }
215 
216     public WikiPage getPage(long nodeId, String title, boolean head)
217         throws PortalException, SystemException {
218 
219         WikiPagePermission.check(
220             getPermissionChecker(), nodeId, title, ActionKeys.VIEW);
221 
222         return wikiPageLocalService.getPage(nodeId, title, head);
223     }
224 
225     public WikiPage getPage(long nodeId, String title, double version)
226         throws PortalException, SystemException {
227 
228         WikiPagePermission.check(
229             getPermissionChecker(), nodeId, title, ActionKeys.VIEW);
230 
231         return wikiPageLocalService.getPage(nodeId, title, version);
232     }
233 
234     public String getPagesRSS(
235             long companyId, long nodeId, String title, int max, String type,
236             double version, String displayStyle, String feedURL,
237             String entryURL, Locale locale)
238         throws PortalException, SystemException {
239 
240         WikiPagePermission.check(
241             getPermissionChecker(), nodeId, title, ActionKeys.VIEW);
242 
243         String description = title;
244         List<WikiPage> pages = wikiPageLocalService.getPages(
245             nodeId, title, 0, max, new PageCreateDateComparator(true));
246         boolean diff = true;
247 
248         return exportToRSS(
249             companyId, title, description, type, version, displayStyle, feedURL,
250             entryURL, pages, diff, locale);
251     }
252 
253     public void movePage(
254             long nodeId, String title, String newTitle,
255             ServiceContext serviceContext)
256         throws PortalException, SystemException {
257 
258         WikiPagePermission.check(
259             getPermissionChecker(), nodeId, title, ActionKeys.DELETE);
260 
261         WikiNodePermission.check(
262             getPermissionChecker(), nodeId, ActionKeys.ADD_PAGE);
263 
264         wikiPageLocalService.movePage(
265             getUserId(), nodeId, title, newTitle, serviceContext);
266     }
267 
268     public WikiPage revertPage(
269             long nodeId, String title, double version,
270             ServiceContext serviceContext)
271         throws PortalException, SystemException {
272 
273         WikiPagePermission.check(
274             getPermissionChecker(), nodeId, title, ActionKeys.UPDATE);
275 
276         return wikiPageLocalService.revertPage(
277             getUserId(), nodeId, title, version, serviceContext);
278     }
279 
280     public void subscribePage(long nodeId, String title)
281         throws PortalException, SystemException {
282 
283         WikiPagePermission.check(
284             getPermissionChecker(), nodeId, title, ActionKeys.SUBSCRIBE);
285 
286         wikiPageLocalService.subscribePage(getUserId(), nodeId, title);
287     }
288 
289     public void unsubscribePage(long nodeId, String title)
290         throws PortalException, SystemException {
291 
292         WikiPagePermission.check(
293             getPermissionChecker(), nodeId, title, ActionKeys.SUBSCRIBE);
294 
295         wikiPageLocalService.unsubscribePage(getUserId(), nodeId, title);
296     }
297 
298     public WikiPage updatePage(
299             long nodeId, String title, double version, String content,
300             String summary, boolean minorEdit, String format,
301             String parentTitle, String redirectTitle,
302             ServiceContext serviceContext)
303         throws PortalException, SystemException {
304 
305         WikiPagePermission.check(
306             getPermissionChecker(), nodeId, title, ActionKeys.UPDATE);
307 
308         return wikiPageLocalService.updatePage(
309             getUserId(), nodeId, title, version, content, summary, minorEdit,
310             format, parentTitle, redirectTitle, serviceContext);
311     }
312 
313     protected String exportToRSS(
314             long companyId, String name, String description, String type,
315             double version, String displayStyle, String feedURL,
316             String entryURL, List<WikiPage> pages, boolean diff, Locale locale)
317         throws SystemException {
318 
319         SyndFeed syndFeed = new SyndFeedImpl();
320 
321         syndFeed.setFeedType(RSSUtil.getFeedType(type, version));
322         syndFeed.setTitle(name);
323         syndFeed.setLink(feedURL);
324         syndFeed.setDescription(description);
325 
326         List<SyndEntry> entries = new ArrayList<SyndEntry>();
327 
328         syndFeed.setEntries(entries);
329 
330         WikiPage latestPage = null;
331 
332         StringBundler link = new StringBundler(7);
333 
334         for (WikiPage page : pages) {
335             String author = HtmlUtil.escape(
336                 PortalUtil.getUserName(page.getUserId(), page.getUserName()));
337 
338             String title =
339                 page.getTitle() + StringPool.SPACE + page.getVersion();
340 
341             if (page.isMinorEdit()) {
342                 title +=
343                     StringPool.SPACE + StringPool.OPEN_PARENTHESIS +
344                         LanguageUtil.get(locale, "minor-edit") +
345                             StringPool.CLOSE_PARENTHESIS;
346             }
347 
348             link.setIndex(0);
349 
350             link.append(entryURL);
351             link.append(StringPool.AMPERSAND);
352             link.append(HttpUtil.encodeURL(page.getTitle()));
353 
354             SyndEntry syndEntry = new SyndEntryImpl();
355 
356             syndEntry.setAuthor(author);
357             syndEntry.setTitle(title);
358             syndEntry.setPublishedDate(page.getCreateDate());
359             syndEntry.setUpdatedDate(page.getModifiedDate());
360 
361             SyndContent syndContent = new SyndContentImpl();
362 
363             syndContent.setType(RSSUtil.DEFAULT_ENTRY_TYPE);
364 
365             if (diff) {
366                 if (latestPage != null) {
367                     link.append(StringPool.QUESTION);
368                     link.append(
369                         PortalUtil.getPortletNamespace(PortletKeys.WIKI));
370                     link.append("version=");
371                     link.append(page.getVersion());
372 
373                     String value = getPageDiff(
374                         companyId, latestPage, page, locale);
375 
376                     syndContent.setValue(value);
377 
378                     syndEntry.setDescription(syndContent);
379 
380                     entries.add(syndEntry);
381                 }
382             }
383             else {
384                 String value = null;
385 
386                 if (displayStyle.equals(RSSUtil.DISPLAY_STYLE_ABSTRACT)) {
387                     value = StringUtil.shorten(
388                         HtmlUtil.extractText(page.getContent()),
389                         _RSS_ABSTRACT_LENGTH, StringPool.BLANK);
390                 }
391                 else if (displayStyle.equals(RSSUtil.DISPLAY_STYLE_TITLE)) {
392                     value = StringPool.BLANK;
393                 }
394                 else {
395                     value = page.getContent();
396                 }
397 
398                 syndContent.setValue(value);
399 
400                 syndEntry.setDescription(syndContent);
401 
402                 entries.add(syndEntry);
403             }
404 
405             syndEntry.setLink(link.toString());
406             syndEntry.setUri(syndEntry.getLink());
407 
408             latestPage = page;
409         }
410 
411         try {
412             return RSSUtil.export(syndFeed);
413         }
414         catch (FeedException fe) {
415             throw new SystemException(fe);
416         }
417     }
418 
419     protected String getPageDiff(
420             long companyId, WikiPage latestPage, WikiPage page,
421             Locale locale)
422         throws SystemException {
423 
424         String sourceContent = WikiUtil.processContent(latestPage.getContent());
425         String targetContent = WikiUtil.processContent(page.getContent());
426 
427         sourceContent = HtmlUtil.escape(sourceContent);
428         targetContent = HtmlUtil.escape(targetContent);
429 
430         List<DiffResult>[] diffResults = DiffUtil.diff(
431             new UnsyncStringReader(sourceContent),
432             new UnsyncStringReader(targetContent));
433 
434         String velocityTemplateId =
435             "com/liferay/portlet/wiki/dependencies/rss.vm";
436         String velocityTemplateContent = ContentUtil.get(velocityTemplateId);
437 
438         VelocityContext velocityContext =
439             VelocityEngineUtil.getWrappedStandardToolsContext();
440 
441         velocityContext.put("companyId", companyId);
442         velocityContext.put("contextLine", Diff.CONTEXT_LINE);
443         velocityContext.put("diffUtil", new DiffUtil());
444         velocityContext.put("languageUtil", LanguageUtil.getLanguage());
445         velocityContext.put("locale", locale);
446         velocityContext.put("sourceResults", diffResults[0]);
447         velocityContext.put("targetResults", diffResults[1]);
448 
449         try {
450             UnsyncStringWriter unsyncStringWriter = new UnsyncStringWriter();
451 
452             VelocityEngineUtil.mergeTemplate(
453                 velocityTemplateId, velocityTemplateContent, velocityContext,
454                 unsyncStringWriter);
455 
456             return unsyncStringWriter.toString();
457         }
458         catch (Exception e) {
459             throw new SystemException(e);
460         }
461     }
462 
463     private static final int _RSS_ABSTRACT_LENGTH = GetterUtil.getInteger(
464         PropsUtil.get(PropsKeys.WIKI_RSS_ABSTRACT_LENGTH));
465 
466 }