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