Changeset View
Changeset View
Standalone View
Standalone View
src/applications/maniphest/editor/ManiphestTransactionEditor.php
Show First 20 Lines • Show All 422 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); | |||||
} | |||||
} | } |