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