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