Example #1
0
  /**
   * Moves all the conversation's {@link Message}s to a different {@link Folder}. Persists the
   * change to the database and the in-memory cache. Updates all relevant unread counts, folder
   * sizes, etc.
   *
   * <p>Messages moved to the Trash folder are automatically marked read. Conversations moved to the
   * Junk folder will not receive newly-delivered messages.
   *
   * <p>Messages in the conversation are omitted from this operation if one or more of the following
   * applies:
   *
   * <ul>
   *   <li>The caller lacks {@link ACL#RIGHT_WRITE} permission on the <code>Message</code>.
   *   <li>The caller has specified a {@link MailItem.TargetConstraint} that explicitly excludes the
   *       <code>Message</code>.
   *   <li>The caller has specified the maximum change number they know about, and the
   *       (modification/content) change number on the <code>Message</code> is greater.
   * </ul>
   *
   * As a result of all these constraints, no messages may actually be moved.
   *
   * @perms {@link ACL#RIGHT_INSERT} on the target folder, {@link ACL#RIGHT_DELETE} on the messages'
   *     source folders
   */
  @Override
  boolean move(Folder target) throws ServiceException {
    if (!target.canContain(TYPE_MESSAGE)) throw MailServiceException.CANNOT_CONTAIN();
    markItemModified(Change.UNMODIFIED);

    List<Message> msgs = getMessages();
    TargetConstraint tcon = mMailbox.getOperationTargetConstraint();
    boolean toTrash = target.inTrash();
    int oldUnread = 0;
    for (Message msg : msgs) if (msg.isUnread()) oldUnread++;
    // if mData.unread is wrong, what to do?  right now, always use the calculated value
    mData.unreadCount = oldUnread;

    boolean excludeAccess = false;

    List<Integer> markedRead = new ArrayList<Integer>();
    List<Message> moved = new ArrayList<Message>();
    List<Message> indexUpdated = new ArrayList<Message>();

    for (Message msg : msgs) {
      Folder source = msg.getFolder();

      // skip messages that don't need to be moved, or that the client can't modify, doesn't know
      // about, or has explicitly excluded
      if (source.getId() == target.getId()) {
        continue;
      } else if (!source.canAccess(ACL.RIGHT_DELETE)) {
        excludeAccess = true;
        continue;
      } else if (target.getId() != Mailbox.ID_FOLDER_TRASH
          && target.getId() != Mailbox.ID_FOLDER_SPAM
          && !target.canAccess(ACL.RIGHT_INSERT)) {
        excludeAccess = true;
        continue;
      } else if (!msg.checkChangeID() || !TargetConstraint.checkItem(tcon, msg)) {
        continue;
      }

      boolean isDeleted = msg.isTagged(Flag.ID_FLAG_DELETED);
      if (msg.isUnread()) {
        if (!toTrash || msg.inTrash()) {
          source.updateUnread(-1, isDeleted ? -1 : 0);
          target.updateUnread(1, isDeleted ? 1 : 0);
        } else {
          // unread messages moved from Mailbox to Trash need to be marked read:
          //   update cached unread counts (message, conversation, folder, tags)
          msg.updateUnread(-1, isDeleted ? -1 : 0);
          //   note that we need to update this message in the DB
          markedRead.add(msg.getId());
        }
      }

      // moved an item out of the spam folder, need to index it
      if (msg.inSpam() && !target.inSpam()) {
        if (msg.isIndexed() && msg.getIndexId() != -1) {
          msg.indexIdChanged(msg.getId());
          indexUpdated.add(msg);
        }
      }

      // if a draft is being moved to Trash then remove any "send-later" info from it
      if (toTrash && msg.isDraft()) msg.setDraftAutoSendTime(0);

      // handle folder message counts
      source.updateSize(-1, isDeleted ? -1 : 0, -msg.getTotalSize());
      target.updateSize(1, isDeleted ? 1 : 0, msg.getTotalSize());

      moved.add(msg);
      msg.folderChanged(target, 0);
    }

    // mark unread messages moved from Mailbox to Trash/Spam as read in the DB
    if (!markedRead.isEmpty()) DbMailItem.alterUnread(target.getMailbox(), markedRead, false);

    if (moved.isEmpty()) {
      if (excludeAccess)
        throw ServiceException.PERM_DENIED("you do not have sufficient permissions");
    } else {
      // moving a conversation to spam closes it
      if (target.inSpam()) detach();
      if (ZimbraLog.mailop.isInfoEnabled()) {
        StringBuilder ids = new StringBuilder();
        for (int i = 0; i < moved.size(); i++) {
          if (i > 0) {
            ids.append(',');
          }
          ids.append(moved.get(i).getId());
        }
        ZimbraLog.mailop.info(
            "Moving %s to %s.  Affected message ids: %s.",
            getMailopContext(this), getMailopContext(target), ids);
      }
      DbMailItem.setFolder(moved, target);

      if (!indexUpdated.isEmpty()) {
        DbMailItem.setIndexIds(mMailbox, indexUpdated);
        for (Message msg : indexUpdated) {
          mMailbox.queueForIndexing(msg, false, null);
        }
      }
    }

    return !moved.isEmpty();
  }