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