public function processRequest()
 {
     $request = $this->getRequest();
     $user = $request->getUser();
     $comments = $request->getStr('comments');
     $task = id(new ManiphestTask())->load($this->id);
     if (!$task) {
         return new Aphront404Response();
     }
     $draft = id(new PhabricatorDraft())->loadOneWhere('authorPHID = %s AND draftKey = %s', $user->getPHID(), $task->getPHID());
     if (!$draft) {
         $draft = new PhabricatorDraft();
         $draft->setAuthorPHID($user->getPHID());
         $draft->setDraftKey($task->getPHID());
     }
     $draft->setDraft($comments);
     $draft->save();
     $phids = array($user->getPHID());
     $action = $request->getStr('action');
     $transaction = new ManiphestTransaction();
     $transaction->setAuthorPHID($user->getPHID());
     $transaction->setComments($comments);
     $transaction->setTransactionType($action);
     $value = $request->getStr('value');
     switch ($action) {
         case ManiphestTransactionType::TYPE_OWNER:
             if (!$value) {
                 $value = $user->getPHID();
             }
             $phids[] = $value;
             break;
         case ManiphestTransactionType::TYPE_PRIORITY:
             $transaction->setOldValue($task->getPriority());
             break;
     }
     $transaction->setNewValue($value);
     $handles = id(new PhabricatorObjectHandleData($phids))->loadHandles();
     $transactions = array();
     $transactions[] = $transaction;
     $engine = PhabricatorMarkupEngine::newManiphestMarkupEngine();
     $transaction_view = new ManiphestTransactionListView();
     $transaction_view->setTransactions($transactions);
     $transaction_view->setHandles($handles);
     $transaction_view->setUser($user);
     $transaction_view->setMarkupEngine($engine);
     $transaction_view->setPreview(true);
     return id(new AphrontAjaxResponse())->setContent($transaction_view->render());
 }
 public function processRequest()
 {
     $request = $this->getRequest();
     if (!$request->validateCSRF()) {
         return new Aphront403Response();
     }
     $task = id(new ManiphestTask())->load($request->getInt('task'));
     if (!$task) {
         return new Aphront404Response();
     }
     if ($request->getInt('after')) {
         $after_task = id(new ManiphestTask())->load($request->getInt('after'));
         if (!$after_task) {
             return new Aphront404Response();
         }
         $after_pri = $after_task->getPriority();
         $after_sub = $after_task->getSubpriority();
     } else {
         $after_pri = $request->getInt('priority');
         $after_sub = null;
     }
     $new_sub = ManiphestTransactionEditor::getNextSubpriority($after_pri, $after_sub);
     if ($after_pri != $task->getPriority()) {
         $xaction = new ManiphestTransaction();
         $xaction->setAuthorPHID($request->getUser()->getPHID());
         // TODO: Content source?
         $xaction->setTransactionType(ManiphestTransactionType::TYPE_PRIORITY);
         $xaction->setNewValue($after_pri);
         $editor = new ManiphestTransactionEditor();
         $editor->applyTransactions($task, array($xaction));
     }
     $task->setSubpriority($new_sub);
     $task->save();
     $pri_class = ManiphestTaskSummaryView::getPriorityClass($task->getPriority());
     $class = 'maniphest-task-handle maniphest-active-handle ' . $pri_class;
     $response = array('className' => $class);
     return id(new AphrontAjaxResponse())->setContent($response);
 }
 public function processRequest()
 {
     $request = $this->getRequest();
     $user = $request->getUser();
     $comments = $request->getStr('comments');
     $task = id(new ManiphestTask())->load($this->id);
     if (!$task) {
         return new Aphront404Response();
     }
     $draft = id(new PhabricatorDraft())->loadOneWhere('authorPHID = %s AND draftKey = %s', $user->getPHID(), $task->getPHID());
     if (!$draft) {
         $draft = new PhabricatorDraft();
         $draft->setAuthorPHID($user->getPHID());
         $draft->setDraftKey($task->getPHID());
     }
     $draft->setDraft($comments);
     $draft->save();
     $action = $request->getStr('action');
     $transaction = new ManiphestTransaction();
     $transaction->setAuthorPHID($user->getPHID());
     $transaction->setComments($comments);
     $transaction->setTransactionType($action);
     $value = $request->getStr('value');
     // grab phids for handles and set transaction values based on action and
     // value (empty or control-specific format) coming in from the wire
     switch ($action) {
         case ManiphestTransactionType::TYPE_PRIORITY:
             $transaction->setOldValue($task->getPriority());
             $transaction->setNewValue($value);
             break;
         case ManiphestTransactionType::TYPE_OWNER:
             if ($value) {
                 $value = current(json_decode($value));
                 $phids = array($value);
             } else {
                 $phids = array();
             }
             $transaction->setNewValue($value);
             break;
         case ManiphestTransactionType::TYPE_CCS:
             if ($value) {
                 $value = json_decode($value);
                 $phids = $value;
                 foreach ($task->getCCPHIDs() as $cc_phid) {
                     $phids[] = $cc_phid;
                     $value[] = $cc_phid;
                 }
                 $transaction->setNewValue($value);
             } else {
                 $phids = array();
                 $transaction->setNewValue(array());
             }
             $transaction->setOldValue($task->getCCPHIDs());
             break;
         case ManiphestTransactionType::TYPE_PROJECTS:
             if ($value) {
                 $value = json_decode($value);
                 $phids = $value;
                 foreach ($task->getProjectPHIDs() as $project_phid) {
                     $phids[] = $project_phid;
                     $value[] = $project_phid;
                 }
                 $transaction->setNewValue($value);
             } else {
                 $phids = array();
                 $transaction->setNewValue(array());
             }
             $transaction->setOldValue($task->getProjectPHIDs());
             break;
         default:
             $phids = array();
             $transaction->setNewValue($value);
             break;
     }
     $phids[] = $user->getPHID();
     $handles = id(new PhabricatorObjectHandleData($phids))->loadHandles();
     $transactions = array();
     $transactions[] = $transaction;
     $engine = new PhabricatorMarkupEngine();
     $engine->addObject($transaction, ManiphestTransaction::MARKUP_FIELD_BODY);
     $engine->process();
     $transaction_view = new ManiphestTransactionListView();
     $transaction_view->setTransactions($transactions);
     $transaction_view->setHandles($handles);
     $transaction_view->setUser($user);
     $transaction_view->setMarkupEngine($engine);
     $transaction_view->setPreview(true);
     return id(new AphrontAjaxResponse())->setContent($transaction_view->render());
 }
 public function processRequest()
 {
     $request = $this->getRequest();
     $user = $request->getUser();
     $comments = $request->getStr('comments');
     $task = id(new ManiphestTaskQuery())->setViewer($user)->withIDs(array($this->id))->executeOne();
     if (!$task) {
         return new Aphront404Response();
     }
     id(new PhabricatorDraft())->setAuthorPHID($user->getPHID())->setDraftKey($task->getPHID())->setDraft($comments)->replaceOrDelete();
     $action = $request->getStr('action');
     $transaction = new ManiphestTransaction();
     $transaction->setAuthorPHID($user->getPHID());
     $transaction->setTransactionType($action);
     // This should really be split into a separate transaction, but it should
     // all come out in the wash once we fully move to modern stuff.
     $transaction->attachComment(id(new ManiphestTransactionComment())->setContent($comments));
     $value = $request->getStr('value');
     // grab phids for handles and set transaction values based on action and
     // value (empty or control-specific format) coming in from the wire
     switch ($action) {
         case ManiphestTransaction::TYPE_PRIORITY:
             $transaction->setOldValue($task->getPriority());
             $transaction->setNewValue($value);
             break;
         case ManiphestTransaction::TYPE_OWNER:
             if ($value) {
                 $value = current(json_decode($value));
                 $phids = array($value);
             } else {
                 $phids = array();
             }
             $transaction->setNewValue($value);
             break;
         case ManiphestTransaction::TYPE_CCS:
             if ($value) {
                 $value = json_decode($value);
             }
             if (!$value) {
                 $value = array();
             }
             $phids = $value;
             foreach ($task->getCCPHIDs() as $cc_phid) {
                 $phids[] = $cc_phid;
                 $value[] = $cc_phid;
             }
             $transaction->setOldValue($task->getCCPHIDs());
             $transaction->setNewValue($value);
             break;
         case ManiphestTransaction::TYPE_PROJECTS:
             if ($value) {
                 $value = json_decode($value);
             }
             if (!$value) {
                 $value = array();
             }
             $phids = array();
             $value = array_fuse($value);
             foreach ($value as $project_phid) {
                 $phids[] = $project_phid;
                 $value[$project_phid] = array('dst' => $project_phid);
             }
             $project_type = PhabricatorProjectObjectHasProjectEdgeType::EDGECONST;
             $transaction->setTransactionType(PhabricatorTransactions::TYPE_EDGE)->setMetadataValue('edge:type', $project_type)->setOldValue(array())->setNewValue($value);
             break;
         case ManiphestTransaction::TYPE_STATUS:
             $phids = array();
             $transaction->setOldValue($task->getStatus());
             $transaction->setNewValue($value);
             break;
         default:
             $phids = array();
             $transaction->setNewValue($value);
             break;
     }
     $phids[] = $user->getPHID();
     $handles = $this->loadViewerHandles($phids);
     $transactions = array();
     $transactions[] = $transaction;
     $engine = new PhabricatorMarkupEngine();
     $engine->setViewer($user);
     if ($transaction->hasComment()) {
         $engine->addObject($transaction->getComment(), PhabricatorApplicationTransactionComment::MARKUP_FIELD_COMMENT);
     }
     $engine->process();
     $transaction->setHandles($handles);
     $view = id(new PhabricatorApplicationTransactionView())->setUser($user)->setTransactions($transactions)->setIsPreview(true);
     return id(new AphrontAjaxResponse())->setContent((string) phutil_implode_html('', $view->buildEvents()));
 }
 public function processRequest()
 {
     $request = $this->getRequest();
     $user = $request->getUser();
     $task = id(new ManiphestTaskQuery())->setViewer($user)->withIDs(array($request->getStr('taskID')))->executeOne();
     if (!$task) {
         return new Aphront404Response();
     }
     $task_uri = '/' . $task->getMonogram();
     $transactions = array();
     $action = $request->getStr('action');
     // Compute new CCs added by @mentions. Several things can cause CCs to
     // be added as side effects: mentions, explicit CCs, users who aren't
     // CC'd interacting with the task, and ownership changes. We build up a
     // list of all the CCs and then construct a transaction for them at the
     // end if necessary.
     $added_ccs = PhabricatorMarkupEngine::extractPHIDsFromMentions($user, array($request->getStr('comments')));
     $cc_transaction = new ManiphestTransaction();
     $cc_transaction->setTransactionType(ManiphestTransaction::TYPE_CCS);
     $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 ManiphestTransaction::TYPE_PROJECTS:
             $projects = $request->getArr('projects');
             $projects = array_merge($projects, $task->getProjectPHIDs());
             $projects = array_filter($projects);
             $projects = array_unique($projects);
             // TODO: Bleh.
             $project_type = PhabricatorProjectObjectHasProjectEdgeType::EDGECONST;
             $transaction->setTransactionType(PhabricatorTransactions::TYPE_EDGE)->setMetadataValue('edge:type', $project_type)->setNewValue(array('+' => array_fuse($projects)));
             break;
         case ManiphestTransaction::TYPE_CCS:
             // Accumulate the new explicit CCs into the array that we'll add in
             // the CC transaction later.
             $added_ccs = array_merge($added_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 ($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->getCCPHIDs())) {
             $added_ccs[] = $user->getPHID();
         }
     }
     // Evade no-effect detection in the new editor stuff until we can switch
     // to subscriptions.
     $added_ccs = array_filter(array_diff($added_ccs, $task->getCCPHIDs()));
     if ($added_ccs) {
         // We've added CCs, so include a CC transaction.
         $all_ccs = array_merge($task->getCCPHIDs(), $added_ccs);
         $cc_transaction->setNewValue($all_ccs);
         $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);
 }
 private function applyTaskTransaction(ManiphestTask $task, $attach_type, array $new_phids)
 {
     if (!$this->user) {
         throw new Exception("Call setUser() before editing attachments!");
     }
     $user = $this->user;
     $editor = new ManiphestTransactionEditor();
     $type = ManiphestTransactionType::TYPE_ATTACH;
     $transaction = new ManiphestTransaction();
     $transaction->setAuthorPHID($user->getPHID());
     $transaction->setTransactionType($type);
     $new = $task->getAttached();
     $new[$attach_type] = array_fill_keys($new_phids, array());
     $transaction->setNewValue($new);
     $editor->applyTransactions($task, array($transaction));
 }
 public function handleRequest(AphrontRequest $request)
 {
     $viewer = $this->getViewer();
     $id = $request->getURIData('id');
     $response_type = $request->getStr('responseType', 'card');
     $newStatus = $request->getStr('newStatus');
     $order = $request->getStr('order', PhabricatorProjectColumn::DEFAULT_ORDER);
     $can_edit_status = $this->hasApplicationCapability(ManiphestEditStatusCapability::CAPABILITY);
     $parent_task = null;
     $template_id = null;
     if ($id) {
         $task = id(new ManiphestTaskQuery())->setViewer($viewer)->requireCapabilities(array(PhabricatorPolicyCapability::CAN_VIEW, PhabricatorPolicyCapability::CAN_EDIT))->withIDs(array($id))->needSubscriberPHIDs(true)->needProjectPHIDs(true)->executeOne();
         if (!$task) {
             return new Aphront404Response();
         }
     } else {
         return new Aphront404Response();
     }
     $transaction = new ManiphestTransaction();
     $transaction->setTransactionType('status');
     if ($newStatus == null) {
         switch ($task->getStatus()) {
             case 'open':
                 $newStatus = 'inprogress';
                 break;
             case 'inprogress':
                 $newStatus = 'finished';
                 break;
             case 'finished':
                 $newStatus = 'delivered';
                 break;
             case 'delivered':
                 $newStatus = 'resolved';
                 break;
             default:
                 return new Aphront404Response();
         }
     }
     $transaction->setNewValue($newStatus);
     $transactions = [$transaction];
     if (!$task->getOwnerPHID() && $newStatus == 'inprogress') {
         // 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;
     }
     $editor = id(new ManiphestTransactionEditor())->setActor($viewer)->setContentSourceFromRequest($request)->setContinueOnMissingFields(true);
     try {
         $editor->applyTransactions($task, $transactions);
     } catch (PhabricatorApplicationTransactionNoEffectException $ex) {
         return id(new PhabricatorApplicationTransactionNoEffectResponse())->setCancelURI($task_uri)->setException($ex);
     }
     $errors = array();
     $e_title = true;
     $field_list = PhabricatorCustomField::getObjectFields($task, PhabricatorCustomField::ROLE_EDIT);
     $field_list->setViewer($viewer);
     $field_list->readFieldsFromStorage($task);
     $aux_fields = $field_list->getFields();
     $v_space = $task->getSpacePHID();
     if ($request->isAjax()) {
         switch ($response_type) {
             case 'card':
                 $owner = null;
                 if ($task->getOwnerPHID()) {
                     $owner = id(new PhabricatorHandleQuery())->setViewer($viewer)->withPHIDs(array($task->getOwnerPHID()))->executeOne();
                 }
                 $tasks = id(new SprintBoardTaskCard())->setViewer($viewer)->setProject(null)->setTask($task)->setOwner($owner)->setCanEdit(true)->getItem();
                 $column = id(new PhabricatorProjectColumnQuery())->setViewer($viewer)->withPHIDs(array($request->getStr('columnPHID')))->executeOne();
                 if (!$column) {
                     return new Aphront404Response();
                 }
                 // re-load projects for accuracy as they are not re-loaded via
                 // the editor
                 $project_phids = PhabricatorEdgeQuery::loadDestinationPHIDs($task->getPHID(), PhabricatorProjectObjectHasProjectEdgeType::EDGECONST);
                 $task->attachProjectPHIDs($project_phids);
                 $remove_from_board = false;
                 if (!in_array($column->getProjectPHID(), $project_phids)) {
                     $remove_from_board = true;
                 }
                 $positions = id(new PhabricatorProjectColumnPositionQuery())->setViewer($viewer)->withColumns(array($column))->execute();
                 $task_phids = mpull($positions, 'getObjectPHID');
                 $column_tasks = id(new ManiphestTaskQuery())->setViewer($viewer)->withPHIDs($task_phids)->execute();
                 if ($order == PhabricatorProjectColumn::ORDER_NATURAL) {
                     // TODO: This is a little bit awkward, because PHP and JS use
                     // slightly different sort order parameters to achieve the same
                     // effect. It would be good to unify this a bit at some point.
                     $sort_map = array();
                     foreach ($positions as $position) {
                         $sort_map[$position->getObjectPHID()] = array(-$position->getSequence(), $position->getID());
                     }
                 } else {
                     $sort_map = mpull($column_tasks, 'getPrioritySortVector', 'getPHID');
                 }
                 $data = array('sortMap' => $sort_map, 'removeFromBoard' => $remove_from_board);
                 break;
             case 'task':
             default:
                 $tasks = $this->renderSingleTask($task);
                 $data = array();
                 break;
         }
         return id(new AphrontAjaxResponse())->setContent(array('tasks' => $tasks, 'data' => $data));
     }
 }
 private function applyTaskTransaction(ManiphestTask $task, $attach_type, array $new_phids)
 {
     $user = $this->getRequest()->getUser();
     $editor = new ManiphestTransactionEditor();
     $type = ManiphestTransactionType::TYPE_ATTACH;
     $transaction = new ManiphestTransaction();
     $transaction->setAuthorPHID($user->getPHID());
     $transaction->setTransactionType($type);
     $new = $task->getAttached();
     $new[$attach_type] = array_fill_keys($new_phids, array());
     $transaction->setNewValue($new);
     $editor->applyTransactions($task, array($transaction));
 }
 public function processRequest()
 {
     $request = $this->getRequest();
     $user = $request->getUser();
     $task = id(new ManiphestTask())->load($request->getStr('taskID'));
     if (!$task) {
         return new Aphront404Response();
     }
     $transactions = array();
     $action = $request->getStr('action');
     // If we have drag-and-dropped files, attach them first in a separate
     // transaction. These can come in on any transaction type, which is why we
     // handle them separately.
     $files = array();
     // Look for drag-and-drop uploads first.
     $file_phids = $request->getArr('files');
     if ($file_phids) {
         $files = id(new PhabricatorFile())->loadAllWhere('phid in (%Ls)', $file_phids);
     }
     // This means "attach a file" even though we store other types of data
     // as 'attached'.
     if ($action == ManiphestTransactionType::TYPE_ATTACH) {
         if (!empty($_FILES['file'])) {
             $err = idx($_FILES['file'], 'error');
             if ($err != UPLOAD_ERR_NO_FILE) {
                 $file = PhabricatorFile::newFromPHPUpload($_FILES['file'], array('authorPHID' => $user->getPHID()));
                 $files[] = $file;
             }
         }
     }
     // If we had explicit or drag-and-drop files, create a transaction
     // for those before we deal with whatever else might have happened.
     $file_transaction = null;
     if ($files) {
         $files = mpull($files, 'getPHID', 'getPHID');
         $new = $task->getAttached();
         foreach ($files as $phid) {
             if (empty($new[PhabricatorPHIDConstants::PHID_TYPE_FILE])) {
                 $new[PhabricatorPHIDConstants::PHID_TYPE_FILE] = array();
             }
             $new[PhabricatorPHIDConstants::PHID_TYPE_FILE][$phid] = array();
         }
         $transaction = new ManiphestTransaction();
         $transaction->setAuthorPHID($user->getPHID())->setTransactionType(ManiphestTransactionType::TYPE_ATTACH);
         $transaction->setNewValue($new);
         $transactions[] = $transaction;
         $file_transaction = $transaction;
     }
     // Compute new CCs added by @mentions. Several things can cause CCs to
     // be added as side effects: mentions, explicit CCs, users who aren't
     // CC'd interacting with the task, and ownership changes. We build up a
     // list of all the CCs and then construct a transaction for them at the
     // end if necessary.
     $added_ccs = PhabricatorMarkupEngine::extractPHIDsFromMentions(array($request->getStr('comments')));
     $cc_transaction = new ManiphestTransaction();
     $cc_transaction->setAuthorPHID($user->getPHID())->setTransactionType(ManiphestTransactionType::TYPE_CCS);
     $force_cc_transaction = false;
     $transaction = new ManiphestTransaction();
     $transaction->setAuthorPHID($user->getPHID())->setComments($request->getStr('comments'))->setTransactionType($action);
     switch ($action) {
         case ManiphestTransactionType::TYPE_STATUS:
             $transaction->setNewValue($request->getStr('resolution'));
             break;
         case ManiphestTransactionType::TYPE_OWNER:
             $assign_to = $request->getArr('assign_to');
             $assign_to = reset($assign_to);
             $transaction->setNewValue($assign_to);
             break;
         case ManiphestTransactionType::TYPE_PROJECTS:
             $projects = $request->getArr('projects');
             $projects = array_merge($projects, $task->getProjectPHIDs());
             $projects = array_filter($projects);
             $projects = array_unique($projects);
             $transaction->setNewValue($projects);
             break;
         case ManiphestTransactionType::TYPE_CCS:
             // Accumulate the new explicit CCs into the array that we'll add in
             // the CC transaction later.
             $added_ccs = array_merge($added_ccs, $request->getArr('ccs'));
             // Transfer any comments over to the CC transaction.
             $cc_transaction->setComments($transaction->getComments());
             // Make sure we include this transaction, even if the user didn't
             // actually add any CC's, because we'll discard their comment otherwise.
             $force_cc_transaction = true;
             // Throw away the primary transaction.
             $transaction = null;
             break;
         case ManiphestTransactionType::TYPE_PRIORITY:
             $transaction->setNewValue($request->getInt('priority'));
             break;
         case ManiphestTransactionType::TYPE_NONE:
         case ManiphestTransactionType::TYPE_ATTACH:
             // If we have a file transaction, just get rid of this secondary
             // transaction and put the comments on it instead.
             if ($file_transaction) {
                 $file_transaction->setComments($transaction->getComments());
                 $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;
     switch ($action) {
         case ManiphestTransactionType::TYPE_OWNER:
             if ($task->getOwnerPHID() == $transaction->getNewValue()) {
                 // If this is actually no-op, don't generate the side effect.
                 break;
             }
             // Otherwise, when a task is reassigned, move the previous owner to CC.
             $added_ccs[] = $task->getOwnerPHID();
             break;
         case ManiphestTransactionType::TYPE_STATUS:
             if (!$task->getOwnerPHID() && $request->getStr('resolution') != ManiphestTaskStatus::STATUS_OPEN) {
                 // Closing an unassigned task. Assign the user as the owner of
                 // this task.
                 $assign = new ManiphestTransaction();
                 $assign->setAuthorPHID($user->getPHID());
                 $assign->setTransactionType(ManiphestTransactionType::TYPE_OWNER);
                 $assign->setNewValue($user->getPHID());
                 $transactions[] = $assign;
                 $implicitly_claimed = true;
             }
             break;
     }
     $user_owns_task = false;
     if ($implicitly_claimed) {
         $user_owns_task = true;
     } else {
         if ($action == ManiphestTransactionType::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.
         $added_ccs[] = $user->getPHID();
     }
     if ($added_ccs || $force_cc_transaction) {
         // We've added CCs, so include a CC transaction. It's safe to do this even
         // if we're just "adding" CCs which already exist, because the
         // ManiphestTransactionEditor is smart enough to ignore them.
         $all_ccs = array_merge($task->getCCPHIDs(), $added_ccs);
         $cc_transaction->setNewValue($all_ccs);
         $transactions[] = $cc_transaction;
     }
     $content_source = PhabricatorContentSource::newForSource(PhabricatorContentSource::SOURCE_WEB, array('ip' => $request->getRemoteAddr()));
     foreach ($transactions as $transaction) {
         $transaction->setContentSource($content_source);
     }
     $editor = new ManiphestTransactionEditor();
     $editor->applyTransactions($task, $transactions);
     $draft = id(new PhabricatorDraft())->loadOneWhere('authorPHID = %s AND draftKey = %s', $user->getPHID(), $task->getPHID());
     if ($draft) {
         $draft->delete();
     }
     return id(new AphrontRedirectResponse())->setURI('/T' . $task->getID());
 }