1   /**
2    * Copyright (c) 2000-2010 Liferay, Inc. All rights reserved.
3    *
4    * The contents of this file are subject to the terms of the Liferay Enterprise
5    * Subscription License ("License"). You may not use this file except in
6    * compliance with the License. You can obtain a copy of the License by
7    * contacting Liferay, Inc. See the License for the specific language governing
8    * permissions and limitations under the License, including but not limited to
9    * distribution rights of the Software.
10   *
11   *
12   * 
13   */
14  
15  package com.liferay.portlet.wiki.service.impl;
16  
17  import com.liferay.portal.PortalException;
18  import com.liferay.portal.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.theme.ThemeDisplay;
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.impl.WikiPageImpl;
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  import javax.portlet.PortletPreferences;
65  
66  /**
67   * <a href="WikiPageServiceImpl.java.html"><b><i>View Source</i></b></a>
68   *
69   * @author Brian Wing Shun Chan
70   * @author Jorge Ferrer
71   * @author Raymond Augé
72   */
73  public class WikiPageServiceImpl extends WikiPageServiceBaseImpl {
74  
75      public WikiPage addPage(
76              long nodeId, String title, String content, String summary,
77              boolean minorEdit, PortletPreferences prefs,
78              ThemeDisplay themeDisplay)
79          throws PortalException, SystemException {
80  
81          WikiNodePermission.check(
82              getPermissionChecker(), nodeId, ActionKeys.ADD_PAGE);
83  
84          return wikiPageLocalService.addPage(
85              getUserId(), nodeId, title, content, summary, minorEdit, prefs,
86              themeDisplay);
87      }
88  
89      public WikiPage addPage(
90              long nodeId, String title, String content, String summary,
91              boolean minorEdit, String format, String parentTitle,
92              String redirectTitle, String[] tagsEntries,
93              PortletPreferences prefs, ThemeDisplay themeDisplay)
94          throws PortalException, SystemException {
95  
96          WikiNodePermission.check(
97              getPermissionChecker(), nodeId, ActionKeys.ADD_PAGE);
98  
99          return wikiPageLocalService.addPage(
100             null, getUserId(), nodeId, title, WikiPageImpl.DEFAULT_VERSION,
101             content, summary, minorEdit, format, true, parentTitle,
102             redirectTitle, tagsEntries, prefs, themeDisplay);
103     }
104 
105     public void addPageAttachments(
106             long nodeId, String title,
107             List<ObjectValuePair<String, byte[]>> files)
108         throws PortalException, SystemException {
109 
110         WikiNodePermission.check(
111             getPermissionChecker(), nodeId, ActionKeys.ADD_ATTACHMENT);
112 
113         wikiPageLocalService.addPageAttachments(nodeId, title, files);
114     }
115 
116     public void changeParent(
117             long nodeId, String title, String newParentTitle,
118             PortletPreferences prefs, ThemeDisplay themeDisplay)
119         throws PortalException, SystemException {
120 
121         WikiPagePermission.check(
122             getPermissionChecker(), nodeId, title, ActionKeys.DELETE);
123 
124         WikiNodePermission.check(
125             getPermissionChecker(), nodeId, ActionKeys.ADD_PAGE);
126 
127         wikiPageLocalService.changeParent(
128             getUserId(), nodeId, title, newParentTitle, prefs, themeDisplay);
129     }
130 
131     public void deletePage(long nodeId, String title)
132         throws PortalException, SystemException {
133 
134         WikiPagePermission.check(
135             getPermissionChecker(), nodeId, title, ActionKeys.DELETE);
136 
137         wikiPageLocalService.deletePage(nodeId, title);
138     }
139 
140     public void deletePageAttachment(long nodeId, String title, String fileName)
141         throws PortalException, SystemException {
142 
143         WikiPagePermission.check(
144             getPermissionChecker(), nodeId, title, ActionKeys.DELETE);
145 
146         wikiPageLocalService.deletePageAttachment(nodeId, title, fileName);
147     }
148 
149     public List<WikiPage> getNodePages(long nodeId, int max)
150         throws PortalException, SystemException {
151 
152         List<WikiPage> pages = new ArrayList<WikiPage>();
153 
154         int lastIntervalStart = 0;
155         boolean listNotExhausted = true;
156 
157         while ((pages.size() < max) && listNotExhausted) {
158             List<WikiPage> pageList = wikiPageLocalService.getPages(
159                 nodeId, true, lastIntervalStart, lastIntervalStart + max);
160 
161             Iterator<WikiPage> itr = pageList.iterator();
162 
163             lastIntervalStart += max;
164             listNotExhausted = (pageList.size() == max);
165 
166             while (itr.hasNext() && (pages.size() < max)) {
167                 WikiPage page = itr.next();
168 
169                 if (WikiPagePermission.contains(getPermissionChecker(), page,
170                         ActionKeys.VIEW)) {
171 
172                     pages.add(page);
173                 }
174             }
175         }
176 
177         return pages;
178     }
179 
180     public String getNodePagesRSS(
181             long nodeId, int max, String type, double version,
182             String displayStyle, String feedURL, String entryURL)
183         throws PortalException, SystemException {
184 
185         WikiNodePermission.check(
186             getPermissionChecker(), nodeId, ActionKeys.VIEW);
187 
188         WikiNode node = wikiNodePersistence.findByPrimaryKey(nodeId);
189 
190         long companyId = node.getCompanyId();
191         String name = node.getName();
192         String description = node.getDescription();
193         List<WikiPage> pages = getNodePages(nodeId, max);
194         boolean diff = false;
195         Locale locale = null;
196 
197         return exportToRSS(
198             companyId, name, description, type, version, displayStyle,
199             feedURL, entryURL, pages, diff, locale);
200     }
201 
202     public WikiPage getPage(long nodeId, String title)
203         throws PortalException, SystemException {
204 
205         WikiPagePermission.check(
206             getPermissionChecker(), nodeId, title, ActionKeys.VIEW);
207 
208         return wikiPageLocalService.getPage(nodeId, title);
209     }
210 
211     public WikiPage getPage(long nodeId, String title, double version)
212         throws PortalException, SystemException {
213 
214         WikiPagePermission.check(
215             getPermissionChecker(), nodeId, title, ActionKeys.VIEW);
216 
217         return wikiPageLocalService.getPage(nodeId, title, version);
218     }
219 
220     public String getPagesRSS(
221             long companyId, long nodeId, String title, int max, String type,
222             double version, String displayStyle, String feedURL,
223             String entryURL, Locale locale)
224         throws PortalException, SystemException {
225 
226         WikiPagePermission.check(
227             getPermissionChecker(), nodeId, title, ActionKeys.VIEW);
228 
229         String description = title;
230         List<WikiPage> pages = wikiPageLocalService.getPages(
231             nodeId, title, 0, max, new PageCreateDateComparator(true));
232         boolean diff = true;
233 
234         return exportToRSS(
235             companyId, title, description, type, version, displayStyle, feedURL,
236             entryURL, pages, diff, locale);
237     }
238 
239     public void movePage(
240             long nodeId, String title, String newTitle,
241             PortletPreferences prefs, ThemeDisplay themeDisplay)
242         throws PortalException, SystemException {
243 
244         WikiPagePermission.check(
245             getPermissionChecker(), nodeId, title, ActionKeys.DELETE);
246 
247         WikiNodePermission.check(
248             getPermissionChecker(), nodeId, ActionKeys.ADD_PAGE);
249 
250         wikiPageLocalService.movePage(
251             getUserId(), nodeId, title, newTitle, prefs, themeDisplay);
252     }
253 
254     public WikiPage revertPage(
255             long nodeId, String title, double version, PortletPreferences prefs,
256             ThemeDisplay themeDisplay)
257         throws PortalException, SystemException {
258 
259         WikiPagePermission.check(
260             getPermissionChecker(), nodeId, title, ActionKeys.UPDATE);
261 
262         return wikiPageLocalService.revertPage(
263             getUserId(), nodeId, title, version, prefs, themeDisplay);
264     }
265 
266     public void subscribePage(long nodeId, String title)
267         throws PortalException, SystemException {
268 
269         WikiPagePermission.check(
270             getPermissionChecker(), nodeId, title, ActionKeys.SUBSCRIBE);
271 
272         wikiPageLocalService.subscribePage(getUserId(), nodeId, title);
273     }
274 
275     public void unsubscribePage(long nodeId, String title)
276         throws PortalException, SystemException {
277 
278         WikiPagePermission.check(
279             getPermissionChecker(), nodeId, title, ActionKeys.SUBSCRIBE);
280 
281         wikiPageLocalService.unsubscribePage(getUserId(), nodeId, title);
282     }
283 
284     public WikiPage updatePage(
285             long nodeId, String title, double version, String content,
286             String summary, boolean minorEdit, String format,
287             String parentTitle, String redirectTitle, String[] tagsEntries,
288             PortletPreferences prefs, ThemeDisplay themeDisplay)
289         throws PortalException, SystemException {
290 
291         WikiPagePermission.check(
292             getPermissionChecker(), nodeId, title, ActionKeys.UPDATE);
293 
294         return wikiPageLocalService.updatePage(
295             getUserId(), nodeId, title, version, content, summary, minorEdit,
296             format, parentTitle, redirectTitle, tagsEntries, prefs,
297             themeDisplay);
298     }
299 
300     protected String exportToRSS(
301             long companyId, String name, String description, String type,
302             double version, String displayStyle, String feedURL,
303             String entryURL, List<WikiPage> pages, boolean diff, Locale locale)
304         throws SystemException {
305 
306         SyndFeed syndFeed = new SyndFeedImpl();
307 
308         syndFeed.setFeedType(RSSUtil.getFeedType(type, version));
309         syndFeed.setTitle(name);
310         syndFeed.setLink(feedURL);
311         syndFeed.setDescription(description);
312 
313         List<SyndEntry> entries = new ArrayList<SyndEntry>();
314 
315         syndFeed.setEntries(entries);
316 
317         WikiPage latestPage = null;
318 
319         StringBundler link = new StringBundler(7);
320 
321         for (WikiPage page : pages) {
322             String author = PortalUtil.getUserName(
323                 page.getUserId(), page.getUserName());
324 
325             String title =
326                 page.getTitle() + StringPool.SPACE + page.getVersion();
327 
328             if (page.isMinorEdit()) {
329                 title +=
330                     StringPool.SPACE + StringPool.OPEN_PARENTHESIS +
331                         LanguageUtil.get(locale, "minor-edit") +
332                             StringPool.CLOSE_PARENTHESIS;
333             }
334 
335             link.setIndex(0);
336 
337             link.append(entryURL);
338             link.append(StringPool.AMPERSAND);
339             link.append(HttpUtil.encodeURL(page.getTitle()));
340 
341             SyndEntry syndEntry = new SyndEntryImpl();
342 
343             syndEntry.setAuthor(author);
344             syndEntry.setTitle(title);
345             syndEntry.setPublishedDate(page.getCreateDate());
346             syndEntry.setUpdatedDate(page.getModifiedDate());
347 
348             SyndContent syndContent = new SyndContentImpl();
349 
350             syndContent.setType(RSSUtil.DEFAULT_ENTRY_TYPE);
351 
352             if (diff) {
353                 if (latestPage != null) {
354                     link.append(StringPool.QUESTION);
355                     link.append(
356                         PortalUtil.getPortletNamespace(PortletKeys.WIKI));
357                     link.append("version=");
358                     link.append(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.toString());
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 UnsyncStringReader(sourceContent),
419             new UnsyncStringReader(targetContent));
420 
421         String velocityTemplateId =
422             "com/liferay/portlet/wiki/dependencies/rss.vm";
423         String velocityTemplateContent = ContentUtil.get(velocityTemplateId);
424 
425         VelocityContext velocityContext =
426             VelocityEngineUtil.getWrappedStandardToolsContext();
427 
428         velocityContext.put("companyId", companyId);
429         velocityContext.put("contextLine", Diff.CONTEXT_LINE);
430         velocityContext.put("diffUtil", new DiffUtil());
431         velocityContext.put("languageUtil", LanguageUtil.getLanguage());
432         velocityContext.put("locale", locale);
433         velocityContext.put("sourceResults", diffResults[0]);
434         velocityContext.put("targetResults", diffResults[1]);
435 
436         try {
437             UnsyncStringWriter unsyncStringWriter = new UnsyncStringWriter(
438                 true);
439 
440             VelocityEngineUtil.mergeTemplate(
441                 velocityTemplateId, velocityTemplateContent, velocityContext,
442                 unsyncStringWriter);
443 
444             return unsyncStringWriter.toString();
445         }
446         catch (Exception e) {
447             throw new SystemException(e);
448         }
449     }
450 
451     private static final int _RSS_ABSTRACT_LENGTH = GetterUtil.getInteger(
452         PropsUtil.get(PropsKeys.WIKI_RSS_ABSTRACT_LENGTH));
453 
454 }