protected long getParentMessageId(String recipient, Message message) throws Exception {

    if (!StringUtil.startsWith(recipient, MBUtil.MESSAGE_POP_PORTLET_PREFIX)) {

      return MBUtil.getParentMessageId(message);
    }

    int pos = recipient.indexOf(CharPool.AT);

    if (pos < 0) {
      return MBUtil.getParentMessageId(message);
    }

    String target = recipient.substring(MBUtil.MESSAGE_POP_PORTLET_PREFIX.length(), pos);

    String[] parts = StringUtil.split(target, StringPool.PERIOD);

    long parentMessageId = 0;

    if (parts.length == 2) {
      parentMessageId = GetterUtil.getLong(parts[1]);
    }

    if (parentMessageId > 0) {
      return parentMessageId;
    }

    return MBUtil.getParentMessageId(message);
  }
  @Override
  public MBThread moveThread(long groupId, long categoryId, long threadId)
      throws PortalException, SystemException {

    MBThread thread = mbThreadPersistence.findByPrimaryKey(threadId);

    long oldCategoryId = thread.getCategoryId();

    MBCategory oldCategory = null;

    if (oldCategoryId != MBCategoryConstants.DEFAULT_PARENT_CATEGORY_ID) {
      oldCategory = mbCategoryPersistence.fetchByPrimaryKey(oldCategoryId);
    }

    MBCategory category = null;

    if (categoryId != MBCategoryConstants.DEFAULT_PARENT_CATEGORY_ID) {
      category = mbCategoryPersistence.fetchByPrimaryKey(categoryId);
    }

    // Thread

    thread.setModifiedDate(new Date());
    thread.setCategoryId(categoryId);

    mbThreadPersistence.update(thread);

    // Messages

    List<MBMessage> messages =
        mbMessagePersistence.findByG_C_T(groupId, oldCategoryId, thread.getThreadId());

    for (MBMessage message : messages) {
      message.setCategoryId(categoryId);

      mbMessagePersistence.update(message);

      // Indexer

      if (!message.isDiscussion()) {
        Indexer indexer = IndexerRegistryUtil.nullSafeGetIndexer(MBMessage.class);

        indexer.reindex(message);
      }
    }

    // Category

    if ((oldCategory != null) && (categoryId != oldCategoryId)) {
      MBUtil.updateCategoryStatistics(oldCategory.getCompanyId(), oldCategory.getCategoryId());
    }

    if ((category != null) && (categoryId != oldCategoryId)) {
      MBUtil.updateCategoryStatistics(category.getCompanyId(), category.getCategoryId());
    }

    return thread;
  }
  @Override
  public String getRestoreMessage(PortletRequest portletRequest, long classPK)
      throws PortalException, SystemException {

    MBThread thread = MBThreadLocalServiceUtil.getThread(classPK);

    return MBUtil.getAbsolutePath(portletRequest, thread.getCategoryId());
  }
  protected String getMessageId(String recipient, Message message) throws Exception {

    if (PropsValues.POP_SERVER_SUBDOMAIN.length() > 0) {
      return recipient;
    } else {
      return MBUtil.getParentMessageIdString(message);
    }
  }
  @Override
  public MBThread updateStatus(long userId, long threadId, int status) throws PortalException {

    MBThread thread = mbThreadPersistence.findByPrimaryKey(threadId);

    // Thread

    User user = userPersistence.findByPrimaryKey(userId);

    thread.setStatus(status);
    thread.setStatusByUserId(user.getUserId());
    thread.setStatusByUserName(user.getFullName());
    thread.setStatusDate(new Date());

    mbThreadPersistence.update(thread);

    // Messages

    if (thread.getCategoryId() != MBCategoryConstants.DEFAULT_PARENT_CATEGORY_ID) {

      // Category

      MBCategory category = mbCategoryPersistence.fetchByPrimaryKey(thread.getCategoryId());

      if (category != null) {
        MBUtil.updateCategoryStatistics(category.getCategoryId());
      }
    }

    // Indexer

    Indexer<MBThread> indexer = IndexerRegistryUtil.nullSafeGetIndexer(MBThread.class);

    indexer.reindex(thread);

    return thread;
  }
  @Override
  public MBThread updateStatus(long userId, long threadId, int status, int categoryStatus)
      throws PortalException, SystemException {

    MBThread thread = mbThreadPersistence.findByPrimaryKey(threadId);

    if (categoryStatus != WorkflowConstants.STATUS_IN_TRASH) {

      // Thread

      User user = userPersistence.findByPrimaryKey(userId);

      Date now = new Date();

      int oldStatus = thread.getStatus();

      thread.setModifiedDate(now);
      thread.setStatus(status);
      thread.setStatusByUserId(user.getUserId());
      thread.setStatusByUserName(user.getFullName());
      thread.setStatusDate(now);

      mbThreadPersistence.update(thread);

      // Messages

      updateDependentStatus(thread.getGroupId(), threadId, status);

      if (thread.getCategoryId() != MBCategoryConstants.DEFAULT_PARENT_CATEGORY_ID) {

        // Category

        MBCategory category = mbCategoryPersistence.fetchByPrimaryKey(thread.getCategoryId());

        if (category != null) {
          MBUtil.updateCategoryStatistics(category.getCompanyId(), category.getCategoryId());
        }
      }

      MBMessage message = mbMessageLocalService.getMBMessage(thread.getRootMessageId());

      JSONObject extraDataJSONObject = JSONFactoryUtil.createJSONObject();

      extraDataJSONObject.put("rootMessageId", thread.getRootMessageId());
      extraDataJSONObject.put("title", message.getSubject());

      if (status == WorkflowConstants.STATUS_IN_TRASH) {

        // Social

        socialActivityLocalService.addActivity(
            userId,
            thread.getGroupId(),
            MBThread.class.getName(),
            thread.getThreadId(),
            SocialActivityConstants.TYPE_MOVE_TO_TRASH,
            extraDataJSONObject.toString(),
            0);

        // Trash

        trashEntryLocalService.addTrashEntry(
            userId,
            thread.getGroupId(),
            MBThread.class.getName(),
            thread.getThreadId(),
            oldStatus,
            null,
            null);
      } else {

        // Social

        socialActivityLocalService.addActivity(
            userId,
            thread.getGroupId(),
            MBThread.class.getName(),
            thread.getThreadId(),
            SocialActivityConstants.TYPE_RESTORE_FROM_TRASH,
            extraDataJSONObject.toString(),
            0);

        // Trash

        trashEntryLocalService.deleteEntry(MBThread.class.getName(), thread.getThreadId());
      }
    } else {
      updateDependentStatus(thread.getGroupId(), threadId, status);
    }

    return thread;
  }
  @Override
  public MBThread splitThread(long messageId, String subject, ServiceContext serviceContext)
      throws PortalException, SystemException {

    MBMessage message = mbMessagePersistence.findByPrimaryKey(messageId);

    if (message.isRoot()) {
      throw new SplitThreadException();
    }

    MBCategory category = message.getCategory();
    MBThread oldThread = message.getThread();
    MBMessage rootMessage = mbMessagePersistence.findByPrimaryKey(oldThread.getRootMessageId());

    // Message flags

    mbMessageLocalService.updateAnswer(message, false, true);

    // Create new thread

    MBThread thread = addThread(message.getCategoryId(), message, serviceContext);

    oldThread.setModifiedDate(serviceContext.getModifiedDate(new Date()));

    mbThreadPersistence.update(oldThread);

    // Update messages

    if (Validator.isNotNull(subject)) {
      MBMessageDisplay messageDisplay =
          mbMessageService.getMessageDisplay(
              messageId, WorkflowConstants.STATUS_ANY, MBThreadConstants.THREAD_VIEW_TREE, false);

      MBTreeWalker treeWalker = messageDisplay.getTreeWalker();

      List<MBMessage> messages = treeWalker.getMessages();

      int[] range = treeWalker.getChildrenRange(message);

      for (int i = range[0]; i < range[1]; i++) {
        MBMessage curMessage = messages.get(i);

        String oldSubject = message.getSubject();
        String curSubject = curMessage.getSubject();

        if (oldSubject.startsWith("RE: ")) {
          curSubject = StringUtil.replace(curSubject, rootMessage.getSubject(), subject);
        } else {
          curSubject = StringUtil.replace(curSubject, oldSubject, subject);
        }

        curMessage.setSubject(curSubject);

        mbMessagePersistence.update(curMessage);
      }

      message.setSubject(subject);
    }

    message.setThreadId(thread.getThreadId());
    message.setRootMessageId(thread.getRootMessageId());
    message.setParentMessageId(0);

    mbMessagePersistence.update(message);

    // Indexer

    if (!message.isDiscussion()) {
      Indexer indexer = IndexerRegistryUtil.nullSafeGetIndexer(MBMessage.class);

      indexer.reindex(message);
    }

    // Update children

    moveChildrenMessages(message, category, oldThread.getThreadId());

    // Update new thread

    MBUtil.updateThreadMessageCount(thread.getCompanyId(), thread.getThreadId());

    // Update old thread

    MBUtil.updateThreadMessageCount(oldThread.getCompanyId(), oldThread.getThreadId());

    // Category

    if ((message.getCategoryId() != MBCategoryConstants.DEFAULT_PARENT_CATEGORY_ID)
        && (message.getCategoryId() != MBCategoryConstants.DISCUSSION_CATEGORY_ID)) {

      MBUtil.updateCategoryThreadCount(category.getCompanyId(), category.getCategoryId());
    }

    return thread;
  }
  @Override
  @SystemEvent(
      action = SystemEventConstants.ACTION_SKIP,
      send = false,
      type = SystemEventConstants.TYPE_DELETE)
  public void deleteThread(MBThread thread) throws PortalException, SystemException {

    MBMessage rootMessage = mbMessagePersistence.findByPrimaryKey(thread.getRootMessageId());

    // Indexer

    Indexer indexer = IndexerRegistryUtil.nullSafeGetIndexer(MBMessage.class);

    indexer.delete(thread);

    // Attachments

    long folderId = thread.getAttachmentsFolderId();

    if (folderId != DLFolderConstants.DEFAULT_PARENT_FOLDER_ID) {
      PortletFileRepositoryUtil.deleteFolder(folderId);
    }

    // Subscriptions

    subscriptionLocalService.deleteSubscriptions(
        thread.getCompanyId(), MBThread.class.getName(), thread.getThreadId());

    // Thread flags

    mbThreadFlagPersistence.removeByThreadId(thread.getThreadId());

    // Messages

    List<MBMessage> messages = mbMessagePersistence.findByThreadId(thread.getThreadId());

    for (MBMessage message : messages) {

      // Ratings

      ratingsStatsLocalService.deleteStats(message.getWorkflowClassName(), message.getMessageId());

      // Asset

      assetEntryLocalService.deleteEntry(message.getWorkflowClassName(), message.getMessageId());

      // Resources

      if (!message.isDiscussion()) {
        resourceLocalService.deleteResource(
            message.getCompanyId(),
            message.getWorkflowClassName(),
            ResourceConstants.SCOPE_INDIVIDUAL,
            message.getMessageId());
      }

      // Message

      mbMessagePersistence.remove(message);

      // Statistics

      if (!message.isDiscussion()) {
        mbStatsUserLocalService.updateStatsUser(message.getGroupId(), message.getUserId());
      }

      // Workflow

      workflowInstanceLinkLocalService.deleteWorkflowInstanceLink(
          message.getCompanyId(), message.getGroupId(),
          message.getWorkflowClassName(), message.getMessageId());
    }

    // Category

    if ((rootMessage.getCategoryId() != MBCategoryConstants.DEFAULT_PARENT_CATEGORY_ID)
        && (rootMessage.getCategoryId() != MBCategoryConstants.DISCUSSION_CATEGORY_ID)) {

      try {
        MBCategory category = mbCategoryPersistence.findByPrimaryKey(thread.getCategoryId());

        MBUtil.updateCategoryStatistics(category.getCompanyId(), category.getCategoryId());
      } catch (NoSuchCategoryException nsce) {
        if (!thread.isInTrash()) {
          throw nsce;
        }
      }
    }

    // Thread Asset

    AssetEntry assetEntry =
        assetEntryLocalService.fetchEntry(MBThread.class.getName(), thread.getThreadId());

    if (assetEntry != null) {
      assetEntry.setTitle(rootMessage.getSubject());

      assetEntryLocalService.updateAssetEntry(assetEntry);
    }

    assetEntryLocalService.deleteEntry(MBThread.class.getName(), thread.getThreadId());

    // Trash

    trashEntryLocalService.deleteEntry(MBThread.class.getName(), thread.getThreadId());

    // Thread

    mbThreadPersistence.remove(thread);
  }
  protected String exportToRSS(
      String name,
      String description,
      String type,
      double version,
      String displayStyle,
      String feedURL,
      String entryURL,
      List<MBMessage> messages,
      ThemeDisplay themeDisplay)
      throws SystemException {

    SyndFeed syndFeed = new SyndFeedImpl();

    syndFeed.setDescription(description);

    List<SyndEntry> syndEntries = new ArrayList<SyndEntry>();

    syndFeed.setEntries(syndEntries);

    for (MBMessage message : messages) {
      SyndEntry syndEntry = new SyndEntryImpl();

      if (!message.isAnonymous()) {
        String author = PortalUtil.getUserName(message);

        syndEntry.setAuthor(author);
      }

      SyndContent syndContent = new SyndContentImpl();

      syndContent.setType(RSSUtil.ENTRY_TYPE_DEFAULT);

      String value = null;

      if (displayStyle.equals(RSSUtil.DISPLAY_STYLE_ABSTRACT)) {
        value =
            StringUtil.shorten(
                HtmlUtil.extractText(message.getBody()),
                PropsValues.MESSAGE_BOARDS_RSS_ABSTRACT_LENGTH,
                StringPool.BLANK);
      } else if (displayStyle.equals(RSSUtil.DISPLAY_STYLE_TITLE)) {
        value = StringPool.BLANK;
      } else if (message.isFormatBBCode()) {
        value = BBCodeTranslatorUtil.getHTML(message.getBody());

        value = MBUtil.replaceMessageBodyPaths(themeDisplay, value);
      } else {
        value = message.getBody();
      }

      syndContent.setValue(value);

      syndEntry.setDescription(syndContent);

      syndEntry.setLink(entryURL + "&messageId=" + message.getMessageId());
      syndEntry.setPublishedDate(message.getCreateDate());
      syndEntry.setTitle(message.getSubject());
      syndEntry.setUpdatedDate(message.getModifiedDate());
      syndEntry.setUri(syndEntry.getLink());

      syndEntries.add(syndEntry);
    }

    syndFeed.setFeedType(RSSUtil.getFeedType(type, version));

    List<SyndLink> syndLinks = new ArrayList<SyndLink>();

    syndFeed.setLinks(syndLinks);

    SyndLink selfSyndLink = new SyndLinkImpl();

    syndLinks.add(selfSyndLink);

    selfSyndLink.setHref(feedURL);
    selfSyndLink.setRel("self");

    syndFeed.setPublishedDate(new Date());
    syndFeed.setTitle(name);
    syndFeed.setUri(feedURL);

    try {
      return RSSUtil.export(syndFeed);
    } catch (FeedException fe) {
      throw new SystemException(fe);
    }
  }
  private long _getCategoryId(PortletRequest portletRequest) {
    MBCategory category = (MBCategory) portletRequest.getAttribute(WebKeys.MESSAGE_BOARDS_CATEGORY);

    return MBUtil.getCategoryId(PortalUtil.getHttpServletRequest(portletRequest), category);
  }
  public void deliver(String from, String recipient, Message message)
      throws MessageListenerException {

    try {
      StopWatch stopWatch = null;

      if (_log.isDebugEnabled()) {
        stopWatch = new StopWatch();

        stopWatch.start();

        _log.debug("Deliver message from " + from + " to " + recipient);
      }

      String messageId = getMessageId(recipient, message);

      Company company = getCompany(messageId);

      if (_log.isDebugEnabled()) {
        _log.debug("Message id " + messageId);
      }

      long groupId = 0;
      long categoryId = getCategoryId(messageId);

      try {
        MBCategory category = MBCategoryLocalServiceUtil.getCategory(categoryId);

        groupId = category.getGroupId();
      } catch (NoSuchCategoryException nsce) {
        groupId = categoryId;
        categoryId = MBCategoryConstants.DEFAULT_PARENT_CATEGORY_ID;
      }

      if (_log.isDebugEnabled()) {
        _log.debug("Group id " + groupId);
        _log.debug("Category id " + categoryId);
      }

      User user = UserLocalServiceUtil.getUserByEmailAddress(company.getCompanyId(), from);

      long parentMessageId = getParentMessageId(recipient, message);

      if (_log.isDebugEnabled()) {
        _log.debug("Parent message id " + parentMessageId);
      }

      MBMessage parentMessage = null;

      try {
        if (parentMessageId > 0) {
          parentMessage = MBMessageLocalServiceUtil.getMessage(parentMessageId);
        }
      } catch (NoSuchMessageException nsme) {

        // If the parent message does not exist we ignore it and post
        // the message as a new thread.

      }

      if (_log.isDebugEnabled()) {
        _log.debug("Parent message " + parentMessage);
      }

      String subject = MBUtil.getSubjectWithoutMessageId(message);

      MBMailMessage collector = new MBMailMessage();

      MBUtil.collectPartContent(message, collector);

      PermissionCheckerUtil.setThreadValues(user);

      ServiceContext serviceContext = new ServiceContext();

      serviceContext.setAddGroupPermissions(true);
      serviceContext.setAddGuestPermissions(true);
      serviceContext.setLayoutFullURL(
          PortalUtil.getLayoutFullURL(groupId, PortletKeys.MESSAGE_BOARDS));
      serviceContext.setScopeGroupId(groupId);

      if (parentMessage == null) {
        MBMessageServiceUtil.addMessage(
            groupId,
            categoryId,
            subject,
            collector.getBody(),
            MBMessageConstants.DEFAULT_FORMAT,
            collector.getFiles(),
            false,
            0.0,
            true,
            serviceContext);
      } else {
        MBMessageServiceUtil.addMessage(
            groupId,
            categoryId,
            parentMessage.getThreadId(),
            parentMessage.getMessageId(),
            subject,
            collector.getBody(),
            MBMessageConstants.DEFAULT_FORMAT,
            collector.getFiles(),
            false,
            0.0,
            true,
            serviceContext);
      }

      if (_log.isDebugEnabled()) {
        _log.debug("Delivering message takes " + stopWatch.getTime() + " ms");
      }
    } catch (PrincipalException pe) {
      if (_log.isDebugEnabled()) {
        _log.debug("Prevented unauthorized post from " + from);
      }

      throw new MessageListenerException(pe);
    } catch (Exception e) {
      _log.error(e, e);

      throw new MessageListenerException(e);
    } finally {
      PermissionCheckerUtil.setThreadValues(null);
    }
  }