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.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 getEmailMessageAddedSubject(
422                    PortletPreferences preferences) {
423    
424                    String emailMessageAddedSubject = preferences.getValue(
425                            "emailMessageAddedSubject", StringPool.BLANK);
426    
427                    if (Validator.isNotNull(emailMessageAddedSubject)) {
428                            return emailMessageAddedSubject;
429                    }
430                    else {
431                            return ContentUtil.get(
432                                    PropsValues.MESSAGE_BOARDS_EMAIL_MESSAGE_ADDED_SUBJECT);
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 getEmailMessageUpdatedSubject(
481                    PortletPreferences preferences) {
482    
483                    String emailMessageUpdatedSubject = preferences.getValue(
484                            "emailMessageUpdatedSubject", StringPool.BLANK);
485    
486                    if (Validator.isNotNull(emailMessageUpdatedSubject)) {
487                            return emailMessageUpdatedSubject;
488                    }
489                    else {
490                            return ContentUtil.get(
491                                    PropsValues.MESSAGE_BOARDS_EMAIL_MESSAGE_UPDATED_SUBJECT);
492                    }
493            }
494    
495            public static List<Object> getEntries(Hits hits) {
496                    List<Object> entries = new ArrayList<Object>();
497    
498                    for (Document document : hits.getDocs()) {
499                            long categoryId = GetterUtil.getLong(
500                                    document.get(Field.CATEGORY_ID));
501    
502                            try {
503                                    MBCategoryLocalServiceUtil.getCategory(categoryId);
504                            }
505                            catch (Exception e) {
506                                    if (_log.isWarnEnabled()) {
507                                            _log.warn(
508                                                    "Message boards search index is stale and contains " +
509                                                            "category " + categoryId);
510                                    }
511    
512                                    continue;
513                            }
514    
515                            long threadId = GetterUtil.getLong(document.get("threadId"));
516    
517                            try {
518                                    MBThreadLocalServiceUtil.getThread(threadId);
519                            }
520                            catch (Exception e) {
521                                    if (_log.isWarnEnabled()) {
522                                            _log.warn(
523                                                    "Message boards search index is stale and contains " +
524                                                            "thread " + threadId);
525                                    }
526    
527                                    continue;
528                            }
529    
530                            String entryClassName = document.get(Field.ENTRY_CLASS_NAME);
531                            long entryClassPK = GetterUtil.getLong(
532                                    document.get(Field.ENTRY_CLASS_PK));
533    
534                            Object obj = null;
535    
536                            try {
537                                    if (entryClassName.equals(DLFileEntry.class.getName())) {
538                                            long classPK = GetterUtil.getLong(
539                                                    document.get(Field.CLASS_PK));
540    
541                                            MBMessageLocalServiceUtil.getMessage(classPK);
542    
543                                            obj = DLFileEntryLocalServiceUtil.getDLFileEntry(
544                                                    entryClassPK);
545                                    }
546                                    else if (entryClassName.equals(MBMessage.class.getName())) {
547                                            obj = MBMessageLocalServiceUtil.getMessage(entryClassPK);
548                                    }
549    
550                                    entries.add(obj);
551                            }
552                            catch (Exception e) {
553                                    if (_log.isWarnEnabled()) {
554                                            _log.warn(
555                                                    "Message boards search index is stale and contains " +
556                                                            "entry {className=" + entryClassName + ", " +
557                                                                    "classPK=" + entryClassPK + "}");
558                                    }
559    
560                                    continue;
561                            }
562                    }
563    
564                    return entries;
565            }
566    
567            public static String getMessageFormat(PortletPreferences preferences) {
568                    String messageFormat = preferences.getValue(
569                            "messageFormat", MBMessageConstants.DEFAULT_FORMAT);
570    
571                    String editorImpl = PropsUtil.get(BB_CODE_EDITOR_WYSIWYG_IMPL_KEY);
572    
573                    if (messageFormat.equals("bbcode") &&
574                            !(editorImpl.equals("bbcode") ||
575                              editorImpl.equals("ckeditor_bbcode"))) {
576    
577                            messageFormat = "html";
578                    }
579    
580                    return messageFormat;
581            }
582    
583            public static long getMessageId(String mailId) {
584                    int x = mailId.indexOf(CharPool.LESS_THAN) + 1;
585                    int y = mailId.indexOf(CharPool.AT);
586    
587                    long messageId = 0;
588    
589                    if ((x > 0 ) && (y != -1)) {
590                            String temp = mailId.substring(x, y);
591    
592                            int z = temp.lastIndexOf(CharPool.PERIOD);
593    
594                            if (z != -1) {
595                                    messageId = GetterUtil.getLong(temp.substring(z + 1));
596                            }
597                    }
598    
599                    return messageId;
600            }
601    
602            public static long getParentMessageId(Message message) throws Exception {
603                    long parentMessageId = -1;
604    
605                    String parentHeader = getParentMessageIdString(message);
606    
607                    if (parentHeader != null) {
608                            if (_log.isDebugEnabled()) {
609                                    _log.debug("Parent header " + parentHeader);
610                            }
611    
612                            parentMessageId = getMessageId(parentHeader);
613    
614                            if (_log.isDebugEnabled()) {
615                                    _log.debug("Previous message id " + parentMessageId);
616                            }
617                    }
618    
619                    return parentMessageId;
620            }
621    
622            public static String getParentMessageIdString(Message message)
623                    throws Exception {
624    
625                    // If the previous block failed, try to get the parent message ID from
626                    // the "References" header as explained in
627                    // http://cr.yp.to/immhf/thread.html. Some mail clients such as Yahoo!
628                    // Mail use the "In-Reply-To" header, so we check that as well.
629    
630                    String parentHeader = null;
631    
632                    String[] references = message.getHeader("References");
633    
634                    if ((references != null) && (references.length > 0)) {
635                            String reference = references[0];
636    
637                            int x = reference.lastIndexOf("<mb.");
638    
639                            if (x > -1) {
640                                    int y = reference.indexOf(">", x);
641    
642                                    parentHeader = reference.substring(x, y);
643                            }
644                    }
645    
646                    if (parentHeader == null) {
647                            String[] inReplyToHeaders = message.getHeader("In-Reply-To");
648    
649                            if ((inReplyToHeaders != null) && (inReplyToHeaders.length > 0)) {
650                                    parentHeader = inReplyToHeaders[0];
651                            }
652                    }
653    
654                    if (Validator.isNull(parentHeader) ||
655                            !parentHeader.startsWith(MESSAGE_POP_PORTLET_PREFIX, 1)) {
656    
657                            parentHeader = _getParentMessageIdFromSubject(message);
658                    }
659    
660                    return parentHeader;
661            }
662    
663            public static String getReplyToAddress(
664                    long categoryId, long messageId, String mx,
665                    String defaultMailingListAddress) {
666    
667                    if (PropsValues.POP_SERVER_SUBDOMAIN.length() <= 0) {
668                            return defaultMailingListAddress;
669                    }
670    
671                    StringBundler sb = new StringBundler(8);
672    
673                    sb.append(MESSAGE_POP_PORTLET_PREFIX);
674                    sb.append(categoryId);
675                    sb.append(StringPool.PERIOD);
676                    sb.append(messageId);
677                    sb.append(StringPool.AT);
678                    sb.append(PropsValues.POP_SERVER_SUBDOMAIN);
679                    sb.append(StringPool.PERIOD);
680                    sb.append(mx);
681    
682                    return sb.toString();
683            }
684    
685            public static String getSubjectWithoutMessageId(Message message)
686                    throws Exception {
687    
688                    String subject = message.getSubject();
689    
690                    String parentMessageId = _getParentMessageIdFromSubject(message);
691    
692                    if (Validator.isNotNull(parentMessageId)) {
693                            int pos = subject.indexOf(parentMessageId);
694    
695                            if (pos != -1) {
696                                    subject = subject.substring(0, pos);
697                            }
698                    }
699    
700                    return subject;
701            }
702    
703            public static String[] getThreadPriority(
704                            PortletPreferences preferences, String languageId, double value,
705                            ThemeDisplay themeDisplay)
706                    throws Exception {
707    
708                    String[] priorities = LocalizationUtil.getPreferencesValues(
709                            preferences, "priorities", languageId);
710    
711                    String[] priorityPair = _findThreadPriority(
712                            value, themeDisplay, priorities);
713    
714                    if (priorityPair == null) {
715                            String defaultLanguageId = LocaleUtil.toLanguageId(
716                                    LocaleUtil.getSiteDefault());
717    
718                            priorities = LocalizationUtil.getPreferencesValues(
719                                    preferences, "priorities", defaultLanguageId);
720    
721                            priorityPair = _findThreadPriority(value, themeDisplay, priorities);
722                    }
723    
724                    return priorityPair;
725            }
726    
727            public static Set<Long> getThreadSubscriptionClassPKs(long userId)
728                    throws SystemException {
729    
730                    List<Subscription> subscriptions =
731                            SubscriptionLocalServiceUtil.getUserSubscriptions(
732                                    userId, MBThread.class.getName());
733    
734                    Set<Long> classPKs = new HashSet<Long>(subscriptions.size());
735    
736                    for (Subscription subscription : subscriptions) {
737                            classPKs.add(subscription.getClassPK());
738                    }
739    
740                    return classPKs;
741            }
742    
743            public static Date getUnbanDate(MBBan ban, int expireInterval) {
744                    Date banDate = ban.getCreateDate();
745    
746                    Calendar cal = Calendar.getInstance();
747    
748                    cal.setTime(banDate);
749    
750                    cal.add(Calendar.DATE, expireInterval);
751    
752                    return cal.getTime();
753            }
754    
755            public static String getUserRank(
756                            PortletPreferences preferences, String languageId, int posts)
757                    throws Exception {
758    
759                    String rank = StringPool.BLANK;
760    
761                    String[] ranks = LocalizationUtil.getPreferencesValues(
762                            preferences, "ranks", languageId);
763    
764                    for (int i = 0; i < ranks.length; i++) {
765                            String[] kvp = StringUtil.split(ranks[i], CharPool.EQUAL);
766    
767                            String kvpName = kvp[0];
768                            int kvpPosts = GetterUtil.getInteger(kvp[1]);
769    
770                            if (posts >= kvpPosts) {
771                                    rank = kvpName;
772                            }
773                            else {
774                                    break;
775                            }
776                    }
777    
778                    return rank;
779            }
780    
781            public static String[] getUserRank(
782                            PortletPreferences preferences, String languageId,
783                            MBStatsUser statsUser)
784                    throws Exception {
785    
786                    String[] rank = {StringPool.BLANK, StringPool.BLANK};
787    
788                    int maxPosts = 0;
789    
790                    Group group = GroupLocalServiceUtil.getGroup(statsUser.getGroupId());
791    
792                    long companyId = group.getCompanyId();
793    
794                    String[] ranks = LocalizationUtil.getPreferencesValues(
795                            preferences, "ranks", languageId);
796    
797                    for (int i = 0; i < ranks.length; i++) {
798                            String[] kvp = StringUtil.split(ranks[i], CharPool.EQUAL);
799    
800                            String curRank = kvp[0];
801                            String curRankValue = kvp[1];
802    
803                            String[] curRankValueKvp = StringUtil.split(
804                                    curRankValue, CharPool.COLON);
805    
806                            if (curRankValueKvp.length <= 1) {
807                                    int posts = GetterUtil.getInteger(curRankValue);
808    
809                                    if ((posts <= statsUser.getMessageCount()) &&
810                                            (posts >= maxPosts)) {
811    
812                                            rank[0] = curRank;
813                                            maxPosts = posts;
814                                    }
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    }