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