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.concurrent.PortalCallable;
018    import com.liferay.portal.kernel.dao.orm.QueryDefinition;
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.CharPool;
031    import com.liferay.portal.kernel.util.ContentTypes;
032    import com.liferay.portal.kernel.util.GetterUtil;
033    import com.liferay.portal.kernel.util.Http;
034    import com.liferay.portal.kernel.util.LocaleUtil;
035    import com.liferay.portal.kernel.util.LocalizationUtil;
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.workflow.WorkflowConstants;
043    import com.liferay.portal.model.Group;
044    import com.liferay.portal.model.Organization;
045    import com.liferay.portal.model.Role;
046    import com.liferay.portal.model.Subscription;
047    import com.liferay.portal.model.UserGroup;
048    import com.liferay.portal.security.permission.ActionKeys;
049    import com.liferay.portal.security.permission.PermissionChecker;
050    import com.liferay.portal.service.GroupLocalServiceUtil;
051    import com.liferay.portal.service.OrganizationLocalServiceUtil;
052    import com.liferay.portal.service.RoleLocalServiceUtil;
053    import com.liferay.portal.service.SubscriptionLocalServiceUtil;
054    import com.liferay.portal.service.UserGroupLocalServiceUtil;
055    import com.liferay.portal.service.UserGroupRoleLocalServiceUtil;
056    import com.liferay.portal.service.UserLocalServiceUtil;
057    import com.liferay.portal.theme.ThemeDisplay;
058    import com.liferay.portal.util.PortalUtil;
059    import com.liferay.portal.util.PropsValues;
060    import com.liferay.portal.util.WebKeys;
061    import com.liferay.portlet.documentlibrary.model.DLFileEntry;
062    import com.liferay.portlet.documentlibrary.service.DLFileEntryLocalServiceUtil;
063    import com.liferay.portlet.messageboards.model.MBBan;
064    import com.liferay.portlet.messageboards.model.MBCategory;
065    import com.liferay.portlet.messageboards.model.MBCategoryConstants;
066    import com.liferay.portlet.messageboards.model.MBMessage;
067    import com.liferay.portlet.messageboards.model.MBMessageConstants;
068    import com.liferay.portlet.messageboards.model.MBStatsUser;
069    import com.liferay.portlet.messageboards.model.MBThread;
070    import com.liferay.portlet.messageboards.service.MBCategoryLocalServiceUtil;
071    import com.liferay.portlet.messageboards.service.MBMessageLocalServiceUtil;
072    import com.liferay.portlet.messageboards.service.MBThreadLocalServiceUtil;
073    import com.liferay.portlet.messageboards.service.permission.MBMessagePermission;
074    import com.liferay.util.ContentUtil;
075    import com.liferay.util.mail.JavaMailUtil;
076    
077    import java.io.InputStream;
078    
079    import java.util.ArrayList;
080    import java.util.Calendar;
081    import java.util.Collections;
082    import java.util.Date;
083    import java.util.HashMap;
084    import java.util.HashSet;
085    import java.util.List;
086    import java.util.Map;
087    import java.util.Set;
088    import java.util.concurrent.Callable;
089    
090    import javax.mail.BodyPart;
091    import javax.mail.Message;
092    import javax.mail.Part;
093    import javax.mail.internet.MimeMessage;
094    import javax.mail.internet.MimeMultipart;
095    
096    import javax.portlet.PortletPreferences;
097    import javax.portlet.PortletRequest;
098    import javax.portlet.PortletURL;
099    import javax.portlet.RenderResponse;
100    
101    import javax.servlet.http.HttpServletRequest;
102    
103    /**
104     * @author Brian Wing Shun Chan
105     */
106    public class MBUtil {
107    
108            public static final String BB_CODE_EDITOR_WYSIWYG_IMPL_KEY =
109                    "editor.wysiwyg.portal-web.docroot.html.portlet.message_boards." +
110                            "edit_message.bb_code.jsp";
111    
112            public static final String MESSAGE_POP_PORTLET_PREFIX = "mb_message.";
113    
114            public static void addPortletBreadcrumbEntries(
115                            long categoryId, HttpServletRequest request,
116                            RenderResponse renderResponse)
117                    throws Exception {
118    
119                    if ((categoryId == MBCategoryConstants.DEFAULT_PARENT_CATEGORY_ID) ||
120                            (categoryId == MBCategoryConstants.DISCUSSION_CATEGORY_ID)) {
121    
122                            return;
123                    }
124    
125                    MBCategory category = MBCategoryLocalServiceUtil.getCategory(
126                            categoryId);
127    
128                    addPortletBreadcrumbEntries(category, request, renderResponse);
129            }
130    
131            public static void addPortletBreadcrumbEntries(
132                            MBCategory category, HttpServletRequest request,
133                            RenderResponse renderResponse)
134                    throws Exception {
135    
136                    String strutsAction = ParamUtil.getString(request, "struts_action");
137    
138                    PortletURL portletURL = renderResponse.createRenderURL();
139    
140                    if (strutsAction.equals("/message_boards/select_category") ||
141                            strutsAction.equals("/message_boards_admin/select_category")) {
142    
143                            ThemeDisplay themeDisplay = (ThemeDisplay)request.getAttribute(
144                                    WebKeys.THEME_DISPLAY);
145    
146                            portletURL.setParameter(
147                                    "struts_action", "/message_boards/select_category");
148                            portletURL.setWindowState(LiferayWindowState.POP_UP);
149    
150                            PortalUtil.addPortletBreadcrumbEntry(
151                                    request, themeDisplay.translate("categories"),
152                                    portletURL.toString());
153                    }
154                    else {
155                            portletURL.setParameter("struts_action", "/message_boards/view");
156                    }
157    
158                    List<MBCategory> ancestorCategories = category.getAncestors();
159    
160                    Collections.reverse(ancestorCategories);
161    
162                    for (MBCategory curCategory : ancestorCategories) {
163                            portletURL.setParameter(
164                                    "mbCategoryId", String.valueOf(curCategory.getCategoryId()));
165    
166                            PortalUtil.addPortletBreadcrumbEntry(
167                                    request, curCategory.getName(), portletURL.toString());
168                    }
169    
170                    portletURL.setParameter(
171                            "mbCategoryId", String.valueOf(category.getCategoryId()));
172    
173                    PortalUtil.addPortletBreadcrumbEntry(
174                            request, category.getName(), portletURL.toString());
175            }
176    
177            public static void addPortletBreadcrumbEntries(
178                            MBMessage message, HttpServletRequest request,
179                            RenderResponse renderResponse)
180                    throws Exception {
181    
182                    if (message.getCategoryId() ==
183                                    MBCategoryConstants.DISCUSSION_CATEGORY_ID) {
184    
185                            return;
186                    }
187    
188                    if (message.getCategoryId() !=
189                                    MBCategoryConstants.DEFAULT_PARENT_CATEGORY_ID) {
190    
191                            addPortletBreadcrumbEntries(
192                                    message.getCategory(), request, renderResponse);
193                    }
194    
195                    PortletURL portletURL = renderResponse.createRenderURL();
196    
197                    portletURL.setParameter(
198                            "struts_action", "/message_boards/view_message");
199                    portletURL.setParameter(
200                            "messageId", String.valueOf(message.getMessageId()));
201    
202                    PortalUtil.addPortletBreadcrumbEntry(
203                            request, message.getSubject(), portletURL.toString());
204            }
205    
206            public static void collectMultipartContent(
207                            MimeMultipart multipart, MBMailMessage collector)
208                    throws Exception {
209    
210                    for (int i = 0; i < multipart.getCount(); i++) {
211                            BodyPart part = multipart.getBodyPart(i);
212    
213                            collectPartContent(part, collector);
214                    }
215            }
216    
217            public static void collectPartContent(
218                            Part part, MBMailMessage mbMailMessage)
219                    throws Exception {
220    
221                    Object partContent = part.getContent();
222    
223                    String contentType = part.getContentType().toLowerCase();
224    
225                    if ((part.getDisposition() != null) &&
226                            part.getDisposition().equalsIgnoreCase(MimeMessage.ATTACHMENT)) {
227    
228                            if (_log.isDebugEnabled()) {
229                                    _log.debug("Processing attachment");
230                            }
231    
232                            byte[] bytes = null;
233    
234                            if (partContent instanceof String) {
235                                    bytes = ((String)partContent).getBytes();
236                            }
237                            else if (partContent instanceof InputStream) {
238                                    bytes = JavaMailUtil.getBytes(part);
239                            }
240    
241                            mbMailMessage.addBytes(part.getFileName(), bytes);
242                    }
243                    else {
244                            if (partContent instanceof MimeMultipart) {
245                                    MimeMultipart mimeMultipart = (MimeMultipart)partContent;
246    
247                                    collectMultipartContent(mimeMultipart, mbMailMessage);
248                            }
249                            else if (partContent instanceof String) {
250                                    Map<String, Object> options = new HashMap<String, Object>();
251    
252                                    options.put("emailPartToMBMessageBody", Boolean.TRUE);
253    
254                                    String messageBody = SanitizerUtil.sanitize(
255                                            0, 0, 0, MBMessage.class.getName(), 0, contentType,
256                                            Sanitizer.MODE_ALL, (String)partContent, options);
257    
258                                    if (contentType.startsWith(ContentTypes.TEXT_HTML)) {
259                                            mbMailMessage.setHtmlBody(messageBody);
260                                    }
261                                    else {
262                                            mbMailMessage.setPlainBody(messageBody);
263                                    }
264                            }
265                    }
266            }
267    
268            public static String getAbsolutePath(
269                            PortletRequest portletRequest, long mbCategoryId)
270                    throws PortalException, SystemException {
271    
272                    ThemeDisplay themeDisplay = (ThemeDisplay)portletRequest.getAttribute(
273                            WebKeys.THEME_DISPLAY);
274    
275                    if (mbCategoryId == MBCategoryConstants.DEFAULT_PARENT_CATEGORY_ID) {
276                            return themeDisplay.translate("home");
277                    }
278    
279                    MBCategory mbCategory = MBCategoryLocalServiceUtil.fetchMBCategory(
280                            mbCategoryId);
281    
282                    List<MBCategory> categories = mbCategory.getAncestors();
283    
284                    Collections.reverse(categories);
285    
286                    StringBundler sb = new StringBundler((categories.size() * 3) + 5);
287    
288                    sb.append(themeDisplay.translate("home"));
289                    sb.append(StringPool.SPACE);
290    
291                    for (MBCategory curCategory : categories) {
292                            sb.append(StringPool.RAQUO);
293                            sb.append(StringPool.SPACE);
294                            sb.append(curCategory.getName());
295                    }
296    
297                    sb.append(StringPool.RAQUO);
298                    sb.append(StringPool.SPACE);
299                    sb.append(mbCategory.getName());
300    
301                    return sb.toString();
302            }
303    
304            public static long getCategoryId(
305                    HttpServletRequest request, MBCategory category) {
306    
307                    long categoryId = MBCategoryConstants.DEFAULT_PARENT_CATEGORY_ID;
308    
309                    if (category != null) {
310                            categoryId = category.getCategoryId();
311                    }
312    
313                    categoryId = ParamUtil.getLong(request, "mbCategoryId", categoryId);
314    
315                    return categoryId;
316            }
317    
318            public static long getCategoryId(
319                    HttpServletRequest request, MBMessage message) {
320    
321                    long categoryId = MBCategoryConstants.DEFAULT_PARENT_CATEGORY_ID;
322    
323                    if (message != null) {
324                            categoryId = message.getCategoryId();
325                    }
326    
327                    categoryId = ParamUtil.getLong(request, "mbCategoryId", categoryId);
328    
329                    return categoryId;
330            }
331    
332            public static Set<Long> getCategorySubscriptionClassPKs(long userId)
333                    throws SystemException {
334    
335                    List<Subscription> subscriptions =
336                            SubscriptionLocalServiceUtil.getUserSubscriptions(
337                                    userId, MBCategory.class.getName());
338    
339                    Set<Long> classPKs = new HashSet<Long>(subscriptions.size());
340    
341                    for (Subscription subscription : subscriptions) {
342                            classPKs.add(subscription.getClassPK());
343                    }
344    
345                    return classPKs;
346            }
347    
348            public static String getEmailFromAddress(
349                            PortletPreferences preferences, long companyId)
350                    throws SystemException {
351    
352                    return PortalUtil.getEmailFromAddress(
353                            preferences, companyId,
354                            PropsValues.MESSAGE_BOARDS_EMAIL_FROM_ADDRESS);
355            }
356    
357            public static String getEmailFromName(
358                            PortletPreferences preferences, long companyId)
359                    throws SystemException {
360    
361                    return PortalUtil.getEmailFromName(
362                            preferences, companyId, PropsValues.MESSAGE_BOARDS_EMAIL_FROM_NAME);
363            }
364    
365            public static boolean getEmailHtmlFormat(PortletPreferences preferences) {
366                    String emailHtmlFormat = preferences.getValue(
367                            "emailHtmlFormat", StringPool.BLANK);
368    
369                    if (Validator.isNotNull(emailHtmlFormat)) {
370                            return GetterUtil.getBoolean(emailHtmlFormat);
371                    }
372                    else {
373                            return PropsValues.MESSAGE_BOARDS_EMAIL_HTML_FORMAT;
374                    }
375            }
376    
377            public static String getEmailMessageAddedBody(
378                    PortletPreferences preferences) {
379    
380                    String emailMessageAddedBody = preferences.getValue(
381                            "emailMessageAddedBody", StringPool.BLANK);
382    
383                    if (Validator.isNotNull(emailMessageAddedBody)) {
384                            return emailMessageAddedBody;
385                    }
386                    else {
387                            return ContentUtil.get(
388                                    PropsValues.MESSAGE_BOARDS_EMAIL_MESSAGE_ADDED_BODY);
389                    }
390            }
391    
392            public static boolean getEmailMessageAddedEnabled(
393                    PortletPreferences preferences) {
394    
395                    String emailMessageAddedEnabled = preferences.getValue(
396                            "emailMessageAddedEnabled", StringPool.BLANK);
397    
398                    if (Validator.isNotNull(emailMessageAddedEnabled)) {
399                            return GetterUtil.getBoolean(emailMessageAddedEnabled);
400                    }
401                    else {
402                            return PropsValues.MESSAGE_BOARDS_EMAIL_MESSAGE_ADDED_ENABLED;
403                    }
404            }
405    
406            public static String getEmailMessageAddedSignature(
407                    PortletPreferences preferences) {
408    
409                    String emailMessageAddedSignature = preferences.getValue(
410                            "emailMessageAddedSignature", StringPool.BLANK);
411    
412                    if (Validator.isNotNull(emailMessageAddedSignature)) {
413                            return emailMessageAddedSignature;
414                    }
415                    else {
416                            return ContentUtil.get(
417                                    PropsValues.MESSAGE_BOARDS_EMAIL_MESSAGE_ADDED_SIGNATURE);
418                    }
419            }
420    
421            public static String getEmailMessageAddedSubjectPrefix(
422                    PortletPreferences preferences) {
423    
424                    String emailMessageAddedSubjectPrefix = preferences.getValue(
425                            "emailMessageAddedSubjectPrefix", StringPool.BLANK);
426    
427                    if (Validator.isNotNull(emailMessageAddedSubjectPrefix)) {
428                            return emailMessageAddedSubjectPrefix;
429                    }
430                    else {
431                            return ContentUtil.get(
432                                    PropsValues.MESSAGE_BOARDS_EMAIL_MESSAGE_ADDED_SUBJECT_PREFIX);
433                    }
434            }
435    
436            public static String getEmailMessageUpdatedBody(
437                    PortletPreferences preferences) {
438    
439                    String emailMessageUpdatedBody = preferences.getValue(
440                            "emailMessageUpdatedBody", StringPool.BLANK);
441    
442                    if (Validator.isNotNull(emailMessageUpdatedBody)) {
443                            return emailMessageUpdatedBody;
444                    }
445                    else {
446                            return ContentUtil.get(
447                                    PropsValues.MESSAGE_BOARDS_EMAIL_MESSAGE_UPDATED_BODY);
448                    }
449            }
450    
451            public static boolean getEmailMessageUpdatedEnabled(
452                    PortletPreferences preferences) {
453    
454                    String emailMessageUpdatedEnabled = preferences.getValue(
455                            "emailMessageUpdatedEnabled", StringPool.BLANK);
456    
457                    if (Validator.isNotNull(emailMessageUpdatedEnabled)) {
458                            return GetterUtil.getBoolean(emailMessageUpdatedEnabled);
459                    }
460                    else {
461                            return PropsValues.MESSAGE_BOARDS_EMAIL_MESSAGE_UPDATED_ENABLED;
462                    }
463            }
464    
465            public static String getEmailMessageUpdatedSignature(
466                    PortletPreferences preferences) {
467    
468                    String emailMessageUpdatedSignature = preferences.getValue(
469                            "emailMessageUpdatedSignature", StringPool.BLANK);
470    
471                    if (Validator.isNotNull(emailMessageUpdatedSignature)) {
472                            return emailMessageUpdatedSignature;
473                    }
474                    else {
475                            return ContentUtil.get(
476                                    PropsValues.MESSAGE_BOARDS_EMAIL_MESSAGE_UPDATED_SIGNATURE);
477                    }
478            }
479    
480            public static String getEmailMessageUpdatedSubjectPrefix(
481                    PortletPreferences preferences) {
482    
483                    String emailMessageUpdatedSubject = preferences.getValue(
484                            "emailMessageUpdatedSubjectPrefix", StringPool.BLANK);
485    
486                    if (Validator.isNotNull(emailMessageUpdatedSubject)) {
487                            return emailMessageUpdatedSubject;
488                    }
489                    else {
490                            return ContentUtil.get(
491                                    PropsValues.
492                                            MESSAGE_BOARDS_EMAIL_MESSAGE_UPDATED_SUBJECT_PREFIX);
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 ((references != null) && (references.length > 0)) {
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 ((inReplyToHeaders != null) && (inReplyToHeaders.length > 0)) {
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.getDefault());
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                            }
818                            else {
819                                    String entityType = curRankValueKvp[0];
820                                    String entityValue = curRankValueKvp[1];
821    
822                                    try {
823                                            if (_isEntityRank(
824                                                            companyId, statsUser, entityType, entityValue)) {
825    
826                                                    rank[1] = curRank;
827    
828                                                    break;
829                                            }
830                                    }
831                                    catch (Exception e) {
832                                            if (_log.isWarnEnabled()) {
833                                                    _log.warn(e);
834                                            }
835                                    }
836                            }
837                    }
838    
839                    return rank;
840            }
841    
842            public static boolean hasMailIdHeader(Message message) throws Exception {
843                    String[] messageIds = message.getHeader("Message-ID");
844    
845                    if (messageIds == null) {
846                            return false;
847                    }
848    
849                    for (String messageId : messageIds) {
850                            if (Validator.isNotNull(PropsValues.POP_SERVER_SUBDOMAIN) &&
851                                    messageId.contains(PropsValues.POP_SERVER_SUBDOMAIN)) {
852    
853                                    return true;
854                            }
855                    }
856    
857                    return false;
858            }
859    
860            public static boolean isAllowAnonymousPosting(
861                    PortletPreferences preferences) {
862    
863                    return GetterUtil.getBoolean(
864                            preferences.getValue("allowAnonymousPosting", null),
865                            PropsValues.MESSAGE_BOARDS_ANONYMOUS_POSTING_ENABLED);
866            }
867    
868            public static boolean isViewableMessage(
869                            ThemeDisplay themeDisplay, MBMessage message)
870                    throws Exception {
871    
872                    return isViewableMessage(themeDisplay, message, message);
873            }
874    
875            public static boolean isViewableMessage(
876                            ThemeDisplay themeDisplay, MBMessage message,
877                            MBMessage parentMessage)
878                    throws Exception {
879    
880                    PermissionChecker permissionChecker =
881                            themeDisplay.getPermissionChecker();
882    
883                    if (!MBMessagePermission.contains(
884                                    permissionChecker, parentMessage, ActionKeys.VIEW)) {
885    
886                            return false;
887                    }
888    
889                    if ((message.getMessageId() != parentMessage.getMessageId()) &&
890                            !MBMessagePermission.contains(
891                                    permissionChecker, message, ActionKeys.VIEW)) {
892    
893                            return false;
894                    }
895    
896                    if (!message.isApproved() &&
897                            !Validator.equals(message.getUserId(), themeDisplay.getUserId()) &&
898                            !permissionChecker.isGroupAdmin(themeDisplay.getScopeGroupId())) {
899    
900                            return false;
901                    }
902    
903                    return true;
904            }
905    
906            public static String replaceMessageBodyPaths(
907                    ThemeDisplay themeDisplay, String messageBody) {
908    
909                    return StringUtil.replace(
910                            messageBody,
911                            new String[] {
912                                    "@theme_images_path@", "href=\"/", "src=\"/"
913                            },
914                            new String[] {
915                                    themeDisplay.getPathThemeImages(),
916                                    "href=\"" + themeDisplay.getURLPortal() + "/",
917                                    "src=\"" + themeDisplay.getURLPortal() + "/"
918                            });
919            }
920    
921            public static void updateCategoryMessageCount(
922                    long companyId, final long categoryId) {
923    
924                    Callable<Void> callable = new PortalCallable<Void>(companyId) {
925    
926                            @Override
927                            protected Void doCall() throws Exception {
928                                    MBCategory category =
929                                            MBCategoryLocalServiceUtil.fetchMBCategory(categoryId);
930    
931                                    if (category == null) {
932                                            return null;
933                                    }
934    
935                                    int messageCount = _getMessageCount(category);
936    
937                                    category.setMessageCount(messageCount);
938    
939                                    MBCategoryLocalServiceUtil.updateMBCategory(category);
940    
941                                    return null;
942                            }
943    
944                    };
945    
946                    TransactionCommitCallbackRegistryUtil.registerCallback(callable);
947            }
948    
949            public static void updateCategoryStatistics(
950                    long companyId, final long categoryId) {
951    
952                    Callable<Void> callable = new PortalCallable<Void>(companyId) {
953    
954                            @Override
955                            protected Void doCall() throws Exception {
956                                    MBCategory category =
957                                            MBCategoryLocalServiceUtil.fetchMBCategory(categoryId);
958    
959                                    if (category == null) {
960                                            return null;
961                                    }
962    
963                                    int messageCount = _getMessageCount(category);
964    
965                                    int threadCount =
966                                            MBThreadLocalServiceUtil.getCategoryThreadsCount(
967                                                    category.getGroupId(), category.getCategoryId(),
968                                                    WorkflowConstants.STATUS_APPROVED);
969    
970                                    category.setMessageCount(messageCount);
971                                    category.setThreadCount(threadCount);
972    
973                                    MBCategoryLocalServiceUtil.updateMBCategory(category);
974    
975                                    return null;
976                            }
977    
978                    };
979    
980                    TransactionCommitCallbackRegistryUtil.registerCallback(callable);
981            }
982    
983            public static void updateCategoryThreadCount(
984                    long companyId, final long categoryId) {
985    
986                    Callable<Void> callable = new PortalCallable<Void>(companyId) {
987    
988                            @Override
989                            protected Void doCall() throws Exception {
990                                    MBCategory category =
991                                            MBCategoryLocalServiceUtil.fetchMBCategory(categoryId);
992    
993                                    if (category == null) {
994                                            return null;
995                                    }
996    
997                                    int threadCount =
998                                            MBThreadLocalServiceUtil.getCategoryThreadsCount(
999                                                    category.getGroupId(), category.getCategoryId(),
1000                                                    WorkflowConstants.STATUS_APPROVED);
1001    
1002                                    category.setThreadCount(threadCount);
1003    
1004                                    MBCategoryLocalServiceUtil.updateMBCategory(category);
1005    
1006                                    return null;
1007                            }
1008    
1009                    };
1010    
1011                    TransactionCommitCallbackRegistryUtil.registerCallback(callable);
1012            }
1013    
1014            public static void updateThreadMessageCount(
1015                    long companyId, final long threadId) {
1016    
1017                    Callable<Void> callable = new PortalCallable<Void>(companyId) {
1018    
1019                            @Override
1020                            protected Void doCall() throws Exception {
1021                                    MBThread thread = MBThreadLocalServiceUtil.fetchThread(
1022                                            threadId);
1023    
1024                                    if (thread == null) {
1025                                            return null;
1026                                    }
1027    
1028                                    int messageCount =
1029                                            MBMessageLocalServiceUtil.getThreadMessagesCount(
1030                                                    threadId, WorkflowConstants.STATUS_APPROVED);
1031    
1032                                    thread.setMessageCount(messageCount);
1033    
1034                                    MBThreadLocalServiceUtil.updateMBThread(thread);
1035    
1036                                    return null;
1037                            }
1038    
1039                    };
1040    
1041                    TransactionCommitCallbackRegistryUtil.registerCallback(callable);
1042            }
1043    
1044            private static String[] _findThreadPriority(
1045                    double value, ThemeDisplay themeDisplay, String[] priorities) {
1046    
1047                    for (int i = 0; i < priorities.length; i++) {
1048                            String[] priority = StringUtil.split(priorities[i]);
1049    
1050                            try {
1051                                    String priorityName = priority[0];
1052                                    String priorityImage = priority[1];
1053                                    double priorityValue = GetterUtil.getDouble(priority[2]);
1054    
1055                                    if (value == priorityValue) {
1056                                            if (!priorityImage.startsWith(Http.HTTP)) {
1057                                                    priorityImage =
1058                                                            themeDisplay.getPathThemeImages() + priorityImage;
1059                                            }
1060    
1061                                            return new String[] {priorityName, priorityImage};
1062                                    }
1063                            }
1064                            catch (Exception e) {
1065                                    _log.error("Unable to determine thread priority", e);
1066                            }
1067                    }
1068    
1069                    return null;
1070            }
1071    
1072            private static int _getMessageCount(MBCategory category)
1073                    throws SystemException {
1074    
1075                    int messageCount = MBMessageLocalServiceUtil.getCategoryMessagesCount(
1076                            category.getGroupId(), category.getCategoryId(),
1077                            WorkflowConstants.STATUS_APPROVED);
1078    
1079                    QueryDefinition queryDefinition = new QueryDefinition(
1080                            WorkflowConstants.STATUS_IN_TRASH);
1081    
1082                    List<MBThread> threads = MBThreadLocalServiceUtil.getGroupThreads(
1083                            category.getGroupId(), queryDefinition);
1084    
1085                    for (MBThread thread : threads) {
1086                            messageCount = messageCount - thread.getMessageCount();
1087                    }
1088    
1089                    return messageCount;
1090            }
1091    
1092            private static String _getParentMessageIdFromSubject(Message message)
1093                    throws Exception {
1094    
1095                    if (message.getSubject() == null) {
1096                            return null;
1097                    }
1098    
1099                    String parentMessageId = null;
1100    
1101                    String subject = StringUtil.reverse(message.getSubject());
1102    
1103                    int pos = subject.indexOf(CharPool.LESS_THAN);
1104    
1105                    if (pos != -1) {
1106                            parentMessageId = StringUtil.reverse(subject.substring(0, pos + 1));
1107                    }
1108    
1109                    return parentMessageId;
1110            }
1111    
1112            private static boolean _isEntityRank(
1113                            long companyId, MBStatsUser statsUser, String entityType,
1114                            String entityValue)
1115                    throws Exception {
1116    
1117                    long groupId = statsUser.getGroupId();
1118                    long userId = statsUser.getUserId();
1119    
1120                    if (entityType.equals("organization-role") ||
1121                            entityType.equals("site-role")) {
1122    
1123                            Role role = RoleLocalServiceUtil.getRole(companyId, entityValue);
1124    
1125                            if (UserGroupRoleLocalServiceUtil.hasUserGroupRole(
1126                                            userId, groupId, role.getRoleId(), true)) {
1127    
1128                                    return true;
1129                            }
1130                    }
1131                    else if (entityType.equals("organization")) {
1132                            Organization organization =
1133                                    OrganizationLocalServiceUtil.getOrganization(
1134                                            companyId, entityValue);
1135    
1136                            if (OrganizationLocalServiceUtil.hasUserOrganization(
1137                                            userId, organization.getOrganizationId(), false, false)) {
1138    
1139                                    return true;
1140                            }
1141                    }
1142                    else if (entityType.equals("regular-role")) {
1143                            if (RoleLocalServiceUtil.hasUserRole(
1144                                            userId, companyId, entityValue, true)) {
1145    
1146                                    return true;
1147                            }
1148                    }
1149                    else if (entityType.equals("user-group")) {
1150                            UserGroup userGroup = UserGroupLocalServiceUtil.getUserGroup(
1151                                    companyId, entityValue);
1152    
1153                            if (UserLocalServiceUtil.hasUserGroupUser(
1154                                            userGroup.getUserGroupId(), userId)) {
1155    
1156                                    return true;
1157                            }
1158                    }
1159    
1160                    return false;
1161            }
1162    
1163            private static Log _log = LogFactoryUtil.getLog(MBUtil.class);
1164    
1165    }