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