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