Page MenuHomePhabricator

D20248.id48318.diff
No OneTemporary

D20248.id48318.diff

diff --git a/resources/celerity/map.php b/resources/celerity/map.php
--- a/resources/celerity/map.php
+++ b/resources/celerity/map.php
@@ -409,13 +409,13 @@
'rsrc/js/application/phortune/phortune-credit-card-form.js' => 'd12d214f',
'rsrc/js/application/policy/behavior-policy-control.js' => '0eaa33a9',
'rsrc/js/application/policy/behavior-policy-rule-editor.js' => '9347f172',
- 'rsrc/js/application/projects/WorkboardBoard.js' => 'e4e2d107',
- 'rsrc/js/application/projects/WorkboardCard.js' => 'c23ddfde',
- 'rsrc/js/application/projects/WorkboardColumn.js' => 'fd9cb972',
+ 'rsrc/js/application/projects/WorkboardBoard.js' => 'a4f1e85d',
+ 'rsrc/js/application/projects/WorkboardCard.js' => '887ef74f',
+ 'rsrc/js/application/projects/WorkboardColumn.js' => 'ca444dca',
'rsrc/js/application/projects/WorkboardController.js' => '42c7a5a7',
- 'rsrc/js/application/projects/WorkboardHeader.js' => '354c5c0e',
- 'rsrc/js/application/projects/WorkboardHeaderTemplate.js' => '9b86cd0d',
- 'rsrc/js/application/projects/behavior-project-boards.js' => 'a3f6b67f',
+ 'rsrc/js/application/projects/WorkboardHeader.js' => '6e75daea',
+ 'rsrc/js/application/projects/WorkboardHeaderTemplate.js' => '2d641f7d',
+ 'rsrc/js/application/projects/behavior-project-boards.js' => 'e2730b90',
'rsrc/js/application/projects/behavior-project-create.js' => '34c53422',
'rsrc/js/application/projects/behavior-reorder-columns.js' => '8ac32fd9',
'rsrc/js/application/releeph/releeph-preview-branch.js' => '75184d68',
@@ -657,7 +657,7 @@
'javelin-behavior-phuix-example' => 'c2c500a7',
'javelin-behavior-policy-control' => '0eaa33a9',
'javelin-behavior-policy-rule-editor' => '9347f172',
- 'javelin-behavior-project-boards' => 'a3f6b67f',
+ 'javelin-behavior-project-boards' => 'e2730b90',
'javelin-behavior-project-create' => '34c53422',
'javelin-behavior-quicksand-blacklist' => '5a6f6a06',
'javelin-behavior-read-only-warning' => 'b9109f8f',
@@ -729,12 +729,12 @@
'javelin-view-renderer' => '9aae2b66',
'javelin-view-visitor' => '308f9fe4',
'javelin-websocket' => 'fdc13e4e',
- 'javelin-workboard-board' => 'e4e2d107',
- 'javelin-workboard-card' => 'c23ddfde',
- 'javelin-workboard-column' => 'fd9cb972',
+ 'javelin-workboard-board' => 'a4f1e85d',
+ 'javelin-workboard-card' => '887ef74f',
+ 'javelin-workboard-column' => 'ca444dca',
'javelin-workboard-controller' => '42c7a5a7',
- 'javelin-workboard-header' => '354c5c0e',
- 'javelin-workboard-header-template' => '9b86cd0d',
+ 'javelin-workboard-header' => '6e75daea',
+ 'javelin-workboard-header-template' => '2d641f7d',
'javelin-workflow' => '958e9045',
'maniphest-report-css' => '3d53188b',
'maniphest-task-edit-css' => '272daa84',
@@ -1125,6 +1125,9 @@
'javelin-dom',
'phabricator-keyboard-shortcut',
),
+ '2d641f7d' => array(
+ 'javelin-install',
+ ),
'2e255291' => array(
'javelin-install',
'javelin-util',
@@ -1163,9 +1166,6 @@
'javelin-stratcom',
'javelin-workflow',
),
- '354c5c0e' => array(
- 'javelin-install',
- ),
'37b8a04a' => array(
'javelin-install',
'javelin-util',
@@ -1452,6 +1452,9 @@
'javelin-install',
'javelin-util',
),
+ '6e75daea' => array(
+ 'javelin-install',
+ ),
70245195 => array(
'javelin-behavior',
'javelin-stratcom',
@@ -1560,6 +1563,9 @@
'javelin-install',
'javelin-dom',
),
+ '887ef74f' => array(
+ 'javelin-install',
+ ),
'89a1ae3a' => array(
'javelin-dom',
'javelin-util',
@@ -1695,9 +1701,6 @@
'javelin-dom',
'javelin-stratcom',
),
- '9b86cd0d' => array(
- 'javelin-install',
- ),
'9cec214e' => array(
'javelin-behavior',
'javelin-stratcom',
@@ -1725,15 +1728,6 @@
'a241536a' => array(
'javelin-install',
),
- 'a3f6b67f' => array(
- 'javelin-behavior',
- 'javelin-dom',
- 'javelin-util',
- 'javelin-vector',
- 'javelin-stratcom',
- 'javelin-workflow',
- 'javelin-workboard-controller',
- ),
'a4356cde' => array(
'javelin-install',
'javelin-dom',
@@ -1759,6 +1753,16 @@
'javelin-request',
'javelin-util',
),
+ 'a4f1e85d' => array(
+ 'javelin-install',
+ 'javelin-dom',
+ 'javelin-util',
+ 'javelin-stratcom',
+ 'javelin-workflow',
+ 'phabricator-draggable-list',
+ 'javelin-workboard-column',
+ 'javelin-workboard-header-template',
+ ),
'a5257c4e' => array(
'javelin-install',
'javelin-dom',
@@ -1903,9 +1907,6 @@
'javelin-stratcom',
'javelin-uri',
),
- 'c23ddfde' => array(
- 'javelin-install',
- ),
'c2c500a7' => array(
'javelin-install',
'javelin-dom',
@@ -1956,6 +1957,11 @@
'javelin-util',
'phabricator-keyboard-shortcut-manager',
),
+ 'ca444dca' => array(
+ 'javelin-install',
+ 'javelin-workboard-card',
+ 'javelin-workboard-header',
+ ),
'cf32921f' => array(
'javelin-behavior',
'javelin-dom',
@@ -2025,15 +2031,14 @@
'javelin-dom',
'javelin-history',
),
- 'e4e2d107' => array(
- 'javelin-install',
+ 'e2730b90' => array(
+ 'javelin-behavior',
'javelin-dom',
'javelin-util',
+ 'javelin-vector',
'javelin-stratcom',
'javelin-workflow',
- 'phabricator-draggable-list',
- 'javelin-workboard-column',
- 'javelin-workboard-header-template',
+ 'javelin-workboard-controller',
),
'e562708c' => array(
'javelin-install',
@@ -2136,11 +2141,6 @@
'javelin-magical-init',
'javelin-util',
),
- 'fd9cb972' => array(
- 'javelin-install',
- 'javelin-workboard-card',
- 'javelin-workboard-header',
- ),
'fdc13e4e' => array(
'javelin-install',
),
diff --git a/src/applications/maniphest/storage/ManiphestTask.php b/src/applications/maniphest/storage/ManiphestTask.php
--- a/src/applications/maniphest/storage/ManiphestTask.php
+++ b/src/applications/maniphest/storage/ManiphestTask.php
@@ -251,6 +251,7 @@
return array(
PhabricatorProjectColumn::ORDER_PRIORITY => array(
(int)-$this->getPriority(),
+ PhabricatorProjectColumn::NODETYPE_CARD,
(double)-$this->getSubpriority(),
(int)-$this->getID(),
),
diff --git a/src/applications/project/controller/PhabricatorProjectBoardViewController.php b/src/applications/project/controller/PhabricatorProjectBoardViewController.php
--- a/src/applications/project/controller/PhabricatorProjectBoardViewController.php
+++ b/src/applications/project/controller/PhabricatorProjectBoardViewController.php
@@ -651,11 +651,15 @@
));
$headers[] = array(
- 'order' => 'priority',
+ 'order' => PhabricatorProjectColumn::ORDER_PRIORITY,
'key' => $header_key,
'template' => hsprintf('%s', $template),
'vector' => array(
(int)-$priority,
+ PhabricatorProjectColumn::NODETYPE_HEADER,
+ ),
+ 'editProperties' => array(
+ PhabricatorProjectColumn::ORDER_PRIORITY => (int)$priority,
),
);
}
diff --git a/src/applications/project/controller/PhabricatorProjectMoveController.php b/src/applications/project/controller/PhabricatorProjectMoveController.php
--- a/src/applications/project/controller/PhabricatorProjectMoveController.php
+++ b/src/applications/project/controller/PhabricatorProjectMoveController.php
@@ -15,6 +15,14 @@
$before_phid = $request->getStr('beforePHID');
$order = $request->getStr('order', PhabricatorProjectColumn::DEFAULT_ORDER);
+ $edit_header = null;
+ $raw_header = $request->getStr('header');
+ if (strlen($raw_header)) {
+ $edit_header = phutil_json_decode($raw_header);
+ } else {
+ $edit_header = array();
+ }
+
$project = id(new PhabricatorProjectQuery())
->setViewer($viewer)
->requireCapabilities(
@@ -87,10 +95,14 @@
));
if ($order == PhabricatorProjectColumn::ORDER_PRIORITY) {
+ $header_priority = idx(
+ $edit_header,
+ PhabricatorProjectColumn::ORDER_PRIORITY);
$priority_xactions = $this->getPriorityTransactions(
$object,
$after_phid,
- $before_phid);
+ $before_phid,
+ $header_priority);
foreach ($priority_xactions as $xaction) {
$xactions[] = $xaction;
}
@@ -110,13 +122,33 @@
private function getPriorityTransactions(
ManiphestTask $task,
$after_phid,
- $before_phid) {
+ $before_phid,
+ $header_priority) {
+
+ $xactions = array();
+ $must_move = false;
+
+ if ($header_priority !== null) {
+ if ($task->getPriority() !== $header_priority) {
+ $task = id(clone $task)
+ ->setPriority($header_priority);
+
+ $keyword_map = ManiphestTaskPriority::getTaskPriorityKeywordsMap();
+ $keyword = head(idx($keyword_map, $header_priority));
+
+ $xactions[] = id(new ManiphestTransaction())
+ ->setTransactionType(
+ ManiphestTaskPriorityTransaction::TRANSACTIONTYPE)
+ ->setNewValue($keyword);
+
+ $must_move = true;
+ }
+ }
list($after_task, $before_task) = $this->loadPriorityTasks(
$after_phid,
$before_phid);
- $must_move = false;
if ($after_task && !$task->isLowerPriorityThan($after_task)) {
$must_move = true;
}
@@ -125,10 +157,10 @@
$must_move = true;
}
- // The move doesn't require a priority change to be valid, so don't
- // change the priority since we are not being forced to.
+ // The move doesn't require a subpriority change to be valid, so don't
+ // change the subpriority since we are not being forced to.
if (!$must_move) {
- return array();
+ return $xactions;
}
$try = array(
@@ -139,28 +171,41 @@
$pri = null;
$sub = null;
foreach ($try as $spec) {
- list($task, $is_after) = $spec;
+ list($nearby_task, $is_after) = $spec;
- if (!$task) {
+ if (!$nearby_task) {
continue;
}
list($pri, $sub) = ManiphestTransactionEditor::getAdjacentSubpriority(
- $task,
+ $nearby_task,
$is_after);
+ // If we drag under a "Low" header between a "Normal" task and a "Low"
+ // task, we don't want to accept a subpriority assignment which changes
+ // our priority to "Normal". Only accept a subpriority that keeps us in
+ // the right primary priority.
+ if ($header_priority !== null) {
+ if ($pri !== $header_priority) {
+ continue;
+ }
+ }
+
// If we find a priority on the first try, don't keep going.
break;
}
- $keyword_map = ManiphestTaskPriority::getTaskPriorityKeywordsMap();
- $keyword = head(idx($keyword_map, $pri));
-
- $xactions = array();
if ($pri !== null) {
- $xactions[] = id(new ManiphestTransaction())
- ->setTransactionType(ManiphestTaskPriorityTransaction::TRANSACTIONTYPE)
- ->setNewValue($keyword);
+ if ($header_priority === null) {
+ $keyword_map = ManiphestTaskPriority::getTaskPriorityKeywordsMap();
+ $keyword = head(idx($keyword_map, $pri));
+
+ $xactions[] = id(new ManiphestTransaction())
+ ->setTransactionType(
+ ManiphestTaskPriorityTransaction::TRANSACTIONTYPE)
+ ->setNewValue($keyword);
+ }
+
$xactions[] = id(new ManiphestTransaction())
->setTransactionType(
ManiphestTaskSubpriorityTransaction::TRANSACTIONTYPE)
diff --git a/src/applications/project/storage/PhabricatorProjectColumn.php b/src/applications/project/storage/PhabricatorProjectColumn.php
--- a/src/applications/project/storage/PhabricatorProjectColumn.php
+++ b/src/applications/project/storage/PhabricatorProjectColumn.php
@@ -16,6 +16,9 @@
const ORDER_NATURAL = 'natural';
const ORDER_PRIORITY = 'priority';
+ const NODETYPE_HEADER = 0;
+ const NODETYPE_CARD = 1;
+
protected $name;
protected $status;
protected $projectPHID;
diff --git a/webroot/rsrc/js/application/projects/WorkboardBoard.js b/webroot/rsrc/js/application/projects/WorkboardBoard.js
--- a/webroot/rsrc/js/application/projects/WorkboardBoard.js
+++ b/webroot/rsrc/js/application/projects/WorkboardBoard.js
@@ -161,11 +161,15 @@
var list = new JX.DraggableList('project-card', column.getRoot())
.setOuterContainer(this.getRoot())
- .setFindItemsHandler(JX.bind(column, column.getCardNodes))
+ .setFindItemsHandler(JX.bind(column, column.getDropTargetNodes))
.setCanDragX(true)
.setHasInfiniteHeight(true)
.setIsDropTargetHandler(JX.bind(column, column.setIsDropTarget));
+ var default_handler = list.getGhostHandler();
+ list.setGhostHandler(
+ JX.bind(column, column.handleDragGhost, default_handler));
+
if (this.getOrder() !== 'natural') {
list.setCompareHandler(JX.bind(column, column.compareHandler));
}
@@ -198,16 +202,39 @@
order: this.getOrder()
};
- if (after_node) {
- data.afterPHID = JX.Stratcom.getData(after_node).objectPHID;
+ var after_data;
+ var after_card = after_node;
+ while (after_card) {
+ after_data = JX.Stratcom.getData(after_card);
+ if (after_data.objectPHID) {
+ break;
+ }
+ after_card = after_card.previousSibling;
+ }
+
+ if (after_data) {
+ data.afterPHID = after_data.objectPHID;
}
- var before_node = item.nextSibling;
- if (before_node) {
- var before_phid = JX.Stratcom.getData(before_node).objectPHID;
- if (before_phid) {
- data.beforePHID = before_phid;
+ var before_data;
+ var before_card = item.nextSibling;
+ while (before_card) {
+ before_data = JX.Stratcom.getData(before_card);
+ if (before_data.objectPHID) {
+ break;
}
+ before_card = before_card.nextSibling;
+ }
+
+ if (before_data) {
+ data.beforePHID = before_data.objectPHID;
+ }
+
+ var header_key = JX.Stratcom.getData(after_node).headerKey;
+ if (header_key) {
+ var properties = this.getHeaderTemplate(header_key)
+ .getEditProperties();
+ data.header = JX.JSON.stringify(properties);
}
var visible_phids = [];
diff --git a/webroot/rsrc/js/application/projects/WorkboardCard.js b/webroot/rsrc/js/application/projects/WorkboardCard.js
--- a/webroot/rsrc/js/application/projects/WorkboardCard.js
+++ b/webroot/rsrc/js/application/projects/WorkboardCard.js
@@ -55,6 +55,10 @@
return this._root;
},
+ isWorkboardHeader: function() {
+ return false;
+ },
+
redraw: function() {
var old_node = this._root;
this._root = null;
diff --git a/webroot/rsrc/js/application/projects/WorkboardColumn.js b/webroot/rsrc/js/application/projects/WorkboardColumn.js
--- a/webroot/rsrc/js/application/projects/WorkboardColumn.js
+++ b/webroot/rsrc/js/application/projects/WorkboardColumn.js
@@ -52,6 +52,10 @@
return this._cards;
},
+ _getObjects: function() {
+ return this._objects;
+ },
+
getCard: function(phid) {
return this._cards[phid];
},
@@ -126,12 +130,13 @@
return this;
},
- getCardNodes: function() {
- var cards = this.getCards();
+ getDropTargetNodes: function() {
+ var objects = this._getObjects();
var nodes = [];
- for (var k in cards) {
- nodes.push(cards[k].getNode());
+ for (var ii = 0; ii < objects.length; ii++) {
+ var object = objects[ii];
+ nodes.push(object.getNode());
}
return nodes;
@@ -160,6 +165,32 @@
return this._headers[key];
},
+ handleDragGhost: function(default_handler, ghost, node) {
+ // If the column has headers, don't let the user drag a card above
+ // the topmost header: for example, you can't change a task to have
+ // a priority higher than the highest possible priority.
+
+ if (this._hasColumnHeaders()) {
+ if (!node) {
+ return false;
+ }
+ }
+
+ return default_handler(ghost, node);
+ },
+
+ _hasColumnHeaders: function() {
+ var board = this.getBoard();
+ var order = board.getOrder();
+
+ switch (order) {
+ case 'natural':
+ return false;
+ }
+
+ return true;
+ },
+
_getCardHeaderKey: function(card, order) {
switch (order) {
case 'priority':
@@ -174,18 +205,16 @@
var order = board.getOrder();
var list;
- var has_headers;
if (order == 'natural') {
list = this._getCardsSortedNaturally();
- has_headers = false;
} else {
list = this._getCardsSortedByKey(order);
- has_headers = true;
}
var ii;
var objects = [];
+ var has_headers = this._hasColumnHeaders();
var header_keys = [];
var seen_headers = {};
if (has_headers) {
@@ -245,15 +274,23 @@
var board = this.getBoard();
var order = board.getOrder();
- var src_phid = JX.Stratcom.getData(src_node).objectPHID;
- var dst_phid = JX.Stratcom.getData(dst_node).objectPHID;
-
- var u_vec = board.getOrderVector(src_phid, order);
- var v_vec = board.getOrderVector(dst_phid, order);
+ var u_vec = this._getNodeOrderVector(src_node, order);
+ var v_vec = this._getNodeOrderVector(dst_node, order);
return board.compareVectors(u_vec, v_vec);
},
+ _getNodeOrderVector: function(node, order) {
+ var board = this.getBoard();
+ var data = JX.Stratcom.getData(node);
+
+ if (data.objectPHID) {
+ return board.getOrderVector(data.objectPHID, order);
+ }
+
+ return board.getHeaderTemplate(data.headerKey).getVector();
+ },
+
setIsDropTarget: function(is_target) {
var node = this.getWorkpanelNode();
JX.DOM.alterClass(node, 'workboard-column-drop-target', is_target);
diff --git a/webroot/rsrc/js/application/projects/WorkboardHeader.js b/webroot/rsrc/js/application/projects/WorkboardHeader.js
--- a/webroot/rsrc/js/application/projects/WorkboardHeader.js
+++ b/webroot/rsrc/js/application/projects/WorkboardHeader.js
@@ -30,8 +30,14 @@
var board = this.getColumn().getBoard();
var template = board.getHeaderTemplate(header_key).getTemplate();
this._root = JX.$H(template).getFragment().firstChild;
+
+ JX.Stratcom.getData(this._root).headerKey = header_key;
}
return this._root;
+ },
+
+ isWorkboardHeader: function() {
+ return true;
}
}
diff --git a/webroot/rsrc/js/application/projects/WorkboardHeaderTemplate.js b/webroot/rsrc/js/application/projects/WorkboardHeaderTemplate.js
--- a/webroot/rsrc/js/application/projects/WorkboardHeaderTemplate.js
+++ b/webroot/rsrc/js/application/projects/WorkboardHeaderTemplate.js
@@ -13,7 +13,8 @@
properties: {
template: null,
order: null,
- vector: null
+ vector: null,
+ editProperties: null
},
members: {
diff --git a/webroot/rsrc/js/application/projects/behavior-project-boards.js b/webroot/rsrc/js/application/projects/behavior-project-boards.js
--- a/webroot/rsrc/js/application/projects/behavior-project-boards.js
+++ b/webroot/rsrc/js/application/projects/behavior-project-boards.js
@@ -112,7 +112,8 @@
board.getHeaderTemplate(header.key)
.setOrder(header.order)
.setTemplate(header.template)
- .setVector(header.vector);
+ .setVector(header.vector)
+ .setEditProperties(header.editProperties);
}
board.start();

File Metadata

Mime Type
text/plain
Expires
Thu, Nov 28, 6:22 PM (20 h, 39 m)
Storage Engine
blob
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
6801715
Default Alt Text
D20248.id48318.diff (19 KB)

Event Timeline