1   /**
2    * Copyright (c) 2000-2009 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   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
12   * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
13   * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
14   * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
15   * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
16   * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
17   * SOFTWARE.
18   */
19  
20  package com.liferay.portlet.wiki.service.impl;
21  
22  import com.liferay.portal.PortalException;
23  import com.liferay.portal.SystemException;
24  import com.liferay.portal.kernel.language.LanguageUtil;
25  import com.liferay.portal.kernel.util.Diff;
26  import com.liferay.portal.kernel.util.DiffResult;
27  import com.liferay.portal.kernel.util.DiffUtil;
28  import com.liferay.portal.kernel.util.GetterUtil;
29  import com.liferay.portal.kernel.util.HtmlUtil;
30  import com.liferay.portal.kernel.util.HttpUtil;
31  import com.liferay.portal.kernel.util.ObjectValuePair;
32  import com.liferay.portal.kernel.util.StringPool;
33  import com.liferay.portal.kernel.util.StringUtil;
34  import com.liferay.portal.kernel.velocity.VelocityContext;
35  import com.liferay.portal.kernel.velocity.VelocityEngineUtil;
36  import com.liferay.portal.security.permission.ActionKeys;
37  import com.liferay.portal.service.ServiceContext;
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.portlet.wiki.model.WikiNode;
44  import com.liferay.portlet.wiki.model.WikiPage;
45  import com.liferay.portlet.wiki.model.impl.WikiPageImpl;
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  import java.io.StringWriter;
63  
64  import java.util.ArrayList;
65  import java.util.Iterator;
66  import java.util.List;
67  import java.util.Locale;
68  
69  /**
70   * <a href="WikiPageServiceImpl.java.html"><b><i>View Source</i></b></a>
71   *
72   * @author Brian Wing Shun Chan
73   * @author Jorge Ferrer
74   * @author Raymond Augé
75   *
76   */
77  public class WikiPageServiceImpl extends WikiPageServiceBaseImpl {
78  
79      public WikiPage addPage(
80              long nodeId, String title, String content, String summary,
81              boolean minorEdit, ServiceContext serviceContext)
82          throws PortalException, SystemException {
83  
84          WikiNodePermission.check(
85              getPermissionChecker(), nodeId, ActionKeys.ADD_PAGE);
86  
87          return wikiPageLocalService.addPage(
88              getUserId(), nodeId, title, content, summary, minorEdit,
89              serviceContext);
90      }
91  
92      public WikiPage addPage(
93              long nodeId, String title, String content, String summary,
94              boolean minorEdit, String format, String parentTitle,
95              String redirectTitle, ServiceContext serviceContext)
96          throws PortalException, SystemException {
97  
98          WikiNodePermission.check(
99              getPermissionChecker(), nodeId, ActionKeys.ADD_PAGE);
100 
101         return wikiPageLocalService.addPage(
102             null, getUserId(), nodeId, title, WikiPageImpl.DEFAULT_VERSION,
103             content, summary, minorEdit, format, true, parentTitle,
104             redirectTitle, serviceContext);
105     }
106 
107     public void addPageAttachments(
108             long nodeId, String title,
109             List<ObjectValuePair<String, byte[]>> files)
110         throws PortalException, SystemException {
111 
112         WikiNodePermission.check(
113             getPermissionChecker(), nodeId, ActionKeys.ADD_ATTACHMENT);
114 
115         wikiPageLocalService.addPageAttachments(nodeId, title, files);
116     }
117 
118     public void changeParent(
119             long nodeId, String title, String newParentTitle,
120             ServiceContext serviceContext)
121         throws PortalException, SystemException {
122 
123         WikiPagePermission.check(
124             getPermissionChecker(), nodeId, title, ActionKeys.DELETE);
125 
126         WikiNodePermission.check(
127             getPermissionChecker(), nodeId, ActionKeys.ADD_PAGE);
128 
129         wikiPageLocalService.changeParent(
130             getUserId(), nodeId, title, newParentTitle, serviceContext);
131     }
132 
133     public void deletePage(long nodeId, String title)
134         throws PortalException, SystemException {
135 
136         WikiPagePermission.check(
137             getPermissionChecker(), nodeId, title, ActionKeys.DELETE);
138 
139         wikiPageLocalService.deletePage(nodeId, title);
140     }
141 
142     public void deletePageAttachment(long nodeId, String title, String fileName)
143         throws PortalException, SystemException {
144 
145         WikiPagePermission.check(
146             getPermissionChecker(), nodeId, title, ActionKeys.DELETE);
147 
148         wikiPageLocalService.deletePageAttachment(nodeId, title, fileName);
149     }
150 
151     public List<WikiPage> getNodePages(long nodeId, int max)
152         throws PortalException, SystemException {
153 
154         List<WikiPage> pages = new ArrayList<WikiPage>();
155 
156         int lastIntervalStart = 0;
157         boolean listNotExhausted = true;
158 
159         while ((pages.size() < max) && listNotExhausted) {
160             List<WikiPage> pageList = wikiPageLocalService.getPages(
161                 nodeId, true, lastIntervalStart, lastIntervalStart + max);
162 
163             Iterator<WikiPage> itr = pageList.iterator();
164 
165             lastIntervalStart += max;
166             listNotExhausted = (pageList.size() == max);
167 
168             while (itr.hasNext() && (pages.size() < max)) {
169                 WikiPage page = itr.next();
170 
171                 if (WikiPagePermission.contains(getPermissionChecker(), page,
172                         ActionKeys.VIEW)) {
173 
174                     pages.add(page);
175                 }
176             }
177         }
178 
179         return pages;
180     }
181 
182     public String getNodePagesRSS(
183             long nodeId, int max, String type, double version,
184             String displayStyle, String feedURL, String entryURL)
185         throws PortalException, SystemException {
186 
187         WikiNodePermission.check(
188             getPermissionChecker(), nodeId, ActionKeys.VIEW);
189 
190         WikiNode node = wikiNodePersistence.findByPrimaryKey(nodeId);
191 
192         long companyId = node.getCompanyId();
193         String name = node.getName();
194         String description = node.getDescription();
195         List<WikiPage> pages = getNodePages(nodeId, max);
196         boolean diff = false;
197         Locale locale = null;
198 
199         return exportToRSS(
200             companyId, name, description, type, version, displayStyle,
201             feedURL, entryURL, pages, diff, locale);
202     }
203 
204     public WikiPage getPage(long nodeId, String title)
205         throws PortalException, SystemException {
206 
207         WikiPagePermission.check(
208             getPermissionChecker(), nodeId, title, ActionKeys.VIEW);
209 
210         return wikiPageLocalService.getPage(nodeId, title);
211     }
212 
213     public WikiPage getPage(long nodeId, String title, double version)
214         throws PortalException, SystemException {
215 
216         WikiPagePermission.check(
217             getPermissionChecker(), nodeId, title, ActionKeys.VIEW);
218 
219         return wikiPageLocalService.getPage(nodeId, title, version);
220     }
221 
222     public String getPagesRSS(
223             long companyId, long nodeId, String title, int max, String type,
224             double version, String displayStyle, String feedURL,
225             String entryURL, Locale locale)
226         throws PortalException, SystemException {
227 
228         WikiPagePermission.check(
229             getPermissionChecker(), nodeId, title, ActionKeys.VIEW);
230 
231         String description = title;
232         List<WikiPage> pages = wikiPageLocalService.getPages(
233             nodeId, title, 0, max, new PageCreateDateComparator(true));
234         boolean diff = true;
235 
236         return exportToRSS(
237             companyId, title, description, type, version, displayStyle, feedURL,
238             entryURL, pages, diff, locale);
239     }
240 
241     public void movePage(
242             long nodeId, String title, String newTitle,
243             ServiceContext serviceContext)
244         throws PortalException, SystemException {
245 
246         WikiPagePermission.check(
247             getPermissionChecker(), nodeId, title, ActionKeys.DELETE);
248 
249         WikiNodePermission.check(
250             getPermissionChecker(), nodeId, ActionKeys.ADD_PAGE);
251 
252         wikiPageLocalService.movePage(
253             getUserId(), nodeId, title, newTitle, serviceContext);
254     }
255 
256     public WikiPage revertPage(
257             long nodeId, String title, double version,
258             ServiceContext serviceContext)
259         throws PortalException, SystemException {
260 
261         WikiPagePermission.check(
262             getPermissionChecker(), nodeId, title, ActionKeys.UPDATE);
263 
264         return wikiPageLocalService.revertPage(
265             getUserId(), nodeId, title, version, serviceContext);
266     }
267 
268     public void subscribePage(long nodeId, String title)
269         throws PortalException, SystemException {
270 
271         WikiPagePermission.check(
272             getPermissionChecker(), nodeId, title, ActionKeys.SUBSCRIBE);
273 
274         wikiPageLocalService.subscribePage(getUserId(), nodeId, title);
275     }
276 
277     public void unsubscribePage(long nodeId, String title)
278         throws PortalException, SystemException {
279 
280         WikiPagePermission.check(
281             getPermissionChecker(), nodeId, title, ActionKeys.SUBSCRIBE);
282 
283         wikiPageLocalService.unsubscribePage(getUserId(), nodeId, title);
284     }
285 
286     public WikiPage updatePage(
287             long nodeId, String title, double version, String content,
288             String summary, boolean minorEdit, String format,
289             String parentTitle, String redirectTitle,
290             ServiceContext serviceContext)
291         throws PortalException, SystemException {
292 
293         WikiPagePermission.check(
294             getPermissionChecker(), nodeId, title, ActionKeys.UPDATE);
295 
296         return wikiPageLocalService.updatePage(
297             getUserId(), nodeId, title, version, content, summary, minorEdit,
298             format, parentTitle, redirectTitle, serviceContext);
299     }
300 
301     protected String exportToRSS(
302             long companyId, String name, String description, String type,
303             double version, String displayStyle, String feedURL,
304             String entryURL, List<WikiPage> pages, boolean diff, Locale locale)
305         throws SystemException {
306 
307         SyndFeed syndFeed = new SyndFeedImpl();
308 
309         syndFeed.setFeedType(RSSUtil.getFeedType(type, version));
310         syndFeed.setTitle(name);
311         syndFeed.setLink(feedURL);
312         syndFeed.setDescription(description);
313 
314         List<SyndEntry> entries = new ArrayList<SyndEntry>();
315 
316         syndFeed.setEntries(entries);
317 
318         WikiPage latestPage = null;
319 
320         for (WikiPage page : pages) {
321             String author = PortalUtil.getUserName(
322                 page.getUserId(), page.getUserName());
323 
324             String title =
325                 page.getTitle() + StringPool.SPACE + page.getVersion();
326 
327             if (page.isMinorEdit()) {
328                 title +=
329                     StringPool.SPACE + StringPool.OPEN_PARENTHESIS +
330                         LanguageUtil.get(locale, "minor-edit") +
331                             StringPool.CLOSE_PARENTHESIS;
332             }
333 
334             String link = entryURL + HttpUtil.encodeURL(page.getTitle());
335 
336             SyndEntry syndEntry = new SyndEntryImpl();
337 
338             syndEntry.setAuthor(author);
339             syndEntry.setTitle(title);
340             syndEntry.setPublishedDate(page.getCreateDate());
341             syndEntry.setUpdatedDate(page.getModifiedDate());
342 
343             SyndContent syndContent = new SyndContentImpl();
344 
345             syndContent.setType(RSSUtil.DEFAULT_ENTRY_TYPE);
346 
347             if (diff) {
348                 if (latestPage != null) {
349                     link +=
350                         "?" + PortalUtil.getPortletNamespace(PortletKeys.WIKI) +
351                             "version=" + page.getVersion();
352 
353                     String value = getPageDiff(
354                         companyId, latestPage, page, locale);
355 
356                     syndContent.setValue(value);
357 
358                     syndEntry.setDescription(syndContent);
359 
360                     entries.add(syndEntry);
361                 }
362             }
363             else {
364                 String value = null;
365 
366                 if (displayStyle.equals(RSSUtil.DISPLAY_STYLE_ABSTRACT)) {
367                     value = StringUtil.shorten(
368                         HtmlUtil.extractText(page.getContent()),
369                         _RSS_ABSTRACT_LENGTH, StringPool.BLANK);
370                 }
371                 else if (displayStyle.equals(RSSUtil.DISPLAY_STYLE_TITLE)) {
372                     value = StringPool.BLANK;
373                 }
374                 else {
375                     value = page.getContent();
376                 }
377 
378                 syndContent.setValue(value);
379 
380                 syndEntry.setDescription(syndContent);
381 
382                 entries.add(syndEntry);
383             }
384 
385             syndEntry.setLink(link);
386             syndEntry.setUri(syndEntry.getLink());
387 
388             latestPage = page;
389         }
390 
391         try {
392             return RSSUtil.export(syndFeed);
393         }
394         catch (FeedException fe) {
395             throw new SystemException(fe);
396         }
397     }
398 
399     protected String getPageDiff(
400             long companyId, WikiPage latestPage, WikiPage page,
401             Locale locale)
402         throws SystemException {
403 
404         String sourceContent = WikiUtil.processContent(latestPage.getContent());
405         String targetContent = WikiUtil.processContent(page.getContent());
406 
407         sourceContent = HtmlUtil.escape(sourceContent);
408         targetContent = HtmlUtil.escape(targetContent);
409 
410         List<DiffResult>[] diffResults = DiffUtil.diff(
411             new StringReader(sourceContent), new StringReader(targetContent));
412 
413         String velocityTemplateId =
414             "com/liferay/portlet/wiki/dependencies/rss.vm";
415         String velocityTemplateContent = ContentUtil.get(velocityTemplateId);
416 
417         VelocityContext velocityContext =
418             VelocityEngineUtil.getWrappedStandardToolsContext();
419 
420         velocityContext.put("companyId", companyId);
421         velocityContext.put("contextLine", Diff.CONTEXT_LINE);
422         velocityContext.put("diffUtil", new DiffUtil());
423         velocityContext.put("languageUtil", LanguageUtil.getLanguage());
424         velocityContext.put("locale", locale);
425         velocityContext.put("sourceResults", diffResults[0]);
426         velocityContext.put("targetResults", diffResults[1]);
427 
428         try {
429             StringWriter stringWriter = new StringWriter();
430 
431             VelocityEngineUtil.mergeTemplate(
432                 velocityTemplateId, velocityTemplateContent, velocityContext,
433                 stringWriter);
434 
435             return stringWriter.toString();
436         }
437         catch (Exception e) {
438             throw new SystemException(e);
439         }
440     }
441 
442     private static final int _RSS_ABSTRACT_LENGTH = GetterUtil.getInteger(
443         PropsUtil.get(PropsKeys.WIKI_RSS_ABSTRACT_LENGTH));
444 
445 }