001    /**
002     * Copyright (c) 2000-2012 Liferay, Inc. All rights reserved.
003     *
004     * This library is free software; you can redistribute it and/or modify it under
005     * the terms of the GNU Lesser General Public License as published by the Free
006     * Software Foundation; either version 2.1 of the License, or (at your option)
007     * any later version.
008     *
009     * This library is distributed in the hope that it will be useful, but WITHOUT
010     * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
011     * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
012     * details.
013     */
014    
015    package com.liferay.portlet.messageboards.util;
016    
017    import com.liferay.portal.kernel.concurrent.PortalCallable;
018    import com.liferay.portal.kernel.exception.PortalException;
019    import com.liferay.portal.kernel.exception.SystemException;
020    import com.liferay.portal.kernel.log.Log;
021    import com.liferay.portal.kernel.log.LogFactoryUtil;
022    import com.liferay.portal.kernel.portlet.LiferayWindowState;
023    import com.liferay.portal.kernel.sanitizer.Sanitizer;
024    import com.liferay.portal.kernel.sanitizer.SanitizerUtil;
025    import com.liferay.portal.kernel.search.Document;
026    import com.liferay.portal.kernel.search.Field;
027    import com.liferay.portal.kernel.search.Hits;
028    import com.liferay.portal.kernel.transaction.TransactionCommitCallbackRegistryUtil;
029    import com.liferay.portal.kernel.util.CharPool;
030    import com.liferay.portal.kernel.util.ContentTypes;
031    import com.liferay.portal.kernel.util.GetterUtil;
032    import com.liferay.portal.kernel.util.Http;
033    import com.liferay.portal.kernel.util.LocaleUtil;
034    import com.liferay.portal.kernel.util.LocalizationUtil;
035    import com.liferay.portal.kernel.util.ParamUtil;
036    import com.liferay.portal.kernel.util.PropsUtil;
037    import com.liferay.portal.kernel.util.StringBundler;
038    import com.liferay.portal.kernel.util.StringPool;
039    import com.liferay.portal.kernel.util.StringUtil;
040    import com.liferay.portal.kernel.util.Validator;
041    import com.liferay.portal.kernel.workflow.WorkflowConstants;
042    import com.liferay.portal.model.Group;
043    import com.liferay.portal.model.Organization;
044    import com.liferay.portal.model.Role;
045    import com.liferay.portal.model.Subscription;
046    import com.liferay.portal.model.UserGroup;
047    import com.liferay.portal.security.permission.ActionKeys;
048    import com.liferay.portal.security.permission.PermissionChecker;
049    import com.liferay.portal.service.GroupLocalServiceUtil;
050    import com.liferay.portal.service.OrganizationLocalServiceUtil;
051    import com.liferay.portal.service.RoleLocalServiceUtil;
052    import com.liferay.portal.service.SubscriptionLocalServiceUtil;
053    import com.liferay.portal.service.UserGroupLocalServiceUtil;
054    import com.liferay.portal.service.UserGroupRoleLocalServiceUtil;
055    import com.liferay.portal.service.UserLocalServiceUtil;
056    import com.liferay.portal.theme.ThemeDisplay;
057    import com.liferay.portal.util.PortalUtil;
058    import com.liferay.portal.util.PropsValues;
059    import com.liferay.portal.util.WebKeys;
060    import com.liferay.portlet.documentlibrary.model.DLFileEntry;
061    import com.liferay.portlet.documentlibrary.service.DLFileEntryLocalServiceUtil;
062    import com.liferay.portlet.messageboards.model.MBBan;
063    import com.liferay.portlet.messageboards.model.MBCategory;
064    import com.liferay.portlet.messageboards.model.MBCategoryConstants;
065    import com.liferay.portlet.messageboards.model.MBMailingList;
066    import com.liferay.portlet.messageboards.model.MBMessage;
067    import com.liferay.portlet.messageboards.model.MBMessageConstants;
068    import com.liferay.portlet.messageboards.model.MBStatsUser;
069    import com.liferay.portlet.messageboards.model.MBThread;
070    import com.liferay.portlet.messageboards.service.MBCategoryLocalServiceUtil;
071    import com.liferay.portlet.messageboards.service.MBMailingListLocalServiceUtil;
072    import com.liferay.portlet.messageboards.service.MBMessageLocalServiceUtil;
073    import com.liferay.portlet.messageboards.service.MBThreadLocalServiceUtil;
074    import com.liferay.portlet.messageboards.service.permission.MBMessagePermission;
075    import com.liferay.util.ContentUtil;
076    import com.liferay.util.mail.JavaMailUtil;
077    
078    import java.io.InputStream;
079    
080    import java.util.ArrayList;
081    import java.util.Calendar;
082    import java.util.Collections;
083    import java.util.Date;
084    import java.util.HashMap;
085    import java.util.HashSet;
086    import java.util.List;
087    import java.util.Map;
088    import java.util.Set;
089    import java.util.concurrent.Callable;
090    
091    import javax.mail.BodyPart;
092    import javax.mail.Message;
093    import javax.mail.Part;
094    import javax.mail.internet.MimeMessage;
095    import javax.mail.internet.MimeMultipart;
096    
097    import javax.portlet.PortletPreferences;
098    import javax.portlet.PortletRequest;
099    import javax.portlet.PortletURL;
100    import javax.portlet.RenderResponse;
101    
102    import javax.servlet.http.HttpServletRequest;
103    
104    /**
105     * @author Brian Wing Shun Chan
106     */
107    public class MBUtil {
108    
109            public static final String BB_CODE_EDITOR_WYSIWYG_IMPL_KEY =
110                    "editor.wysiwyg.portal-web.docroot.html.portlet.message_boards." +
111                            "edit_message.bb_code.jsp";
112    
113            public static final String MESSAGE_POP_PORTLET_PREFIX = "mb_message.";
114    
115            public static void addPortletBreadcrumbEntries(
116                            long categoryId, HttpServletRequest request,
117                            RenderResponse renderResponse)
118                    throws Exception {
119    
120                    if ((categoryId == MBCategoryConstants.DEFAULT_PARENT_CATEGORY_ID) ||
121                            (categoryId == MBCategoryConstants.DISCUSSION_CATEGORY_ID)) {
122    
123                            return;
124                    }
125    
126                    MBCategory category = MBCategoryLocalServiceUtil.getCategory(
127                            categoryId);
128    
129                    addPortletBreadcrumbEntries(category, request, renderResponse);
130            }
131    
132            public static void addPortletBreadcrumbEntries(
133                            MBCategory category, HttpServletRequest request,
134                            RenderResponse renderResponse)
135                    throws Exception {
136    
137                    String strutsAction = ParamUtil.getString(request, "struts_action");
138    
139                    PortletURL portletURL = renderResponse.createRenderURL();
140    
141                    if (strutsAction.equals("/message_boards/select_category") ||
142                            strutsAction.equals("/message_boards_admin/select_category")) {
143    
144                            ThemeDisplay themeDisplay = (ThemeDisplay)request.getAttribute(
145                                    WebKeys.THEME_DISPLAY);
146    
147                            portletURL.setWindowState(LiferayWindowState.POP_UP);
148    
149                            portletURL.setParameter(
150                                    "struts_action", "/message_boards/select_category");
151    
152                            PortalUtil.addPortletBreadcrumbEntry(
153                                    request, themeDisplay.translate("categories"),
154                                    portletURL.toString());
155                    }
156                    else {
157                            portletURL.setParameter("struts_action", "/message_boards/view");
158                    }
159    
160                    List<MBCategory> ancestorCategories = category.getAncestors();
161    
162                    Collections.reverse(ancestorCategories);
163    
164                    for (MBCategory curCategory : ancestorCategories) {
165                            portletURL.setParameter(
166                                    "mbCategoryId", String.valueOf(curCategory.getCategoryId()));
167    
168                            PortalUtil.addPortletBreadcrumbEntry(
169                                    request, curCategory.getName(), portletURL.toString());
170                    }
171    
172                    portletURL.setParameter(
173                            "mbCategoryId", String.valueOf(category.getCategoryId()));
174    
175                    PortalUtil.addPortletBreadcrumbEntry(
176                            request, category.getName(), portletURL.toString());
177            }
178    
179            public static void addPortletBreadcrumbEntries(
180                            MBMessage message, HttpServletRequest request,
181                            RenderResponse renderResponse)
182                    throws Exception {
183    
184                    if (message.getCategoryId() ==
185                                    MBCategoryConstants.DISCUSSION_CATEGORY_ID) {
186    
187                            return;
188                    }
189    
190                    if (message.getCategoryId() !=
191                                    MBCategoryConstants.DEFAULT_PARENT_CATEGORY_ID) {
192    
193                            addPortletBreadcrumbEntries(
194                                    message.getCategory(), request, renderResponse);
195                    }
196    
197                    PortletURL portletURL = renderResponse.createRenderURL();
198    
199                    portletURL.setParameter(
200                            "struts_action", "/message_boards/view_message");
201                    portletURL.setParameter(
202                            "messageId", String.valueOf(message.getMessageId()));
203    
204                    PortalUtil.addPortletBreadcrumbEntry(
205                            request, message.getSubject(), portletURL.toString());
206            }
207    
208            public static void collectMultipartContent(
209                            MimeMultipart multipart, MBMailMessage collector)
210                    throws Exception {
211    
212                    for (int i = 0; i < multipart.getCount(); i++) {
213                            BodyPart part = multipart.getBodyPart(i);
214    
215                            collectPartContent(part, collector);
216                    }
217            }
218    
219            public static void collectPartContent(
220                            Part part, MBMailMessage mbMailMessage)
221                    throws Exception {
222    
223                    Object partContent = part.getContent();
224    
225                    String contentType = part.getContentType().toLowerCase();
226    
227                    if ((part.getDisposition() != null) &&
228                            part.getDisposition().equalsIgnoreCase(MimeMessage.ATTACHMENT)) {
229    
230                            if (_log.isDebugEnabled()) {
231                                    _log.debug("Processing attachment");
232                            }
233    
234                            byte[] bytes = null;
235    
236                            if (partContent instanceof String) {
237                                    bytes = ((String)partContent).getBytes();
238                            }
239                            else if (partContent instanceof InputStream) {
240                                    bytes = JavaMailUtil.getBytes(part);
241                            }
242    
243                            mbMailMessage.addBytes(part.getFileName(), bytes);
244                    }
245                    else {
246                            if (partContent instanceof MimeMultipart) {
247                                    MimeMultipart mimeMultipart = (MimeMultipart)partContent;
248    
249                                    collectMultipartContent(mimeMultipart, mbMailMessage);
250                            }
251                            else if (partContent instanceof String) {
252                                    Map<String, Object> options = new HashMap<String, Object>();
253    
254                                    options.put("emailPartToMBMessageBody", Boolean.TRUE);
255    
256                                    String messageBody = SanitizerUtil.sanitize(
257                                            0, 0, 0, MBMessage.class.getName(), 0, contentType,
258                                            Sanitizer.MODE_ALL, (String)partContent, options);
259    
260                                    if (contentType.startsWith(ContentTypes.TEXT_HTML)) {
261                                            mbMailMessage.setHtmlBody(messageBody);
262                                    }
263                                    else {
264                                            mbMailMessage.setPlainBody(messageBody);
265                                    }
266                            }
267                    }
268            }
269    
270            public static String getAbsolutePath(
271                            PortletRequest portletRequest, long mbCategoryId)
272                    throws PortalException, SystemException {
273    
274                    ThemeDisplay themeDisplay = (ThemeDisplay)portletRequest.getAttribute(
275                            WebKeys.THEME_DISPLAY);
276    
277                    if (mbCategoryId == MBCategoryConstants.DEFAULT_PARENT_CATEGORY_ID) {
278                            return themeDisplay.translate("home");
279                    }
280    
281                    MBCategory mbCategory = MBCategoryLocalServiceUtil.fetchMBCategory(
282                            mbCategoryId);
283    
284                    List<MBCategory> categories = mbCategory.getAncestors();
285    
286                    Collections.reverse(categories);
287    
288                    StringBundler sb = new StringBundler((categories.size() * 3) + 5);
289    
290                    sb.append(themeDisplay.translate("home"));
291                    sb.append(StringPool.SPACE);
292    
293                    for (MBCategory curCategory : categories) {
294                            sb.append(StringPool.RAQUO);
295                            sb.append(StringPool.SPACE);
296                            sb.append(curCategory.getName());
297                    }
298    
299                    sb.append(StringPool.RAQUO);
300                    sb.append(StringPool.SPACE);
301                    sb.append(mbCategory.getName());
302    
303                    return sb.toString();
304            }
305    
306            public static long getCategoryId(
307                    HttpServletRequest request, MBCategory category) {
308    
309                    long categoryId = MBCategoryConstants.DEFAULT_PARENT_CATEGORY_ID;
310    
311                    if (category != null) {
312                            categoryId = category.getCategoryId();
313                    }
314    
315                    categoryId = ParamUtil.getLong(request, "mbCategoryId", categoryId);
316    
317                    return categoryId;
318            }
319    
320            public static long getCategoryId(
321                    HttpServletRequest request, MBMessage message) {
322    
323                    long categoryId = MBCategoryConstants.DEFAULT_PARENT_CATEGORY_ID;
324    
325                    if (message != null) {
326                            categoryId = message.getCategoryId();
327                    }
328    
329                    categoryId = ParamUtil.getLong(request, "mbCategoryId", categoryId);
330    
331                    return categoryId;
332            }
333    
334            public static Set<Long> getCategorySubscriptionClassPKs(long userId)
335                    throws SystemException {
336    
337                    List<Subscription> subscriptions =
338                            SubscriptionLocalServiceUtil.getUserSubscriptions(
339                                    userId, MBCategory.class.getName());
340    
341                    Set<Long> classPKs = new HashSet<Long>(subscriptions.size());
342    
343                    for (Subscription subscription : subscriptions) {
344                            classPKs.add(subscription.getClassPK());
345                    }
346    
347                    return classPKs;
348            }
349    
350            public static String getEmailFromAddress(
351                            PortletPreferences preferences, long companyId)
352                    throws SystemException {
353    
354                    return PortalUtil.getEmailFromAddress(
355                            preferences, companyId,
356                            PropsValues.MESSAGE_BOARDS_EMAIL_FROM_ADDRESS);
357            }
358    
359            public static String getEmailFromName(
360                            PortletPreferences preferences, long companyId)
361                    throws SystemException {
362    
363                    return PortalUtil.getEmailFromName(
364                            preferences, companyId, PropsValues.MESSAGE_BOARDS_EMAIL_FROM_NAME);
365            }
366    
367            public static boolean getEmailHtmlFormat(PortletPreferences preferences) {
368                    String emailHtmlFormat = preferences.getValue(
369                            "emailHtmlFormat", StringPool.BLANK);
370    
371                    if (Validator.isNotNull(emailHtmlFormat)) {
372                            return GetterUtil.getBoolean(emailHtmlFormat);
373                    }
374                    else {
375                            return PropsValues.MESSAGE_BOARDS_EMAIL_HTML_FORMAT;
376                    }
377            }
378    
379            public static String getEmailMessageAddedBody(
380                    PortletPreferences preferences) {
381    
382                    String emailMessageAddedBody = preferences.getValue(
383                            "emailMessageAddedBody", StringPool.BLANK);
384    
385                    if (Validator.isNotNull(emailMessageAddedBody)) {
386                            return emailMessageAddedBody;
387                    }
388                    else {
389                            return ContentUtil.get(
390                                    PropsValues.MESSAGE_BOARDS_EMAIL_MESSAGE_ADDED_BODY);
391                    }
392            }
393    
394            public static boolean getEmailMessageAddedEnabled(
395                    PortletPreferences preferences) {
396    
397                    String emailMessageAddedEnabled = preferences.getValue(
398                            "emailMessageAddedEnabled", StringPool.BLANK);
399    
400                    if (Validator.isNotNull(emailMessageAddedEnabled)) {
401                            return GetterUtil.getBoolean(emailMessageAddedEnabled);
402                    }
403                    else {
404                            return PropsValues.MESSAGE_BOARDS_EMAIL_MESSAGE_ADDED_ENABLED;
405                    }
406            }
407    
408            public static String getEmailMessageAddedSignature(
409                    PortletPreferences preferences) {
410    
411                    String emailMessageAddedSignature = preferences.getValue(
412                            "emailMessageAddedSignature", StringPool.BLANK);
413    
414                    if (Validator.isNotNull(emailMessageAddedSignature)) {
415                            return emailMessageAddedSignature;
416                    }
417                    else {
418                            return ContentUtil.get(
419                                    PropsValues.MESSAGE_BOARDS_EMAIL_MESSAGE_ADDED_SIGNATURE);
420                    }
421            }
422    
423            public static String getEmailMessageAddedSubjectPrefix(
424                    PortletPreferences preferences) {
425    
426                    String emailMessageAddedSubjectPrefix = preferences.getValue(
427                            "emailMessageAddedSubjectPrefix", StringPool.BLANK);
428    
429                    if (Validator.isNotNull(emailMessageAddedSubjectPrefix)) {
430                            return emailMessageAddedSubjectPrefix;
431                    }
432                    else {
433                            return ContentUtil.get(
434                                    PropsValues.MESSAGE_BOARDS_EMAIL_MESSAGE_ADDED_SUBJECT_PREFIX);
435                    }
436            }
437    
438            public static String getEmailMessageUpdatedBody(
439                    PortletPreferences preferences) {
440    
441                    String emailMessageUpdatedBody = preferences.getValue(
442                            "emailMessageUpdatedBody", StringPool.BLANK);
443    
444                    if (Validator.isNotNull(emailMessageUpdatedBody)) {
445                            return emailMessageUpdatedBody;
446                    }
447                    else {
448                            return ContentUtil.get(
449                                    PropsValues.MESSAGE_BOARDS_EMAIL_MESSAGE_UPDATED_BODY);
450                    }
451            }
452    
453            public static boolean getEmailMessageUpdatedEnabled(
454                    PortletPreferences preferences) {
455    
456                    String emailMessageUpdatedEnabled = preferences.getValue(
457                            "emailMessageUpdatedEnabled", StringPool.BLANK);
458    
459                    if (Validator.isNotNull(emailMessageUpdatedEnabled)) {
460                            return GetterUtil.getBoolean(emailMessageUpdatedEnabled);
461                    }
462                    else {
463                            return PropsValues.MESSAGE_BOARDS_EMAIL_MESSAGE_UPDATED_ENABLED;
464                    }
465            }
466    
467            public static String getEmailMessageUpdatedSignature(
468                    PortletPreferences preferences) {
469    
470                    String emailMessageUpdatedSignature = preferences.getValue(
471                            "emailMessageUpdatedSignature", StringPool.BLANK);
472    
473                    if (Validator.isNotNull(emailMessageUpdatedSignature)) {
474                            return emailMessageUpdatedSignature;
475                    }
476                    else {
477                            return ContentUtil.get(
478                                    PropsValues.MESSAGE_BOARDS_EMAIL_MESSAGE_UPDATED_SIGNATURE);
479                    }
480            }
481    
482            public static String getEmailMessageUpdatedSubjectPrefix(
483                    PortletPreferences preferences) {
484    
485                    String emailMessageUpdatedSubject = preferences.getValue(
486                            "emailMessageUpdatedSubjectPrefix", StringPool.BLANK);
487    
488                    if (Validator.isNotNull(emailMessageUpdatedSubject)) {
489                            return emailMessageUpdatedSubject;
490                    }
491                    else {
492                            return ContentUtil.get(
493                                    PropsValues.
494                                            MESSAGE_BOARDS_EMAIL_MESSAGE_UPDATED_SUBJECT_PREFIX);
495                    }
496            }
497    
498            public static List<Object> getEntries(Hits hits) {
499                    List<Object> entries = new ArrayList<Object>();
500    
501                    for (Document document : hits.getDocs()) {
502                            long categoryId = GetterUtil.getLong(
503                                    document.get(Field.CATEGORY_ID));
504    
505                            try {
506                                    MBCategoryLocalServiceUtil.getCategory(categoryId);
507                            }
508                            catch (Exception e) {
509                                    if (_log.isWarnEnabled()) {
510                                            _log.warn(
511                                                    "Message boards search index is stale and contains " +
512                                                            "category " + categoryId);
513                                    }
514    
515                                    continue;
516                            }
517    
518                            long threadId = GetterUtil.getLong(document.get("threadId"));
519    
520                            try {
521                                    MBThreadLocalServiceUtil.getThread(threadId);
522                            }
523                            catch (Exception e) {
524                                    if (_log.isWarnEnabled()) {
525                                            _log.warn(
526                                                    "Message boards search index is stale and contains " +
527                                                            "thread " + threadId);
528                                    }
529    
530                                    continue;
531                            }
532    
533                            String entryClassName = document.get(Field.ENTRY_CLASS_NAME);
534                            long entryClassPK = GetterUtil.getLong(
535                                    document.get(Field.ENTRY_CLASS_PK));
536    
537                            Object obj = null;
538    
539                            try {
540                                    if (entryClassName.equals(DLFileEntry.class.getName())) {
541                                            long classPK = GetterUtil.getLong(
542                                                    document.get(Field.CLASS_PK));
543    
544                                            MBMessageLocalServiceUtil.getMessage(classPK);
545    
546                                            obj = DLFileEntryLocalServiceUtil.getDLFileEntry(
547                                                    entryClassPK);
548                                    }
549                                    else if (entryClassName.equals(MBMessage.class.getName())) {
550                                            obj = MBMessageLocalServiceUtil.getMessage(entryClassPK);
551                                    }
552    
553                                    entries.add(obj);
554                            }
555                            catch (Exception e) {
556                                    if (_log.isWarnEnabled()) {
557                                            _log.warn(
558                                                    "Message boards search index is stale and contains " +
559                                                            "entry {className=" + entryClassName + ", " +
560                                                                    "classPK=" + entryClassPK + "}");
561                                    }
562    
563                                    continue;
564                            }
565                    }
566    
567                    return entries;
568            }
569    
570            public static String getMailingListAddress(
571                    long groupId, long categoryId, long messageId, String mx,
572                    String defaultMailingListAddress) {
573    
574                    if (PropsValues.POP_SERVER_SUBDOMAIN.length() <= 0) {
575                            String mailingListAddress = defaultMailingListAddress;
576    
577                            try {
578                                    MBMailingList mailingList =
579                                            MBMailingListLocalServiceUtil.getCategoryMailingList(
580                                                    groupId, categoryId);
581    
582                                    if (mailingList.isActive()) {
583                                            mailingListAddress = mailingList.getEmailAddress();
584                                    }
585                            }
586                            catch (Exception e) {
587                            }
588    
589                            return mailingListAddress;
590                    }
591    
592                    StringBundler sb = new StringBundler(8);
593    
594                    sb.append(MESSAGE_POP_PORTLET_PREFIX);
595                    sb.append(categoryId);
596                    sb.append(StringPool.PERIOD);
597                    sb.append(messageId);
598                    sb.append(StringPool.AT);
599                    sb.append(PropsValues.POP_SERVER_SUBDOMAIN);
600                    sb.append(StringPool.PERIOD);
601                    sb.append(mx);
602    
603                    return sb.toString();
604            }
605    
606            public static String getMessageFormat(PortletPreferences preferences) {
607                    String messageFormat = preferences.getValue(
608                            "messageFormat", MBMessageConstants.DEFAULT_FORMAT);
609    
610                    String editorImpl = PropsUtil.get(BB_CODE_EDITOR_WYSIWYG_IMPL_KEY);
611    
612                    if (messageFormat.equals("bbcode") &&
613                            !(editorImpl.equals("bbcode") ||
614                              editorImpl.equals("ckeditor_bbcode"))) {
615    
616                            messageFormat = "html";
617                    }
618    
619                    return messageFormat;
620            }
621    
622            public static long getMessageId(String mailId) {
623                    int x = mailId.indexOf(CharPool.LESS_THAN) + 1;
624                    int y = mailId.indexOf(CharPool.AT);
625    
626                    long messageId = 0;
627    
628                    if ((x > 0 ) && (y != -1)) {
629                            String temp = mailId.substring(x, y);
630    
631                            int z = temp.lastIndexOf(CharPool.PERIOD);
632    
633                            if (z != -1) {
634                                    messageId = GetterUtil.getLong(temp.substring(z + 1));
635                            }
636                    }
637    
638                    return messageId;
639            }
640    
641            public static long getParentMessageId(Message message) throws Exception {
642                    long parentMessageId = -1;
643    
644                    String parentHeader = getParentMessageIdString(message);
645    
646                    if (parentHeader != null) {
647                            if (_log.isDebugEnabled()) {
648                                    _log.debug("Parent header " + parentHeader);
649                            }
650    
651                            parentMessageId = getMessageId(parentHeader);
652    
653                            if (_log.isDebugEnabled()) {
654                                    _log.debug("Previous message id " + parentMessageId);
655                            }
656                    }
657    
658                    return parentMessageId;
659            }
660    
661            public static String getParentMessageIdString(Message message)
662                    throws Exception {
663    
664                    // If the previous block failed, try to get the parent message ID from
665                    // the "References" header as explained in
666                    // http://cr.yp.to/immhf/thread.html. Some mail clients such as Yahoo!
667                    // Mail use the "In-Reply-To" header, so we check that as well.
668    
669                    String parentHeader = null;
670    
671                    String[] references = message.getHeader("References");
672    
673                    if ((references != null) && (references.length > 0)) {
674                            String reference = references[0];
675    
676                            int x = reference.lastIndexOf("<mb.");
677    
678                            if (x > -1) {
679                                    int y = reference.indexOf(">", x);
680    
681                                    parentHeader = reference.substring(x, y);
682                            }
683                    }
684    
685                    if (parentHeader == null) {
686                            String[] inReplyToHeaders = message.getHeader("In-Reply-To");
687    
688                            if ((inReplyToHeaders != null) && (inReplyToHeaders.length > 0)) {
689                                    parentHeader = inReplyToHeaders[0];
690                            }
691                    }
692    
693                    if (Validator.isNull(parentHeader) ||
694                            !parentHeader.startsWith(MESSAGE_POP_PORTLET_PREFIX, 1)) {
695    
696                            parentHeader = _getParentMessageIdFromSubject(message);
697                    }
698    
699                    return parentHeader;
700            }
701    
702            public static String getSubjectWithoutMessageId(Message message)
703                    throws Exception {
704    
705                    String subject = message.getSubject();
706    
707                    String parentMessageId = _getParentMessageIdFromSubject(message);
708    
709                    if (Validator.isNotNull(parentMessageId)) {
710                            int pos = subject.indexOf(parentMessageId);
711    
712                            if (pos != -1) {
713                                    subject = subject.substring(0, pos);
714                            }
715                    }
716    
717                    return subject;
718            }
719    
720            public static String[] getThreadPriority(
721                            PortletPreferences preferences, String languageId, double value,
722                            ThemeDisplay themeDisplay)
723                    throws Exception {
724    
725                    String[] priorities = LocalizationUtil.getPreferencesValues(
726                            preferences, "priorities", languageId);
727    
728                    String[] priorityPair = _findThreadPriority(
729                            value, themeDisplay, priorities);
730    
731                    if (priorityPair == null) {
732                            String defaultLanguageId = LocaleUtil.toLanguageId(
733                                    LocaleUtil.getDefault());
734    
735                            priorities = LocalizationUtil.getPreferencesValues(
736                                    preferences, "priorities", defaultLanguageId);
737    
738                            priorityPair = _findThreadPriority(value, themeDisplay, priorities);
739                    }
740    
741                    return priorityPair;
742            }
743    
744            public static Set<Long> getThreadSubscriptionClassPKs(long userId)
745                    throws SystemException {
746    
747                    List<Subscription> subscriptions =
748                            SubscriptionLocalServiceUtil.getUserSubscriptions(
749                                    userId, MBThread.class.getName());
750    
751                    Set<Long> classPKs = new HashSet<Long>(subscriptions.size());
752    
753                    for (Subscription subscription : subscriptions) {
754                            classPKs.add(subscription.getClassPK());
755                    }
756    
757                    return classPKs;
758            }
759    
760            public static Date getUnbanDate(MBBan ban, int expireInterval) {
761                    Date banDate = ban.getCreateDate();
762    
763                    Calendar cal = Calendar.getInstance();
764    
765                    cal.setTime(banDate);
766    
767                    cal.add(Calendar.DATE, expireInterval);
768    
769                    return cal.getTime();
770            }
771    
772            public static String getUserRank(
773                            PortletPreferences preferences, String languageId, int posts)
774                    throws Exception {
775    
776                    String rank = StringPool.BLANK;
777    
778                    String[] ranks = LocalizationUtil.getPreferencesValues(
779                            preferences, "ranks", languageId);
780    
781                    for (int i = 0; i < ranks.length; i++) {
782                            String[] kvp = StringUtil.split(ranks[i], CharPool.EQUAL);
783    
784                            String kvpName = kvp[0];
785                            int kvpPosts = GetterUtil.getInteger(kvp[1]);
786    
787                            if (posts >= kvpPosts) {
788                                    rank = kvpName;
789                            }
790                            else {
791                                    break;
792                            }
793                    }
794    
795                    return rank;
796            }
797    
798            public static String[] getUserRank(
799                            PortletPreferences preferences, String languageId,
800                            MBStatsUser statsUser)
801                    throws Exception {
802    
803                    String[] rank = {StringPool.BLANK, StringPool.BLANK};
804    
805                    int maxPosts = 0;
806    
807                    Group group = GroupLocalServiceUtil.getGroup(statsUser.getGroupId());
808    
809                    long companyId = group.getCompanyId();
810    
811                    String[] ranks = LocalizationUtil.getPreferencesValues(
812                            preferences, "ranks", languageId);
813    
814                    for (int i = 0; i < ranks.length; i++) {
815                            String[] kvp = StringUtil.split(ranks[i], CharPool.EQUAL);
816    
817                            String curRank = kvp[0];
818                            String curRankValue = kvp[1];
819    
820                            String[] curRankValueKvp = StringUtil.split(
821                                    curRankValue, CharPool.COLON);
822    
823                            if (curRankValueKvp.length <= 1) {
824                                    int posts = GetterUtil.getInteger(curRankValue);
825    
826                                    if ((posts <= statsUser.getMessageCount()) &&
827                                            (posts >= maxPosts)) {
828    
829                                            rank[0] = curRank;
830                                            maxPosts = posts;
831                                    }
832    
833                            }
834                            else {
835                                    String entityType = curRankValueKvp[0];
836                                    String entityValue = curRankValueKvp[1];
837    
838                                    try {
839                                            if (_isEntityRank(
840                                                            companyId, statsUser, entityType, entityValue)) {
841    
842                                                    rank[1] = curRank;
843    
844                                                    break;
845                                            }
846                                    }
847                                    catch (Exception e) {
848                                            if (_log.isWarnEnabled()) {
849                                                    _log.warn(e);
850                                            }
851                                    }
852                            }
853                    }
854    
855                    return rank;
856            }
857    
858            public static boolean hasMailIdHeader(Message message) throws Exception {
859                    String[] messageIds = message.getHeader("Message-ID");
860    
861                    if (messageIds == null) {
862                            return false;
863                    }
864    
865                    for (String messageId : messageIds) {
866                            if (Validator.isNotNull(PropsValues.POP_SERVER_SUBDOMAIN) &&
867                                    messageId.contains(PropsValues.POP_SERVER_SUBDOMAIN)) {
868    
869                                    return true;
870                            }
871                    }
872    
873                    return false;
874            }
875    
876            public static boolean isAllowAnonymousPosting(
877                    PortletPreferences preferences) {
878    
879                    return GetterUtil.getBoolean(
880                            preferences.getValue("allowAnonymousPosting", null),
881                            PropsValues.MESSAGE_BOARDS_ANONYMOUS_POSTING_ENABLED);
882            }
883    
884            public static boolean isViewableMessage(
885                            ThemeDisplay themeDisplay, MBMessage message)
886                    throws Exception {
887    
888                    return isViewableMessage(themeDisplay, message, message);
889            }
890    
891            public static boolean isViewableMessage(
892                            ThemeDisplay themeDisplay, MBMessage message,
893                            MBMessage parentMessage)
894                    throws Exception {
895    
896                    PermissionChecker permissionChecker =
897                            themeDisplay.getPermissionChecker();
898    
899                    if (!MBMessagePermission.contains(
900                                    permissionChecker, parentMessage, ActionKeys.VIEW)) {
901    
902                            return false;
903                    }
904    
905                    if ((message.getMessageId() != parentMessage.getMessageId()) &&
906                            !MBMessagePermission.contains(
907                                    permissionChecker, message, ActionKeys.VIEW)) {
908    
909                            return false;
910                    }
911    
912                    if (!message.isApproved() &&
913                            !Validator.equals(message.getUserId(), themeDisplay.getUserId()) &&
914                            !permissionChecker.isGroupAdmin(themeDisplay.getScopeGroupId())) {
915    
916                            return false;
917                    }
918    
919                    return true;
920            }
921    
922            public static String replaceMessageBodyPaths(
923                    ThemeDisplay themeDisplay, String messageBody) {
924    
925                    return StringUtil.replace(
926                            messageBody,
927                            new String[] {
928                                    "@theme_images_path@", "href=\"/", "src=\"/"
929                            },
930                            new String[] {
931                                    themeDisplay.getPathThemeImages(),
932                                    "href=\"" + themeDisplay.getURLPortal() + "/",
933                                    "src=\"" + themeDisplay.getURLPortal() + "/"
934                            });
935            }
936    
937            public static void updateCategoryMessageCount(final MBCategory category) {
938                    Callable<Void> callable = new PortalCallable<Void>(
939                            category.getCompanyId()) {
940    
941                            @Override
942                            protected Void doCall() throws Exception {
943                                    int messageCount =
944                                            MBMessageLocalServiceUtil.getCategoryMessagesCount(
945                                                    category.getGroupId(), category.getCategoryId(),
946                                                    WorkflowConstants.STATUS_APPROVED);
947    
948                                    category.setMessageCount(messageCount);
949    
950                                    MBCategoryLocalServiceUtil.updateMBCategory(category);
951    
952                                    return null;
953                            }
954                    };
955    
956                    TransactionCommitCallbackRegistryUtil.registerCallback(callable);
957            }
958    
959            public static void updateCategoryStatistics(final MBCategory category) {
960                    Callable<Void> callable = new PortalCallable<Void>(
961                            category.getCompanyId()) {
962    
963                            @Override
964                            protected Void doCall() throws Exception {
965                                    int messageCount =
966                                            MBMessageLocalServiceUtil.getCategoryMessagesCount(
967                                                    category.getGroupId(), category.getCategoryId(),
968                                                    WorkflowConstants.STATUS_APPROVED);
969    
970                                    int threadCount =
971                                            MBThreadLocalServiceUtil.getCategoryThreadsCount(
972                                                    category.getGroupId(), category.getCategoryId(),
973                                                    WorkflowConstants.STATUS_APPROVED);
974    
975                                    category.setMessageCount(messageCount);
976                                    category.setThreadCount(threadCount);
977    
978                                    MBCategoryLocalServiceUtil.updateMBCategory(category);
979    
980                                    return null;
981                            }
982                    };
983    
984                    TransactionCommitCallbackRegistryUtil.registerCallback(callable);
985            }
986    
987            public static void updateCategoryThreadCount(final MBCategory category) {
988                    Callable<Void> callable = new PortalCallable<Void>(
989                            category.getCompanyId()) {
990    
991                            @Override
992                            protected Void doCall() throws Exception {
993                                    int threadCount =
994                                            MBThreadLocalServiceUtil.getCategoryThreadsCount(
995                                                    category.getGroupId(), category.getCategoryId(),
996                                                    WorkflowConstants.STATUS_APPROVED);
997    
998                                    category.setThreadCount(threadCount);
999    
1000                                    MBCategoryLocalServiceUtil.updateMBCategory(category);
1001    
1002                                    return null;
1003                            }
1004                    };
1005    
1006                    TransactionCommitCallbackRegistryUtil.registerCallback(callable);
1007            }
1008    
1009            public static void updateThreadMessageCount(final MBThread thread) {
1010                    Callable<Void> callable = new PortalCallable<Void>(
1011                            thread.getCompanyId()) {
1012    
1013                            @Override
1014                            protected Void doCall() throws Exception {
1015                                    int messageCount =
1016                                            MBMessageLocalServiceUtil.getThreadMessagesCount(
1017                                                    thread.getThreadId(),
1018                                                    WorkflowConstants.STATUS_APPROVED);
1019    
1020                                    thread.setMessageCount(messageCount);
1021    
1022                                    MBThreadLocalServiceUtil.updateMBThread(thread);
1023    
1024                                    return null;
1025                            }
1026                    };
1027    
1028                    TransactionCommitCallbackRegistryUtil.registerCallback(callable);
1029            }
1030    
1031            private static String[] _findThreadPriority(
1032                    double value, ThemeDisplay themeDisplay, String[] priorities) {
1033    
1034                    for (int i = 0; i < priorities.length; i++) {
1035                            String[] priority = StringUtil.split(priorities[i]);
1036    
1037                            try {
1038                                    String priorityName = priority[0];
1039                                    String priorityImage = priority[1];
1040                                    double priorityValue = GetterUtil.getDouble(priority[2]);
1041    
1042                                    if (value == priorityValue) {
1043                                            if (!priorityImage.startsWith(Http.HTTP)) {
1044                                                    priorityImage =
1045                                                            themeDisplay.getPathThemeImages() + priorityImage;
1046                                            }
1047    
1048                                            return new String[] {priorityName, priorityImage};
1049                                    }
1050                            }
1051                            catch (Exception e) {
1052                                    _log.error("Unable to determine thread priority", e);
1053                            }
1054                    }
1055    
1056                    return null;
1057            }
1058    
1059            private static String _getParentMessageIdFromSubject(Message message)
1060                    throws Exception {
1061    
1062                    if (message.getSubject() == null) {
1063                            return null;
1064                    }
1065    
1066                    String parentMessageId = null;
1067    
1068                    String subject = StringUtil.reverse(message.getSubject());
1069    
1070                    int pos = subject.indexOf(CharPool.LESS_THAN);
1071    
1072                    if (pos != -1) {
1073                            parentMessageId = StringUtil.reverse(subject.substring(0, pos + 1));
1074                    }
1075    
1076                    return parentMessageId;
1077            }
1078    
1079            private static boolean _isEntityRank(
1080                            long companyId, MBStatsUser statsUser, String entityType,
1081                            String entityValue)
1082                    throws Exception {
1083    
1084                    long groupId = statsUser.getGroupId();
1085                    long userId = statsUser.getUserId();
1086    
1087                    if (entityType.equals("organization-role") ||
1088                            entityType.equals("site-role")) {
1089    
1090                            Role role = RoleLocalServiceUtil.getRole(companyId, entityValue);
1091    
1092                            if (UserGroupRoleLocalServiceUtil.hasUserGroupRole(
1093                                            userId, groupId, role.getRoleId(), true)) {
1094    
1095                                    return true;
1096                            }
1097                    }
1098                    else if (entityType.equals("organization")) {
1099                            Organization organization =
1100                                    OrganizationLocalServiceUtil.getOrganization(
1101                                            companyId, entityValue);
1102    
1103                            if (OrganizationLocalServiceUtil.hasUserOrganization(
1104                                            userId, organization.getOrganizationId(), false, false)) {
1105    
1106                                    return true;
1107                            }
1108                    }
1109                    else if (entityType.equals("regular-role")) {
1110                            if (RoleLocalServiceUtil.hasUserRole(
1111                                            userId, companyId, entityValue, true)) {
1112    
1113                                    return true;
1114                            }
1115                    }
1116                    else if (entityType.equals("user-group")) {
1117                            UserGroup userGroup = UserGroupLocalServiceUtil.getUserGroup(
1118                                    companyId, entityValue);
1119    
1120                            if (UserLocalServiceUtil.hasUserGroupUser(
1121                                            userGroup.getUserGroupId(), userId)) {
1122    
1123                                    return true;
1124                            }
1125                    }
1126    
1127                    return false;
1128            }
1129    
1130            private static Log _log = LogFactoryUtil.getLog(MBUtil.class);
1131    
1132    }