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