001    /**
002     * Copyright (c) 2000-2010 Liferay, Inc. All rights reserved.
003     *
004     * This library is free software; you can redistribute it and/or modify it under
005     * the terms of the GNU Lesser General Public License as published by the Free
006     * Software Foundation; either version 2.1 of the License, or (at your option)
007     * any later version.
008     *
009     * This library is distributed in the hope that it will be useful, but WITHOUT
010     * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
011     * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
012     * details.
013     */
014    
015    package com.liferay.portlet.wiki.service.impl;
016    
017    import com.liferay.portal.kernel.exception.PortalException;
018    import com.liferay.portal.kernel.exception.SystemException;
019    import com.liferay.portal.kernel.io.unsync.UnsyncStringReader;
020    import com.liferay.portal.kernel.io.unsync.UnsyncStringWriter;
021    import com.liferay.portal.kernel.language.LanguageUtil;
022    import com.liferay.portal.kernel.util.Diff;
023    import com.liferay.portal.kernel.util.DiffResult;
024    import com.liferay.portal.kernel.util.DiffUtil;
025    import com.liferay.portal.kernel.util.GetterUtil;
026    import com.liferay.portal.kernel.util.HtmlUtil;
027    import com.liferay.portal.kernel.util.HttpUtil;
028    import com.liferay.portal.kernel.util.ObjectValuePair;
029    import com.liferay.portal.kernel.util.PropsKeys;
030    import com.liferay.portal.kernel.util.StringBundler;
031    import com.liferay.portal.kernel.util.StringPool;
032    import com.liferay.portal.kernel.util.StringUtil;
033    import com.liferay.portal.kernel.velocity.VelocityContext;
034    import com.liferay.portal.kernel.velocity.VelocityEngineUtil;
035    import com.liferay.portal.security.permission.ActionKeys;
036    import com.liferay.portal.service.ServiceContext;
037    import com.liferay.portal.util.ContentUtil;
038    import com.liferay.portal.util.PortalUtil;
039    import com.liferay.portal.util.PortletKeys;
040    import com.liferay.portal.util.PropsUtil;
041    import com.liferay.portlet.wiki.model.WikiNode;
042    import com.liferay.portlet.wiki.model.WikiPage;
043    import com.liferay.portlet.wiki.model.WikiPageConstants;
044    import com.liferay.portlet.wiki.service.base.WikiPageServiceBaseImpl;
045    import com.liferay.portlet.wiki.service.permission.WikiNodePermission;
046    import com.liferay.portlet.wiki.service.permission.WikiPagePermission;
047    import com.liferay.portlet.wiki.util.WikiUtil;
048    import com.liferay.portlet.wiki.util.comparator.PageCreateDateComparator;
049    import com.liferay.util.RSSUtil;
050    
051    import com.sun.syndication.feed.synd.SyndContent;
052    import com.sun.syndication.feed.synd.SyndContentImpl;
053    import com.sun.syndication.feed.synd.SyndEntry;
054    import com.sun.syndication.feed.synd.SyndEntryImpl;
055    import com.sun.syndication.feed.synd.SyndFeed;
056    import com.sun.syndication.feed.synd.SyndFeedImpl;
057    import com.sun.syndication.io.FeedException;
058    
059    import java.util.ArrayList;
060    import java.util.Iterator;
061    import java.util.List;
062    import java.util.Locale;
063    
064    /**
065     * @author Brian Wing Shun Chan
066     * @author Jorge Ferrer
067     * @author Raymond Augé
068     */
069    public class WikiPageServiceImpl extends WikiPageServiceBaseImpl {
070    
071            public WikiPage addPage(
072                            long nodeId, String title, String content, String summary,
073                            boolean minorEdit, ServiceContext serviceContext)
074                    throws PortalException, SystemException {
075    
076                    WikiNodePermission.check(
077                            getPermissionChecker(), nodeId, ActionKeys.ADD_PAGE);
078    
079                    return wikiPageLocalService.addPage(
080                            getUserId(), nodeId, title, content, summary, minorEdit,
081                            serviceContext);
082            }
083    
084            public WikiPage addPage(
085                            long nodeId, String title, String content, String summary,
086                            boolean minorEdit, String format, String parentTitle,
087                            String redirectTitle, ServiceContext serviceContext)
088                    throws PortalException, SystemException {
089    
090                    WikiNodePermission.check(
091                            getPermissionChecker(), nodeId, ActionKeys.ADD_PAGE);
092    
093                    return wikiPageLocalService.addPage(
094                            getUserId(), nodeId, title, WikiPageConstants.DEFAULT_VERSION,
095                            content, summary, minorEdit, format, true, parentTitle,
096                            redirectTitle, serviceContext);
097            }
098    
099            public void addPageAttachments(
100                            long nodeId, String title,
101                            List<ObjectValuePair<String, byte[]>> files)
102                    throws PortalException, SystemException {
103    
104                    WikiNodePermission.check(
105                            getPermissionChecker(), nodeId, ActionKeys.ADD_ATTACHMENT);
106    
107                    wikiPageLocalService.addPageAttachments(nodeId, title, files);
108            }
109    
110            public void changeParent(
111                            long nodeId, String title, String newParentTitle,
112                            ServiceContext serviceContext)
113                    throws PortalException, SystemException {
114    
115                    WikiPagePermission.check(
116                            getPermissionChecker(), nodeId, title, ActionKeys.DELETE);
117    
118                    WikiNodePermission.check(
119                            getPermissionChecker(), nodeId, ActionKeys.ADD_PAGE);
120    
121                    wikiPageLocalService.changeParent(
122                            getUserId(), nodeId, title, newParentTitle, serviceContext);
123            }
124    
125            public void deletePage(long nodeId, String title)
126                    throws PortalException, SystemException {
127    
128                    WikiPagePermission.check(
129                            getPermissionChecker(), nodeId, title, ActionKeys.DELETE);
130    
131                    wikiPageLocalService.deletePage(nodeId, title);
132            }
133    
134            public void deletePage(long nodeId, String title, double version)
135                    throws PortalException, SystemException {
136    
137                    WikiPagePermission.check(
138                            getPermissionChecker(), nodeId, title, version, ActionKeys.DELETE);
139    
140                    wikiPageLocalService.deletePage(nodeId, title, version);
141            }
142    
143            public void deletePageAttachment(long nodeId, String title, String fileName)
144                    throws PortalException, SystemException {
145    
146                    WikiPagePermission.check(
147                            getPermissionChecker(), nodeId, title, ActionKeys.DELETE);
148    
149                    wikiPageLocalService.deletePageAttachment(nodeId, title, fileName);
150            }
151    
152            public WikiPage getDraftPage(long nodeId, String title)
153                    throws PortalException, SystemException {
154    
155                    WikiPagePermission.check(
156                            getPermissionChecker(), nodeId, title, ActionKeys.VIEW);
157    
158                    return wikiPageLocalService.getDraftPage(nodeId, title);
159            }
160    
161            public List<WikiPage> getNodePages(long nodeId, int max)
162                    throws PortalException, SystemException {
163    
164                    List<WikiPage> pages = new ArrayList<WikiPage>();
165    
166                    int lastIntervalStart = 0;
167                    boolean listNotExhausted = true;
168    
169                    while ((pages.size() < max) && listNotExhausted) {
170                            List<WikiPage> pageList = wikiPageLocalService.getPages(
171                                    nodeId, true, lastIntervalStart, lastIntervalStart + max);
172    
173                            Iterator<WikiPage> itr = pageList.iterator();
174    
175                            lastIntervalStart += max;
176                            listNotExhausted = (pageList.size() == max);
177    
178                            while (itr.hasNext() && (pages.size() < max)) {
179                                    WikiPage page = itr.next();
180    
181                                    if (WikiPagePermission.contains(getPermissionChecker(), page,
182                                                    ActionKeys.VIEW)) {
183    
184                                            pages.add(page);
185                                    }
186                            }
187                    }
188    
189                    return pages;
190            }
191    
192            public String getNodePagesRSS(
193                            long nodeId, int max, String type, double version,
194                            String displayStyle, String feedURL, String entryURL)
195                    throws PortalException, SystemException {
196    
197                    WikiNodePermission.check(
198                            getPermissionChecker(), nodeId, ActionKeys.VIEW);
199    
200                    WikiNode node = wikiNodePersistence.findByPrimaryKey(nodeId);
201    
202                    long companyId = node.getCompanyId();
203                    String name = node.getName();
204                    String description = node.getDescription();
205                    List<WikiPage> pages = getNodePages(nodeId, max);
206                    boolean diff = false;
207                    Locale locale = null;
208    
209                    return exportToRSS(
210                            companyId, name, description, type, version, displayStyle,
211                            feedURL, entryURL, pages, diff, locale);
212            }
213    
214            public WikiPage getPage(long nodeId, String title)
215                    throws PortalException, SystemException {
216    
217                    WikiPagePermission.check(
218                            getPermissionChecker(), nodeId, title, ActionKeys.VIEW);
219    
220                    return wikiPageLocalService.getPage(nodeId, title);
221            }
222    
223            public WikiPage getPage(long nodeId, String title, Boolean head)
224                    throws PortalException, SystemException {
225    
226                    WikiPagePermission.check(
227                            getPermissionChecker(), nodeId, title, ActionKeys.VIEW);
228    
229                    return wikiPageLocalService.getPage(nodeId, title, head);
230            }
231    
232            public WikiPage getPage(long nodeId, String title, double version)
233                    throws PortalException, SystemException {
234    
235                    WikiPagePermission.check(
236                            getPermissionChecker(), nodeId, title, ActionKeys.VIEW);
237    
238                    return wikiPageLocalService.getPage(nodeId, title, version);
239            }
240    
241            public String getPagesRSS(
242                            long companyId, long nodeId, String title, int max, String type,
243                            double version, String displayStyle, String feedURL,
244                            String entryURL, Locale locale)
245                    throws PortalException, SystemException {
246    
247                    WikiPagePermission.check(
248                            getPermissionChecker(), nodeId, title, ActionKeys.VIEW);
249    
250                    String description = title;
251                    List<WikiPage> pages = wikiPageLocalService.getPages(
252                            nodeId, title, 0, max, new PageCreateDateComparator(true));
253                    boolean diff = true;
254    
255                    return exportToRSS(
256                            companyId, title, description, type, version, displayStyle, feedURL,
257                            entryURL, pages, diff, locale);
258            }
259    
260            public void movePage(
261                            long nodeId, String title, String newTitle,
262                            ServiceContext serviceContext)
263                    throws PortalException, SystemException {
264    
265                    WikiPagePermission.check(
266                            getPermissionChecker(), nodeId, title, ActionKeys.DELETE);
267    
268                    WikiNodePermission.check(
269                            getPermissionChecker(), nodeId, ActionKeys.ADD_PAGE);
270    
271                    wikiPageLocalService.movePage(
272                            getUserId(), nodeId, title, newTitle, serviceContext);
273            }
274    
275            public WikiPage revertPage(
276                            long nodeId, String title, double version,
277                            ServiceContext serviceContext)
278                    throws PortalException, SystemException {
279    
280                    WikiPagePermission.check(
281                            getPermissionChecker(), nodeId, title, ActionKeys.UPDATE);
282    
283                    return wikiPageLocalService.revertPage(
284                            getUserId(), nodeId, title, version, serviceContext);
285            }
286    
287            public void subscribePage(long nodeId, String title)
288                    throws PortalException, SystemException {
289    
290                    WikiPagePermission.check(
291                            getPermissionChecker(), nodeId, title, ActionKeys.SUBSCRIBE);
292    
293                    wikiPageLocalService.subscribePage(getUserId(), nodeId, title);
294            }
295    
296            public void unsubscribePage(long nodeId, String title)
297                    throws PortalException, SystemException {
298    
299                    WikiPagePermission.check(
300                            getPermissionChecker(), nodeId, title, ActionKeys.SUBSCRIBE);
301    
302                    wikiPageLocalService.unsubscribePage(getUserId(), nodeId, title);
303            }
304    
305            public WikiPage updatePage(
306                            long nodeId, String title, double version, String content,
307                            String summary, boolean minorEdit, String format,
308                            String parentTitle, String redirectTitle,
309                            ServiceContext serviceContext)
310                    throws PortalException, SystemException {
311    
312                    WikiPagePermission.check(
313                            getPermissionChecker(), nodeId, title, ActionKeys.UPDATE);
314    
315                    return wikiPageLocalService.updatePage(
316                            getUserId(), nodeId, title, version, content, summary, minorEdit,
317                            format, parentTitle, redirectTitle, serviceContext);
318            }
319    
320            protected String exportToRSS(
321                            long companyId, String name, String description, String type,
322                            double version, String displayStyle, String feedURL,
323                            String entryURL, List<WikiPage> pages, boolean diff, Locale locale)
324                    throws SystemException {
325    
326                    SyndFeed syndFeed = new SyndFeedImpl();
327    
328                    syndFeed.setFeedType(RSSUtil.getFeedType(type, version));
329                    syndFeed.setTitle(name);
330                    syndFeed.setLink(feedURL);
331                    syndFeed.setDescription(description);
332    
333                    List<SyndEntry> entries = new ArrayList<SyndEntry>();
334    
335                    syndFeed.setEntries(entries);
336    
337                    WikiPage latestPage = null;
338    
339                    StringBundler link = new StringBundler(7);
340    
341                    for (WikiPage page : pages) {
342                            String author = HtmlUtil.escape(
343                                    PortalUtil.getUserName(page.getUserId(), page.getUserName()));
344    
345                            String title =
346                                    page.getTitle() + StringPool.SPACE + page.getVersion();
347    
348                            if (page.isMinorEdit()) {
349                                    title +=
350                                            StringPool.SPACE + StringPool.OPEN_PARENTHESIS +
351                                                    LanguageUtil.get(locale, "minor-edit") +
352                                                            StringPool.CLOSE_PARENTHESIS;
353                            }
354    
355                            link.setIndex(0);
356    
357                            link.append(entryURL);
358                            link.append(StringPool.AMPERSAND);
359                            link.append(HttpUtil.encodeURL(page.getTitle()));
360    
361                            SyndEntry syndEntry = new SyndEntryImpl();
362    
363                            syndEntry.setAuthor(author);
364                            syndEntry.setTitle(title);
365                            syndEntry.setPublishedDate(page.getCreateDate());
366                            syndEntry.setUpdatedDate(page.getModifiedDate());
367    
368                            SyndContent syndContent = new SyndContentImpl();
369    
370                            syndContent.setType(RSSUtil.DEFAULT_ENTRY_TYPE);
371    
372                            if (diff) {
373                                    if (latestPage != null) {
374                                            link.append(StringPool.QUESTION);
375                                            link.append(
376                                                    PortalUtil.getPortletNamespace(PortletKeys.WIKI));
377                                            link.append("version=");
378                                            link.append(page.getVersion());
379    
380                                            String value = getPageDiff(
381                                                    companyId, latestPage, page, locale);
382    
383                                            syndContent.setValue(value);
384    
385                                            syndEntry.setDescription(syndContent);
386    
387                                            entries.add(syndEntry);
388                                    }
389                            }
390                            else {
391                                    String value = null;
392    
393                                    if (displayStyle.equals(RSSUtil.DISPLAY_STYLE_ABSTRACT)) {
394                                            value = StringUtil.shorten(
395                                                    HtmlUtil.extractText(page.getContent()),
396                                                    _RSS_ABSTRACT_LENGTH, StringPool.BLANK);
397                                    }
398                                    else if (displayStyle.equals(RSSUtil.DISPLAY_STYLE_TITLE)) {
399                                            value = StringPool.BLANK;
400                                    }
401                                    else {
402                                            value = page.getContent();
403                                    }
404    
405                                    syndContent.setValue(value);
406    
407                                    syndEntry.setDescription(syndContent);
408    
409                                    entries.add(syndEntry);
410                            }
411    
412                            syndEntry.setLink(link.toString());
413                            syndEntry.setUri(syndEntry.getLink());
414    
415                            latestPage = page;
416                    }
417    
418                    try {
419                            return RSSUtil.export(syndFeed);
420                    }
421                    catch (FeedException fe) {
422                            throw new SystemException(fe);
423                    }
424            }
425    
426            protected String getPageDiff(
427                            long companyId, WikiPage latestPage, WikiPage page,
428                            Locale locale)
429                    throws SystemException {
430    
431                    String sourceContent = WikiUtil.processContent(latestPage.getContent());
432                    String targetContent = WikiUtil.processContent(page.getContent());
433    
434                    sourceContent = HtmlUtil.escape(sourceContent);
435                    targetContent = HtmlUtil.escape(targetContent);
436    
437                    List<DiffResult>[] diffResults = DiffUtil.diff(
438                            new UnsyncStringReader(sourceContent),
439                            new UnsyncStringReader(targetContent));
440    
441                    String velocityTemplateId =
442                            "com/liferay/portlet/wiki/dependencies/rss.vm";
443                    String velocityTemplateContent = ContentUtil.get(velocityTemplateId);
444    
445                    VelocityContext velocityContext =
446                            VelocityEngineUtil.getWrappedStandardToolsContext();
447    
448                    velocityContext.put("companyId", companyId);
449                    velocityContext.put("contextLine", Diff.CONTEXT_LINE);
450                    velocityContext.put("diffUtil", new DiffUtil());
451                    velocityContext.put("languageUtil", LanguageUtil.getLanguage());
452                    velocityContext.put("locale", locale);
453                    velocityContext.put("sourceResults", diffResults[0]);
454                    velocityContext.put("targetResults", diffResults[1]);
455    
456                    try {
457                            UnsyncStringWriter unsyncStringWriter = new UnsyncStringWriter();
458    
459                            VelocityEngineUtil.mergeTemplate(
460                                    velocityTemplateId, velocityTemplateContent, velocityContext,
461                                    unsyncStringWriter);
462    
463                            return unsyncStringWriter.toString();
464                    }
465                    catch (Exception e) {
466                            throw new SystemException(e);
467                    }
468            }
469    
470            private static final int _RSS_ABSTRACT_LENGTH = GetterUtil.getInteger(
471                    PropsUtil.get(PropsKeys.WIKI_RSS_ABSTRACT_LENGTH));
472    
473    }