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