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