diff --git a/src/applications/maniphest/controller/ManiphestTransactionSaveController.php b/src/applications/maniphest/controller/ManiphestTransactionSaveController.php index 52cb751140..f12171a474 100644 --- a/src/applications/maniphest/controller/ManiphestTransactionSaveController.php +++ b/src/applications/maniphest/controller/ManiphestTransactionSaveController.php @@ -1,200 +1,210 @@ getRequest(); $user = $request->getUser(); $task = id(new ManiphestTaskQuery()) ->setViewer($user) ->withIDs(array($request->getStr('taskID'))) ->needSubscriberPHIDs(true) ->needProjectPHIDs(true) ->executeOne(); if (!$task) { return new Aphront404Response(); } $task_uri = '/'.$task->getMonogram(); $transactions = array(); $action = $request->getStr('action'); - $added_ccs = array(); - - $cc_transaction = new ManiphestTransaction(); - $cc_transaction - ->setTransactionType(PhabricatorTransactions::TYPE_SUBSCRIBERS); + $implicit_ccs = array(); + $explicit_ccs = array(); $transaction = new ManiphestTransaction(); $transaction ->setTransactionType($action); switch ($action) { case ManiphestTransaction::TYPE_STATUS: $transaction->setNewValue($request->getStr('resolution')); break; case ManiphestTransaction::TYPE_OWNER: $assign_to = $request->getArr('assign_to'); $assign_to = reset($assign_to); $transaction->setNewValue($assign_to); break; case PhabricatorTransactions::TYPE_EDGE: $projects = $request->getArr('projects'); $projects = array_merge($projects, $task->getProjectPHIDs()); $projects = array_filter($projects); $projects = array_unique($projects); $project_type = PhabricatorProjectObjectHasProjectEdgeType::EDGECONST; $transaction ->setMetadataValue('edge:type', $project_type) ->setNewValue( array( '+' => array_fuse($projects), )); break; case PhabricatorTransactions::TYPE_SUBSCRIBERS: // Accumulate the new explicit CCs into the array that we'll add in // the CC transaction later. - $added_ccs = $request->getArr('ccs'); + $explicit_ccs = $request->getArr('ccs'); // Throw away the primary transaction. $transaction = null; break; case ManiphestTransaction::TYPE_PRIORITY: $transaction->setNewValue($request->getInt('priority')); break; case PhabricatorTransactions::TYPE_COMMENT: // Nuke this, we're going to create it below. $transaction = null; break; default: throw new Exception('unknown action'); } if ($transaction) { $transactions[] = $transaction; } // When you interact with a task, we add you to the CC list so you get // further updates, and possibly assign the task to you if you took an // ownership action (closing it) but it's currently unowned. We also move // previous owners to CC if ownership changes. Detect all these conditions // and create side-effect transactions for them. $implicitly_claimed = false; if ($action == ManiphestTransaction::TYPE_OWNER) { if ($task->getOwnerPHID() == $transaction->getNewValue()) { // If this is actually no-op, don't generate the side effect. } else { // Otherwise, when a task is reassigned, move the previous owner to CC. - $added_ccs[] = $task->getOwnerPHID(); + if ($task->getOwnerPHID()) { + $implicit_ccs[] = $task->getOwnerPHID(); + } } } if ($action == ManiphestTransaction::TYPE_STATUS) { $resolution = $request->getStr('resolution'); if (!$task->getOwnerPHID() && ManiphestTaskStatus::isClosedStatus($resolution)) { // Closing an unassigned task. Assign the user as the owner of // this task. $assign = new ManiphestTransaction(); $assign->setTransactionType(ManiphestTransaction::TYPE_OWNER); $assign->setNewValue($user->getPHID()); $transactions[] = $assign; $implicitly_claimed = true; } } $user_owns_task = false; if ($implicitly_claimed) { $user_owns_task = true; } else { if ($action == ManiphestTransaction::TYPE_OWNER) { if ($transaction->getNewValue() == $user->getPHID()) { $user_owns_task = true; } } else if ($task->getOwnerPHID() == $user->getPHID()) { $user_owns_task = true; } } if (!$user_owns_task) { // If we aren't making the user the new task owner and they aren't the // existing task owner, add them to CC unless they're aleady CC'd. if (!in_array($user->getPHID(), $task->getSubscriberPHIDs())) { - $added_ccs[] = $user->getPHID(); + $implicit_ccs[] = $user->getPHID(); } } - if ($added_ccs) { - // We've added CCs, so include a CC transaction. - $all_ccs = array_merge($task->getSubscriberPHIDs(), $added_ccs); - $cc_transaction->setNewValue(array('=' => $all_ccs)); + if ($implicit_ccs || $explicit_ccs) { + + // TODO: These implicit CC rules should probably be handled inside the + // Editor, eventually. + + $all_ccs = array_fuse($implicit_ccs) + array_fuse($explicit_ccs); + + $cc_transaction = id(new ManiphestTransaction()) + ->setTransactionType(PhabricatorTransactions::TYPE_SUBSCRIBERS) + ->setNewValue(array('+' => $all_ccs)); + + if (!$explicit_ccs) { + $cc_transaction->setIgnoreOnNoEffect(true); + } + $transactions[] = $cc_transaction; } $comments = $request->getStr('comments'); if (strlen($comments) || !$transactions) { $transactions[] = id(new ManiphestTransaction()) ->setTransactionType(PhabricatorTransactions::TYPE_COMMENT) ->attachComment( id(new ManiphestTransactionComment()) ->setContent($comments)); } $event = new PhabricatorEvent( PhabricatorEventType::TYPE_MANIPHEST_WILLEDITTASK, array( 'task' => $task, 'new' => false, 'transactions' => $transactions, )); $event->setUser($user); $event->setAphrontRequest($request); PhutilEventEngine::dispatchEvent($event); $task = $event->getValue('task'); $transactions = $event->getValue('transactions'); $editor = id(new ManiphestTransactionEditor()) ->setActor($user) ->setContentSourceFromRequest($request) ->setContinueOnMissingFields(true) ->setContinueOnNoEffect($request->isContinueRequest()); try { $editor->applyTransactions($task, $transactions); } catch (PhabricatorApplicationTransactionNoEffectException $ex) { return id(new PhabricatorApplicationTransactionNoEffectResponse()) ->setCancelURI($task_uri) ->setException($ex); } $draft = id(new PhabricatorDraft())->loadOneWhere( 'authorPHID = %s AND draftKey = %s', $user->getPHID(), $task->getPHID()); if ($draft) { $draft->delete(); } $event = new PhabricatorEvent( PhabricatorEventType::TYPE_MANIPHEST_DIDEDITTASK, array( 'task' => $task, 'new' => false, 'transactions' => $transactions, )); $event->setUser($user); $event->setAphrontRequest($request); PhutilEventEngine::dispatchEvent($event); return id(new AphrontRedirectResponse())->setURI($task_uri); } }