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