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