Changeset View
Changeset View
Standalone View
Standalone View
src/applications/maniphest/editor/ManiphestTransactionEditor.php
| Show First 20 Lines • Show All 425 Lines • ▼ Show 20 Lines | protected function expandTransaction( | ||||
| } | } | ||||
| return $results; | return $results; | ||||
| } | } | ||||
| private function buildMoveTransaction( | private function buildMoveTransaction( | ||||
| PhabricatorLiskDAO $object, | PhabricatorLiskDAO $object, | ||||
| PhabricatorApplicationTransaction $xaction) { | PhabricatorApplicationTransaction $xaction) { | ||||
| $actor = $this->getActor(); | |||||
| $new = $xaction->getNewValue(); | $new = $xaction->getNewValue(); | ||||
| if (!is_array($new)) { | if (!is_array($new)) { | ||||
| $this->validateColumnPHID($new); | $this->validateColumnPHID($new); | ||||
| $new = array($new); | $new = array($new); | ||||
| } | } | ||||
| $nearby_phids = array(); | $relative_phids = array(); | ||||
| foreach ($new as $key => $value) { | foreach ($new as $key => $value) { | ||||
| if (!is_array($value)) { | if (!is_array($value)) { | ||||
| $this->validateColumnPHID($value); | $this->validateColumnPHID($value); | ||||
| $value = array( | $value = array( | ||||
| 'columnPHID' => $value, | 'columnPHID' => $value, | ||||
| ); | ); | ||||
| } | } | ||||
| PhutilTypeSpec::checkMap( | PhutilTypeSpec::checkMap( | ||||
| $value, | $value, | ||||
| array( | array( | ||||
| 'columnPHID' => 'string', | 'columnPHID' => 'string', | ||||
| 'beforePHIDs' => 'optional list<string>', | |||||
| 'afterPHIDs' => 'optional list<string>', | |||||
| // Deprecated older variations of "beforePHIDs" and "afterPHIDs". | |||||
| 'beforePHID' => 'optional string', | 'beforePHID' => 'optional string', | ||||
| 'afterPHID' => 'optional string', | 'afterPHID' => 'optional string', | ||||
| )); | )); | ||||
| $new[$key] = $value; | $value = $value + array( | ||||
| 'beforePHIDs' => array(), | |||||
| 'afterPHIDs' => array(), | |||||
| ); | |||||
| if (!empty($value['beforePHID'])) { | // Normalize the legacy keys "beforePHID" and "afterPHID" keys to the | ||||
| $nearby_phids[] = $value['beforePHID']; | // modern format. | ||||
| if (!empty($value['afterPHID'])) { | |||||
| if ($value['afterPHIDs']) { | |||||
| throw new Exception( | |||||
| pht( | |||||
| 'Transaction specifies both "afterPHID" and "afterPHIDs". '. | |||||
| 'Specify only "afterPHIDs".')); | |||||
| } | |||||
| $value['afterPHIDs'] = array($value['afterPHID']); | |||||
| unset($value['afterPHID']); | |||||
| } | } | ||||
| if (!empty($value['afterPHID'])) { | if (isset($value['beforePHID'])) { | ||||
| $nearby_phids[] = $value['afterPHID']; | if ($value['beforePHIDs']) { | ||||
| throw new Exception( | |||||
| pht( | |||||
| 'Transaction specifies both "beforePHID" and "beforePHIDs". '. | |||||
| 'Specify only "beforePHIDs".')); | |||||
| } | } | ||||
| $value['beforePHIDs'] = array($value['beforePHID']); | |||||
| unset($value['beforePHID']); | |||||
| } | } | ||||
| if ($nearby_phids) { | foreach ($value['beforePHIDs'] as $phid) { | ||||
| $nearby_objects = id(new PhabricatorObjectQuery()) | $relative_phids[] = $phid; | ||||
| ->setViewer($this->getActor()) | } | ||||
| ->withPHIDs($nearby_phids) | |||||
| foreach ($value['afterPHIDs'] as $phid) { | |||||
| $relative_phids[] = $phid; | |||||
| } | |||||
| $new[$key] = $value; | |||||
| } | |||||
| // We require that objects you specify in "beforePHIDs" or "afterPHIDs" | |||||
| // are real objects which exist and which you have permission to view. | |||||
| // If you provide other objects, we remove them from the specification. | |||||
| if ($relative_phids) { | |||||
| $objects = id(new PhabricatorObjectQuery()) | |||||
| ->setViewer($actor) | |||||
| ->withPHIDs($relative_phids) | |||||
| ->execute(); | ->execute(); | ||||
| $nearby_objects = mpull($nearby_objects, null, 'getPHID'); | $objects = mpull($objects, null, 'getPHID'); | ||||
| } else { | } else { | ||||
| $nearby_objects = array(); | $objects = array(); | ||||
| } | |||||
| foreach ($new as $key => $value) { | |||||
| $value['afterPHIDs'] = $this->filterValidPHIDs( | |||||
| $value['afterPHIDs'], | |||||
| $objects); | |||||
| $value['beforePHIDs'] = $this->filterValidPHIDs( | |||||
| $value['beforePHIDs'], | |||||
| $objects); | |||||
| $new[$key] = $value; | |||||
| } | } | ||||
| $column_phids = ipull($new, 'columnPHID'); | $column_phids = ipull($new, 'columnPHID'); | ||||
| if ($column_phids) { | if ($column_phids) { | ||||
| $columns = id(new PhabricatorProjectColumnQuery()) | $columns = id(new PhabricatorProjectColumnQuery()) | ||||
| ->setViewer($this->getActor()) | ->setViewer($actor) | ||||
| ->withPHIDs($column_phids) | ->withPHIDs($column_phids) | ||||
| ->execute(); | ->execute(); | ||||
| $columns = mpull($columns, null, 'getPHID'); | $columns = mpull($columns, null, 'getPHID'); | ||||
| } else { | } else { | ||||
| $columns = array(); | $columns = array(); | ||||
| } | } | ||||
| $board_phids = mpull($columns, 'getProjectPHID'); | $board_phids = mpull($columns, 'getProjectPHID'); | ||||
| $object_phid = $object->getPHID(); | $object_phid = $object->getPHID(); | ||||
| $object_phids = $nearby_phids; | |||||
| // Note that we may not have an object PHID if we're creating a new | // Note that we may not have an object PHID if we're creating a new | ||||
| // object. | // object. | ||||
| $object_phids = array(); | |||||
| if ($object_phid) { | if ($object_phid) { | ||||
| $object_phids[] = $object_phid; | $object_phids[] = $object_phid; | ||||
| } | } | ||||
| if ($object_phids) { | if ($object_phids) { | ||||
| $layout_engine = id(new PhabricatorBoardLayoutEngine()) | $layout_engine = id(new PhabricatorBoardLayoutEngine()) | ||||
| ->setViewer($this->getActor()) | ->setViewer($this->getActor()) | ||||
| ->setBoardPHIDs($board_phids) | ->setBoardPHIDs($board_phids) | ||||
| Show All 10 Lines | foreach ($new as $key => $spec) { | ||||
| pht( | pht( | ||||
| 'Column move transaction specifies column PHID "%s", but there '. | 'Column move transaction specifies column PHID "%s", but there '. | ||||
| 'is no corresponding column with this PHID.', | 'is no corresponding column with this PHID.', | ||||
| $column_phid)); | $column_phid)); | ||||
| } | } | ||||
| $board_phid = $column->getProjectPHID(); | $board_phid = $column->getProjectPHID(); | ||||
| $nearby = array(); | |||||
| if (!empty($spec['beforePHID'])) { | |||||
| $nearby['beforePHID'] = $spec['beforePHID']; | |||||
| } | |||||
| if (!empty($spec['afterPHID'])) { | |||||
| $nearby['afterPHID'] = $spec['afterPHID']; | |||||
| } | |||||
| if (count($nearby) > 1) { | |||||
| throw new Exception( | |||||
| pht( | |||||
| 'Column move transaction moves object to multiple positions. '. | |||||
| 'Specify only "beforePHID" or "afterPHID", not both.')); | |||||
| } | |||||
| foreach ($nearby as $where => $nearby_phid) { | |||||
| if (empty($nearby_objects[$nearby_phid])) { | |||||
| throw new Exception( | |||||
| pht( | |||||
| 'Column move transaction specifies object "%s" as "%s", but '. | |||||
| 'there is no corresponding object with this PHID.', | |||||
| $object_phid, | |||||
| $where)); | |||||
| } | |||||
| $nearby_columns = $layout_engine->getObjectColumns( | |||||
| $board_phid, | |||||
| $nearby_phid); | |||||
| $nearby_columns = mpull($nearby_columns, null, 'getPHID'); | |||||
| if (empty($nearby_columns[$column_phid])) { | |||||
| throw new Exception( | |||||
| pht( | |||||
| 'Column move transaction specifies object "%s" as "%s" in '. | |||||
| 'column "%s", but this object is not in that column!', | |||||
| $nearby_phid, | |||||
| $where, | |||||
| $column_phid)); | |||||
| } | |||||
| } | |||||
| if ($object_phid) { | if ($object_phid) { | ||||
| $old_columns = $layout_engine->getObjectColumns( | $old_columns = $layout_engine->getObjectColumns( | ||||
| $board_phid, | $board_phid, | ||||
| $object_phid); | $object_phid); | ||||
| $old_column_phids = mpull($old_columns, 'getPHID'); | $old_column_phids = mpull($old_columns, 'getPHID'); | ||||
| } else { | } else { | ||||
| $old_column_phids = array(); | $old_column_phids = array(); | ||||
| } | } | ||||
| $spec += array( | $spec += array( | ||||
| 'boardPHID' => $board_phid, | 'boardPHID' => $board_phid, | ||||
| 'fromColumnPHIDs' => $old_column_phids, | 'fromColumnPHIDs' => $old_column_phids, | ||||
| ); | ); | ||||
| // Check if the object is already in this column, and isn't being moved. | // Check if the object is already in this column, and isn't being moved. | ||||
| // We can just drop this column change if it has no effect. | // We can just drop this column change if it has no effect. | ||||
| $from_map = array_fuse($spec['fromColumnPHIDs']); | $from_map = array_fuse($spec['fromColumnPHIDs']); | ||||
| $already_here = isset($from_map[$column_phid]); | $already_here = isset($from_map[$column_phid]); | ||||
| $is_reordering = (bool)$nearby; | |||||
| $is_reordering = ($spec['afterPHIDs'] || $spec['beforePHIDs']); | |||||
| if ($already_here && !$is_reordering) { | if ($already_here && !$is_reordering) { | ||||
| unset($new[$key]); | unset($new[$key]); | ||||
| } else { | } else { | ||||
| $new[$key] = $spec; | $new[$key] = $spec; | ||||
| } | } | ||||
| } | } | ||||
| $new = array_values($new); | $new = array_values($new); | ||||
| ▲ Show 20 Lines • Show All 81 Lines • ▼ Show 20 Lines | private function buildMoveTransaction( | ||||
| } | } | ||||
| return $more; | return $more; | ||||
| } | } | ||||
| private function applyBoardMove($object, array $move) { | private function applyBoardMove($object, array $move) { | ||||
| $board_phid = $move['boardPHID']; | $board_phid = $move['boardPHID']; | ||||
| $column_phid = $move['columnPHID']; | $column_phid = $move['columnPHID']; | ||||
| $before_phid = idx($move, 'beforePHID'); | |||||
| $after_phid = idx($move, 'afterPHID'); | $before_phids = $move['beforePHIDs']; | ||||
| $after_phids = $move['afterPHIDs']; | |||||
| $object_phid = $object->getPHID(); | $object_phid = $object->getPHID(); | ||||
| // We're doing layout with the omnipotent viewer to make sure we don't | // We're doing layout with the omnipotent viewer to make sure we don't | ||||
| // remove positions in columns that exist, but which the actual actor | // remove positions in columns that exist, but which the actual actor | ||||
| // can't see. | // can't see. | ||||
| $omnipotent_viewer = PhabricatorUser::getOmnipotentUser(); | $omnipotent_viewer = PhabricatorUser::getOmnipotentUser(); | ||||
| Show All 35 Lines | private function applyBoardMove($object, array $move) { | ||||
| $columns = $engine->getObjectColumns($board_phid, $object_phid); | $columns = $engine->getObjectColumns($board_phid, $object_phid); | ||||
| foreach ($columns as $column) { | foreach ($columns as $column) { | ||||
| $engine->queueRemovePosition( | $engine->queueRemovePosition( | ||||
| $board_phid, | $board_phid, | ||||
| $column->getPHID(), | $column->getPHID(), | ||||
| $object_phid); | $object_phid); | ||||
| } | } | ||||
| if ($before_phid) { | |||||
| $engine->queueAddPositionBefore( | |||||
| $board_phid, | |||||
| $column_phid, | |||||
| $object_phid, | |||||
| $before_phid); | |||||
| } else if ($after_phid) { | |||||
| $engine->queueAddPositionAfter( | |||||
| $board_phid, | |||||
| $column_phid, | |||||
| $object_phid, | |||||
| $after_phid); | |||||
| } else { | |||||
| $engine->queueAddPosition( | $engine->queueAddPosition( | ||||
| $board_phid, | $board_phid, | ||||
| $column_phid, | $column_phid, | ||||
| $object_phid); | $object_phid, | ||||
| } | $after_phids, | ||||
| $before_phids); | |||||
| $engine->applyPositionUpdates(); | $engine->applyPositionUpdates(); | ||||
| } | } | ||||
| private function validateColumnPHID($value) { | private function validateColumnPHID($value) { | ||||
| if (phid_get_type($value) == PhabricatorProjectColumnPHIDType::TYPECONST) { | if (phid_get_type($value) == PhabricatorProjectColumnPHIDType::TYPECONST) { | ||||
| return; | return; | ||||
| ▲ Show 20 Lines • Show All 85 Lines • ▼ Show 20 Lines | if (!$now_locked) { | ||||
| $message, | $message, | ||||
| $problem_xaction); | $problem_xaction); | ||||
| } | } | ||||
| } | } | ||||
| return $errors; | return $errors; | ||||
| } | } | ||||
| private function filterValidPHIDs($phid_list, array $object_map) { | |||||
| foreach ($phid_list as $key => $phid) { | |||||
| if (isset($object_map[$phid])) { | |||||
| continue; | |||||
| } | |||||
| unset($phid_list[$key]); | |||||
| } | |||||
| return array_values($phid_list); | |||||
| } | |||||
| } | } | ||||