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