@Override
  protected void doExportStagedModel(PortletDataContext portletDataContext, MBThreadFlag threadFlag)
      throws Exception {

    MBThread thread = MBThreadLocalServiceUtil.getThread(threadFlag.getThreadId());

    MBMessage rootMessage = MBMessageLocalServiceUtil.getMessage(thread.getRootMessageId());

    if ((rootMessage.getStatus() != WorkflowConstants.STATUS_APPROVED)
        || (rootMessage.getCategoryId() == MBCategoryConstants.DISCUSSION_CATEGORY_ID)) {

      return;
    }

    StagedModelDataHandlerUtil.exportStagedModel(portletDataContext, rootMessage);

    Element threadFlagElement = portletDataContext.getExportDataElement(threadFlag);

    threadFlagElement.addAttribute("root-message-id", String.valueOf(rootMessage.getMessageId()));

    portletDataContext.addClassedModel(
        threadFlagElement,
        ExportImportPathUtil.getModelPath(threadFlag),
        threadFlag,
        MBPortletDataHandler.NAMESPACE);
  }
  @Override
  protected void doExportStagedModel(PortletDataContext portletDataContext, MBMessage message)
      throws Exception {

    if ((message.getStatus() != WorkflowConstants.STATUS_APPROVED)
        || (message.getCategoryId() == MBCategoryConstants.DISCUSSION_CATEGORY_ID)) {

      return;
    }

    StagedModelDataHandlerUtil.exportStagedModel(portletDataContext, message.getCategory());

    Element messageElement = portletDataContext.getExportDataStagedModelElement(message);

    message.setPriority(message.getPriority());

    MBThread thread = message.getThread();

    messageElement.addAttribute("question", String.valueOf(thread.isQuestion()));

    boolean hasAttachmentsFileEntries = message.getAttachmentsFileEntriesCount() > 0;

    messageElement.addAttribute(
        "hasAttachmentsFileEntries", String.valueOf(hasAttachmentsFileEntries));

    if (portletDataContext.getBooleanParameter(MBPortletDataHandler.NAMESPACE, "attachments")
        && hasAttachmentsFileEntries) {

      for (FileEntry fileEntry : message.getAttachmentsFileEntries()) {
        String name = fileEntry.getTitle();
        String binPath = ExportImportPathUtil.getModelPath(message, name);

        Element attachmentElement = messageElement.addElement("attachment");

        attachmentElement.addAttribute("name", name);
        attachmentElement.addAttribute("bin-path", binPath);

        portletDataContext.addZipEntry(binPath, fileEntry.getContentStream());
      }

      long folderId = message.getAttachmentsFolderId();

      if (folderId != DLFolderConstants.DEFAULT_PARENT_FOLDER_ID) {
        message.setAttachmentsFolderId(folderId);
      }
    }

    portletDataContext.addClassedModel(
        messageElement,
        ExportImportPathUtil.getModelPath(message),
        message,
        MBPortletDataHandler.NAMESPACE);
  }
  protected long importMBMessage(
      MBMessage mbMessage, long threadId, long calendarBookingId, Map<Long, Long> mbMessageIds)
      throws PortalException, SystemException {

    Long messageId = mbMessageIds.get(mbMessage.getMessageId());

    if (messageId != null) {
      return messageId;
    }

    messageId = counterLocalService.increment();

    addMBMessage(
        PortalUUIDUtil.generate(),
        messageId,
        mbMessage.getGroupId(),
        mbMessage.getCompanyId(),
        mbMessage.getUserId(),
        mbMessage.getUserName(),
        mbMessage.getCreateDate(),
        mbMessage.getModifiedDate(),
        classNameLocalService.getClassNameId(CalendarBooking.class.getName()),
        calendarBookingId,
        mbMessage.getCategoryId(),
        threadId,
        mbMessage.getRootMessageId(),
        mbMessage.getParentMessageId(),
        mbMessage.getSubject(),
        mbMessage.getBody(),
        mbMessage.getFormat(),
        mbMessage.isAnonymous(),
        mbMessage.getPriority(),
        mbMessage.getAllowPingbacks(),
        mbMessage.isAnswer(),
        mbMessage.getStatus(),
        mbMessage.getStatusByUserId(),
        mbMessage.getStatusByUserName(),
        mbMessage.getStatusDate(),
        mbMessageIds);

    long mbDiscussionClassNameId =
        classNameLocalService.getClassNameId(MBDiscussion.class.getName());

    importRatings(
        mbDiscussionClassNameId, mbMessage.getMessageId(), mbDiscussionClassNameId, messageId);

    mbMessageIds.put(mbMessage.getMessageId(), messageId);

    return messageId;
  }
  protected void updateDependentStatus(long groupId, long threadId, int status)
      throws PortalException, SystemException {

    Set<Long> userIds = new HashSet<Long>();

    List<MBMessage> messages =
        mbMessageLocalService.getThreadMessages(threadId, WorkflowConstants.STATUS_ANY);

    for (MBMessage message : messages) {
      if (message.isDiscussion()) {
        continue;
      }

      userIds.add(message.getUserId());

      if (status == WorkflowConstants.STATUS_IN_TRASH) {

        // Asset

        if (message.getStatus() == WorkflowConstants.STATUS_APPROVED) {
          assetEntryLocalService.updateVisible(
              MBMessage.class.getName(), message.getMessageId(), false);
        }

        // Social

        socialActivityCounterLocalService.disableActivityCounters(
            MBMessage.class.getName(), message.getMessageId());

        // Index

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

        indexer.reindex(message);

        // Workflow

        if (message.getStatus() == WorkflowConstants.STATUS_PENDING) {
          message.setStatus(WorkflowConstants.STATUS_DRAFT);

          mbMessagePersistence.update(message);

          workflowInstanceLinkLocalService.deleteWorkflowInstanceLink(
              message.getCompanyId(), message.getGroupId(),
              MBMessage.class.getName(), message.getMessageId());
        }
      } else {

        // Asset

        if (message.getStatus() == WorkflowConstants.STATUS_APPROVED) {
          assetEntryLocalService.updateVisible(
              MBMessage.class.getName(), message.getMessageId(), true);
        }

        // Social

        socialActivityCounterLocalService.enableActivityCounters(
            MBMessage.class.getName(), message.getMessageId());

        // Index

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

        indexer.reindex(message);
      }
    }

    // Statistics

    for (long userId : userIds) {
      mbStatsUserLocalService.updateStatsUser(groupId, userId);
    }
  }
  @Override
  public MBThread addThread(long categoryId, MBMessage message, ServiceContext serviceContext)
      throws PortalException, SystemException {

    // Thread

    Date now = new Date();

    long threadId = message.getThreadId();

    if (!message.isRoot() || (threadId <= 0)) {
      threadId = counterLocalService.increment();
    }

    MBThread thread = mbThreadPersistence.create(threadId);

    thread.setUuid(serviceContext.getUuid());
    thread.setGroupId(message.getGroupId());
    thread.setCompanyId(message.getCompanyId());
    thread.setUserId(message.getUserId());
    thread.setUserName(message.getUserName());
    thread.setCreateDate(serviceContext.getCreateDate(now));
    thread.setModifiedDate(serviceContext.getModifiedDate(now));
    thread.setCategoryId(categoryId);
    thread.setRootMessageId(message.getMessageId());
    thread.setRootMessageUserId(message.getUserId());

    if (message.isAnonymous()) {
      thread.setLastPostByUserId(0);
    } else {
      thread.setLastPostByUserId(message.getUserId());
    }

    thread.setLastPostDate(message.getCreateDate());

    if (message.getPriority() != MBThreadConstants.PRIORITY_NOT_GIVEN) {
      thread.setPriority(message.getPriority());
    }

    thread.setStatus(message.getStatus());
    thread.setStatusByUserId(message.getStatusByUserId());
    thread.setStatusByUserName(message.getStatusByUserName());
    thread.setStatusDate(message.getStatusDate());

    mbThreadPersistence.update(thread);

    // Asset

    if (categoryId >= 0) {
      assetEntryLocalService.updateEntry(
          message.getUserId(),
          message.getGroupId(),
          thread.getStatusDate(),
          thread.getLastPostDate(),
          MBThread.class.getName(),
          thread.getThreadId(),
          thread.getUuid(),
          0,
          new long[0],
          new String[0],
          false,
          null,
          null,
          null,
          null,
          String.valueOf(thread.getRootMessageId()),
          null,
          null,
          null,
          null,
          0,
          0,
          null,
          false);
    }

    return thread;
  }
  @Override
  protected void doImportStagedModel(PortletDataContext portletDataContext, MBMessage message)
      throws Exception {

    long userId = portletDataContext.getUserId(message.getUserUuid());

    String userName = message.getUserName();

    Map<Long, Long> categoryIds =
        (Map<Long, Long>) portletDataContext.getNewPrimaryKeysMap(MBCategory.class);

    long parentCategoryId =
        MapUtil.getLong(categoryIds, message.getCategoryId(), message.getCategoryId());

    Map<Long, Long> threadIds =
        (Map<Long, Long>) portletDataContext.getNewPrimaryKeysMap(MBThread.class);

    long threadId = MapUtil.getLong(threadIds, message.getThreadId(), 0);

    Map<Long, Long> messageIds =
        (Map<Long, Long>) portletDataContext.getNewPrimaryKeysMap(MBMessage.class);

    long parentMessageId =
        MapUtil.getLong(messageIds, message.getParentMessageId(), message.getParentMessageId());

    Element element = portletDataContext.getImportDataStagedModelElement(message);

    List<ObjectValuePair<String, InputStream>> inputStreamOVPs =
        getAttachments(portletDataContext, element, message);

    try {
      ServiceContext serviceContext =
          portletDataContext.createServiceContext(message, MBPortletDataHandler.NAMESPACE);

      if (message.getStatus() != WorkflowConstants.STATUS_APPROVED) {
        serviceContext.setWorkflowAction(WorkflowConstants.ACTION_SAVE_DRAFT);
      }

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

        String categoryPath =
            ExportImportPathUtil.getModelPath(
                portletDataContext, MBCategory.class.getName(), parentCategoryId);

        MBCategory category = (MBCategory) portletDataContext.getZipEntryAsObject(categoryPath);

        StagedModelDataHandlerUtil.importStagedModel(portletDataContext, category);

        parentCategoryId =
            MapUtil.getLong(categoryIds, message.getCategoryId(), message.getCategoryId());
      }

      MBMessage importedMessage = null;

      if (portletDataContext.isDataStrategyMirror()) {
        MBMessage existingMessage =
            MBMessageUtil.fetchByUUID_G(message.getUuid(), portletDataContext.getScopeGroupId());

        if (existingMessage == null) {
          serviceContext.setUuid(message.getUuid());

          importedMessage =
              MBMessageLocalServiceUtil.addMessage(
                  userId,
                  userName,
                  portletDataContext.getScopeGroupId(),
                  parentCategoryId,
                  threadId,
                  parentMessageId,
                  message.getSubject(),
                  message.getBody(),
                  message.getFormat(),
                  inputStreamOVPs,
                  message.getAnonymous(),
                  message.getPriority(),
                  message.getAllowPingbacks(),
                  serviceContext);
        } else {
          importedMessage =
              MBMessageLocalServiceUtil.updateMessage(
                  userId,
                  existingMessage.getMessageId(),
                  message.getSubject(),
                  message.getBody(),
                  inputStreamOVPs,
                  new ArrayList<String>(),
                  message.getPriority(),
                  message.getAllowPingbacks(),
                  serviceContext);
        }
      } else {
        importedMessage =
            MBMessageLocalServiceUtil.addMessage(
                userId,
                userName,
                portletDataContext.getScopeGroupId(),
                parentCategoryId,
                threadId,
                parentMessageId,
                message.getSubject(),
                message.getBody(),
                message.getFormat(),
                inputStreamOVPs,
                message.getAnonymous(),
                message.getPriority(),
                message.getAllowPingbacks(),
                serviceContext);
      }

      importedMessage.setAnswer(message.getAnswer());

      if (importedMessage.isRoot()) {
        MBThreadLocalServiceUtil.updateQuestion(
            importedMessage.getThreadId(),
            GetterUtil.getBoolean(element.attributeValue("question")));
      }

      threadIds.put(message.getThreadId(), importedMessage.getThreadId());

      portletDataContext.importClassedModel(
          message, importedMessage, MBPortletDataHandler.NAMESPACE);
    } finally {
      for (ObjectValuePair<String, InputStream> inputStreamOVP : inputStreamOVPs) {

        InputStream inputStream = inputStreamOVP.getValue();

        StreamUtil.cleanUp(inputStream);
      }
    }
  }
  @Test
  public void testUpdateExisting() throws Exception {
    long pk = RandomTestUtil.nextLong();

    MBMessage newMBMessage = _persistence.create(pk);

    newMBMessage.setUuid(RandomTestUtil.randomString());

    newMBMessage.setGroupId(RandomTestUtil.nextLong());

    newMBMessage.setCompanyId(RandomTestUtil.nextLong());

    newMBMessage.setUserId(RandomTestUtil.nextLong());

    newMBMessage.setUserName(RandomTestUtil.randomString());

    newMBMessage.setCreateDate(RandomTestUtil.nextDate());

    newMBMessage.setModifiedDate(RandomTestUtil.nextDate());

    newMBMessage.setClassNameId(RandomTestUtil.nextLong());

    newMBMessage.setClassPK(RandomTestUtil.nextLong());

    newMBMessage.setCategoryId(RandomTestUtil.nextLong());

    newMBMessage.setThreadId(RandomTestUtil.nextLong());

    newMBMessage.setRootMessageId(RandomTestUtil.nextLong());

    newMBMessage.setParentMessageId(RandomTestUtil.nextLong());

    newMBMessage.setSubject(RandomTestUtil.randomString());

    newMBMessage.setBody(RandomTestUtil.randomString());

    newMBMessage.setFormat(RandomTestUtil.randomString());

    newMBMessage.setAnonymous(RandomTestUtil.randomBoolean());

    newMBMessage.setPriority(RandomTestUtil.nextDouble());

    newMBMessage.setAllowPingbacks(RandomTestUtil.randomBoolean());

    newMBMessage.setAnswer(RandomTestUtil.randomBoolean());

    newMBMessage.setStatus(RandomTestUtil.nextInt());

    newMBMessage.setStatusByUserId(RandomTestUtil.nextLong());

    newMBMessage.setStatusByUserName(RandomTestUtil.randomString());

    newMBMessage.setStatusDate(RandomTestUtil.nextDate());

    _mbMessages.add(_persistence.update(newMBMessage));

    MBMessage existingMBMessage = _persistence.findByPrimaryKey(newMBMessage.getPrimaryKey());

    Assert.assertEquals(existingMBMessage.getUuid(), newMBMessage.getUuid());
    Assert.assertEquals(existingMBMessage.getMessageId(), newMBMessage.getMessageId());
    Assert.assertEquals(existingMBMessage.getGroupId(), newMBMessage.getGroupId());
    Assert.assertEquals(existingMBMessage.getCompanyId(), newMBMessage.getCompanyId());
    Assert.assertEquals(existingMBMessage.getUserId(), newMBMessage.getUserId());
    Assert.assertEquals(existingMBMessage.getUserName(), newMBMessage.getUserName());
    Assert.assertEquals(
        Time.getShortTimestamp(existingMBMessage.getCreateDate()),
        Time.getShortTimestamp(newMBMessage.getCreateDate()));
    Assert.assertEquals(
        Time.getShortTimestamp(existingMBMessage.getModifiedDate()),
        Time.getShortTimestamp(newMBMessage.getModifiedDate()));
    Assert.assertEquals(existingMBMessage.getClassNameId(), newMBMessage.getClassNameId());
    Assert.assertEquals(existingMBMessage.getClassPK(), newMBMessage.getClassPK());
    Assert.assertEquals(existingMBMessage.getCategoryId(), newMBMessage.getCategoryId());
    Assert.assertEquals(existingMBMessage.getThreadId(), newMBMessage.getThreadId());
    Assert.assertEquals(existingMBMessage.getRootMessageId(), newMBMessage.getRootMessageId());
    Assert.assertEquals(existingMBMessage.getParentMessageId(), newMBMessage.getParentMessageId());
    Assert.assertEquals(existingMBMessage.getSubject(), newMBMessage.getSubject());
    Assert.assertEquals(existingMBMessage.getBody(), newMBMessage.getBody());
    Assert.assertEquals(existingMBMessage.getFormat(), newMBMessage.getFormat());
    Assert.assertEquals(existingMBMessage.getAnonymous(), newMBMessage.getAnonymous());
    AssertUtils.assertEquals(existingMBMessage.getPriority(), newMBMessage.getPriority());
    Assert.assertEquals(existingMBMessage.getAllowPingbacks(), newMBMessage.getAllowPingbacks());
    Assert.assertEquals(existingMBMessage.getAnswer(), newMBMessage.getAnswer());
    Assert.assertEquals(existingMBMessage.getStatus(), newMBMessage.getStatus());
    Assert.assertEquals(existingMBMessage.getStatusByUserId(), newMBMessage.getStatusByUserId());
    Assert.assertEquals(
        existingMBMessage.getStatusByUserName(), newMBMessage.getStatusByUserName());
    Assert.assertEquals(
        Time.getShortTimestamp(existingMBMessage.getStatusDate()),
        Time.getShortTimestamp(newMBMessage.getStatusDate()));
  }
  @Override
  public void moveDependentsToTrash(long groupId, long threadId, long trashEntryId)
      throws PortalException {

    Set<Long> userIds = new HashSet<>();

    MBThread thread = mbThreadLocalService.getThread(threadId);

    List<MBMessage> messages =
        mbMessageLocalService.getThreadMessages(threadId, WorkflowConstants.STATUS_ANY);

    for (MBMessage message : messages) {

      // Message

      if (message.isDiscussion()) {
        continue;
      }

      int oldStatus = message.getStatus();

      message.setStatus(WorkflowConstants.STATUS_IN_TRASH);

      mbMessagePersistence.update(message);

      userIds.add(message.getUserId());

      // Trash

      int status = oldStatus;

      if (oldStatus == WorkflowConstants.STATUS_PENDING) {
        status = WorkflowConstants.STATUS_DRAFT;
      }

      if (oldStatus != WorkflowConstants.STATUS_APPROVED) {
        trashVersionLocalService.addTrashVersion(
            trashEntryId, MBMessage.class.getName(), message.getMessageId(), status, null);
      }

      // Asset

      if (oldStatus == WorkflowConstants.STATUS_APPROVED) {
        assetEntryLocalService.updateVisible(
            MBMessage.class.getName(), message.getMessageId(), false);
      }

      // Attachments

      for (FileEntry fileEntry : message.getAttachmentsFileEntries()) {
        PortletFileRepositoryUtil.movePortletFileEntryToTrash(
            thread.getStatusByUserId(), fileEntry.getFileEntryId());
      }

      // Indexer

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

      indexer.reindex(message);

      // Workflow

      if (oldStatus == WorkflowConstants.STATUS_PENDING) {
        workflowInstanceLinkLocalService.deleteWorkflowInstanceLink(
            message.getCompanyId(), message.getGroupId(),
            MBMessage.class.getName(), message.getMessageId());
      }
    }

    // Statistics

    for (long userId : userIds) {
      mbStatsUserLocalService.updateStatsUser(groupId, userId);
    }
  }