mirror of
https://we.phorge.it/source/phorge.git
synced 2024-12-20 12:30:56 +01:00
Fix literally thousands of drag-to-reorder priority bugs
Summary: Fixes T7563. Fixes T5201. Reframe this as two separate operations: - Move before or after a task. - Move to the beginning or end of a priority. Then: - Make all the order queries unambiguous and properly reversible, with an explicit `id` order. - Just reuse `ManiphestTask` to get results in the correct order. - Simplify the actual transaction apply logic. - Detect and recover from cases where tasks have identical or similar subpriorities. Test Plan: - Wrote and executed unit tests. - Dragged and dropped tasks within priorities and between priorities in the main Maniphest view. - Dragged and dropped tasks within priorities in the workboard view, when ordered by priority. - Also poked at the "natural" order, but that shouldn't be affected. Reviewers: btrahan, chad Reviewed By: chad Subscribers: chad, epriestley Maniphest Tasks: T5201, T7563 Differential Revision: https://secure.phabricator.com/D12121
This commit is contained in:
parent
ac029d0a50
commit
5aca529980
6 changed files with 314 additions and 116 deletions
|
@ -1053,6 +1053,7 @@ phutil_register_library_map(array(
|
||||||
'ManiphestTaskStatus' => 'applications/maniphest/constants/ManiphestTaskStatus.php',
|
'ManiphestTaskStatus' => 'applications/maniphest/constants/ManiphestTaskStatus.php',
|
||||||
'ManiphestTaskStatusDatasource' => 'applications/maniphest/typeahead/ManiphestTaskStatusDatasource.php',
|
'ManiphestTaskStatusDatasource' => 'applications/maniphest/typeahead/ManiphestTaskStatusDatasource.php',
|
||||||
'ManiphestTaskStatusTestCase' => 'applications/maniphest/constants/__tests__/ManiphestTaskStatusTestCase.php',
|
'ManiphestTaskStatusTestCase' => 'applications/maniphest/constants/__tests__/ManiphestTaskStatusTestCase.php',
|
||||||
|
'ManiphestTaskTestCase' => 'applications/maniphest/__tests__/ManiphestTaskTestCase.php',
|
||||||
'ManiphestTransaction' => 'applications/maniphest/storage/ManiphestTransaction.php',
|
'ManiphestTransaction' => 'applications/maniphest/storage/ManiphestTransaction.php',
|
||||||
'ManiphestTransactionComment' => 'applications/maniphest/storage/ManiphestTransactionComment.php',
|
'ManiphestTransactionComment' => 'applications/maniphest/storage/ManiphestTransactionComment.php',
|
||||||
'ManiphestTransactionEditor' => 'applications/maniphest/editor/ManiphestTransactionEditor.php',
|
'ManiphestTransactionEditor' => 'applications/maniphest/editor/ManiphestTransactionEditor.php',
|
||||||
|
@ -4292,6 +4293,7 @@ phutil_register_library_map(array(
|
||||||
'ManiphestTaskStatus' => 'ManiphestConstants',
|
'ManiphestTaskStatus' => 'ManiphestConstants',
|
||||||
'ManiphestTaskStatusDatasource' => 'PhabricatorTypeaheadDatasource',
|
'ManiphestTaskStatusDatasource' => 'PhabricatorTypeaheadDatasource',
|
||||||
'ManiphestTaskStatusTestCase' => 'PhabricatorTestCase',
|
'ManiphestTaskStatusTestCase' => 'PhabricatorTestCase',
|
||||||
|
'ManiphestTaskTestCase' => 'PhabricatorTestCase',
|
||||||
'ManiphestTransaction' => 'PhabricatorApplicationTransaction',
|
'ManiphestTransaction' => 'PhabricatorApplicationTransaction',
|
||||||
'ManiphestTransactionComment' => 'PhabricatorApplicationTransactionComment',
|
'ManiphestTransactionComment' => 'PhabricatorApplicationTransactionComment',
|
||||||
'ManiphestTransactionEditor' => 'PhabricatorApplicationTransactionEditor',
|
'ManiphestTransactionEditor' => 'PhabricatorApplicationTransactionEditor',
|
||||||
|
|
174
src/applications/maniphest/__tests__/ManiphestTaskTestCase.php
Normal file
174
src/applications/maniphest/__tests__/ManiphestTaskTestCase.php
Normal file
|
@ -0,0 +1,174 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class ManiphestTaskTestCase extends PhabricatorTestCase {
|
||||||
|
|
||||||
|
protected function getPhabricatorTestCaseConfiguration() {
|
||||||
|
return array(
|
||||||
|
self::PHABRICATOR_TESTCONFIG_BUILD_STORAGE_FIXTURES => true,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testTaskReordering() {
|
||||||
|
$viewer = $this->generateNewTestUser();
|
||||||
|
|
||||||
|
$t1 = $this->newTask($viewer, 'Task 1');
|
||||||
|
$t2 = $this->newTask($viewer, 'Task 2');
|
||||||
|
$t3 = $this->newTask($viewer, 'Task 3');
|
||||||
|
|
||||||
|
|
||||||
|
// Default order should be reverse creation.
|
||||||
|
$tasks = $this->loadTasks($viewer);
|
||||||
|
$t1 = $tasks[1];
|
||||||
|
$t2 = $tasks[2];
|
||||||
|
$t3 = $tasks[3];
|
||||||
|
$this->assertEqual(array(3, 2, 1), array_keys($tasks));
|
||||||
|
|
||||||
|
|
||||||
|
// Move T3 to the middle.
|
||||||
|
$this->moveTask($viewer, $t3, $t2, true);
|
||||||
|
$tasks = $this->loadTasks($viewer);
|
||||||
|
$t1 = $tasks[1];
|
||||||
|
$t2 = $tasks[2];
|
||||||
|
$t3 = $tasks[3];
|
||||||
|
$this->assertEqual(array(2, 3, 1), array_keys($tasks));
|
||||||
|
|
||||||
|
|
||||||
|
// Move T3 to the end.
|
||||||
|
$this->moveTask($viewer, $t3, $t1, true);
|
||||||
|
$tasks = $this->loadTasks($viewer);
|
||||||
|
$t1 = $tasks[1];
|
||||||
|
$t2 = $tasks[2];
|
||||||
|
$t3 = $tasks[3];
|
||||||
|
$this->assertEqual(array(2, 1, 3), array_keys($tasks));
|
||||||
|
|
||||||
|
|
||||||
|
// Repeat the move above, there should be no overall change in order.
|
||||||
|
$this->moveTask($viewer, $t3, $t1, true);
|
||||||
|
$tasks = $this->loadTasks($viewer);
|
||||||
|
$t1 = $tasks[1];
|
||||||
|
$t2 = $tasks[2];
|
||||||
|
$t3 = $tasks[3];
|
||||||
|
$this->assertEqual(array(2, 1, 3), array_keys($tasks));
|
||||||
|
|
||||||
|
|
||||||
|
// Move T3 to the first slot in the priority.
|
||||||
|
$this->movePriority($viewer, $t3, $t3->getPriority(), false);
|
||||||
|
$tasks = $this->loadTasks($viewer);
|
||||||
|
$t1 = $tasks[1];
|
||||||
|
$t2 = $tasks[2];
|
||||||
|
$t3 = $tasks[3];
|
||||||
|
$this->assertEqual(array(3, 2, 1), array_keys($tasks));
|
||||||
|
|
||||||
|
|
||||||
|
// Move T3 to the last slot in the priority.
|
||||||
|
$this->movePriority($viewer, $t3, $t3->getPriority(), true);
|
||||||
|
$tasks = $this->loadTasks($viewer);
|
||||||
|
$t1 = $tasks[1];
|
||||||
|
$t2 = $tasks[2];
|
||||||
|
$t3 = $tasks[3];
|
||||||
|
$this->assertEqual(array(2, 1, 3), array_keys($tasks));
|
||||||
|
|
||||||
|
|
||||||
|
// Move T3 before T2.
|
||||||
|
$this->moveTask($viewer, $t3, $t2, false);
|
||||||
|
$tasks = $this->loadTasks($viewer);
|
||||||
|
$t1 = $tasks[1];
|
||||||
|
$t2 = $tasks[2];
|
||||||
|
$t3 = $tasks[3];
|
||||||
|
$this->assertEqual(array(3, 2, 1), array_keys($tasks));
|
||||||
|
|
||||||
|
|
||||||
|
// Move T3 before T1.
|
||||||
|
$this->moveTask($viewer, $t3, $t1, false);
|
||||||
|
$tasks = $this->loadTasks($viewer);
|
||||||
|
$t1 = $tasks[1];
|
||||||
|
$t2 = $tasks[2];
|
||||||
|
$t3 = $tasks[3];
|
||||||
|
$this->assertEqual(array(2, 3, 1), array_keys($tasks));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private function newTask(PhabricatorUser $viewer, $title) {
|
||||||
|
$task = ManiphestTask::initializeNewTask($viewer);
|
||||||
|
|
||||||
|
$xactions = array();
|
||||||
|
|
||||||
|
$xactions[] = id(new ManiphestTransaction())
|
||||||
|
->setTransactionType(ManiphestTransaction::TYPE_TITLE)
|
||||||
|
->setNewValue($title);
|
||||||
|
|
||||||
|
|
||||||
|
$this->applyTaskTransactions($viewer, $task, $xactions);
|
||||||
|
|
||||||
|
return $task;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function loadTasks(PhabricatorUser $viewer) {
|
||||||
|
$tasks = id(new ManiphestTaskQuery())
|
||||||
|
->setViewer($viewer)
|
||||||
|
->setOrderBy(ManiphestTaskQuery::ORDER_PRIORITY)
|
||||||
|
->execute();
|
||||||
|
|
||||||
|
$tasks = mpull($tasks, null, 'getID');
|
||||||
|
|
||||||
|
return $tasks;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function moveTask(PhabricatorUser $viewer, $src, $dst, $is_after) {
|
||||||
|
list($pri, $sub) = ManiphestTransactionEditor::getAdjacentSubpriority(
|
||||||
|
$dst,
|
||||||
|
$is_after);
|
||||||
|
|
||||||
|
$xactions = array();
|
||||||
|
|
||||||
|
$xactions[] = id(new ManiphestTransaction())
|
||||||
|
->setTransactionType(ManiphestTransaction::TYPE_PRIORITY)
|
||||||
|
->setNewValue($pri);
|
||||||
|
|
||||||
|
$xactions[] = id(new ManiphestTransaction())
|
||||||
|
->setTransactionType(ManiphestTransaction::TYPE_SUBPRIORITY)
|
||||||
|
->setNewValue($sub);
|
||||||
|
|
||||||
|
return $this->applyTaskTransactions($viewer, $src, $xactions);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function movePriority(
|
||||||
|
PhabricatorUser $viewer,
|
||||||
|
$src,
|
||||||
|
$target_priority,
|
||||||
|
$is_end) {
|
||||||
|
|
||||||
|
list($pri, $sub) = ManiphestTransactionEditor::getEdgeSubpriority(
|
||||||
|
$target_priority,
|
||||||
|
$is_end);
|
||||||
|
|
||||||
|
$xactions = array();
|
||||||
|
|
||||||
|
$xactions[] = id(new ManiphestTransaction())
|
||||||
|
->setTransactionType(ManiphestTransaction::TYPE_PRIORITY)
|
||||||
|
->setNewValue($pri);
|
||||||
|
|
||||||
|
$xactions[] = id(new ManiphestTransaction())
|
||||||
|
->setTransactionType(ManiphestTransaction::TYPE_SUBPRIORITY)
|
||||||
|
->setNewValue($sub);
|
||||||
|
|
||||||
|
return $this->applyTaskTransactions($viewer, $src, $xactions);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function applyTaskTransactions(
|
||||||
|
PhabricatorUser $viewer,
|
||||||
|
ManiphestTask $task,
|
||||||
|
array $xactions) {
|
||||||
|
|
||||||
|
$content_source = PhabricatorContentSource::newConsoleSource();
|
||||||
|
|
||||||
|
$editor = id(new ManiphestTransactionEditor())
|
||||||
|
->setActor($viewer)
|
||||||
|
->setContentSource($content_source)
|
||||||
|
->setContinueOnNoEffect(true)
|
||||||
|
->applyTransactions($task, $xactions);
|
||||||
|
|
||||||
|
return $task;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -32,21 +32,25 @@ final class ManiphestSubpriorityController extends ManiphestController {
|
||||||
if (!$after_task) {
|
if (!$after_task) {
|
||||||
return new Aphront404Response();
|
return new Aphront404Response();
|
||||||
}
|
}
|
||||||
$after_pri = $after_task->getPriority();
|
list($pri, $sub) = ManiphestTransactionEditor::getAdjacentSubpriority(
|
||||||
$after_sub = $after_task->getSubpriority();
|
$after_task,
|
||||||
|
$is_after = true);
|
||||||
} else {
|
} else {
|
||||||
$after_pri = $request->getInt('priority');
|
list($pri, $sub) = ManiphestTransactionEditor::getEdgeSubpriority(
|
||||||
$after_sub = null;
|
$request->getInt('priority'),
|
||||||
|
$is_end = false);
|
||||||
}
|
}
|
||||||
|
|
||||||
$xactions = array(id(new ManiphestTransaction())
|
$xactions = array();
|
||||||
|
|
||||||
|
$xactions[] = id(new ManiphestTransaction())
|
||||||
|
->setTransactionType(ManiphestTransaction::TYPE_PRIORITY)
|
||||||
|
->setNewValue($pri);
|
||||||
|
|
||||||
|
$xactions[] = id(new ManiphestTransaction())
|
||||||
->setTransactionType(ManiphestTransaction::TYPE_SUBPRIORITY)
|
->setTransactionType(ManiphestTransaction::TYPE_SUBPRIORITY)
|
||||||
->setNewValue(array(
|
->setNewValue($sub);
|
||||||
'newPriority' => $after_pri,
|
|
||||||
'newSubpriorityBase' => $after_sub,
|
|
||||||
'direction' => '>',
|
|
||||||
)),
|
|
||||||
);
|
|
||||||
$editor = id(new ManiphestTransactionEditor())
|
$editor = id(new ManiphestTransactionEditor())
|
||||||
->setActor($user)
|
->setActor($user)
|
||||||
->setContinueOnMissingFields(true)
|
->setContinueOnMissingFields(true)
|
||||||
|
|
|
@ -147,12 +147,7 @@ final class ManiphestTransactionEditor
|
||||||
|
|
||||||
return $object->setOwnerPHID($phid);
|
return $object->setOwnerPHID($phid);
|
||||||
case ManiphestTransaction::TYPE_SUBPRIORITY:
|
case ManiphestTransaction::TYPE_SUBPRIORITY:
|
||||||
$data = $xaction->getNewValue();
|
$object->setSubpriority($xaction->getNewValue());
|
||||||
$new_sub = $this->getNextSubpriority(
|
|
||||||
$data['newPriority'],
|
|
||||||
$data['newSubpriorityBase'],
|
|
||||||
$data['direction']);
|
|
||||||
$object->setSubpriority($new_sub);
|
|
||||||
return;
|
return;
|
||||||
case ManiphestTransaction::TYPE_PROJECT_COLUMN:
|
case ManiphestTransaction::TYPE_PROJECT_COLUMN:
|
||||||
// these do external (edge) updates
|
// these do external (edge) updates
|
||||||
|
@ -165,28 +160,6 @@ final class ManiphestTransactionEditor
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function expandTransaction(
|
|
||||||
PhabricatorLiskDAO $object,
|
|
||||||
PhabricatorApplicationTransaction $xaction) {
|
|
||||||
|
|
||||||
$xactions = parent::expandTransaction($object, $xaction);
|
|
||||||
switch ($xaction->getTransactionType()) {
|
|
||||||
case ManiphestTransaction::TYPE_SUBPRIORITY:
|
|
||||||
$data = $xaction->getNewValue();
|
|
||||||
$new_pri = $data['newPriority'];
|
|
||||||
if ($new_pri != $object->getPriority()) {
|
|
||||||
$xactions[] = id(new ManiphestTransaction())
|
|
||||||
->setTransactionType(ManiphestTransaction::TYPE_PRIORITY)
|
|
||||||
->setNewValue($new_pri);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $xactions;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function applyCustomExternalTransaction(
|
protected function applyCustomExternalTransaction(
|
||||||
PhabricatorLiskDAO $object,
|
PhabricatorLiskDAO $object,
|
||||||
PhabricatorApplicationTransaction $xaction) {
|
PhabricatorApplicationTransaction $xaction) {
|
||||||
|
@ -643,54 +616,99 @@ final class ManiphestTransactionEditor
|
||||||
return $copy;
|
return $copy;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function getNextSubpriority($pri, $sub, $dir = '>') {
|
/**
|
||||||
switch ($dir) {
|
* Get priorities for moving a task to a new priority.
|
||||||
case '>':
|
*/
|
||||||
$order = 'ASC';
|
public static function getEdgeSubpriority(
|
||||||
break;
|
$priority,
|
||||||
case '<':
|
$is_end) {
|
||||||
$order = 'DESC';
|
|
||||||
break;
|
$query = id(new ManiphestTaskQuery())
|
||||||
default:
|
->setViewer(PhabricatorUser::getOmnipotentUser())
|
||||||
throw new Exception('$dir must be ">" or "<".');
|
->setOrderBy(ManiphestTaskQuery::ORDER_PRIORITY)
|
||||||
break;
|
->withPriorities(array($priority))
|
||||||
|
->setLimit(1);
|
||||||
|
|
||||||
|
if ($is_end) {
|
||||||
|
$query->setReversePaging(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($sub === null) {
|
$result = $query->executeOne();
|
||||||
$base = 0;
|
$step = (double)(2 << 32);
|
||||||
|
|
||||||
|
if ($result) {
|
||||||
|
$base = $result->getSubpriority();
|
||||||
|
if ($is_end) {
|
||||||
|
$sub = ($base - $step);
|
||||||
} else {
|
} else {
|
||||||
$base = $sub;
|
$sub = ($base + $step);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$sub = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($sub === null) {
|
return array($priority, $sub);
|
||||||
$next = id(new ManiphestTask())->loadOneWhere(
|
|
||||||
'priority = %d ORDER BY subpriority %Q LIMIT 1',
|
|
||||||
$pri,
|
|
||||||
$order);
|
|
||||||
if ($next) {
|
|
||||||
if ($dir == '>') {
|
|
||||||
return $next->getSubpriority() - ((double)(2 << 16));
|
|
||||||
} else {
|
|
||||||
return $next->getSubpriority() + ((double)(2 << 16));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get priorities for moving a task before or after another task.
|
||||||
|
*/
|
||||||
|
public static function getAdjacentSubpriority(
|
||||||
|
ManiphestTask $dst,
|
||||||
|
$is_after) {
|
||||||
|
|
||||||
|
$query = id(new ManiphestTaskQuery())
|
||||||
|
->setViewer(PhabricatorUser::getOmnipotentUser())
|
||||||
|
->setOrderBy(ManiphestTaskQuery::ORDER_PRIORITY)
|
||||||
|
->withPriorities(array($dst->getPriority()))
|
||||||
|
->setLimit(1);
|
||||||
|
|
||||||
|
if ($is_after) {
|
||||||
|
$query->setAfterID($dst->getID());
|
||||||
} else {
|
} else {
|
||||||
$next = id(new ManiphestTask())->loadOneWhere(
|
$query->setBeforeID($dst->getID());
|
||||||
'priority = %d AND subpriority %Q %f ORDER BY subpriority %Q LIMIT 1',
|
}
|
||||||
$pri,
|
|
||||||
$dir,
|
$adjacent = $query->executeOne();
|
||||||
$sub,
|
|
||||||
$order);
|
$base = $dst->getSubpriority();
|
||||||
if ($next) {
|
$step = (double)(2 << 32);
|
||||||
return ($sub + $next->getSubpriority()) / 2;
|
|
||||||
|
// If we find an adjacent task, we average the two subpriorities and
|
||||||
|
// return the result.
|
||||||
|
if ($adjacent) {
|
||||||
|
// If the adjacent task has a subpriority that is identical or very
|
||||||
|
// close to the task we're looking at, we're going to move it a little
|
||||||
|
// farther down the subpriority list.
|
||||||
|
if (abs($adjacent->getSubpriority() - $base) < 0.01) {
|
||||||
|
list($shift_pri, $shift_sub) = self::getAdjacentSubpriority(
|
||||||
|
$adjacent,
|
||||||
|
$is_after);
|
||||||
|
|
||||||
|
queryfx(
|
||||||
|
$adjacent->establishConnection('r'),
|
||||||
|
'UPDATE %T SET subpriority = %f WHERE id = %d',
|
||||||
|
$adjacent->getTableName(),
|
||||||
|
$shift_sub,
|
||||||
|
$adjacent->getID());
|
||||||
|
|
||||||
|
$adjacent->setSubpriority($shift_sub);
|
||||||
|
}
|
||||||
|
|
||||||
|
$sub = ($adjacent->getSubpriority() + $base) / 2;
|
||||||
|
} else {
|
||||||
|
// Otherwise, we take a step away from the target's subpriority and
|
||||||
|
// use that.
|
||||||
|
if ($is_after) {
|
||||||
|
$sub = ($base - $step);
|
||||||
|
} else {
|
||||||
|
$sub = ($base + $step);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($dir == '>') {
|
return array($dst->getPriority(), $sub);
|
||||||
return $base + (double)(2 << 32);
|
|
||||||
} else {
|
|
||||||
return $base - (double)(2 << 32);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,6 +21,7 @@ final class ManiphestTaskQuery extends PhabricatorCursorPagedPolicyAwareQuery {
|
||||||
private $dateCreatedBefore;
|
private $dateCreatedBefore;
|
||||||
private $dateModifiedAfter;
|
private $dateModifiedAfter;
|
||||||
private $dateModifiedBefore;
|
private $dateModifiedBefore;
|
||||||
|
private $reversePaging;
|
||||||
|
|
||||||
private $fullTextSearch = '';
|
private $fullTextSearch = '';
|
||||||
|
|
||||||
|
@ -765,13 +766,13 @@ final class ManiphestTaskQuery extends PhabricatorCursorPagedPolicyAwareQuery {
|
||||||
foreach ($app_order as $order_by) {
|
foreach ($app_order as $order_by) {
|
||||||
$order[] = $order_by;
|
$order[] = $order_by;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if ($reverse) {
|
if ($reverse) {
|
||||||
$order[] = 'task.id ASC';
|
$order[] = 'task.id ASC';
|
||||||
} else {
|
} else {
|
||||||
$order[] = 'task.id DESC';
|
$order[] = 'task.id DESC';
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return 'ORDER BY '.implode(', ', $order);
|
return 'ORDER BY '.implode(', ', $order);
|
||||||
}
|
}
|
||||||
|
@ -1068,7 +1069,6 @@ final class ManiphestTaskQuery extends PhabricatorCursorPagedPolicyAwareQuery {
|
||||||
'name' => 'task.subpriority',
|
'name' => 'task.subpriority',
|
||||||
'value' => $cursor->getSubpriority(),
|
'value' => $cursor->getSubpriority(),
|
||||||
'type' => 'float',
|
'type' => 'float',
|
||||||
'reverse' => true,
|
|
||||||
);
|
);
|
||||||
$columns[] = array(
|
$columns[] = array(
|
||||||
'name' => 'task.dateModified',
|
'name' => 'task.dateModified',
|
||||||
|
@ -1120,4 +1120,13 @@ final class ManiphestTaskQuery extends PhabricatorCursorPagedPolicyAwareQuery {
|
||||||
return 'PhabricatorManiphestApplication';
|
return 'PhabricatorManiphestApplication';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function setReversePaging($reverse_paging) {
|
||||||
|
$this->reversePaging = $reverse_paging;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getReversePaging() {
|
||||||
|
return $this->reversePaging;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -112,40 +112,31 @@ final class PhabricatorProjectMoveController
|
||||||
}
|
}
|
||||||
$tasks = mpull($tasks, null, 'getPHID');
|
$tasks = mpull($tasks, null, 'getPHID');
|
||||||
|
|
||||||
$a_task = idx($tasks, $after_phid);
|
$try = array(
|
||||||
$b_task = idx($tasks, $before_phid);
|
array($after_phid, true),
|
||||||
|
array($before_phid, false),
|
||||||
|
);
|
||||||
|
|
||||||
if ($a_task &&
|
$pri = null;
|
||||||
(($a_task->getPriority() < $object->getPriority()) ||
|
$sub = null;
|
||||||
($a_task->getPriority() == $object->getPriority() &&
|
foreach ($try as $spec) {
|
||||||
$a_task->getSubpriority() >= $object->getSubpriority()))) {
|
list($task_phid, $is_after) = $spec;
|
||||||
|
$task = idx($tasks, $task_phid);
|
||||||
$after_pri = $a_task->getPriority();
|
if ($task) {
|
||||||
$after_sub = $a_task->getSubpriority();
|
list($pri, $sub) = ManiphestTransactionEditor::getAdjacentSubpriority(
|
||||||
|
$task,
|
||||||
|
$is_after);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($pri !== null) {
|
||||||
|
$xactions[] = id(new ManiphestTransaction())
|
||||||
|
->setTransactionType(ManiphestTransaction::TYPE_PRIORITY)
|
||||||
|
->setNewValue($pri);
|
||||||
$xactions[] = id(new ManiphestTransaction())
|
$xactions[] = id(new ManiphestTransaction())
|
||||||
->setTransactionType(ManiphestTransaction::TYPE_SUBPRIORITY)
|
->setTransactionType(ManiphestTransaction::TYPE_SUBPRIORITY)
|
||||||
->setNewValue(array(
|
->setNewValue($sub);
|
||||||
'newPriority' => $after_pri,
|
|
||||||
'newSubpriorityBase' => $after_sub,
|
|
||||||
'direction' => '>',
|
|
||||||
));
|
|
||||||
|
|
||||||
} else if ($b_task &&
|
|
||||||
(($b_task->getPriority() > $object->getPriority()) ||
|
|
||||||
($b_task->getPriority() == $object->getPriority() &&
|
|
||||||
$b_task->getSubpriority() <= $object->getSubpriority()))) {
|
|
||||||
|
|
||||||
$before_pri = $b_task->getPriority();
|
|
||||||
$before_sub = $b_task->getSubpriority();
|
|
||||||
|
|
||||||
$xactions[] = id(new ManiphestTransaction())
|
|
||||||
->setTransactionType(ManiphestTransaction::TYPE_SUBPRIORITY)
|
|
||||||
->setNewValue(array(
|
|
||||||
'newPriority' => $before_pri,
|
|
||||||
'newSubpriorityBase' => $before_sub,
|
|
||||||
'direction' => '<',
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue