Exemplo n.º 1
0
  /**
   * Tags or untags all messages in the conversation. Persists the change to the database and cache.
   * If the conversation includes at least one unread {@link Message} whose tagged state is
   * changing, updates the {@link Tag}'s unread count appropriately.
   *
   * <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 tagged/untagged.
   *
   * @perms {@link ACL#RIGHT_WRITE} on all the messages
   */
  @Override
  void alterTag(Tag tag, boolean add) throws ServiceException {
    if (tag == null) throw ServiceException.FAILURE("missing tag argument", null);
    if (!add && !isTagged(tag)) return;
    if (tag.getId() == Flag.ID_FLAG_UNREAD)
      throw ServiceException.FAILURE("unread state must be set with alterUnread", null);
    // don't let the user tag things as "has attachments" or "draft"
    if (tag instanceof Flag && (tag.getBitmask() & Flag.FLAG_SYSTEM) != 0)
      throw MailServiceException.CANNOT_TAG(tag, this);

    markItemModified(tag instanceof Flag ? Change.MODIFIED_FLAGS : Change.MODIFIED_TAGS);

    TargetConstraint tcon = mMailbox.getOperationTargetConstraint();
    boolean excludeAccess = false;

    List<Message> msgs = getMessages();
    List<Integer> targets = new ArrayList<Integer>(msgs.size());
    for (Message msg : msgs) {
      // skip messages that don't need to be changed, or that the client can't modify, doesn't know
      // about, or has explicitly excluded
      if (msg.isTagged(tag) == add) {
        continue;
      } else if (!msg.canAccess(ACL.RIGHT_WRITE)) {
        excludeAccess = true;
        continue;
      } else if (!msg.checkChangeID() || !TargetConstraint.checkItem(tcon, msg)) {
        continue;
      } else if (add && !tag.canTag(msg)) {
        throw MailServiceException.CANNOT_TAG(tag, this);
      }

      targets.add(msg.getId());
      msg.tagChanged(tag, add);

      // since we're adding/removing a tag, the tag's unread count may change
      int delta = add ? 1 : -1;
      if (tag.trackUnread() && msg.isUnread())
        tag.updateUnread(delta, isTagged(Flag.ID_FLAG_DELETED) ? delta : 0);

      // if we're adding/removing the \Deleted flag, update the folder and tag "deleted" and
      // "deleted unread" counts
      if (tag.getId() == Flag.ID_FLAG_DELETED) {
        getFolder().updateSize(0, delta, 0);
        // note that Message.updateUnread() calls updateTagUnread()
        if (msg.isUnread()) msg.updateUnread(0, delta);
      }
    }

    if (targets.isEmpty()) {
      if (excludeAccess)
        throw ServiceException.PERM_DENIED("you do not have sufficient permissions");
    } else {
      if (ZimbraLog.mailop.isDebugEnabled()) {
        String operation = add ? "Setting" : "Unsetting";
        ZimbraLog.mailop.debug(
            "%s %s for %s.  Affected ids: %s",
            operation,
            getMailopContext(tag),
            getMailopContext(this),
            StringUtil.join(",", targets));
      }
      recalculateCounts(msgs);
      DbMailItem.alterTag(tag, targets, add);
    }
  }