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 long getCategoryId(String messageIdString) {
339                    String[] parts = _getMessageIdStringParts(messageIdString);
340    
341                    return GetterUtil.getLong(parts[0]);
342            }
343    
344            public static Set<Long> getCategorySubscriptionClassPKs(long userId)
345                    throws SystemException {
346    
347                    List<Subscription> subscriptions =
348                            SubscriptionLocalServiceUtil.getUserSubscriptions(
349                                    userId, MBCategory.class.getName());
350    
351                    Set<Long> classPKs = new HashSet<Long>(subscriptions.size());
352    
353                    for (Subscription subscription : subscriptions) {
354                            classPKs.add(subscription.getClassPK());
355                    }
356    
357                    return classPKs;
358            }
359    
360            public static String getEmailFromAddress(
361                            PortletPreferences preferences, long companyId)
362                    throws SystemException {
363    
364                    return PortalUtil.getEmailFromAddress(
365                            preferences, companyId,
366                            PropsValues.MESSAGE_BOARDS_EMAIL_FROM_ADDRESS);
367            }
368    
369            public static String getEmailFromName(
370                            PortletPreferences preferences, long companyId)
371                    throws SystemException {
372    
373                    return PortalUtil.getEmailFromName(
374                            preferences, companyId, PropsValues.MESSAGE_BOARDS_EMAIL_FROM_NAME);
375            }
376    
377            public static boolean getEmailHtmlFormat(PortletPreferences preferences) {
378                    String emailHtmlFormat = preferences.getValue(
379                            "emailHtmlFormat", StringPool.BLANK);
380    
381                    if (Validator.isNotNull(emailHtmlFormat)) {
382                            return GetterUtil.getBoolean(emailHtmlFormat);
383                    }
384                    else {
385                            return PropsValues.MESSAGE_BOARDS_EMAIL_HTML_FORMAT;
386                    }
387            }
388    
389            public static String getEmailMessageAddedBody(
390                    PortletPreferences preferences) {
391    
392                    String emailMessageAddedBody = preferences.getValue(
393                            "emailMessageAddedBody", StringPool.BLANK);
394    
395                    if (Validator.isNotNull(emailMessageAddedBody)) {
396                            return emailMessageAddedBody;
397                    }
398                    else {
399                            return ContentUtil.get(
400                                    PropsValues.MESSAGE_BOARDS_EMAIL_MESSAGE_ADDED_BODY);
401                    }
402            }
403    
404            public static boolean getEmailMessageAddedEnabled(
405                    PortletPreferences preferences) {
406    
407                    String emailMessageAddedEnabled = preferences.getValue(
408                            "emailMessageAddedEnabled", StringPool.BLANK);
409    
410                    if (Validator.isNotNull(emailMessageAddedEnabled)) {
411                            return GetterUtil.getBoolean(emailMessageAddedEnabled);
412                    }
413                    else {
414                            return PropsValues.MESSAGE_BOARDS_EMAIL_MESSAGE_ADDED_ENABLED;
415                    }
416            }
417    
418            public static String getEmailMessageAddedSignature(
419                    PortletPreferences preferences) {
420    
421                    String emailMessageAddedSignature = preferences.getValue(
422                            "emailMessageAddedSignature", StringPool.BLANK);
423    
424                    if (Validator.isNotNull(emailMessageAddedSignature)) {
425                            return emailMessageAddedSignature;
426                    }
427                    else {
428                            return ContentUtil.get(
429                                    PropsValues.MESSAGE_BOARDS_EMAIL_MESSAGE_ADDED_SIGNATURE);
430                    }
431            }
432    
433            public static String getEmailMessageAddedSubject(
434                    PortletPreferences preferences) {
435    
436                    String emailMessageAddedSubject = preferences.getValue(
437                            "emailMessageAddedSubject", StringPool.BLANK);
438    
439                    if (Validator.isNotNull(emailMessageAddedSubject)) {
440                            return emailMessageAddedSubject;
441                    }
442                    else {
443                            return ContentUtil.get(
444                                    PropsValues.MESSAGE_BOARDS_EMAIL_MESSAGE_ADDED_SUBJECT);
445                    }
446            }
447    
448            public static String getEmailMessageUpdatedBody(
449                    PortletPreferences preferences) {
450    
451                    String emailMessageUpdatedBody = preferences.getValue(
452                            "emailMessageUpdatedBody", StringPool.BLANK);
453    
454                    if (Validator.isNotNull(emailMessageUpdatedBody)) {
455                            return emailMessageUpdatedBody;
456                    }
457                    else {
458                            return ContentUtil.get(
459                                    PropsValues.MESSAGE_BOARDS_EMAIL_MESSAGE_UPDATED_BODY);
460                    }
461            }
462    
463            public static boolean getEmailMessageUpdatedEnabled(
464                    PortletPreferences preferences) {
465    
466                    String emailMessageUpdatedEnabled = preferences.getValue(
467                            "emailMessageUpdatedEnabled", StringPool.BLANK);
468    
469                    if (Validator.isNotNull(emailMessageUpdatedEnabled)) {
470                            return GetterUtil.getBoolean(emailMessageUpdatedEnabled);
471                    }
472                    else {
473                            return PropsValues.MESSAGE_BOARDS_EMAIL_MESSAGE_UPDATED_ENABLED;
474                    }
475            }
476    
477            public static String getEmailMessageUpdatedSignature(
478                    PortletPreferences preferences) {
479    
480                    String emailMessageUpdatedSignature = preferences.getValue(
481                            "emailMessageUpdatedSignature", StringPool.BLANK);
482    
483                    if (Validator.isNotNull(emailMessageUpdatedSignature)) {
484                            return emailMessageUpdatedSignature;
485                    }
486                    else {
487                            return ContentUtil.get(
488                                    PropsValues.MESSAGE_BOARDS_EMAIL_MESSAGE_UPDATED_SIGNATURE);
489                    }
490            }
491    
492            public static String getEmailMessageUpdatedSubject(
493                    PortletPreferences preferences) {
494    
495                    String emailMessageUpdatedSubject = preferences.getValue(
496                            "emailMessageUpdatedSubject", StringPool.BLANK);
497    
498                    if (Validator.isNotNull(emailMessageUpdatedSubject)) {
499                            return emailMessageUpdatedSubject;
500                    }
501                    else {
502                            return ContentUtil.get(
503                                    PropsValues.MESSAGE_BOARDS_EMAIL_MESSAGE_UPDATED_SUBJECT);
504                    }
505            }
506    
507            public static List<Object> getEntries(Hits hits) {
508                    List<Object> entries = new ArrayList<Object>();
509    
510                    for (Document document : hits.getDocs()) {
511                            long categoryId = GetterUtil.getLong(
512                                    document.get(Field.CATEGORY_ID));
513    
514                            try {
515                                    MBCategoryLocalServiceUtil.getCategory(categoryId);
516                            }
517                            catch (Exception e) {
518                                    if (_log.isWarnEnabled()) {
519                                            _log.warn(
520                                                    "Message boards search index is stale and contains " +
521                                                            "category " + categoryId);
522                                    }
523    
524                                    continue;
525                            }
526    
527                            long threadId = GetterUtil.getLong(document.get("threadId"));
528    
529                            try {
530                                    MBThreadLocalServiceUtil.getThread(threadId);
531                            }
532                            catch (Exception e) {
533                                    if (_log.isWarnEnabled()) {
534                                            _log.warn(
535                                                    "Message boards search index is stale and contains " +
536                                                            "thread " + threadId);
537                                    }
538    
539                                    continue;
540                            }
541    
542                            String entryClassName = document.get(Field.ENTRY_CLASS_NAME);
543                            long entryClassPK = GetterUtil.getLong(
544                                    document.get(Field.ENTRY_CLASS_PK));
545    
546                            Object obj = null;
547    
548                            try {
549                                    if (entryClassName.equals(DLFileEntry.class.getName())) {
550                                            long classPK = GetterUtil.getLong(
551                                                    document.get(Field.CLASS_PK));
552    
553                                            MBMessageLocalServiceUtil.getMessage(classPK);
554    
555                                            obj = DLFileEntryLocalServiceUtil.getDLFileEntry(
556                                                    entryClassPK);
557                                    }
558                                    else if (entryClassName.equals(MBMessage.class.getName())) {
559                                            obj = MBMessageLocalServiceUtil.getMessage(entryClassPK);
560                                    }
561    
562                                    entries.add(obj);
563                            }
564                            catch (Exception e) {
565                                    if (_log.isWarnEnabled()) {
566                                            _log.warn(
567                                                    "Message boards search index is stale and contains " +
568                                                            "entry {className=" + entryClassName + ", " +
569                                                                    "classPK=" + entryClassPK + "}");
570                                    }
571    
572                                    continue;
573                            }
574                    }
575    
576                    return entries;
577            }
578    
579            public static String getMessageFormat(PortletPreferences preferences) {
580                    String messageFormat = preferences.getValue(
581                            "messageFormat", MBMessageConstants.DEFAULT_FORMAT);
582    
583                    if (isValidMessageFormat(messageFormat)) {
584                            return messageFormat;
585                    }
586    
587                    return "html";
588            }
589    
590            public static long getMessageId(String messageIdString) {
591                    String[] parts = _getMessageIdStringParts(messageIdString);
592    
593                    return GetterUtil.getLong(parts[1]);
594            }
595    
596            public static int getMessageIdStringOffset() {
597                    if (PropsValues.POP_SERVER_SUBDOMAIN.length() == 0) {
598                            return 1;
599                    }
600    
601                    return 0;
602            }
603    
604            public static long getParentMessageId(Message message) throws Exception {
605                    long parentMessageId = -1;
606    
607                    String parentMessageIdString = getParentMessageIdString(message);
608    
609                    if (parentMessageIdString != null) {
610                            if (_log.isDebugEnabled()) {
611                                    _log.debug("Parent header " + parentMessageIdString);
612                            }
613    
614                            parentMessageId = getMessageId(parentMessageIdString);
615    
616                            if (_log.isDebugEnabled()) {
617                                    _log.debug("Parent message id " + parentMessageId);
618                            }
619                    }
620    
621                    return parentMessageId;
622            }
623    
624            public static String getParentMessageIdString(Message message)
625                    throws Exception {
626    
627                    // If the previous block failed, try to get the parent message ID from
628                    // the "References" header as explained in
629                    // http://cr.yp.to/immhf/thread.html. Some mail clients such as Yahoo!
630                    // Mail use the "In-Reply-To" header, so we check that as well.
631    
632                    String parentHeader = null;
633    
634                    String[] references = message.getHeader("References");
635    
636                    if (ArrayUtil.isNotEmpty(references)) {
637                            String reference = references[0];
638    
639                            int x = reference.lastIndexOf(
640                                    StringPool.LESS_THAN + MESSAGE_POP_PORTLET_PREFIX);
641    
642                            if (x > -1) {
643                                    int y = reference.indexOf(StringPool.GREATER_THAN, x);
644    
645                                    parentHeader = reference.substring(x, y + 1);
646                            }
647                    }
648    
649                    if (parentHeader == null) {
650                            String[] inReplyToHeaders = message.getHeader("In-Reply-To");
651    
652                            if (ArrayUtil.isNotEmpty(inReplyToHeaders)) {
653                                    parentHeader = inReplyToHeaders[0];
654                            }
655                    }
656    
657                    if (Validator.isNull(parentHeader) ||
658                            !parentHeader.startsWith(MESSAGE_POP_PORTLET_PREFIX, 1)) {
659    
660                            parentHeader = _getParentMessageIdFromSubject(message);
661                    }
662    
663                    return parentHeader;
664            }
665    
666            public static String getReplyToAddress(
667                    long categoryId, long messageId, String mx,
668                    String defaultMailingListAddress) {
669    
670                    if (PropsValues.POP_SERVER_SUBDOMAIN.length() <= 0) {
671                            return defaultMailingListAddress;
672                    }
673    
674                    StringBundler sb = new StringBundler(8);
675    
676                    sb.append(MESSAGE_POP_PORTLET_PREFIX);
677                    sb.append(categoryId);
678                    sb.append(StringPool.PERIOD);
679                    sb.append(messageId);
680                    sb.append(StringPool.AT);
681                    sb.append(PropsValues.POP_SERVER_SUBDOMAIN);
682                    sb.append(StringPool.PERIOD);
683                    sb.append(mx);
684    
685                    return sb.toString();
686            }
687    
688            public static String getSubjectForEmail(MBMessage message)
689                    throws Exception {
690    
691                    String subject = message.getSubject();
692    
693                    if (subject.startsWith("RE:")) {
694                            return subject;
695                    }
696                    else {
697                            return "RE: " + message.getSubject();
698                    }
699            }
700    
701            public static String getSubjectWithoutMessageId(Message message)
702                    throws Exception {
703    
704                    String subject = message.getSubject();
705    
706                    String parentMessageId = _getParentMessageIdFromSubject(message);
707    
708                    if (Validator.isNotNull(parentMessageId)) {
709                            int pos = subject.indexOf(parentMessageId);
710    
711                            if (pos != -1) {
712                                    subject = subject.substring(0, pos);
713                            }
714                    }
715    
716                    return subject;
717            }
718    
719            public static String[] getThreadPriority(
720                            PortletPreferences preferences, String languageId, double value,
721                            ThemeDisplay themeDisplay)
722                    throws Exception {
723    
724                    String[] priorities = LocalizationUtil.getPreferencesValues(
725                            preferences, "priorities", languageId);
726    
727                    String[] priorityPair = _findThreadPriority(
728                            value, themeDisplay, priorities);
729    
730                    if (priorityPair == null) {
731                            String defaultLanguageId = LocaleUtil.toLanguageId(
732                                    LocaleUtil.getSiteDefault());
733    
734                            priorities = LocalizationUtil.getPreferencesValues(
735                                    preferences, "priorities", defaultLanguageId);
736    
737                            priorityPair = _findThreadPriority(value, themeDisplay, priorities);
738                    }
739    
740                    return priorityPair;
741            }
742    
743            public static Set<Long> getThreadSubscriptionClassPKs(long userId)
744                    throws SystemException {
745    
746                    List<Subscription> subscriptions =
747                            SubscriptionLocalServiceUtil.getUserSubscriptions(
748                                    userId, MBThread.class.getName());
749    
750                    Set<Long> classPKs = new HashSet<Long>(subscriptions.size());
751    
752                    for (Subscription subscription : subscriptions) {
753                            classPKs.add(subscription.getClassPK());
754                    }
755    
756                    return classPKs;
757            }
758    
759            public static Date getUnbanDate(MBBan ban, int expireInterval) {
760                    Date banDate = ban.getCreateDate();
761    
762                    Calendar cal = Calendar.getInstance();
763    
764                    cal.setTime(banDate);
765    
766                    cal.add(Calendar.DATE, expireInterval);
767    
768                    return cal.getTime();
769            }
770    
771            public static String getUserRank(
772                            PortletPreferences preferences, String languageId, int posts)
773                    throws Exception {
774    
775                    String rank = StringPool.BLANK;
776    
777                    String[] ranks = LocalizationUtil.getPreferencesValues(
778                            preferences, "ranks", languageId);
779    
780                    for (int i = 0; i < ranks.length; i++) {
781                            String[] kvp = StringUtil.split(ranks[i], CharPool.EQUAL);
782    
783                            String kvpName = kvp[0];
784                            int kvpPosts = GetterUtil.getInteger(kvp[1]);
785    
786                            if (posts >= kvpPosts) {
787                                    rank = kvpName;
788                            }
789                            else {
790                                    break;
791                            }
792                    }
793    
794                    return rank;
795            }
796    
797            public static String[] getUserRank(
798                            PortletPreferences preferences, String languageId,
799                            MBStatsUser statsUser)
800                    throws Exception {
801    
802                    String[] rank = {StringPool.BLANK, StringPool.BLANK};
803    
804                    int maxPosts = 0;
805    
806                    Group group = GroupLocalServiceUtil.getGroup(statsUser.getGroupId());
807    
808                    long companyId = group.getCompanyId();
809    
810                    String[] ranks = LocalizationUtil.getPreferencesValues(
811                            preferences, "ranks", languageId);
812    
813                    for (int i = 0; i < ranks.length; i++) {
814                            String[] kvp = StringUtil.split(ranks[i], CharPool.EQUAL);
815    
816                            String curRank = kvp[0];
817                            String curRankValue = kvp[1];
818    
819                            String[] curRankValueKvp = StringUtil.split(
820                                    curRankValue, CharPool.COLON);
821    
822                            if (curRankValueKvp.length <= 1) {
823                                    int posts = GetterUtil.getInteger(curRankValue);
824    
825                                    if ((posts <= statsUser.getMessageCount()) &&
826                                            (posts >= maxPosts)) {
827    
828                                            rank[0] = curRank;
829                                            maxPosts = posts;
830                                    }
831                            }
832                            else {
833                                    String entityType = curRankValueKvp[0];
834                                    String entityValue = curRankValueKvp[1];
835    
836                                    try {
837                                            if (_isEntityRank(
838                                                            companyId, statsUser, entityType, entityValue)) {
839    
840                                                    rank[1] = curRank;
841    
842                                                    break;
843                                            }
844                                    }
845                                    catch (Exception e) {
846                                            if (_log.isWarnEnabled()) {
847                                                    _log.warn(e);
848                                            }
849                                    }
850                            }
851                    }
852    
853                    return rank;
854            }
855    
856            public static boolean hasMailIdHeader(Message message) throws Exception {
857                    String[] messageIds = message.getHeader("Message-ID");
858    
859                    if (messageIds == null) {
860                            return false;
861                    }
862    
863                    for (String messageId : messageIds) {
864                            if (Validator.isNotNull(PropsValues.POP_SERVER_SUBDOMAIN) &&
865                                    messageId.contains(PropsValues.POP_SERVER_SUBDOMAIN)) {
866    
867                                    return true;
868                            }
869                    }
870    
871                    return false;
872            }
873    
874            public static boolean isAllowAnonymousPosting(
875                    PortletPreferences preferences) {
876    
877                    return GetterUtil.getBoolean(
878                            preferences.getValue("allowAnonymousPosting", null),
879                            PropsValues.MESSAGE_BOARDS_ANONYMOUS_POSTING_ENABLED);
880            }
881    
882            public static boolean isValidMessageFormat(String messageFormat) {
883                    String editorImpl = PropsUtil.get(BB_CODE_EDITOR_WYSIWYG_IMPL_KEY);
884    
885                    if (messageFormat.equals("bbcode") &&
886                            !(editorImpl.equals("bbcode") ||
887                              editorImpl.equals("ckeditor_bbcode"))) {
888    
889                            return false;
890                    }
891    
892                    return true;
893            }
894    
895            public static boolean isViewableMessage(
896                            ThemeDisplay themeDisplay, MBMessage message)
897                    throws Exception {
898    
899                    return isViewableMessage(themeDisplay, message, message);
900            }
901    
902            public static boolean isViewableMessage(
903                            ThemeDisplay themeDisplay, MBMessage message,
904                            MBMessage parentMessage)
905                    throws Exception {
906    
907                    PermissionChecker permissionChecker =
908                            themeDisplay.getPermissionChecker();
909    
910                    if (!MBMessagePermission.contains(
911                                    permissionChecker, parentMessage, ActionKeys.VIEW)) {
912    
913                            return false;
914                    }
915    
916                    if ((message.getMessageId() != parentMessage.getMessageId()) &&
917                            !MBMessagePermission.contains(
918                                    permissionChecker, message, ActionKeys.VIEW)) {
919    
920                            return false;
921                    }
922    
923                    if (!message.isApproved() &&
924                            !Validator.equals(message.getUserId(), themeDisplay.getUserId()) &&
925                            !permissionChecker.isGroupAdmin(themeDisplay.getScopeGroupId())) {
926    
927                            return false;
928                    }
929    
930                    return true;
931            }
932    
933            public static void propagatePermissions(
934                            long companyId, long groupId, long parentMessageId,
935                            ServiceContext serviceContext)
936                    throws PortalException, SystemException {
937    
938                    MBMessage parentMessage = MBMessageLocalServiceUtil.getMBMessage(
939                            parentMessageId);
940    
941                    Role defaultGroupRole = RoleLocalServiceUtil.getDefaultGroupRole(
942                            groupId);
943                    Role guestRole = RoleLocalServiceUtil.getRole(
944                            companyId, RoleConstants.GUEST);
945    
946                    long[] roleIds = {defaultGroupRole.getRoleId(), guestRole.getRoleId()};
947    
948                    List<String> actionIds = ResourceActionsUtil.getModelResourceActions(
949                            MBMessage.class.getName());
950    
951                    Map<Long, Set<String>> roleIdsToActionIds =
952                            ResourcePermissionLocalServiceUtil.
953                                    getAvailableResourcePermissionActionIds(
954                                            companyId, MBMessage.class.getName(),
955                                            ResourceConstants.SCOPE_INDIVIDUAL,
956                                            String.valueOf(parentMessage.getMessageId()), roleIds,
957                                            actionIds);
958    
959                    Set<String> defaultGroupActionIds = roleIdsToActionIds.get(
960                            defaultGroupRole.getRoleId());
961    
962                    if (defaultGroupActionIds == null) {
963                            serviceContext.setGroupPermissions(new String[]{});
964                    }
965                    else {
966                            serviceContext.setGroupPermissions(
967                                    defaultGroupActionIds.toArray(
968                                            new String[defaultGroupActionIds.size()]));
969                    }
970    
971                    Set<String> guestActionIds = roleIdsToActionIds.get(
972                            guestRole.getRoleId());
973    
974                    if (guestActionIds == null) {
975                            serviceContext.setGuestPermissions(new String[]{});
976                    }
977                    else {
978                            serviceContext.setGuestPermissions(
979                                    guestActionIds.toArray(new String[guestActionIds.size()]));
980                    }
981            }
982    
983            public static String replaceMessageBodyPaths(
984                    ThemeDisplay themeDisplay, String messageBody) {
985    
986                    return StringUtil.replace(
987                            messageBody,
988                            new String[] {
989                                    "@theme_images_path@", "href=\"/", "src=\"/"
990                            },
991                            new String[] {
992                                    themeDisplay.getPathThemeImages(),
993                                    "href=\"" + themeDisplay.getURLPortal() + "/",
994                                    "src=\"" + themeDisplay.getURLPortal() + "/"
995                            });
996            }
997    
998            public static void updateCategoryMessageCount(
999                    long companyId, final long categoryId) {
1000    
1001                    Callable<Void> callable = new ShardCallable<Void>(companyId) {
1002    
1003                            @Override
1004                            protected Void doCall() throws Exception {
1005                                    MBCategory category =
1006                                            MBCategoryLocalServiceUtil.fetchMBCategory(categoryId);
1007    
1008                                    if (category == null) {
1009                                            return null;
1010                                    }
1011    
1012                                    int messageCount = _getMessageCount(category);
1013    
1014                                    category.setMessageCount(messageCount);
1015    
1016                                    MBCategoryLocalServiceUtil.updateMBCategory(category);
1017    
1018                                    return null;
1019                            }
1020    
1021                    };
1022    
1023                    TransactionCommitCallbackRegistryUtil.registerCallback(callable);
1024            }
1025    
1026            public static void updateCategoryStatistics(
1027                    long companyId, final long categoryId) {
1028    
1029                    Callable<Void> callable = new ShardCallable<Void>(companyId) {
1030    
1031                            @Override
1032                            protected Void doCall() throws Exception {
1033                                    MBCategory category =
1034                                            MBCategoryLocalServiceUtil.fetchMBCategory(categoryId);
1035    
1036                                    if (category == null) {
1037                                            return null;
1038                                    }
1039    
1040                                    int messageCount = _getMessageCount(category);
1041    
1042                                    int threadCount =
1043                                            MBThreadLocalServiceUtil.getCategoryThreadsCount(
1044                                                    category.getGroupId(), category.getCategoryId(),
1045                                                    WorkflowConstants.STATUS_APPROVED);
1046    
1047                                    category.setMessageCount(messageCount);
1048                                    category.setThreadCount(threadCount);
1049    
1050                                    MBCategoryLocalServiceUtil.updateMBCategory(category);
1051    
1052                                    return null;
1053                            }
1054    
1055                    };
1056    
1057                    TransactionCommitCallbackRegistryUtil.registerCallback(callable);
1058            }
1059    
1060            public static void updateCategoryThreadCount(
1061                    long companyId, final long categoryId) {
1062    
1063                    Callable<Void> callable = new ShardCallable<Void>(companyId) {
1064    
1065                            @Override
1066                            protected Void doCall() throws Exception {
1067                                    MBCategory category =
1068                                            MBCategoryLocalServiceUtil.fetchMBCategory(categoryId);
1069    
1070                                    if (category == null) {
1071                                            return null;
1072                                    }
1073    
1074                                    int threadCount =
1075                                            MBThreadLocalServiceUtil.getCategoryThreadsCount(
1076                                                    category.getGroupId(), category.getCategoryId(),
1077                                                    WorkflowConstants.STATUS_APPROVED);
1078    
1079                                    category.setThreadCount(threadCount);
1080    
1081                                    MBCategoryLocalServiceUtil.updateMBCategory(category);
1082    
1083                                    return null;
1084                            }
1085    
1086                    };
1087    
1088                    TransactionCommitCallbackRegistryUtil.registerCallback(callable);
1089            }
1090    
1091            public static void updateThreadMessageCount(
1092                    long companyId, final long threadId) {
1093    
1094                    Callable<Void> callable = new ShardCallable<Void>(companyId) {
1095    
1096                            @Override
1097                            protected Void doCall() throws Exception {
1098                                    MBThread thread = MBThreadLocalServiceUtil.fetchThread(
1099                                            threadId);
1100    
1101                                    if (thread == null) {
1102                                            return null;
1103                                    }
1104    
1105                                    int messageCount =
1106                                            MBMessageLocalServiceUtil.getThreadMessagesCount(
1107                                                    threadId, WorkflowConstants.STATUS_APPROVED);
1108    
1109                                    thread.setMessageCount(messageCount);
1110    
1111                                    MBThreadLocalServiceUtil.updateMBThread(thread);
1112    
1113                                    return null;
1114                            }
1115    
1116                    };
1117    
1118                    TransactionCommitCallbackRegistryUtil.registerCallback(callable);
1119            }
1120    
1121            private static String[] _findThreadPriority(
1122                    double value, ThemeDisplay themeDisplay, String[] priorities) {
1123    
1124                    for (int i = 0; i < priorities.length; i++) {
1125                            String[] priority = StringUtil.split(priorities[i]);
1126    
1127                            try {
1128                                    String priorityName = priority[0];
1129                                    String priorityImage = priority[1];
1130                                    double priorityValue = GetterUtil.getDouble(priority[2]);
1131    
1132                                    if (value == priorityValue) {
1133                                            if (!priorityImage.startsWith(Http.HTTP)) {
1134                                                    priorityImage =
1135                                                            themeDisplay.getPathThemeImages() + priorityImage;
1136                                            }
1137    
1138                                            return new String[] {priorityName, priorityImage};
1139                                    }
1140                            }
1141                            catch (Exception e) {
1142                                    _log.error("Unable to determine thread priority", e);
1143                            }
1144                    }
1145    
1146                    return null;
1147            }
1148    
1149            private static int _getMessageCount(MBCategory category)
1150                    throws SystemException {
1151    
1152                    return MBMessageLocalServiceUtil.getCategoryMessagesCount(
1153                            category.getGroupId(), category.getCategoryId(),
1154                            WorkflowConstants.STATUS_APPROVED);
1155            }
1156    
1157            private static String[] _getMessageIdStringParts(String messageIdString) {
1158                    int pos = messageIdString.indexOf(CharPool.AT);
1159    
1160                    return StringUtil.split(
1161                            messageIdString.substring(
1162                                    MBUtil.MESSAGE_POP_PORTLET_PREFIX.length() +
1163                                            getMessageIdStringOffset(),
1164                                    pos),
1165                            CharPool.PERIOD);
1166            }
1167    
1168            private static String _getParentMessageIdFromSubject(Message message)
1169                    throws Exception {
1170    
1171                    if (message.getSubject() == null) {
1172                            return null;
1173                    }
1174    
1175                    String parentMessageId = null;
1176    
1177                    String subject = StringUtil.reverse(message.getSubject());
1178    
1179                    int pos = subject.indexOf(CharPool.LESS_THAN);
1180    
1181                    if (pos != -1) {
1182                            parentMessageId = StringUtil.reverse(subject.substring(0, pos + 1));
1183                    }
1184    
1185                    return parentMessageId;
1186            }
1187    
1188            private static boolean _isEntityRank(
1189                            long companyId, MBStatsUser statsUser, String entityType,
1190                            String entityValue)
1191                    throws Exception {
1192    
1193                    long groupId = statsUser.getGroupId();
1194                    long userId = statsUser.getUserId();
1195    
1196                    if (entityType.equals("organization-role") ||
1197                            entityType.equals("site-role")) {
1198    
1199                            Role role = RoleLocalServiceUtil.getRole(companyId, entityValue);
1200    
1201                            if (UserGroupRoleLocalServiceUtil.hasUserGroupRole(
1202                                            userId, groupId, role.getRoleId(), true)) {
1203    
1204                                    return true;
1205                            }
1206                    }
1207                    else if (entityType.equals("organization")) {
1208                            Organization organization =
1209                                    OrganizationLocalServiceUtil.getOrganization(
1210                                            companyId, entityValue);
1211    
1212                            if (OrganizationLocalServiceUtil.hasUserOrganization(
1213                                            userId, organization.getOrganizationId(), false, false)) {
1214    
1215                                    return true;
1216                            }
1217                    }
1218                    else if (entityType.equals("regular-role")) {
1219                            if (RoleLocalServiceUtil.hasUserRole(
1220                                            userId, companyId, entityValue, true)) {
1221    
1222                                    return true;
1223                            }
1224                    }
1225                    else if (entityType.equals("user-group")) {
1226                            UserGroup userGroup = UserGroupLocalServiceUtil.getUserGroup(
1227                                    companyId, entityValue);
1228    
1229                            if (UserLocalServiceUtil.hasUserGroupUser(
1230                                            userGroup.getUserGroupId(), userId)) {
1231    
1232                                    return true;
1233                            }
1234                    }
1235    
1236                    return false;
1237            }
1238    
1239            private static Log _log = LogFactoryUtil.getLog(MBUtil.class);
1240    
1241    }