1   /**
2    * Copyright (c) 2000-2010 Liferay, Inc. All rights reserved.
3    *
4    * This library is free software; you can redistribute it and/or modify it under
5    * the terms of the GNU Lesser General Public License as published by the Free
6    * Software Foundation; either version 2.1 of the License, or (at your option)
7    * any later version.
8    *
9    * This library is distributed in the hope that it will be useful, but WITHOUT
10   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
11   * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
12   * details.
13   */
14  
15  package com.liferay.portlet.messageboards.service.impl;
16  
17  import com.liferay.portal.kernel.exception.PortalException;
18  import com.liferay.portal.kernel.exception.SystemException;
19  import com.liferay.portal.kernel.util.GetterUtil;
20  import com.liferay.portal.kernel.util.HtmlUtil;
21  import com.liferay.portal.kernel.util.ObjectValuePair;
22  import com.liferay.portal.kernel.util.PropsKeys;
23  import com.liferay.portal.kernel.util.StringPool;
24  import com.liferay.portal.kernel.util.StringUtil;
25  import com.liferay.portal.model.Company;
26  import com.liferay.portal.model.Group;
27  import com.liferay.portal.model.User;
28  import com.liferay.portal.security.auth.PrincipalException;
29  import com.liferay.portal.security.permission.ActionKeys;
30  import com.liferay.portal.service.ServiceContext;
31  import com.liferay.portal.theme.ThemeDisplay;
32  import com.liferay.portal.util.PortalUtil;
33  import com.liferay.portal.util.PropsUtil;
34  import com.liferay.portlet.messageboards.LockedThreadException;
35  import com.liferay.portlet.messageboards.NoSuchCategoryException;
36  import com.liferay.portlet.messageboards.model.MBCategory;
37  import com.liferay.portlet.messageboards.model.MBCategoryConstants;
38  import com.liferay.portlet.messageboards.model.MBMessage;
39  import com.liferay.portlet.messageboards.model.MBMessageDisplay;
40  import com.liferay.portlet.messageboards.model.MBThread;
41  import com.liferay.portlet.messageboards.model.MBThreadConstants;
42  import com.liferay.portlet.messageboards.service.base.MBMessageServiceBaseImpl;
43  import com.liferay.portlet.messageboards.service.permission.MBCategoryPermission;
44  import com.liferay.portlet.messageboards.service.permission.MBDiscussionPermission;
45  import com.liferay.portlet.messageboards.service.permission.MBMessagePermission;
46  import com.liferay.portlet.messageboards.util.BBCodeUtil;
47  import com.liferay.portlet.messageboards.util.comparator.MessageCreateDateComparator;
48  import com.liferay.util.RSSUtil;
49  
50  import com.sun.syndication.feed.synd.SyndContent;
51  import com.sun.syndication.feed.synd.SyndContentImpl;
52  import com.sun.syndication.feed.synd.SyndEntry;
53  import com.sun.syndication.feed.synd.SyndEntryImpl;
54  import com.sun.syndication.feed.synd.SyndFeed;
55  import com.sun.syndication.feed.synd.SyndFeedImpl;
56  import com.sun.syndication.io.FeedException;
57  
58  import java.util.ArrayList;
59  import java.util.Iterator;
60  import java.util.List;
61  
62  /**
63   * <a href="MBMessageServiceImpl.java.html"><b><i>View Source</i></b></a>
64   *
65   * @author Brian Wing Shun Chan
66   * @author Mika Koivisto
67   */
68  public class MBMessageServiceImpl extends MBMessageServiceBaseImpl {
69  
70      public MBMessage addDiscussionMessage(
71              String className, long classPK, String permissionClassName,
72              long permissionClassPK, long threadId, long parentMessageId,
73              String subject, String body, ServiceContext serviceContext)
74          throws PortalException, SystemException {
75  
76          User user = getGuestOrUser();
77  
78          MBDiscussionPermission.check(
79              getPermissionChecker(), user.getCompanyId(),
80              serviceContext.getScopeGroupId(), permissionClassName,
81              permissionClassPK, user.getUserId(), ActionKeys.ADD_DISCUSSION);
82  
83          return mbMessageLocalService.addDiscussionMessage(
84              user.getUserId(), null, className, classPK, threadId,
85              parentMessageId, subject, body, serviceContext);
86      }
87  
88      public MBMessage addMessage(
89              long groupId, long categoryId, String subject, String body,
90              List<ObjectValuePair<String, byte[]>> files,
91              boolean anonymous, double priority, boolean allowPingbacks,
92              ServiceContext serviceContext)
93          throws PortalException, SystemException {
94  
95          MBCategoryPermission.check(
96              getPermissionChecker(), groupId, categoryId,
97              ActionKeys.ADD_MESSAGE);
98  
99          if (!MBCategoryPermission.contains(
100                 getPermissionChecker(), groupId, categoryId,
101                 ActionKeys.ADD_FILE)) {
102 
103             files.clear();
104         }
105 
106         if (!MBCategoryPermission.contains(
107                 getPermissionChecker(), groupId, categoryId,
108                 ActionKeys.UPDATE_THREAD_PRIORITY)) {
109 
110             priority = MBThreadConstants.PRIORITY_NOT_GIVEN;
111         }
112 
113         return mbMessageLocalService.addMessage(
114             getGuestOrUserId(), null, groupId, categoryId, subject, body, files,
115             anonymous, priority, allowPingbacks, serviceContext);
116     }
117 
118     public MBMessage addMessage(
119             long groupId, long categoryId, long threadId, long parentMessageId,
120             String subject, String body,
121             List<ObjectValuePair<String, byte[]>> files, boolean anonymous,
122             double priority, boolean allowPingbacks,
123             ServiceContext serviceContext)
124         throws PortalException, SystemException {
125 
126         checkReplyToPermission(groupId, categoryId, parentMessageId);
127 
128         if (lockLocalService.isLocked(
129                 MBThread.class.getName(), threadId)) {
130 
131             throw new LockedThreadException();
132         }
133 
134         if (!MBCategoryPermission.contains(
135                 getPermissionChecker(), groupId, categoryId,
136                 ActionKeys.ADD_FILE)) {
137 
138             files.clear();
139         }
140 
141         if (!MBCategoryPermission.contains(
142                 getPermissionChecker(), groupId, categoryId,
143                 ActionKeys.UPDATE_THREAD_PRIORITY)) {
144 
145             priority = MBThreadConstants.PRIORITY_NOT_GIVEN;
146         }
147 
148         return mbMessageLocalService.addMessage(
149             getGuestOrUserId(), null, groupId, categoryId, threadId,
150             parentMessageId, subject, body, files, anonymous, priority,
151             allowPingbacks, serviceContext);
152     }
153 
154     public void deleteDiscussionMessage(
155             long groupId, String className, long classPK,
156             String permissionClassName, long permissionClassPK, long messageId)
157         throws PortalException, SystemException {
158 
159         User user = getUser();
160 
161         MBDiscussionPermission.check(
162             getPermissionChecker(), user.getCompanyId(), groupId,
163             permissionClassName, permissionClassPK, messageId, user.getUserId(),
164             ActionKeys.DELETE_DISCUSSION);
165 
166         mbMessageLocalService.deleteDiscussionMessage(messageId);
167     }
168 
169     public void deleteMessage(long messageId)
170         throws PortalException, SystemException {
171 
172         MBMessagePermission.check(
173             getPermissionChecker(), messageId, ActionKeys.DELETE);
174 
175         mbMessageLocalService.deleteMessage(messageId);
176     }
177 
178     public List<MBMessage> getCategoryMessages(
179             long groupId, long categoryId, int status, int start, int end)
180         throws PortalException, SystemException {
181 
182         List<MBMessage> messages = new ArrayList<MBMessage>();
183 
184         Iterator<MBMessage> itr = mbMessageLocalService.getCategoryMessages(
185             groupId, categoryId, status, start, end).iterator();
186 
187         while (itr.hasNext()) {
188             MBMessage message = itr.next();
189 
190             if (MBMessagePermission.contains(
191                     getPermissionChecker(), message, ActionKeys.VIEW)) {
192 
193                 messages.add(message);
194             }
195         }
196 
197         return messages;
198     }
199 
200     public int getCategoryMessagesCount(
201             long groupId, long categoryId, int status)
202         throws SystemException {
203 
204         return mbMessageLocalService.getCategoryMessagesCount(
205             groupId, categoryId, status);
206     }
207 
208     public String getCategoryMessagesRSS(
209             long groupId, long categoryId, int status, int max, String type,
210             double version, String displayStyle, String feedURL,
211             String entryURL, ThemeDisplay themeDisplay)
212         throws PortalException, SystemException {
213 
214         String name = StringPool.BLANK;
215         String description = StringPool.BLANK;
216 
217         try {
218             MBCategory category = mbCategoryLocalService.getCategory(
219                 categoryId);
220 
221             groupId = category.getGroupId();
222             name = category.getName();
223             description = category.getDescription();
224         }
225         catch (NoSuchCategoryException nsce) {
226             Group group = groupLocalService.getGroup(categoryId);
227 
228             groupId = group.getGroupId();
229             categoryId = MBCategoryConstants.DEFAULT_PARENT_CATEGORY_ID;
230             name = group.getName();
231             description = group.getDescription();
232         }
233 
234         List<MBMessage> messages = new ArrayList<MBMessage>();
235 
236         int lastIntervalStart = 0;
237         boolean listNotExhausted = true;
238         MessageCreateDateComparator comparator =
239             new MessageCreateDateComparator(false);
240 
241         while ((messages.size() < max) && listNotExhausted) {
242             List<MBMessage> messageList =
243                 mbMessageLocalService.getCategoryMessages(
244                     groupId, categoryId, status, lastIntervalStart,
245                     lastIntervalStart + max, comparator);
246 
247             Iterator<MBMessage> itr = messageList.iterator();
248 
249             lastIntervalStart += max;
250             listNotExhausted = (messageList.size() == max);
251 
252             while (itr.hasNext() && (messages.size() < max)) {
253                 MBMessage message = itr.next();
254 
255                 if (MBMessagePermission.contains(
256                         getPermissionChecker(), message, ActionKeys.VIEW)) {
257 
258                     messages.add(message);
259                 }
260             }
261         }
262 
263         return exportToRSS(
264             name, description, type, version, displayStyle, feedURL, entryURL,
265             messages, themeDisplay);
266     }
267 
268     public String getCompanyMessagesRSS(
269             long companyId, int status, int max, String type, double version,
270             String displayStyle, String feedURL, String entryURL,
271             ThemeDisplay themeDisplay)
272         throws PortalException, SystemException {
273 
274         Company company = companyPersistence.findByPrimaryKey(companyId);
275 
276         String name = company.getName();
277         String description = company.getName();
278 
279         List<MBMessage> messages = new ArrayList<MBMessage>();
280 
281         int lastIntervalStart = 0;
282         boolean listNotExhausted = true;
283         MessageCreateDateComparator comparator =
284             new MessageCreateDateComparator(false);
285 
286         while ((messages.size() < max) && listNotExhausted) {
287             List<MBMessage> messageList =
288                 mbMessageLocalService.getCompanyMessages(
289                     companyId, status, lastIntervalStart,
290                     lastIntervalStart + max, comparator);
291 
292             Iterator<MBMessage> itr = messageList.iterator();
293 
294             lastIntervalStart += max;
295             listNotExhausted = (messageList.size() == max);
296 
297             while (itr.hasNext() && (messages.size() < max)) {
298                 MBMessage message = itr.next();
299 
300                 if (MBMessagePermission.contains(
301                         getPermissionChecker(), message, ActionKeys.VIEW)) {
302 
303                     messages.add(message);
304                 }
305             }
306         }
307 
308         return exportToRSS(
309             name, description, type, version, displayStyle, feedURL, entryURL,
310             messages, themeDisplay);
311     }
312 
313     public String getGroupMessagesRSS(
314             long groupId, int status, int max, String type, double version,
315             String displayStyle, String feedURL, String entryURL,
316             ThemeDisplay themeDisplay)
317         throws PortalException, SystemException {
318 
319         String name = StringPool.BLANK;
320         String description = StringPool.BLANK;
321 
322         List<MBMessage> messages = new ArrayList<MBMessage>();
323 
324         int lastIntervalStart = 0;
325         boolean listNotExhausted = true;
326         MessageCreateDateComparator comparator =
327             new MessageCreateDateComparator(false);
328 
329         while ((messages.size() < max) && listNotExhausted) {
330             List<MBMessage> messageList =
331                 mbMessageLocalService.getGroupMessages(
332                     groupId, status, lastIntervalStart, lastIntervalStart + max,
333                     comparator);
334 
335             Iterator<MBMessage> itr = messageList.iterator();
336 
337             lastIntervalStart += max;
338             listNotExhausted = (messageList.size() == max);
339 
340             while (itr.hasNext() && (messages.size() < max)) {
341                 MBMessage message = itr.next();
342 
343                 if (MBMessagePermission.contains(
344                         getPermissionChecker(), message, ActionKeys.VIEW)) {
345 
346                     messages.add(message);
347                 }
348             }
349         }
350 
351         if (messages.size() > 0) {
352             MBMessage message = messages.get(messages.size() - 1);
353 
354             name = message.getSubject();
355             description = message.getSubject();
356         }
357 
358         return exportToRSS(
359             name, description, type, version, displayStyle, feedURL, entryURL,
360             messages, themeDisplay);
361     }
362 
363     public String getGroupMessagesRSS(
364             long groupId, long userId, int status, int max, String type,
365             double version, String displayStyle, String feedURL,
366             String entryURL, ThemeDisplay themeDisplay)
367         throws PortalException, SystemException {
368 
369         String name = StringPool.BLANK;
370         String description = StringPool.BLANK;
371 
372         List<MBMessage> messages = new ArrayList<MBMessage>();
373 
374         int lastIntervalStart = 0;
375         boolean listNotExhausted = true;
376         MessageCreateDateComparator comparator =
377             new MessageCreateDateComparator(false);
378 
379         while ((messages.size() < max) && listNotExhausted) {
380             List<MBMessage> messageList =
381                 mbMessageLocalService.getGroupMessages(
382                     groupId, userId, status, lastIntervalStart,
383                     lastIntervalStart + max, comparator);
384 
385             Iterator<MBMessage> itr = messageList.iterator();
386 
387             lastIntervalStart += max;
388             listNotExhausted = (messageList.size() == max);
389 
390             while (itr.hasNext() && (messages.size() < max)) {
391                 MBMessage message = itr.next();
392 
393                 if (MBMessagePermission.contains(
394                         getPermissionChecker(), message, ActionKeys.VIEW)) {
395 
396                     messages.add(message);
397                 }
398             }
399         }
400 
401         if (messages.size() > 0) {
402             MBMessage message = messages.get(messages.size() - 1);
403 
404             name = message.getSubject();
405             description = message.getSubject();
406         }
407 
408         return exportToRSS(
409             name, description, type, version, displayStyle, feedURL, entryURL,
410             messages, themeDisplay);
411     }
412 
413     public MBMessage getMessage(long messageId)
414         throws PortalException, SystemException {
415 
416         MBMessagePermission.check(
417             getPermissionChecker(), messageId, ActionKeys.VIEW);
418 
419         return mbMessageLocalService.getMessage(messageId);
420     }
421 
422     public MBMessageDisplay getMessageDisplay(
423             long messageId, int status, String threadView)
424         throws PortalException, SystemException {
425 
426         MBMessagePermission.check(
427             getPermissionChecker(), messageId, ActionKeys.VIEW);
428 
429         return mbMessageLocalService.getMessageDisplay(
430             messageId, status, threadView);
431     }
432 
433     public String getThreadMessagesRSS(
434             long threadId, int status, int max, String type, double version,
435             String displayStyle, String feedURL, String entryURL,
436             ThemeDisplay themeDisplay)
437         throws PortalException, SystemException {
438 
439         String name = StringPool.BLANK;
440         String description = StringPool.BLANK;
441 
442         List<MBMessage> messages = new ArrayList<MBMessage>();
443 
444         MBThread thread = mbThreadLocalService.getThread(threadId);
445 
446         if (MBMessagePermission.contains(
447                 getPermissionChecker(), thread.getRootMessageId(),
448                 ActionKeys.VIEW)) {
449 
450             MessageCreateDateComparator comparator =
451                 new MessageCreateDateComparator(false);
452 
453             Iterator<MBMessage> itr = mbMessageLocalService.getThreadMessages(
454                 threadId, status, comparator).iterator();
455 
456             while (itr.hasNext() && (messages.size() < max)) {
457                 MBMessage message = itr.next();
458 
459                 if (MBMessagePermission.contains(
460                         getPermissionChecker(), message, ActionKeys.VIEW)) {
461 
462                     messages.add(message);
463                 }
464             }
465 
466             if (messages.size() > 0) {
467                 MBMessage message = messages.get(messages.size() - 1);
468 
469                 name = message.getSubject();
470                 description = message.getSubject();
471             }
472         }
473 
474         return exportToRSS(
475             name, description, type, version, displayStyle, feedURL, entryURL,
476             messages, themeDisplay);
477     }
478 
479     public void subscribeMessage(long messageId)
480         throws PortalException, SystemException {
481 
482         MBMessagePermission.check(
483             getPermissionChecker(), messageId, ActionKeys.SUBSCRIBE);
484 
485         mbMessageLocalService.subscribeMessage(getUserId(), messageId);
486     }
487 
488     public void unsubscribeMessage(long messageId)
489         throws PortalException, SystemException {
490 
491         MBMessagePermission.check(
492             getPermissionChecker(), messageId, ActionKeys.SUBSCRIBE);
493 
494         mbMessageLocalService.unsubscribeMessage(getUserId(), messageId);
495     }
496 
497     public MBMessage updateDiscussionMessage(
498             String className, long classPK, String permissionClassName,
499             long permissionClassPK, long messageId, String subject, String body,
500             ServiceContext serviceContext)
501         throws PortalException, SystemException {
502 
503         User user = getUser();
504 
505         MBDiscussionPermission.check(
506             getPermissionChecker(), user.getCompanyId(),
507             serviceContext.getScopeGroupId(), permissionClassName,
508             permissionClassPK, messageId, user.getUserId(),
509             ActionKeys.UPDATE_DISCUSSION);
510 
511         return mbMessageLocalService.updateDiscussionMessage(
512             getUserId(), messageId, subject, body, serviceContext.getStatus());
513     }
514 
515     public MBMessage updateMessage(
516             long messageId, String subject, String body,
517             List<ObjectValuePair<String, byte[]>> files,
518             List<String> existingFiles, double priority, boolean allowPingbacks,
519             ServiceContext serviceContext)
520         throws PortalException, SystemException {
521 
522         MBMessage message = mbMessageLocalService.getMessage(messageId);
523 
524         MBMessagePermission.check(
525             getPermissionChecker(), messageId, ActionKeys.UPDATE);
526 
527         if (lockLocalService.isLocked(
528                 MBThread.class.getName(), message.getThreadId())) {
529 
530             throw new LockedThreadException();
531         }
532 
533         if (!MBCategoryPermission.contains(
534                 getPermissionChecker(), message.getGroupId(),
535                 message.getCategoryId(), ActionKeys.ADD_FILE)) {
536 
537             files.clear();
538         }
539 
540         if (!MBCategoryPermission.contains(
541                 getPermissionChecker(), message.getGroupId(),
542                 message.getCategoryId(), ActionKeys.UPDATE_THREAD_PRIORITY)) {
543 
544             MBThread thread = mbThreadLocalService.getThread(
545                 message.getThreadId());
546 
547             priority = thread.getPriority();
548         }
549 
550         return mbMessageLocalService.updateMessage(
551             getUserId(), messageId, subject, body, files, existingFiles,
552             priority, allowPingbacks, serviceContext);
553     }
554 
555     protected void checkReplyToPermission(
556             long groupId, long categoryId, long parentMessageId)
557         throws PortalException, SystemException {
558 
559         if (parentMessageId > 0) {
560             if (MBCategoryPermission.contains(
561                     getPermissionChecker(), groupId, categoryId,
562                     ActionKeys.ADD_MESSAGE)) {
563 
564                 return;
565             }
566 
567             MBMessage parentMessage = mbMessagePersistence.fetchByPrimaryKey(
568                 parentMessageId);
569 
570             if ((parentMessage == null) ||
571                 !MBCategoryPermission.contains(
572                     getPermissionChecker(), groupId, categoryId,
573                     ActionKeys.REPLY_TO_MESSAGE)) {
574 
575                 throw new PrincipalException();
576             }
577         }
578         else {
579             MBCategoryPermission.check(
580                 getPermissionChecker(), groupId, categoryId,
581                 ActionKeys.ADD_MESSAGE);
582         }
583     }
584 
585     protected String exportToRSS(
586             String name, String description, String type, double version,
587             String displayStyle, String feedURL, String entryURL,
588             List<MBMessage> messages, ThemeDisplay themeDisplay)
589         throws SystemException {
590 
591         SyndFeed syndFeed = new SyndFeedImpl();
592 
593         syndFeed.setFeedType(RSSUtil.getFeedType(type, version));
594         syndFeed.setTitle(name);
595         syndFeed.setLink(feedURL);
596         syndFeed.setDescription(description);
597 
598         List<SyndEntry> entries = new ArrayList<SyndEntry>();
599 
600         syndFeed.setEntries(entries);
601 
602         Iterator<MBMessage> itr = messages.iterator();
603 
604         while (itr.hasNext()) {
605             MBMessage message = itr.next();
606 
607             String author = HtmlUtil.escape(
608                 PortalUtil.getUserName(
609                     message.getUserId(), message.getUserName()));
610 
611             String value = null;
612 
613             if (displayStyle.equals(RSSUtil.DISPLAY_STYLE_ABSTRACT)) {
614                 value = StringUtil.shorten(
615                     HtmlUtil.extractText(message.getBody()),
616                     _RSS_ABSTRACT_LENGTH, StringPool.BLANK);
617             }
618             else if (displayStyle.equals(RSSUtil.DISPLAY_STYLE_TITLE)) {
619                 value = StringPool.BLANK;
620             }
621             else {
622                 value = BBCodeUtil.getHTML(message);
623 
624                 value = StringUtil.replace(
625                     value,
626                     new String[] {
627                         "@theme_images_path@",
628                         "href=\"/",
629                         "src=\"/"
630                     },
631                     new String[] {
632                         themeDisplay.getURLPortal() +
633                             themeDisplay.getPathThemeImages(),
634                         "href=\"" + themeDisplay.getURLPortal() + "/",
635                         "src=\"" + themeDisplay.getURLPortal() + "/"
636                     });
637             }
638 
639             SyndEntry syndEntry = new SyndEntryImpl();
640 
641             if (!message.isAnonymous()) {
642                 syndEntry.setAuthor(author);
643             }
644 
645             syndEntry.setTitle(message.getSubject());
646             syndEntry.setLink(
647                 entryURL + "&messageId=" + message.getMessageId());
648             syndEntry.setUri(syndEntry.getLink());
649             syndEntry.setPublishedDate(message.getCreateDate());
650             syndEntry.setUpdatedDate(message.getModifiedDate());
651 
652             SyndContent syndContent = new SyndContentImpl();
653 
654             syndContent.setType(RSSUtil.DEFAULT_ENTRY_TYPE);
655             syndContent.setValue(value);
656 
657             syndEntry.setDescription(syndContent);
658 
659             entries.add(syndEntry);
660         }
661 
662         try {
663             return RSSUtil.export(syndFeed);
664         }
665         catch (FeedException fe) {
666             throw new SystemException(fe);
667         }
668     }
669 
670     private static final int _RSS_ABSTRACT_LENGTH = GetterUtil.getInteger(
671         PropsUtil.get(PropsKeys.MESSAGE_BOARDS_RSS_ABSTRACT_LENGTH));
672 
673 }