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.HttpUtil;
34  import com.liferay.portal.kernel.util.ObjectValuePair;
35  import com.liferay.portal.kernel.util.StringPool;
36  import com.liferay.portal.kernel.util.StringUtil;
37  import com.liferay.portal.kernel.velocity.VelocityContext;
38  import com.liferay.portal.kernel.velocity.VelocityEngineUtil;
39  import com.liferay.portal.security.permission.ActionKeys;
40  import com.liferay.portal.service.ServiceContext;
41  import com.liferay.portal.util.ContentUtil;
42  import com.liferay.portal.util.PortalUtil;
43  import com.liferay.portal.util.PortletKeys;
44  import com.liferay.portal.util.PropsKeys;
45  import com.liferay.portal.util.PropsUtil;
46  import com.liferay.portlet.wiki.model.WikiNode;
47  import com.liferay.portlet.wiki.model.WikiPage;
48  import com.liferay.portlet.wiki.model.impl.WikiPageImpl;
49  import com.liferay.portlet.wiki.service.base.WikiPageServiceBaseImpl;
50  import com.liferay.portlet.wiki.service.permission.WikiNodePermission;
51  import com.liferay.portlet.wiki.service.permission.WikiPagePermission;
52  import com.liferay.portlet.wiki.util.WikiUtil;
53  import com.liferay.portlet.wiki.util.comparator.PageCreateDateComparator;
54  import com.liferay.util.RSSUtil;
55  
56  import com.sun.syndication.feed.synd.SyndContent;
57  import com.sun.syndication.feed.synd.SyndContentImpl;
58  import com.sun.syndication.feed.synd.SyndEntry;
59  import com.sun.syndication.feed.synd.SyndEntryImpl;
60  import com.sun.syndication.feed.synd.SyndFeed;
61  import com.sun.syndication.feed.synd.SyndFeedImpl;
62  import com.sun.syndication.io.FeedException;
63  
64  import java.io.StringReader;
65  import java.io.StringWriter;
66  
67  import java.util.ArrayList;
68  import java.util.Iterator;
69  import java.util.List;
70  import java.util.Locale;
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   * @author Raymond Augé
78   *
79   */
80  public class WikiPageServiceImpl extends WikiPageServiceBaseImpl {
81  
82      public WikiPage addPage(
83              long nodeId, String title, String content, String summary,
84              boolean minorEdit, ServiceContext serviceContext)
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,
92              serviceContext);
93      }
94  
95      public WikiPage addPage(
96              long nodeId, String title, String content, String summary,
97              boolean minorEdit, String format, String parentTitle,
98              String redirectTitle, ServiceContext serviceContext)
99          throws PortalException, SystemException {
100 
101         WikiNodePermission.check(
102             getPermissionChecker(), nodeId, ActionKeys.ADD_PAGE);
103 
104         return wikiPageLocalService.addPage(
105             null, getUserId(), nodeId, title, WikiPageImpl.DEFAULT_VERSION,
106             content, summary, minorEdit, format, true, parentTitle,
107             redirectTitle, serviceContext);
108     }
109 
110     public void addPageAttachments(
111             long nodeId, String title,
112             List<ObjectValuePair<String, byte[]>> files)
113         throws PortalException, SystemException {
114 
115         WikiNodePermission.check(
116             getPermissionChecker(), nodeId, ActionKeys.ADD_ATTACHMENT);
117 
118         wikiPageLocalService.addPageAttachments(nodeId, title, files);
119     }
120 
121     public void changeParent(
122             long nodeId, String title, String newParentTitle,
123             ServiceContext serviceContext)
124         throws PortalException, SystemException {
125 
126         WikiPagePermission.check(
127             getPermissionChecker(), nodeId, title, ActionKeys.DELETE);
128 
129         WikiNodePermission.check(
130             getPermissionChecker(), nodeId, ActionKeys.ADD_PAGE);
131 
132         wikiPageLocalService.changeParent(
133             getUserId(), nodeId, title, newParentTitle, serviceContext);
134     }
135 
136     public void deletePage(long nodeId, String title)
137         throws PortalException, SystemException {
138 
139         WikiPagePermission.check(
140             getPermissionChecker(), nodeId, title, ActionKeys.DELETE);
141 
142         wikiPageLocalService.deletePage(nodeId, title);
143     }
144 
145     public void deletePageAttachment(long nodeId, String title, String fileName)
146         throws PortalException, SystemException {
147 
148         WikiPagePermission.check(
149             getPermissionChecker(), nodeId, title, ActionKeys.DELETE);
150 
151         wikiPageLocalService.deletePageAttachment(nodeId, title, fileName);
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, double version)
217         throws PortalException, SystemException {
218 
219         WikiPagePermission.check(
220             getPermissionChecker(), nodeId, title, ActionKeys.VIEW);
221 
222         return wikiPageLocalService.getPage(nodeId, title, version);
223     }
224 
225     public String getPagesRSS(
226             long companyId, long nodeId, String title, int max, String type,
227             double version, String displayStyle, String feedURL,
228             String entryURL, Locale locale)
229         throws PortalException, SystemException {
230 
231         WikiPagePermission.check(
232             getPermissionChecker(), nodeId, title, ActionKeys.VIEW);
233 
234         String description = title;
235         List<WikiPage> pages = wikiPageLocalService.getPages(
236             nodeId, title, 0, max, new PageCreateDateComparator(true));
237         boolean diff = true;
238 
239         return exportToRSS(
240             companyId, title, description, type, version, displayStyle, feedURL,
241             entryURL, pages, diff, locale);
242     }
243 
244     public void movePage(
245             long nodeId, String title, String newTitle,
246             ServiceContext serviceContext)
247         throws PortalException, SystemException {
248 
249         WikiPagePermission.check(
250             getPermissionChecker(), nodeId, title, ActionKeys.DELETE);
251 
252         WikiNodePermission.check(
253             getPermissionChecker(), nodeId, ActionKeys.ADD_PAGE);
254 
255         wikiPageLocalService.movePage(
256             getUserId(), nodeId, title, newTitle, serviceContext);
257     }
258 
259     public WikiPage revertPage(
260             long nodeId, String title, double version,
261             ServiceContext serviceContext)
262         throws PortalException, SystemException {
263 
264         WikiPagePermission.check(
265             getPermissionChecker(), nodeId, title, ActionKeys.UPDATE);
266 
267         return wikiPageLocalService.revertPage(
268             getUserId(), nodeId, title, version, serviceContext);
269     }
270 
271     public void subscribePage(long nodeId, String title)
272         throws PortalException, SystemException {
273 
274         WikiPagePermission.check(
275             getPermissionChecker(), nodeId, title, ActionKeys.SUBSCRIBE);
276 
277         wikiPageLocalService.subscribePage(getUserId(), nodeId, title);
278     }
279 
280     public void unsubscribePage(long nodeId, String title)
281         throws PortalException, SystemException {
282 
283         WikiPagePermission.check(
284             getPermissionChecker(), nodeId, title, ActionKeys.SUBSCRIBE);
285 
286         wikiPageLocalService.unsubscribePage(getUserId(), nodeId, title);
287     }
288 
289     public WikiPage updatePage(
290             long nodeId, String title, double version, String content,
291             String summary, boolean minorEdit, String format,
292             String parentTitle, String redirectTitle,
293             ServiceContext serviceContext)
294         throws PortalException, SystemException {
295 
296         WikiPagePermission.check(
297             getPermissionChecker(), nodeId, title, ActionKeys.UPDATE);
298 
299         return wikiPageLocalService.updatePage(
300             getUserId(), nodeId, title, version, content, summary, minorEdit,
301             format, parentTitle, redirectTitle, serviceContext);
302     }
303 
304     protected String exportToRSS(
305             long companyId, String name, String description, String type,
306             double version, String displayStyle, String feedURL,
307             String entryURL, List<WikiPage> pages, boolean diff, Locale locale)
308         throws SystemException {
309 
310         SyndFeed syndFeed = new SyndFeedImpl();
311 
312         syndFeed.setFeedType(RSSUtil.getFeedType(type, version));
313         syndFeed.setTitle(name);
314         syndFeed.setLink(feedURL);
315         syndFeed.setDescription(description);
316 
317         List<SyndEntry> entries = new ArrayList<SyndEntry>();
318 
319         syndFeed.setEntries(entries);
320 
321         WikiPage latestPage = null;
322 
323         for (WikiPage page : pages) {
324             String author = PortalUtil.getUserName(
325                 page.getUserId(), page.getUserName());
326 
327             String title =
328                 page.getTitle() + StringPool.SPACE + page.getVersion();
329 
330             if (page.isMinorEdit()) {
331                 title +=
332                     StringPool.SPACE + StringPool.OPEN_PARENTHESIS +
333                         LanguageUtil.get(locale, "minor-edit") +
334                             StringPool.CLOSE_PARENTHESIS;
335             }
336 
337             String link = entryURL + HttpUtil.encodeURL(page.getTitle());
338 
339             SyndEntry syndEntry = new SyndEntryImpl();
340 
341             syndEntry.setAuthor(author);
342             syndEntry.setTitle(title);
343             syndEntry.setPublishedDate(page.getCreateDate());
344             syndEntry.setUpdatedDate(page.getModifiedDate());
345 
346             SyndContent syndContent = new SyndContentImpl();
347 
348             syndContent.setType(RSSUtil.DEFAULT_ENTRY_TYPE);
349 
350             if (diff) {
351                 if (latestPage != null) {
352                     link +=
353                         "?" + PortalUtil.getPortletNamespace(PortletKeys.WIKI) +
354                             "version=" + page.getVersion();
355 
356                     String value = getPageDiff(
357                         companyId, latestPage, page, locale);
358 
359                     syndContent.setValue(value);
360 
361                     syndEntry.setDescription(syndContent);
362 
363                     entries.add(syndEntry);
364                 }
365             }
366             else {
367                 String value = null;
368 
369                 if (displayStyle.equals(RSSUtil.DISPLAY_STYLE_ABSTRACT)) {
370                     value = StringUtil.shorten(
371                         HtmlUtil.extractText(page.getContent()),
372                         _RSS_ABSTRACT_LENGTH, StringPool.BLANK);
373                 }
374                 else if (displayStyle.equals(RSSUtil.DISPLAY_STYLE_TITLE)) {
375                     value = StringPool.BLANK;
376                 }
377                 else {
378                     value = page.getContent();
379                 }
380 
381                 syndContent.setValue(value);
382 
383                 syndEntry.setDescription(syndContent);
384 
385                 entries.add(syndEntry);
386             }
387 
388             syndEntry.setLink(link);
389             syndEntry.setUri(syndEntry.getLink());
390 
391             latestPage = page;
392         }
393 
394         try {
395             return RSSUtil.export(syndFeed);
396         }
397         catch (FeedException fe) {
398             throw new SystemException(fe);
399         }
400     }
401 
402     protected String getPageDiff(
403             long companyId, WikiPage latestPage, WikiPage page,
404             Locale locale)
405         throws SystemException {
406 
407         String sourceContent = WikiUtil.processContent(latestPage.getContent());
408         String targetContent = WikiUtil.processContent(page.getContent());
409 
410         sourceContent = HtmlUtil.escape(sourceContent);
411         targetContent = HtmlUtil.escape(targetContent);
412 
413         List<DiffResult>[] diffResults = DiffUtil.diff(
414             new StringReader(sourceContent), new StringReader(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             StringWriter stringWriter = new StringWriter();
433 
434             VelocityEngineUtil.mergeTemplate(
435                 velocityTemplateId, velocityTemplateContent, velocityContext,
436                 stringWriter);
437 
438             return stringWriter.toString();
439         }
440         catch (Exception e) {
441             throw new SystemException(e);
442         }
443     }
444 
445     private static final int _RSS_ABSTRACT_LENGTH = GetterUtil.getInteger(
446         PropsUtil.get(PropsKeys.WIKI_RSS_ABSTRACT_LENGTH));
447 
448 }