diff --git a/src/applications/maniphest/controller/ManiphestTaskEditProController.php b/src/applications/maniphest/controller/ManiphestTaskEditProController.php index fb4d9d5027..728cabf639 100644 --- a/src/applications/maniphest/controller/ManiphestTaskEditProController.php +++ b/src/applications/maniphest/controller/ManiphestTaskEditProController.php @@ -1,12 +1,15 @@ setController($this) ->addContextParameter('ungrippable') + ->addContextParameter('responseType') + ->addContextParameter('columnPHID') + ->addContextParameter('order') ->buildResponse(); } } diff --git a/src/applications/maniphest/editor/ManiphestEditEngine.php b/src/applications/maniphest/editor/ManiphestEditEngine.php index 7e5558701c..c588201287 100644 --- a/src/applications/maniphest/editor/ManiphestEditEngine.php +++ b/src/applications/maniphest/editor/ManiphestEditEngine.php @@ -1,220 +1,311 @@ getViewer()); } protected function newObjectQuery() { return id(new ManiphestTaskQuery()); } protected function getObjectCreateTitleText($object) { return pht('Create New Task'); } protected function getObjectEditTitleText($object) { return pht('Edit %s %s', $object->getMonogram(), $object->getTitle()); } protected function getObjectEditShortText($object) { return $object->getMonogram(); } protected function getObjectCreateShortText() { return pht('Create Task'); } protected function getCommentViewHeaderText($object) { $is_serious = PhabricatorEnv::getEnvConfig('phabricator.serious-business'); if (!$is_serious) { return pht('Weigh In'); } return parent::getCommentViewHeaderText($object); } protected function getObjectViewURI($object) { return '/'.$object->getMonogram(); } protected function buildCustomEditFields($object) { $status_map = $this->getTaskStatusMap($object); $priority_map = $this->getTaskPriorityMap($object); if ($object->isClosed()) { $priority_label = null; $default_status = ManiphestTaskStatus::getDefaultStatus(); } else { $priority_label = pht('Change Priority'); $default_status = ManiphestTaskStatus::getDefaultClosedStatus(); } if ($object->getOwnerPHID()) { $owner_value = array($object->getOwnerPHID()); } else { $owner_value = array($this->getViewer()->getPHID()); } return array( id(new PhabricatorHandlesEditField()) ->setKey('parent') ->setLabel(pht('Parent Task')) ->setDescription(pht('Task to make this a subtask of.')) ->setAliases(array('parentPHID')) ->setTransactionType(ManiphestTransaction::TYPE_PARENT) ->setSingleValue(null), id(new PhabricatorTextEditField()) ->setKey('title') ->setLabel(pht('Title')) ->setDescription(pht('Name of the task.')) ->setTransactionType(ManiphestTransaction::TYPE_TITLE) ->setIsRequired(true) ->setValue($object->getTitle()), id(new PhabricatorUsersEditField()) ->setKey('owner') ->setAliases(array('ownerPHID', 'assign', 'assigned')) ->setLabel(pht('Assigned To')) ->setDescription(pht('User who is responsible for the task.')) ->setTransactionType(ManiphestTransaction::TYPE_OWNER) ->setIsCopyable(true) ->setSingleValue($object->getOwnerPHID()) ->setCommentActionLabel(pht('Assign / Claim')) ->setCommentActionDefaultValue($owner_value), id(new PhabricatorSelectEditField()) ->setKey('status') ->setLabel(pht('Status')) ->setDescription(pht('Status of the task.')) ->setTransactionType(ManiphestTransaction::TYPE_STATUS) ->setIsCopyable(true) ->setValue($object->getStatus()) ->setOptions($status_map) ->setCommentActionLabel(pht('Change Status')) ->setCommentActionDefaultValue($default_status), id(new PhabricatorSelectEditField()) ->setKey('priority') ->setLabel(pht('Priority')) ->setDescription(pht('Priority of the task.')) ->setTransactionType(ManiphestTransaction::TYPE_PRIORITY) ->setIsCopyable(true) ->setValue($object->getPriority()) ->setOptions($priority_map) ->setCommentActionLabel($priority_label), id(new PhabricatorRemarkupEditField()) ->setKey('description') ->setLabel(pht('Description')) ->setDescription(pht('Task description.')) ->setTransactionType(ManiphestTransaction::TYPE_DESCRIPTION) ->setValue($object->getDescription()), ); } protected function getEditorURI() { // TODO: Remove when cutting over. return $this->getApplication()->getApplicationURI('editpro/'); } private function getTaskStatusMap(ManiphestTask $task) { $status_map = ManiphestTaskStatus::getTaskStatusMap(); $current_status = $task->getStatus(); // If the current status is something we don't recognize (maybe an older // status which was deleted), put a dummy entry in the status map so that // saving the form doesn't destroy any data by accident. if (idx($status_map, $current_status) === null) { $status_map[$current_status] = pht('', $current_status); } $dup_status = ManiphestTaskStatus::getDuplicateStatus(); foreach ($status_map as $status => $status_name) { // Always keep the task's current status. if ($status == $current_status) { continue; } // Don't allow tasks to be changed directly into "Closed, Duplicate" // status. Instead, you have to merge them. See T4819. if ($status == $dup_status) { unset($status_map[$status]); continue; } // Don't let new or existing tasks be moved into a disabled status. if (ManiphestTaskStatus::isDisabledStatus($status)) { unset($status_map[$status]); continue; } } return $status_map; } private function getTaskPriorityMap(ManiphestTask $task) { $priority_map = ManiphestTaskPriority::getTaskPriorityMap(); $current_priority = $task->getPriority(); // If the current value isn't a legitimate one, put it in the dropdown // anyway so saving the form doesn't cause a side effects. if (idx($priority_map, $current_priority) === null) { $priority_map[$current_priority] = pht( '', $current_priority); } foreach ($priority_map as $priority => $priority_name) { // Always keep the current priority. if ($priority == $current_priority) { continue; } if (ManiphestTaskPriority::isDisabledPriority($priority)) { unset($priority_map[$priority]); continue; } } return $priority_map; } protected function newEditResponse( AphrontRequest $request, $object, array $xactions) { - $viewer = $this->getViewer(); - $controller = $this->getController(); if ($request->isAjax()) { - // Reload the task to make sure we pick up the final task state. + $viewer = $this->getViewer(); $task = id(new ManiphestTaskQuery()) ->setViewer($viewer) ->withIDs(array($object->getID())) ->needSubscriberPHIDs(true) ->needProjectPHIDs(true) ->executeOne(); - $payload = array( - 'tasks' => $controller->renderSingleTask($task), - 'data' => array(), - ); + switch ($request->getStr('responseType')) { + case 'card': + return $this->buildCardResponse($task); + default: + return $this->buildListResponse($task); + } - return id(new AphrontAjaxResponse())->setContent($payload); } return parent::newEditResponse(); } + private function buildListResponse(ManiphestTask $task) { + $controller = $this->getController(); + + $payload = array( + 'tasks' => $controller->renderSingleTask($task), + 'data' => array(), + ); + + return id(new AphrontAjaxResponse())->setContent($payload); + } + + private function buildCardResponse(ManiphestTask $task) { + $controller = $this->getController(); + $request = $controller->getRequest(); + $viewer = $request->getViewer(); + + $column_phid = $request->getStr('columnPHID'); + $order = $request->getStr('order'); + + $column = id(new PhabricatorProjectColumnQuery()) + ->setViewer($viewer) + ->withPHIDs(array($column_phid)) + ->executeOne(); + if (!$column) { + return new Aphront404Response(); + } + + // If the workboard's project has been removed from the card's project + // list, we are going to remove it from the board completely. + $project_map = array_fuse($task->getProjectPHIDs()); + $remove_card = empty($project_map[$column->getProjectPHID()]); + + $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( + 'removeFromBoard' => $remove_card, + 'sortMap' => $sort_map, + ); + + // TODO: This should just use HandlePool once we get through the EditEngine + // transition. + $owner = null; + if ($task->getOwnerPHID()) { + $owner = id(new PhabricatorHandleQuery()) + ->setViewer($viewer) + ->withPHIDs(array($task->getOwnerPHID())) + ->executeOne(); + } + + $tasks = id(new ProjectBoardTaskCard()) + ->setViewer($viewer) + ->setTask($task) + ->setOwner($owner) + ->setCanEdit(true) + ->getItem(); + + $payload = array( + 'tasks' => $tasks, + 'data' => $data, + ); + + return id(new AphrontAjaxResponse())->setContent($payload); + } + + } diff --git a/src/applications/project/view/ProjectBoardTaskCard.php b/src/applications/project/view/ProjectBoardTaskCard.php index 4fc26988d7..c5ae6bc004 100644 --- a/src/applications/project/view/ProjectBoardTaskCard.php +++ b/src/applications/project/view/ProjectBoardTaskCard.php @@ -1,79 +1,79 @@ viewer = $viewer; return $this; } public function getViewer() { return $this->viewer; } public function setTask(ManiphestTask $task) { $this->task = $task; return $this; } public function getTask() { return $this->task; } public function setOwner(PhabricatorObjectHandle $owner = null) { $this->owner = $owner; return $this; } public function getOwner() { return $this->owner; } public function setCanEdit($can_edit) { $this->canEdit = $can_edit; return $this; } public function getCanEdit() { return $this->canEdit; } public function getItem() { $task = $this->getTask(); $owner = $this->getOwner(); $can_edit = $this->getCanEdit(); $color_map = ManiphestTaskPriority::getColorMap(); $bar_color = idx($color_map, $task->getPriority(), 'grey'); $card = id(new PHUIObjectItemView()) ->setObject($task) ->setUser($this->getViewer()) ->setObjectName('T'.$task->getID()) ->setHeader($task->getTitle()) ->setGrippable($can_edit) ->setHref('/T'.$task->getID()) ->addSigil('project-card') ->setDisabled($task->isClosed()) ->setMetadata( array( 'objectPHID' => $task->getPHID(), )) ->addAction( id(new PHUIListItemView()) ->setName(pht('Edit')) ->setIcon('fa-pencil') ->addSigil('edit-project-card') - ->setHref('/maniphest/task/edit/'.$task->getID().'/')) + ->setHref('/maniphest/editpro/'.$task->getID().'/')) ->setBarColor($bar_color); if ($owner) { $card->addAttribute($owner->renderLink()); } return $card; } }