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