public function handleRequest(AphrontRequest $request) { $viewer = $this->getViewer(); $task = id(new ManiphestTaskQuery())->setViewer($viewer)->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'); $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. $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(pht("Unknown action '%s'!", $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. 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($viewer->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() == $viewer->getPHID()) { $user_owns_task = true; } } else { if ($task->getOwnerPHID() == $viewer->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($viewer->getPHID(), $task->getSubscriberPHIDs())) { $implicit_ccs[] = $viewer->getPHID(); } } 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($viewer); $event->setAphrontRequest($request); PhutilEventEngine::dispatchEvent($event); $task = $event->getValue('task'); $transactions = $event->getValue('transactions'); $editor = id(new ManiphestTransactionEditor())->setActor($viewer)->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', $viewer->getPHID(), $task->getPHID()); if ($draft) { $draft->delete(); } $event = new PhabricatorEvent(PhabricatorEventType::TYPE_MANIPHEST_DIDEDITTASK, array('task' => $task, 'new' => false, 'transactions' => $transactions)); $event->setUser($viewer); $event->setAphrontRequest($request); PhutilEventEngine::dispatchEvent($event); return id(new AphrontRedirectResponse())->setURI($task_uri); }