Page MenuHomePhabricator

D20269.diff
No OneTemporary

D20269.diff

diff --git a/resources/celerity/map.php b/resources/celerity/map.php
--- a/resources/celerity/map.php
+++ b/resources/celerity/map.php
@@ -408,14 +408,14 @@
'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' => '2181739b',
- 'rsrc/js/application/projects/WorkboardCard.js' => 'bc92741f',
- 'rsrc/js/application/projects/WorkboardCardTemplate.js' => 'b0b5f90a',
- 'rsrc/js/application/projects/WorkboardColumn.js' => '6461f58b',
+ 'rsrc/js/application/projects/WorkboardBoard.js' => 'fc1664ff',
+ 'rsrc/js/application/projects/WorkboardCard.js' => '0392a5d8',
+ 'rsrc/js/application/projects/WorkboardCardTemplate.js' => '2a61f8d4',
+ 'rsrc/js/application/projects/WorkboardColumn.js' => '533dd592',
'rsrc/js/application/projects/WorkboardController.js' => '42c7a5a7',
- 'rsrc/js/application/projects/WorkboardHeader.js' => '6e75daea',
- 'rsrc/js/application/projects/WorkboardHeaderTemplate.js' => '2d641f7d',
- 'rsrc/js/application/projects/behavior-project-boards.js' => 'cca3f5f8',
+ 'rsrc/js/application/projects/WorkboardHeader.js' => '111bfd2d',
+ 'rsrc/js/application/projects/WorkboardHeaderTemplate.js' => 'b65351bd',
+ 'rsrc/js/application/projects/behavior-project-boards.js' => '285c337a',
'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',
@@ -656,7 +656,7 @@
'javelin-behavior-phuix-example' => 'c2c500a7',
'javelin-behavior-policy-control' => '0eaa33a9',
'javelin-behavior-policy-rule-editor' => '9347f172',
- 'javelin-behavior-project-boards' => 'cca3f5f8',
+ 'javelin-behavior-project-boards' => '285c337a',
'javelin-behavior-project-create' => '34c53422',
'javelin-behavior-quicksand-blacklist' => '5a6f6a06',
'javelin-behavior-read-only-warning' => 'b9109f8f',
@@ -728,13 +728,13 @@
'javelin-view-renderer' => '9aae2b66',
'javelin-view-visitor' => '308f9fe4',
'javelin-websocket' => 'fdc13e4e',
- 'javelin-workboard-board' => '2181739b',
- 'javelin-workboard-card' => 'bc92741f',
- 'javelin-workboard-card-template' => 'b0b5f90a',
- 'javelin-workboard-column' => '6461f58b',
+ 'javelin-workboard-board' => 'fc1664ff',
+ 'javelin-workboard-card' => '0392a5d8',
+ 'javelin-workboard-card-template' => '2a61f8d4',
+ 'javelin-workboard-column' => '533dd592',
'javelin-workboard-controller' => '42c7a5a7',
- 'javelin-workboard-header' => '6e75daea',
- 'javelin-workboard-header-template' => '2d641f7d',
+ 'javelin-workboard-header' => '111bfd2d',
+ 'javelin-workboard-header-template' => 'b65351bd',
'javelin-workflow' => '958e9045',
'maniphest-report-css' => '3d53188b',
'maniphest-task-edit-css' => '272daa84',
@@ -909,6 +909,9 @@
'javelin-uri',
'javelin-util',
),
+ '0392a5d8' => array(
+ 'javelin-install',
+ ),
'04023d82' => array(
'javelin-install',
'phuix-button-view',
@@ -993,6 +996,9 @@
'javelin-workflow',
'phuix-icon-view',
),
+ '111bfd2d' => array(
+ 'javelin-install',
+ ),
'1325b731' => array(
'javelin-behavior',
'javelin-uri',
@@ -1048,17 +1054,6 @@
'javelin-behavior',
'javelin-request',
),
- '2181739b' => array(
- 'javelin-install',
- 'javelin-dom',
- 'javelin-util',
- 'javelin-stratcom',
- 'javelin-workflow',
- 'phabricator-draggable-list',
- 'javelin-workboard-column',
- 'javelin-workboard-header-template',
- 'javelin-workboard-card-template',
- ),
'225bbb98' => array(
'javelin-install',
'javelin-reactor',
@@ -1110,6 +1105,15 @@
'javelin-json',
'phabricator-prefab',
),
+ '285c337a' => array(
+ 'javelin-behavior',
+ 'javelin-dom',
+ 'javelin-util',
+ 'javelin-vector',
+ 'javelin-stratcom',
+ 'javelin-workflow',
+ 'javelin-workboard-controller',
+ ),
'289bf236' => array(
'javelin-install',
'javelin-util',
@@ -1119,6 +1123,9 @@
'javelin-stratcom',
'javelin-behavior',
),
+ '2a61f8d4' => array(
+ 'javelin-install',
+ ),
'2a8b62d9' => array(
'multirow-row-manager',
'javelin-install',
@@ -1142,9 +1149,6 @@
'javelin-dom',
'phabricator-keyboard-shortcut',
),
- '2d641f7d' => array(
- 'javelin-install',
- ),
'2e255291' => array(
'javelin-install',
'javelin-util',
@@ -1343,6 +1347,11 @@
'javelin-dom',
'javelin-fx',
),
+ '533dd592' => array(
+ 'javelin-install',
+ 'javelin-workboard-card',
+ 'javelin-workboard-header',
+ ),
'534f1757' => array(
'phui-oi-list-view-css',
),
@@ -1427,11 +1436,6 @@
'60cd9241' => array(
'javelin-behavior',
),
- '6461f58b' => array(
- 'javelin-install',
- 'javelin-workboard-card',
- 'javelin-workboard-header',
- ),
'65bb0011' => array(
'javelin-behavior',
'javelin-dom',
@@ -1477,9 +1481,6 @@
'javelin-install',
'javelin-util',
),
- '6e75daea' => array(
- 'javelin-install',
- ),
70245195 => array(
'javelin-behavior',
'javelin-stratcom',
@@ -1839,9 +1840,6 @@
'javelin-behavior-device',
'javelin-vector',
),
- 'b0b5f90a' => array(
- 'javelin-install',
- ),
'b105a3a6' => array(
'javelin-behavior',
'javelin-stratcom',
@@ -1875,6 +1873,9 @@
'javelin-stratcom',
'javelin-dom',
),
+ 'b65351bd' => array(
+ 'javelin-install',
+ ),
'b7b73831' => array(
'javelin-behavior',
'javelin-dom',
@@ -1896,9 +1897,6 @@
'bc16cf33' => array(
'phui-workcard-view-css',
),
- 'bc92741f' => array(
- 'javelin-install',
- ),
'bdce4d78' => array(
'javelin-install',
'javelin-util',
@@ -1968,15 +1966,6 @@
'javelin-util',
'phabricator-keyboard-shortcut-manager',
),
- 'cca3f5f8' => array(
- 'javelin-behavior',
- 'javelin-dom',
- 'javelin-util',
- 'javelin-vector',
- 'javelin-stratcom',
- 'javelin-workflow',
- 'javelin-workboard-controller',
- ),
'cf32921f' => array(
'javelin-behavior',
'javelin-dom',
@@ -2134,6 +2123,17 @@
'phabricator-keyboard-shortcut',
'conpherence-thread-manager',
),
+ 'fc1664ff' => array(
+ 'javelin-install',
+ 'javelin-dom',
+ 'javelin-util',
+ 'javelin-stratcom',
+ 'javelin-workflow',
+ 'phabricator-draggable-list',
+ 'javelin-workboard-column',
+ 'javelin-workboard-header-template',
+ 'javelin-workboard-card-template',
+ ),
'fce5d170' => array(
'javelin-magical-init',
'javelin-util',
diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php
--- a/src/__phutil_library_map__.php
+++ b/src/__phutil_library_map__.php
@@ -4052,10 +4052,14 @@
'PhabricatorProjectColumn' => 'applications/project/storage/PhabricatorProjectColumn.php',
'PhabricatorProjectColumnDetailController' => 'applications/project/controller/PhabricatorProjectColumnDetailController.php',
'PhabricatorProjectColumnEditController' => 'applications/project/controller/PhabricatorProjectColumnEditController.php',
+ 'PhabricatorProjectColumnHeader' => 'applications/project/order/PhabricatorProjectColumnHeader.php',
'PhabricatorProjectColumnHideController' => 'applications/project/controller/PhabricatorProjectColumnHideController.php',
+ 'PhabricatorProjectColumnNaturalOrder' => 'applications/project/order/PhabricatorProjectColumnNaturalOrder.php',
+ 'PhabricatorProjectColumnOrder' => 'applications/project/order/PhabricatorProjectColumnOrder.php',
'PhabricatorProjectColumnPHIDType' => 'applications/project/phid/PhabricatorProjectColumnPHIDType.php',
'PhabricatorProjectColumnPosition' => 'applications/project/storage/PhabricatorProjectColumnPosition.php',
'PhabricatorProjectColumnPositionQuery' => 'applications/project/query/PhabricatorProjectColumnPositionQuery.php',
+ 'PhabricatorProjectColumnPriorityOrder' => 'applications/project/order/PhabricatorProjectColumnPriorityOrder.php',
'PhabricatorProjectColumnQuery' => 'applications/project/query/PhabricatorProjectColumnQuery.php',
'PhabricatorProjectColumnSearchEngine' => 'applications/project/query/PhabricatorProjectColumnSearchEngine.php',
'PhabricatorProjectColumnTransaction' => 'applications/project/storage/PhabricatorProjectColumnTransaction.php',
@@ -10132,13 +10136,17 @@
),
'PhabricatorProjectColumnDetailController' => 'PhabricatorProjectBoardController',
'PhabricatorProjectColumnEditController' => 'PhabricatorProjectBoardController',
+ 'PhabricatorProjectColumnHeader' => 'Phobject',
'PhabricatorProjectColumnHideController' => 'PhabricatorProjectBoardController',
+ 'PhabricatorProjectColumnNaturalOrder' => 'PhabricatorProjectColumnOrder',
+ 'PhabricatorProjectColumnOrder' => 'Phobject',
'PhabricatorProjectColumnPHIDType' => 'PhabricatorPHIDType',
'PhabricatorProjectColumnPosition' => array(
'PhabricatorProjectDAO',
'PhabricatorPolicyInterface',
),
'PhabricatorProjectColumnPositionQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
+ 'PhabricatorProjectColumnPriorityOrder' => 'PhabricatorProjectColumnOrder',
'PhabricatorProjectColumnQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
'PhabricatorProjectColumnSearchEngine' => 'PhabricatorApplicationSearchEngine',
'PhabricatorProjectColumnTransaction' => 'PhabricatorApplicationTransaction',
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
@@ -248,14 +248,6 @@
return idx($this->properties, 'cover.thumbnailPHID');
}
- public function getWorkboardOrderVectors() {
- return array(
- PhabricatorProjectColumn::ORDER_PRIORITY => array(
- (int)-$this->getPriority(),
- ),
- );
- }
-
public function getPriorityKeyword() {
$priority = $this->getPriority();
@@ -267,14 +259,6 @@
return ManiphestTaskPriority::UNKNOWN_PRIORITY_KEYWORD;
}
- public function getWorkboardProperties() {
- return array(
- 'status' => $this->getStatus(),
- 'points' => (double)$this->getPoints(),
- 'priority' => $this->getPriority(),
- );
- }
-
/* -( PhabricatorSubscribableInterface )----------------------------------- */
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
@@ -614,49 +614,25 @@
$board->addPanel($panel);
}
- // It's possible for tasks to have an invalid/unknown priority in the
- // database. We still want to generate a header for these tasks so we
- // don't break the workboard.
- $priorities =
- ManiphestTaskPriority::getTaskPriorityMap() +
- mpull($all_tasks, null, 'getPriority');
- $priorities = array_keys($priorities);
-
- $headers = array();
- foreach ($priorities as $priority) {
- $header_key = sprintf('priority(%s)', $priority);
-
- $priority_name = ManiphestTaskPriority::getTaskPriorityName($priority);
- $priority_color = ManiphestTaskPriority::getTaskPriorityColor($priority);
- $priority_icon = ManiphestTaskPriority::getTaskPriorityIcon($priority);
-
- $icon_view = id(new PHUIIconView())
- ->setIcon("{$priority_icon} {$priority_color}");
-
- $template = phutil_tag(
- 'li',
- array(
- 'class' => 'workboard-group-header',
- ),
- array(
- $icon_view,
- $priority_name,
- ));
+ $order_key = $this->sortKey;
- $headers[] = array(
- '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,
- ),
- );
+ $ordering_map = PhabricatorProjectColumnOrder::getAllOrders();
+ $ordering = id(clone $ordering_map[$order_key])
+ ->setViewer($viewer);
+
+ $headers = $ordering->getHeadersForObjects($all_tasks);
+ $headers = mpull($headers, 'toDictionary');
+
+ $vectors = $ordering->getSortVectorsForObjects($all_tasks);
+ $vector_map = array();
+ foreach ($vectors as $task_phid => $vector) {
+ $vector_map[$task_phid][$order_key] = $vector;
}
+ $header_keys = $ordering->getHeaderKeysForObjects($all_tasks);
+
+ $properties = array();
+
$behavior_config = array(
'moveURI' => $this->getApplicationURI('move/'.$project->getID().'/'),
'uploadURI' => '/file/dropupload/',
@@ -667,10 +643,11 @@
'boardPHID' => $project->getPHID(),
'order' => $this->sortKey,
'headers' => $headers,
+ 'headerKeys' => $header_keys,
'templateMap' => $templates,
'columnMaps' => $column_maps,
- 'orderMaps' => mpull($all_tasks, 'getWorkboardOrderVectors'),
- 'propertyMaps' => mpull($all_tasks, 'getWorkboardProperties'),
+ 'orderMaps' => $vector_map,
+ 'propertyMaps' => $properties,
'boardID' => $board_id,
'projectPHID' => $project->getPHID(),
@@ -680,7 +657,8 @@
$sort_menu = $this->buildSortMenu(
$viewer,
$project,
- $this->sortKey);
+ $this->sortKey,
+ $ordering_map);
$filter_menu = $this->buildFilterMenu(
$viewer,
@@ -775,7 +753,7 @@
return $default_sort;
}
- return PhabricatorProjectColumn::DEFAULT_ORDER;
+ return PhabricatorProjectColumnNaturalOrder::ORDERKEY;
}
private function getDefaultFilter(PhabricatorProject $project) {
@@ -789,41 +767,37 @@
}
private function isValidSort($sort) {
- switch ($sort) {
- case PhabricatorProjectColumn::ORDER_NATURAL:
- case PhabricatorProjectColumn::ORDER_PRIORITY:
- return true;
- }
-
- return false;
+ $map = PhabricatorProjectColumnOrder::getAllOrders();
+ return isset($map[$sort]);
}
private function buildSortMenu(
PhabricatorUser $viewer,
PhabricatorProject $project,
- $sort_key) {
-
- $sort_icon = id(new PHUIIconView())
- ->setIcon('fa-sort-amount-asc bluegrey');
-
- $named = array(
- PhabricatorProjectColumn::ORDER_NATURAL => pht('Natural'),
- PhabricatorProjectColumn::ORDER_PRIORITY => pht('Sort by Priority'),
- );
+ $sort_key,
+ array $ordering_map) {
$base_uri = $this->getURIWithState();
$items = array();
- foreach ($named as $key => $name) {
- $is_selected = ($key == $sort_key);
+ foreach ($ordering_map as $key => $ordering) {
+ // TODO: It would be desirable to build a real "PHUIIconView" here, but
+ // the pathway for threading that through all the view classes ends up
+ // being fairly complex, since some callers read the icon out of other
+ // views. For now, just stick with a string.
+ $ordering_icon = $ordering->getMenuIconIcon();
+ $ordering_name = $ordering->getDisplayName();
+
+ $is_selected = ($key === $sort_key);
if ($is_selected) {
- $active_order = $name;
+ $active_name = $ordering_name;
+ $active_icon = $ordering_icon;
}
$item = id(new PhabricatorActionView())
- ->setIcon('fa-sort-amount-asc')
+ ->setIcon($ordering_icon)
->setSelected($is_selected)
- ->setName($name);
+ ->setName($ordering_name);
$uri = $base_uri->alter('order', $key);
$item->setHref($uri);
@@ -856,8 +830,8 @@
}
$sort_button = id(new PHUIListItemView())
- ->setName($active_order)
- ->setIcon('fa-sort-amount-asc')
+ ->setName($active_name)
+ ->setIcon($active_icon)
->setHref('#')
->addSigil('boards-dropdown-menu')
->setMetadata(
diff --git a/src/applications/project/controller/PhabricatorProjectController.php b/src/applications/project/controller/PhabricatorProjectController.php
--- a/src/applications/project/controller/PhabricatorProjectController.php
+++ b/src/applications/project/controller/PhabricatorProjectController.php
@@ -149,7 +149,11 @@
return $this;
}
- protected function newCardResponse($board_phid, $object_phid) {
+ protected function newCardResponse(
+ $board_phid,
+ $object_phid,
+ PhabricatorProjectColumnOrder $ordering = null) {
+
$viewer = $this->getViewer();
$request = $this->getRequest();
@@ -158,12 +162,17 @@
$visible_phids = array();
}
- return id(new PhabricatorBoardResponseEngine())
+ $engine = id(new PhabricatorBoardResponseEngine())
->setViewer($viewer)
->setBoardPHID($board_phid)
->setObjectPHID($object_phid)
- ->setVisiblePHIDs($visible_phids)
- ->buildResponse();
+ ->setVisiblePHIDs($visible_phids);
+
+ if ($ordering) {
+ $engine->setOrdering($ordering);
+ }
+
+ return $engine->buildResponse();
}
public function renderHashtags(array $tags) {
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
@@ -13,7 +13,15 @@
$object_phid = $request->getStr('objectPHID');
$after_phid = $request->getStr('afterPHID');
$before_phid = $request->getStr('beforePHID');
- $order = $request->getStr('order', PhabricatorProjectColumn::DEFAULT_ORDER);
+
+ $order = $request->getStr('order');
+ if (!strlen($order)) {
+ $order = PhabricatorProjectColumnNaturalOrder::ORDERKEY;
+ }
+
+ $ordering = PhabricatorProjectColumnOrder::getOrderByKey($order);
+ $ordering = id(clone $ordering)
+ ->setViewer($viewer);
$edit_header = null;
$raw_header = $request->getStr('header');
@@ -88,9 +96,8 @@
) + $order_params,
));
- $header_xactions = $this->newHeaderTransactions(
+ $header_xactions = $ordering->getColumnTransactions(
$object,
- $order,
$edit_header);
foreach ($header_xactions as $header_xaction) {
$xactions[] = $header_xaction;
@@ -104,33 +111,7 @@
$editor->applyTransactions($object, $xactions);
- return $this->newCardResponse($board_phid, $object_phid);
- }
-
- private function newHeaderTransactions(
- ManiphestTask $task,
- $order,
- array $header) {
-
- $xactions = array();
-
- switch ($order) {
- case PhabricatorProjectColumn::ORDER_PRIORITY:
- $new_priority = idx($header, $order);
-
- if ($task->getPriority() !== $new_priority) {
- $keyword_map = ManiphestTaskPriority::getTaskPriorityKeywordsMap();
- $keyword = head(idx($keyword_map, $new_priority));
-
- $xactions[] = id(new ManiphestTransaction())
- ->setTransactionType(
- ManiphestTaskPriorityTransaction::TRANSACTIONTYPE)
- ->setNewValue($keyword);
- }
- break;
- }
-
- return $xactions;
+ return $this->newCardResponse($board_phid, $object_phid, $ordering);
}
}
diff --git a/src/applications/project/engine/PhabricatorBoardResponseEngine.php b/src/applications/project/engine/PhabricatorBoardResponseEngine.php
--- a/src/applications/project/engine/PhabricatorBoardResponseEngine.php
+++ b/src/applications/project/engine/PhabricatorBoardResponseEngine.php
@@ -6,6 +6,7 @@
private $boardPHID;
private $objectPHID;
private $visiblePHIDs;
+ private $ordering;
public function setViewer(PhabricatorUser $viewer) {
$this->viewer = $viewer;
@@ -43,10 +44,20 @@
return $this->visiblePHIDs;
}
+ public function setOrdering(PhabricatorProjectColumnOrder $ordering) {
+ $this->ordering = $ordering;
+ return $this;
+ }
+
+ public function getOrdering() {
+ return $this->ordering;
+ }
+
public function buildResponse() {
$viewer = $this->getViewer();
$object_phid = $this->getObjectPHID();
$board_phid = $this->getBoardPHID();
+ $ordering = $this->getOrdering();
// Load all the other tasks that are visible in the affected columns and
// perform layout for them.
@@ -74,10 +85,17 @@
->setViewer($viewer)
->withPHIDs($visible_phids)
->execute();
-
- $order_maps = array();
- foreach ($all_visible as $visible) {
- $order_maps[$visible->getPHID()] = $visible->getWorkboardOrderVectors();
+ $all_visible = mpull($all_visible, null, 'getPHID');
+
+ if ($ordering) {
+ $vectors = $ordering->getSortVectorsForObjects($all_visible);
+ $header_keys = $ordering->getHeaderKeysForObjects($all_visible);
+ $headers = $ordering->getHeadersForObjects($all_visible);
+ $headers = mpull($headers, 'toDictionary');
+ } else {
+ $vectors = array();
+ $header_keys = array();
+ $headers = array();
}
$object = id(new ManiphestTaskQuery())
@@ -91,14 +109,50 @@
$template = $this->buildTemplate($object);
+ $cards = array();
+ foreach ($all_visible as $card_phid => $object) {
+ $card = array(
+ 'vectors' => array(),
+ 'headers' => array(),
+ 'properties' => array(),
+ 'nodeHTMLTemplate' => null,
+ );
+
+ if ($ordering) {
+ $order_key = $ordering->getColumnOrderKey();
+
+ $vector = idx($vectors, $card_phid);
+ if ($vector !== null) {
+ $card['vectors'][$order_key] = $vector;
+ }
+
+ $header = idx($header_keys, $card_phid);
+ if ($header !== null) {
+ $card['headers'][$order_key] = $header;
+ }
+
+ $card['properties'] = array(
+ 'points' => (double)$object->getPoints(),
+ 'status' => $object->getStatus(),
+ );
+ }
+
+ if ($card_phid === $object_phid) {
+ $card['nodeHTMLTemplate'] = hsprintf('%s', $template);
+ }
+
+ $card['vectors'] = (object)$card['vectors'];
+ $card['headers'] = (object)$card['headers'];
+ $card['properties'] = (object)$card['properties'];
+
+ $cards[$card_phid] = $card;
+ }
+
$payload = array(
'objectPHID' => $object_phid,
- 'cardHTML' => $template,
'columnMaps' => $natural,
- 'orderMaps' => $order_maps,
- 'propertyMaps' => array(
- $object_phid => $object->getWorkboardProperties(),
- ),
+ 'cards' => $cards,
+ 'headers' => $headers,
);
return id(new AphrontAjaxResponse())
diff --git a/src/applications/project/order/PhabricatorProjectColumnHeader.php b/src/applications/project/order/PhabricatorProjectColumnHeader.php
new file mode 100644
--- /dev/null
+++ b/src/applications/project/order/PhabricatorProjectColumnHeader.php
@@ -0,0 +1,94 @@
+<?php
+
+final class PhabricatorProjectColumnHeader
+ extends Phobject {
+
+ private $orderKey;
+ private $headerKey;
+ private $sortVector;
+ private $name;
+ private $icon;
+ private $editProperties;
+
+ public function setOrderKey($order_key) {
+ $this->orderKey = $order_key;
+ return $this;
+ }
+
+ public function getOrderKey() {
+ return $this->orderKey;
+ }
+
+ public function setHeaderKey($header_key) {
+ $this->headerKey = $header_key;
+ return $this;
+ }
+
+ public function getHeaderKey() {
+ return $this->headerKey;
+ }
+
+ public function setSortVector(array $sort_vector) {
+ $this->sortVector = $sort_vector;
+ return $this;
+ }
+
+ public function getSortVector() {
+ return $this->sortVector;
+ }
+
+ public function setName($name) {
+ $this->name = $name;
+ return $this;
+ }
+
+ public function getName() {
+ return $this->name;
+ }
+
+ public function setIcon(PHUIIconView$icon) {
+ $this->icon = $icon;
+ return $this;
+ }
+
+ public function getIcon() {
+ return $this->icon;
+ }
+
+ public function setEditProperties(array $edit_properties) {
+ $this->editProperties = $edit_properties;
+ return $this;
+ }
+
+ public function getEditProperties() {
+ return $this->editProperties;
+ }
+
+ public function toDictionary() {
+ return array(
+ 'order' => $this->getOrderKey(),
+ 'key' => $this->getHeaderKey(),
+ 'template' => hsprintf('%s', $this->newView()),
+ 'vector' => $this->getSortVector(),
+ 'editProperties' => $this->getEditProperties(),
+ );
+ }
+
+ private function newView() {
+ $icon_view = $this->getIcon();
+ $name = $this->getName();
+
+ $template = phutil_tag(
+ 'li',
+ array(
+ 'class' => 'workboard-group-header',
+ ),
+ array(
+ $icon_view,
+ $name,
+ ));
+
+ return $template;
+ }
+
+}
diff --git a/src/applications/project/order/PhabricatorProjectColumnNaturalOrder.php b/src/applications/project/order/PhabricatorProjectColumnNaturalOrder.php
new file mode 100644
--- /dev/null
+++ b/src/applications/project/order/PhabricatorProjectColumnNaturalOrder.php
@@ -0,0 +1,12 @@
+<?php
+
+final class PhabricatorProjectColumnNaturalOrder
+ extends PhabricatorProjectColumnOrder {
+
+ const ORDERKEY = 'natural';
+
+ public function getDisplayName() {
+ return pht('Natural');
+ }
+
+}
diff --git a/src/applications/project/order/PhabricatorProjectColumnOrder.php b/src/applications/project/order/PhabricatorProjectColumnOrder.php
new file mode 100644
--- /dev/null
+++ b/src/applications/project/order/PhabricatorProjectColumnOrder.php
@@ -0,0 +1,176 @@
+<?php
+
+abstract class PhabricatorProjectColumnOrder
+ extends Phobject {
+
+ private $viewer;
+
+ final public function setViewer(PhabricatorUser $viewer) {
+ $this->viewer = $viewer;
+ return $this;
+ }
+
+ final public function getViewer() {
+ return $this->viewer;
+ }
+
+ final public function getColumnOrderKey() {
+ return $this->getPhobjectClassConstant('ORDERKEY');
+ }
+
+ final public static function getAllOrders() {
+ return id(new PhutilClassMapQuery())
+ ->setAncestorClass(__CLASS__)
+ ->setUniqueMethod('getColumnOrderKey')
+ ->execute();
+ }
+
+ final public static function getOrderByKey($key) {
+ $map = self::getAllOrders();
+
+ if (!isset($map[$key])) {
+ throw new Exception(
+ pht(
+ 'No column ordering exists with key "%s".',
+ $key));
+ }
+
+ return $map[$key];
+ }
+
+ final public function getColumnTransactions($object, array $header) {
+ $result = $this->newColumnTransactions($object, $header);
+
+ if (!is_array($result) && !is_null($result)) {
+ throw new Exception(
+ pht(
+ 'Expected "newColumnTransactions()" on "%s" to return "null" or a '.
+ 'list of transactions, but got "%s".',
+ get_class($this),
+ phutil_describe_type($result)));
+ }
+
+ if ($result === null) {
+ $result = array();
+ }
+
+ assert_instances_of($result, 'PhabricatorApplicationTransaction');
+
+ return $result;
+ }
+
+ final public function getMenuIconIcon() {
+ return $this->newMenuIconIcon();
+ }
+
+ protected function newMenuIconIcon() {
+ return 'fa-sort-amount-asc';
+ }
+
+ abstract public function getDisplayName();
+
+ protected function newColumnTransactions($object, array $header) {
+ return array();
+ }
+
+ final public function getHeadersForObjects(array $objects) {
+ $headers = $this->newHeadersForObjects($objects);
+
+ if (!is_array($headers)) {
+ throw new Exception(
+ pht(
+ 'Expected "newHeadersForObjects()" on "%s" to return a list '.
+ 'of headers, but got "%s".',
+ get_class($this),
+ phutil_describe_type($headers)));
+ }
+
+ assert_instances_of($headers, 'PhabricatorProjectColumnHeader');
+
+ // Add a "0" to the end of each header. This makes them sort above object
+ // cards in the same group.
+ foreach ($headers as $header) {
+ $vector = $header->getSortVector();
+ $vector[] = 0;
+ $header->setSortVector($vector);
+ }
+
+ return $headers;
+ }
+
+ protected function newHeadersForObjects(array $objects) {
+ return array();
+ }
+
+ final public function getSortVectorsForObjects(array $objects) {
+ $vectors = $this->newSortVectorsForObjects($objects);
+
+ if (!is_array($vectors)) {
+ throw new Exception(
+ pht(
+ 'Expected "newSortVectorsForObjects()" on "%s" to return a '.
+ 'map of vectors, but got "%s".',
+ get_class($this),
+ phutil_describe_type($vectors)));
+ }
+
+ assert_same_keys($objects, $vectors);
+
+ return $vectors;
+ }
+
+ protected function newSortVectorsForObjects(array $objects) {
+ $vectors = array();
+
+ foreach ($objects as $key => $object) {
+ $vectors[$key] = $this->newSortVectorForObject($object);
+ }
+
+ return $vectors;
+ }
+
+ protected function newSortVectorForObject($object) {
+ return array();
+ }
+
+ final public function getHeaderKeysForObjects(array $objects) {
+ $header_keys = $this->newHeaderKeysForObjects($objects);
+
+ if (!is_array($header_keys)) {
+ throw new Exception(
+ pht(
+ 'Expected "newHeaderKeysForObject()" on "%s" to return a '.
+ 'map of header keys, but got "%s".',
+ get_class($this),
+ phutil_describe_type($header_keys)));
+ }
+
+ assert_same_keys($objects, $header_keys);
+
+ return $header_keys;
+ }
+
+ protected function newHeaderKeysForObjects(array $objects) {
+ $header_keys = array();
+
+ foreach ($objects as $key => $object) {
+ $header_keys[$key] = $this->newHeaderKeyForObject($object);
+ }
+
+ return $header_keys;
+ }
+
+ protected function newHeaderKeyForObject($object) {
+ return null;
+ }
+
+ final protected function newTransaction($object) {
+ return $object->getApplicationTransactionTemplate();
+ }
+
+ final protected function newHeader() {
+ return id(new PhabricatorProjectColumnHeader())
+ ->setOrderKey($this->getColumnOrderKey());
+ }
+
+}
diff --git a/src/applications/project/order/PhabricatorProjectColumnPriorityOrder.php b/src/applications/project/order/PhabricatorProjectColumnPriorityOrder.php
new file mode 100644
--- /dev/null
+++ b/src/applications/project/order/PhabricatorProjectColumnPriorityOrder.php
@@ -0,0 +1,91 @@
+<?php
+
+final class PhabricatorProjectColumnPriorityOrder
+ extends PhabricatorProjectColumnOrder {
+
+ const ORDERKEY = 'priority';
+
+ public function getDisplayName() {
+ return pht('Group by Priority');
+ }
+
+ protected function newMenuIconIcon() {
+ return 'fa-sort-numeric-asc';
+ }
+
+ protected function newHeaderKeyForObject($object) {
+ return $this->newHeaderKeyForPriority($object->getPriority());
+ }
+
+ private function newHeaderKeyForPriority($priority) {
+ return sprintf('priority(%d)', $priority);
+ }
+
+ protected function newSortVectorForObject($object) {
+ return $this->newSortVectorForPriority($object->getPriority());
+ }
+
+ private function newSortVectorForPriority($priority) {
+ return array(
+ (int)-$priority,
+ );
+ }
+
+ protected function newHeadersForObjects(array $objects) {
+ $priorities = ManiphestTaskPriority::getTaskPriorityMap();
+
+ // It's possible for tasks to have an invalid/unknown priority in the
+ // database. We still want to generate a header for these tasks so we
+ // don't break the workboard.
+ $priorities = $priorities + mpull($objects, null, 'getPriority');
+
+ $priorities = array_keys($priorities);
+
+ $headers = array();
+ foreach ($priorities as $priority) {
+ $header_key = $this->newHeaderKeyForPriority($priority);
+ $sort_vector = $this->newSortVectorForPriority($priority);
+
+ $priority_name = ManiphestTaskPriority::getTaskPriorityName($priority);
+ $priority_color = ManiphestTaskPriority::getTaskPriorityColor($priority);
+ $priority_icon = ManiphestTaskPriority::getTaskPriorityIcon($priority);
+
+ $icon_view = id(new PHUIIconView())
+ ->setIcon($priority_icon, $priority_color);
+
+ $header = $this->newHeader()
+ ->setHeaderKey($header_key)
+ ->setSortVector($sort_vector)
+ ->setName($priority_name)
+ ->setIcon($icon_view)
+ ->setEditProperties(
+ array(
+ 'value' => (int)$priority,
+ ));
+
+ $headers[] = $header;
+ }
+
+ return $headers;
+ }
+
+ protected function newColumnTransactions($object, array $header) {
+ $new_priority = idx($header, 'value');
+
+ if ($object->getPriority() === $new_priority) {
+ return null;
+ }
+
+ $keyword_map = ManiphestTaskPriority::getTaskPriorityKeywordsMap();
+ $keyword = head(idx($keyword_map, $new_priority));
+
+ $xactions = array();
+ $xactions[] = $this->newTransaction($object)
+ ->setTransactionType(ManiphestTaskPriorityTransaction::TRANSACTIONTYPE)
+ ->setNewValue($keyword);
+
+ return $xactions;
+ }
+
+
+}
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
@@ -12,13 +12,6 @@
const STATUS_ACTIVE = 0;
const STATUS_HIDDEN = 1;
- const DEFAULT_ORDER = 'natural';
- 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
@@ -289,7 +289,6 @@
var columns = this.getColumns();
var phid = response.objectPHID;
- var card = this.getCardTemplate(phid);
for (var add_phid in response.columnMaps) {
var target_column = this.getColumn(add_phid);
@@ -302,18 +301,6 @@
target_column.newCard(phid);
}
- card.setNodeHTMLTemplate(response.cardHTML);
-
- var order_maps = response.orderMaps;
- for (var order_phid in order_maps) {
- var card_template = this.getCardTemplate(order_phid);
- for (var order_key in order_maps[order_phid]) {
- card_template.setSortVector(
- order_key,
- order_maps[order_phid][order_key]);
- }
- }
-
var column_maps = response.columnMaps;
var natural_column;
for (var natural_phid in column_maps) {
@@ -329,10 +316,37 @@
natural_column.setNaturalOrder(column_maps[natural_phid]);
}
- var property_maps = response.propertyMaps;
- for (var property_phid in property_maps) {
- this.getCardTemplate(property_phid)
- .setObjectProperties(property_maps[property_phid]);
+ for (var card_phid in response.cards) {
+ var card_data = response.cards[card_phid];
+ var card_template = this.getCardTemplate(card_phid);
+
+ if (card_data.nodeHTMLTemplate) {
+ card_template.setNodeHTMLTemplate(card_data.nodeHTMLTemplate);
+ }
+
+ var order;
+ for (order in card_data.vectors) {
+ card_template.setSortVector(order, card_data.vectors[order]);
+ }
+
+ for (order in card_data.headers) {
+ card_template.setHeaderKey(order, card_data.headers[order]);
+ }
+
+ for (var key in card_data.properties) {
+ card_template.setObjectProperty(key, card_data.properties[key]);
+ }
+ }
+
+ var headers = response.headers;
+ for (var jj = 0; jj < headers.length; jj++) {
+ var header = headers[jj];
+
+ this.getHeaderTemplate(header.key)
+ .setOrder(header.order)
+ .setNodeHTMLTemplate(header.template)
+ .setVector(header.vector)
+ .setEditProperties(header.editProperties);
}
for (var column_phid in columns) {
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
@@ -29,7 +29,8 @@
},
getProperties: function() {
- return this.getColumn().getBoard().getCardTemplate(this.getPHID())
+ return this.getColumn().getBoard()
+ .getCardTemplate(this.getPHID())
.getObjectProperties();
},
@@ -41,10 +42,6 @@
return this.getProperties().status;
},
- getPriority: function(order) {
- return this.getProperties().priority;
- },
-
getNode: function() {
if (!this._root) {
var phid = this.getPHID();
diff --git a/webroot/rsrc/js/application/projects/WorkboardCardTemplate.js b/webroot/rsrc/js/application/projects/WorkboardCardTemplate.js
--- a/webroot/rsrc/js/application/projects/WorkboardCardTemplate.js
+++ b/webroot/rsrc/js/application/projects/WorkboardCardTemplate.js
@@ -9,6 +9,7 @@
construct: function(phid) {
this._phid = phid;
this._vectors = {};
+ this._headerKeys = {};
this.setObjectProperties({});
},
@@ -19,7 +20,9 @@
members: {
_phid: null,
+ _html: null,
_vectors: null,
+ _headerKeys: null,
getPHID: function() {
return this._phid;
@@ -39,8 +42,22 @@
return this._vectors[order];
},
+ setHeaderKey: function(order, key) {
+ this._headerKeys[order] = key;
+ return this;
+ },
+
+ getHeaderKey: function(order) {
+ return this._headerKeys[order];
+ },
+
newNode: function() {
return JX.$H(this._html).getFragment().firstChild;
+ },
+
+ setObjectProperty: function(key, value) {
+ this.getObjectProperties()[key] = value;
+ return this;
}
}
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
@@ -189,6 +189,9 @@
var board = this.getBoard();
var order = board.getOrder();
+ // TODO: This should be modularized into "ProjectColumnOrder" classes,
+ // but is currently hard-coded.
+
switch (order) {
case 'natural':
return false;
@@ -197,15 +200,6 @@
return true;
},
- _getCardHeaderKey: function(card, order) {
- switch (order) {
- case 'priority':
- return 'priority(' + card.getPriority() + ')';
- default:
- return null;
- }
- },
-
redraw: function() {
var board = this.getBoard();
var order = board.getOrder();
@@ -235,7 +229,9 @@
// cards in a column.
if (has_headers) {
- var header_key = this._getCardHeaderKey(card, order);
+ var header_key = board.getCardTemplate(card.getPHID())
+ .getHeaderKey(order);
+
if (!seen_headers[header_key]) {
while (header_keys.length) {
var next = header_keys.pop();
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
@@ -27,12 +27,16 @@
getNode: function() {
if (!this._root) {
var header_key = this.getHeaderKey();
- 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;
+ var root = this.getColumn().getBoard()
+ .getHeaderTemplate(header_key)
+ .newNode();
+
+ JX.Stratcom.getData(root).headerKey = header_key;
+
+ this._root = root;
}
+
return this._root;
},
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
@@ -19,9 +19,19 @@
members: {
_headerKey: null,
+ _html: null,
getHeaderKey: function() {
return this._headerKey;
+ },
+
+ setNodeHTMLTemplate: function(html) {
+ this._html = html;
+ return this;
+ },
+
+ newNode: function() {
+ return JX.$H(this._html).getFragment().firstChild;
}
}
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
@@ -116,11 +116,17 @@
board.getHeaderTemplate(header.key)
.setOrder(header.order)
- .setTemplate(header.template)
+ .setNodeHTMLTemplate(header.template)
.setVector(header.vector)
.setEditProperties(header.editProperties);
}
+ var header_keys = config.headerKeys;
+ for (var header_phid in header_keys) {
+ board.getCardTemplate(header_phid)
+ .setHeaderKey(config.order, header_keys[header_phid]);
+ }
+
board.start();
});

File Metadata

Mime Type
text/plain
Expires
Mar 12 2025, 10:00 PM (4 w, 3 d ago)
Storage Engine
amazon-s3
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
phabricator/secure/t6/nr/e42jfcv5dlpkaf5o
Default Alt Text
D20269.diff (41 KB)

Event Timeline