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