@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.) hq.recaptime.dev/wiki/Phorge
phorge phabricator
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Rewrite workboards to have way more bugs

Summary:
Ref T4900. Briefly:

- Much more layout and rendering is now done in Javascript.
- This should otherwise be identical to the behavior at HEAD, except that:
- editing a task and removing the current board from it no longer removes the task; and
- points still don't work.

However, this can now plausibly support realtime workboard updates and other complex state-based behaviors like points calculations in a future change.

Test Plan:
- Changed card covers.
- Moved cards.
- Sorted board by priority and natural.
- Added new cards.
- Edited cards in place.

Reviewers: chad

Reviewed By: chad

Maniphest Tasks: T4900

Differential Revision: https://secure.phabricator.com/D15234

+936 -542
+45 -25
resources/celerity/map.php
··· 415 415 'rsrc/js/application/phortune/phortune-credit-card-form.js' => '2290aeef', 416 416 'rsrc/js/application/policy/behavior-policy-control.js' => 'd0c516d5', 417 417 'rsrc/js/application/policy/behavior-policy-rule-editor.js' => '5e9f347c', 418 - 'rsrc/js/application/projects/Workboard.js' => '088b2495', 419 - 'rsrc/js/application/projects/behavior-project-boards.js' => '37eb99e4', 418 + 'rsrc/js/application/projects/WorkboardBoard.js' => '069d6dd3', 419 + 'rsrc/js/application/projects/WorkboardCard.js' => '2fcefa17', 420 + 'rsrc/js/application/projects/WorkboardColumn.js' => 'e8f303bb', 421 + 'rsrc/js/application/projects/WorkboardController.js' => 'fa1378c3', 422 + 'rsrc/js/application/projects/behavior-project-boards.js' => 'e1b56d72', 420 423 'rsrc/js/application/projects/behavior-project-create.js' => '065227cc', 421 424 'rsrc/js/application/projects/behavior-reorder-columns.js' => 'e1d25dfb', 422 425 'rsrc/js/application/releeph/releeph-preview-branch.js' => 'b2b4fbaf', ··· 656 659 'javelin-behavior-phui-profile-menu' => '12884df9', 657 660 'javelin-behavior-policy-control' => 'd0c516d5', 658 661 'javelin-behavior-policy-rule-editor' => '5e9f347c', 659 - 'javelin-behavior-project-boards' => '37eb99e4', 662 + 'javelin-behavior-project-boards' => 'e1b56d72', 660 663 'javelin-behavior-project-create' => '065227cc', 661 664 'javelin-behavior-quicksand-blacklist' => '7927a7d3', 662 665 'javelin-behavior-recurring-edit' => '5f1c4d5f', ··· 723 726 'javelin-view-renderer' => '6c2b09a2', 724 727 'javelin-view-visitor' => 'efe49472', 725 728 'javelin-websocket' => 'e292eaf4', 726 - 'javelin-workboard' => '088b2495', 729 + 'javelin-workboard-board' => '069d6dd3', 730 + 'javelin-workboard-card' => '2fcefa17', 731 + 'javelin-workboard-column' => 'e8f303bb', 732 + 'javelin-workboard-controller' => 'fa1378c3', 727 733 'javelin-workflow' => '5b2e3e2b', 728 734 'lightbox-attachment-css' => '7acac05d', 729 735 'maniphest-batch-editor' => 'b0f0b6d5', ··· 913 919 'javelin-stratcom', 914 920 'javelin-workflow', 915 921 ), 922 + '069d6dd3' => array( 923 + 'javelin-install', 924 + 'javelin-dom', 925 + 'javelin-util', 926 + 'javelin-stratcom', 927 + 'javelin-workflow', 928 + 'phabricator-draggable-list', 929 + 'javelin-workboard-column', 930 + ), 916 931 '06c32383' => array( 917 932 'javelin-behavior', 918 933 'javelin-typeahead-ondemand-source', ··· 929 944 'javelin-dom', 930 945 'javelin-stratcom', 931 946 'javelin-vector', 932 - ), 933 - '088b2495' => array( 934 - 'javelin-install', 935 - 'javelin-dom', 936 - 'javelin-util', 937 - 'javelin-vector', 938 - 'javelin-stratcom', 939 - 'javelin-workflow', 940 - 'phabricator-draggable-list', 941 - 'phabricator-drag-and-drop-file-upload', 942 947 ), 943 948 '0a3f3021' => array( 944 949 'javelin-behavior', ··· 1077 1082 '2ee659ce' => array( 1078 1083 'javelin-install', 1079 1084 ), 1085 + '2fcefa17' => array( 1086 + 'javelin-install', 1087 + ), 1080 1088 '327a00d1' => array( 1081 1089 'javelin-behavior', 1082 1090 'javelin-stratcom', ··· 1095 1103 'javelin-workflow', 1096 1104 'javelin-vector', 1097 1105 'phuix-autocomplete', 1098 - ), 1099 - '37eb99e4' => array( 1100 - 'javelin-behavior', 1101 - 'javelin-dom', 1102 - 'javelin-util', 1103 - 'javelin-vector', 1104 - 'javelin-stratcom', 1105 - 'javelin-workflow', 1106 - 'phabricator-draggable-list', 1107 - 'phabricator-drag-and-drop-file-upload', 1108 - 'javelin-workboard', 1109 1106 ), 1110 1107 '3ab51e2c' => array( 1111 1108 'javelin-behavior', ··· 1937 1934 'javelin-dom', 1938 1935 'phabricator-prefab', 1939 1936 ), 1937 + 'e1b56d72' => array( 1938 + 'javelin-behavior', 1939 + 'javelin-dom', 1940 + 'javelin-util', 1941 + 'javelin-vector', 1942 + 'javelin-stratcom', 1943 + 'javelin-workflow', 1944 + 'javelin-workboard-controller', 1945 + ), 1940 1946 'e1d25dfb' => array( 1941 1947 'javelin-behavior', 1942 1948 'javelin-stratcom', ··· 2004 2010 'e6e25838' => array( 2005 2011 'javelin-install', 2006 2012 ), 2013 + 'e8f303bb' => array( 2014 + 'javelin-install', 2015 + 'javelin-workboard-card', 2016 + ), 2007 2017 'e9581f08' => array( 2008 2018 'javelin-behavior', 2009 2019 'javelin-stratcom', ··· 2087 2097 'javelin-dom', 2088 2098 'javelin-vector', 2089 2099 'javelin-magical-init', 2100 + ), 2101 + 'fa1378c3' => array( 2102 + 'javelin-install', 2103 + 'javelin-dom', 2104 + 'javelin-util', 2105 + 'javelin-vector', 2106 + 'javelin-stratcom', 2107 + 'javelin-workflow', 2108 + 'phabricator-drag-and-drop-file-upload', 2109 + 'javelin-workboard-board', 2090 2110 ), 2091 2111 'fb20ac8d' => array( 2092 2112 'javelin-behavior',
+2
src/__phutil_library_map__.php
··· 1820 1820 'PhabricatorBitbucketAuthProvider' => 'applications/auth/provider/PhabricatorBitbucketAuthProvider.php', 1821 1821 'PhabricatorBoardLayoutEngine' => 'applications/project/engine/PhabricatorBoardLayoutEngine.php', 1822 1822 'PhabricatorBoardRenderingEngine' => 'applications/project/engine/PhabricatorBoardRenderingEngine.php', 1823 + 'PhabricatorBoardResponseEngine' => 'applications/project/engine/PhabricatorBoardResponseEngine.php', 1823 1824 'PhabricatorBot' => 'infrastructure/daemon/bot/PhabricatorBot.php', 1824 1825 'PhabricatorBotChannel' => 'infrastructure/daemon/bot/target/PhabricatorBotChannel.php', 1825 1826 'PhabricatorBotDebugLogHandler' => 'infrastructure/daemon/bot/handler/PhabricatorBotDebugLogHandler.php', ··· 6058 6059 'PhabricatorBitbucketAuthProvider' => 'PhabricatorOAuth1AuthProvider', 6059 6060 'PhabricatorBoardLayoutEngine' => 'Phobject', 6060 6061 'PhabricatorBoardRenderingEngine' => 'Phobject', 6062 + 'PhabricatorBoardResponseEngine' => 'Phobject', 6061 6063 'PhabricatorBot' => 'PhabricatorDaemon', 6062 6064 'PhabricatorBotChannel' => 'PhabricatorBotTarget', 6063 6065 'PhabricatorBotDebugLogHandler' => 'PhabricatorBotHandler',
+1
src/applications/maniphest/controller/ManiphestTaskEditController.php
··· 9 9 ->addContextParameter('responseType') 10 10 ->addContextParameter('columnPHID') 11 11 ->addContextParameter('order') 12 + ->addContextParameter('visiblePHIDs') 12 13 ->buildResponse(); 13 14 } 14 15
+11 -90
src/applications/maniphest/editor/ManiphestEditEngine.php
··· 289 289 $viewer = $request->getViewer(); 290 290 291 291 $column_phid = $request->getStr('columnPHID'); 292 - $order = $request->getStr('order'); 292 + 293 + $visible_phids = $request->getStrList('visiblePHIDs'); 294 + if (!$visible_phids) { 295 + $visible_phids = array(); 296 + } 293 297 294 298 $column = id(new PhabricatorProjectColumnQuery()) 295 299 ->setViewer($viewer) ··· 299 303 return new Aphront404Response(); 300 304 } 301 305 302 - // If the workboard's project and all descendant projects have been removed 303 - // from the card's project list, we are going to remove it from the board 304 - // completely. 305 - 306 - // TODO: If the user did something sneaky and changed a subproject, we'll 307 - // currently leave the card where it was but should really move it to the 308 - // proper new column. 309 - 310 306 $board_phid = $column->getProjectPHID(); 307 + $object_phid = $task->getPHID(); 311 308 312 - $descendant_projects = id(new PhabricatorProjectQuery()) 309 + return id(new PhabricatorBoardResponseEngine()) 313 310 ->setViewer($viewer) 314 - ->withAncestorProjectPHIDs(array($column->getProjectPHID())) 315 - ->execute(); 316 - $board_phids = mpull($descendant_projects, 'getPHID', 'getPHID'); 317 - $board_phids[$board_phid] = $board_phid; 318 - 319 - $project_map = array_fuse($task->getProjectPHIDs()); 320 - $remove_card = !array_intersect_key($board_phids, $project_map); 321 - 322 - // TODO: Maybe the caller should pass a list of visible task PHIDs so we 323 - // know which ones we need to reorder? This is a HUGE overfetch. 324 - $objects = id(new ManiphestTaskQuery()) 325 - ->setViewer($viewer) 326 - ->withEdgeLogicPHIDs( 327 - PhabricatorProjectObjectHasProjectEdgeType::EDGECONST, 328 - PhabricatorQueryConstraint::OPERATOR_ANCESTOR, 329 - array($board_phids)) 330 - ->setViewer($viewer) 331 - ->execute(); 332 - $objects = mpull($objects, null, 'getPHID'); 333 - 334 - $layout_engine = id(new PhabricatorBoardLayoutEngine()) 335 - ->setViewer($viewer) 336 - ->setBoardPHIDs(array($board_phid)) 337 - ->setObjectPHIDs(array_keys($objects)) 338 - ->executeLayout(); 339 - 340 - $positions = $layout_engine->getColumnObjectPositions( 341 - $board_phid, 342 - $column_phid); 343 - 344 - $column_phids = $layout_engine->getColumnObjectPHIDs( 345 - $board_phid, 346 - $column_phid); 347 - 348 - $column_tasks = array_select_keys($objects, $column_phids); 349 - 350 - if ($order == PhabricatorProjectColumn::ORDER_NATURAL) { 351 - // TODO: This is a little bit awkward, because PHP and JS use 352 - // slightly different sort order parameters to achieve the same 353 - // effect. It would be good to unify this a bit at some point. 354 - $sort_map = array(); 355 - foreach ($positions as $position) { 356 - $sort_map[$position->getObjectPHID()] = array( 357 - -$position->getSequence(), 358 - $position->getID(), 359 - ); 360 - } 361 - } else { 362 - $sort_map = mpull( 363 - $column_tasks, 364 - 'getPrioritySortVector', 365 - 'getPHID'); 366 - } 367 - 368 - $data = array( 369 - 'removeFromBoard' => $remove_card, 370 - 'sortMap' => $sort_map, 371 - ); 372 - 373 - $rendering_engine = id(new PhabricatorBoardRenderingEngine()) 374 - ->setViewer($viewer) 375 - ->setObjects(array($task)) 376 - ->setExcludedProjectPHIDs($board_phids); 377 - 378 - $card = $rendering_engine->renderCard($task->getPHID()); 379 - 380 - $item = $card->getItem(); 381 - $item->addClass('phui-workcard'); 382 - 383 - $payload = array( 384 - 'tasks' => $item, 385 - 'data' => $data, 386 - ); 387 - 388 - return id(new AphrontAjaxResponse()) 389 - ->setContent( 390 - array( 391 - 'tasks' => $item, 392 - 'data' => $data, 393 - )); 311 + ->setBoardPHID($board_phid) 312 + ->setObjectPHID($object_phid) 313 + ->setVisiblePHIDs($visible_phids) 314 + ->buildResponse(); 394 315 } 395 316 396 317
+10 -8
src/applications/maniphest/storage/ManiphestTask.php
··· 194 194 return ManiphestTaskStatus::isClosedStatus($this->getStatus()); 195 195 } 196 196 197 - public function getPrioritySortVector() { 198 - return array( 199 - $this->getPriority(), 200 - -$this->getSubpriority(), 201 - $this->getID(), 202 - ); 203 - } 204 - 205 197 public function setProperty($key, $value) { 206 198 $this->properties[$key] = $value; 207 199 return $this; ··· 217 209 218 210 public function getCoverImageThumbnailPHID() { 219 211 return idx($this->properties, 'cover.thumbnailPHID'); 212 + } 213 + 214 + public function getWorkboardOrderVectors() { 215 + return array( 216 + PhabricatorProjectColumn::ORDER_PRIORITY => array( 217 + (int)-$this->getPriority(), 218 + (double)-$this->getSubpriority(), 219 + (int)-$this->getID(), 220 + ), 221 + ); 220 222 } 221 223 222 224
+26 -16
src/applications/project/controller/PhabricatorProjectBoardViewController.php
··· 238 238 'boardPHID' => $project->getPHID(), 239 239 )); 240 240 241 - $behavior_config = array( 242 - 'boardID' => $board_id, 243 - 'projectPHID' => $project->getPHID(), 244 - 'moveURI' => $this->getApplicationURI('move/'.$project->getID().'/'), 245 - 'createURI' => $this->getCreateURI(), 246 - 'uploadURI' => '/file/dropupload/', 247 - 'coverURI' => $this->getApplicationURI('cover/'), 248 - 'chunkThreshold' => PhabricatorFileStorageEngine::getChunkThreshold(), 249 - 'order' => $this->sortKey, 250 - ); 251 - $this->initBehavior( 252 - 'project-boards', 253 - $behavior_config); 254 - 255 241 $visible_columns = array(); 256 242 $column_phids = array(); 257 243 $visible_phids = array(); ··· 297 283 ->setEditMap($task_can_edit_map) 298 284 ->setExcludedProjectPHIDs($select_phids); 299 285 286 + $templates = array(); 287 + $column_maps = array(); 288 + $all_tasks = array(); 300 289 foreach ($visible_columns as $column_phid => $column) { 301 290 $column_tasks = $column_phids[$column_phid]; 302 291 ··· 356 345 )); 357 346 358 347 foreach ($column_tasks as $task) { 359 - $card = $rendering_engine->renderCard($task->getPHID()); 360 - $cards->addItem($card->getItem()); 348 + $object_phid = $task->getPHID(); 349 + 350 + $card = $rendering_engine->renderCard($object_phid); 351 + $templates[$object_phid] = hsprintf('%s', $card->getItem()); 352 + $column_maps[$column_phid][] = $object_phid; 353 + 354 + $all_tasks[$object_phid] = $task; 361 355 } 362 356 363 357 $panel->setCards($cards); 364 358 $board->addPanel($panel); 365 359 } 360 + 361 + $behavior_config = array( 362 + 'boardID' => $board_id, 363 + 'projectPHID' => $project->getPHID(), 364 + 'moveURI' => $this->getApplicationURI('move/'.$project->getID().'/'), 365 + 'createURI' => $this->getCreateURI(), 366 + 'uploadURI' => '/file/dropupload/', 367 + 'coverURI' => $this->getApplicationURI('cover/'), 368 + 'chunkThreshold' => PhabricatorFileStorageEngine::getChunkThreshold(), 369 + 'order' => $this->sortKey, 370 + 'templateMap' => $templates, 371 + 'columnMaps' => $column_maps, 372 + 'orderMaps' => mpull($all_tasks, 'getWorkboardOrderVectors'), 373 + ); 374 + $this->initBehavior('project-boards', $behavior_config); 375 + 366 376 367 377 $sort_menu = $this->buildSortMenu( 368 378 $viewer,
+9 -42
src/applications/project/controller/PhabricatorProjectController.php
··· 150 150 protected function newCardResponse($board_phid, $object_phid) { 151 151 $viewer = $this->getViewer(); 152 152 153 - $project = id(new PhabricatorProjectQuery()) 154 - ->setViewer($viewer) 155 - ->withPHIDs(array($board_phid)) 156 - ->executeOne(); 157 - if (!$project) { 158 - return new Aphront404Response(); 153 + $request = $this->getRequest(); 154 + $visible_phids = $request->getStrList('visiblePHIDs'); 155 + if (!$visible_phids) { 156 + $visible_phids = array(); 159 157 } 160 158 161 - // Reload the object so it reflects edits which have been applied. 162 - $object = id(new ManiphestTaskQuery()) 159 + return id(new PhabricatorBoardResponseEngine()) 163 160 ->setViewer($viewer) 164 - ->withPHIDs(array($object_phid)) 165 - ->needProjectPHIDs(true) 166 - ->executeOne(); 167 - if (!$object) { 168 - return new Aphront404Response(); 169 - } 170 - 171 - $except_phids = array($board_phid); 172 - if ($project->getHasSubprojects() || $project->getHasMilestones()) { 173 - $descendants = id(new PhabricatorProjectQuery()) 174 - ->setViewer($viewer) 175 - ->withAncestorProjectPHIDs($except_phids) 176 - ->execute(); 177 - foreach ($descendants as $descendant) { 178 - $except_phids[] = $descendant->getPHID(); 179 - } 180 - } 181 - 182 - $rendering_engine = id(new PhabricatorBoardRenderingEngine()) 183 - ->setViewer($viewer) 184 - ->setObjects(array($object)) 185 - ->setExcludedProjectPHIDs($except_phids); 186 - 187 - $card = $rendering_engine->renderCard($object->getPHID()); 188 - 189 - $item = $card->getItem(); 190 - $item->addClass('phui-workcard'); 191 - 192 - return id(new AphrontAjaxResponse()) 193 - ->setContent( 194 - array( 195 - 'objectPHID' => $object->getPHID(), 196 - 'cardHTML' => $item, 197 - )); 161 + ->setBoardPHID($board_phid) 162 + ->setObjectPHID($object_phid) 163 + ->setVisiblePHIDs($visible_phids) 164 + ->buildResponse(); 198 165 } 199 166 200 167 }
+146
src/applications/project/engine/PhabricatorBoardResponseEngine.php
··· 1 + <?php 2 + 3 + final class PhabricatorBoardResponseEngine extends Phobject { 4 + 5 + private $viewer; 6 + private $boardPHID; 7 + private $objectPHID; 8 + private $visiblePHIDs; 9 + 10 + public function setViewer(PhabricatorUser $viewer) { 11 + $this->viewer = $viewer; 12 + return $this; 13 + } 14 + 15 + public function getViewer() { 16 + return $this->viewer; 17 + } 18 + 19 + public function setBoardPHID($board_phid) { 20 + $this->boardPHID = $board_phid; 21 + return $this; 22 + } 23 + 24 + public function getBoardPHID() { 25 + return $this->boardPHID; 26 + } 27 + 28 + public function setObjectPHID($object_phid) { 29 + $this->objectPHID = $object_phid; 30 + return $this; 31 + } 32 + 33 + public function getObjectPHID() { 34 + return $this->objectPHID; 35 + } 36 + 37 + public function setVisiblePHIDs(array $visible_phids) { 38 + $this->visiblePHIDs = $visible_phids; 39 + return $this; 40 + } 41 + 42 + public function getVisiblePHIDs() { 43 + return $this->visiblePHIDs; 44 + } 45 + 46 + public function buildResponse() { 47 + $viewer = $this->getViewer(); 48 + $object_phid = $this->getObjectPHID(); 49 + $board_phid = $this->getBoardPHID(); 50 + 51 + // Load all the other tasks that are visible in the affected columns and 52 + // perform layout for them. 53 + $visible_phids = $this->getAllVisiblePHIDs(); 54 + 55 + $layout_engine = id(new PhabricatorBoardLayoutEngine()) 56 + ->setViewer($viewer) 57 + ->setBoardPHIDs(array($board_phid)) 58 + ->setObjectPHIDs($visible_phids) 59 + ->executeLayout(); 60 + 61 + $object_columns = $layout_engine->getObjectColumns( 62 + $board_phid, 63 + $object_phid); 64 + 65 + $natural = array(); 66 + foreach ($object_columns as $column_phid => $column) { 67 + $column_object_phids = $layout_engine->getColumnObjectPHIDs( 68 + $board_phid, 69 + $column_phid); 70 + $natural[$column_phid] = array_values($column_object_phids); 71 + } 72 + 73 + $all_visible = id(new ManiphestTaskQuery()) 74 + ->setViewer($viewer) 75 + ->withPHIDs($visible_phids) 76 + ->execute(); 77 + 78 + $order_maps = array(); 79 + foreach ($all_visible as $visible) { 80 + $order_maps[$visible->getPHID()] = $visible->getWorkboardOrderVectors(); 81 + } 82 + 83 + $template = $this->buildTemplate(); 84 + 85 + $payload = array( 86 + 'objectPHID' => $object_phid, 87 + 'cardHTML' => $template, 88 + 'columnMaps' => $natural, 89 + 'orderMaps' => $order_maps, 90 + ); 91 + 92 + return id(new AphrontAjaxResponse()) 93 + ->setContent($payload); 94 + } 95 + 96 + private function buildTemplate() { 97 + $viewer = $this->getViewer(); 98 + $object_phid = $this->getObjectPHID(); 99 + 100 + $excluded_phids = $this->loadExcludedProjectPHIDs(); 101 + 102 + $object = id(new ManiphestTaskQuery()) 103 + ->setViewer($viewer) 104 + ->withPHIDs(array($object_phid)) 105 + ->needProjectPHIDs(true) 106 + ->executeOne(); 107 + if (!$object) { 108 + return new Aphront404Response(); 109 + } 110 + 111 + $rendering_engine = id(new PhabricatorBoardRenderingEngine()) 112 + ->setViewer($viewer) 113 + ->setObjects(array($object)) 114 + ->setExcludedProjectPHIDs($excluded_phids); 115 + 116 + $card = $rendering_engine->renderCard($object_phid); 117 + 118 + return hsprintf('%s', $card->getItem()); 119 + } 120 + 121 + private function loadExcludedProjectPHIDs() { 122 + $viewer = $this->getViewer(); 123 + $board_phid = $this->getBoardPHID(); 124 + 125 + $exclude_phids = array($board_phid); 126 + 127 + $descendants = id(new PhabricatorProjectQuery()) 128 + ->setViewer($viewer) 129 + ->withAncestorProjectPHIDs($exclude_phids) 130 + ->execute(); 131 + 132 + foreach ($descendants as $descendant) { 133 + $exclude_phids[] = $descendant->getPHID(); 134 + } 135 + 136 + return array_fuse($exclude_phids); 137 + } 138 + 139 + private function getAllVisiblePHIDs() { 140 + $visible_phids = $this->getVisiblePHIDs(); 141 + $visible_phids[] = $this->getObjectPHID(); 142 + $visible_phids = array_fuse($visible_phids); 143 + return $visible_phids; 144 + } 145 + 146 + }
+2 -4
src/applications/project/view/ProjectBoardTaskCard.php
··· 78 78 ->setHref('/T'.$task->getID()) 79 79 ->addSigil('project-card') 80 80 ->setDisabled($task->isClosed()) 81 - ->setMetadata( 82 - array( 83 - 'objectPHID' => $task->getPHID(), 84 - )) 85 81 ->addAction( 86 82 id(new PHUIListItemView()) 87 83 ->setName(pht('Edit')) ··· 114 110 ->setHandles($project_handles); 115 111 $card->addAttribute($tag_list); 116 112 } 113 + 114 + $card->addClass('phui-workcard'); 117 115 118 116 return $card; 119 117 }
-242
webroot/rsrc/js/application/projects/Workboard.js
··· 1 - /** 2 - * @provides javelin-workboard 3 - * @requires javelin-install 4 - * javelin-dom 5 - * javelin-util 6 - * javelin-vector 7 - * javelin-stratcom 8 - * javelin-workflow 9 - * phabricator-draggable-list 10 - * phabricator-drag-and-drop-file-upload 11 - * @javelin 12 - */ 13 - 14 - JX.install('Workboard', { 15 - 16 - construct: function(config) { 17 - this._config = config; 18 - 19 - this._boardNodes = {}; 20 - this._columnMap = {}; 21 - }, 22 - 23 - properties: { 24 - uploadURI: null, 25 - coverURI: null, 26 - moveURI: null, 27 - chunkThreshold: null 28 - }, 29 - 30 - members: { 31 - _config: null, 32 - _boardNodes: null, 33 - _currentBoard: null, 34 - 35 - _panOrigin: null, 36 - _panNode: null, 37 - _panX: null, 38 - 39 - _columnMap: null, 40 - 41 - start: function() { 42 - this._setupCoverImageHandlers(); 43 - this._setupPanHandlers(); 44 - 45 - return this; 46 - }, 47 - 48 - addBoard: function(board_phid, board_node) { 49 - this._currentBoard = board_phid; 50 - this._boardNodes[board_phid] = board_node; 51 - this._setupDragHandlers(board_node); 52 - }, 53 - 54 - _getConfig: function() { 55 - return this._config; 56 - }, 57 - 58 - _setupCoverImageHandlers: function() { 59 - if (!JX.PhabricatorDragAndDropFileUpload.isSupported()) { 60 - return; 61 - } 62 - 63 - var drop = new JX.PhabricatorDragAndDropFileUpload('project-card') 64 - .setURI(this.getUploadURI()) 65 - .setChunkThreshold(this.getChunkThreshold()); 66 - 67 - drop.listen('didBeginDrag', function(node) { 68 - JX.DOM.alterClass(node, 'phui-workcard-upload-target', true); 69 - }); 70 - 71 - drop.listen('didEndDrag', function(node) { 72 - JX.DOM.alterClass(node, 'phui-workcard-upload-target', false); 73 - }); 74 - 75 - drop.listen('didUpload', JX.bind(this, this._oncoverupload)); 76 - 77 - drop.start(); 78 - }, 79 - 80 - _oncoverupload: function(file) { 81 - var node = file.getTargetNode(); 82 - var board = JX.DOM.findAbove(node, 'div', 'jx-workboard'); 83 - 84 - var data = { 85 - boardPHID: JX.Stratcom.getData(board).boardPHID, 86 - objectPHID: JX.Stratcom.getData(node).objectPHID, 87 - filePHID: file.getPHID() 88 - }; 89 - 90 - new JX.Workflow(this.getCoverURI(), data) 91 - .setHandler(JX.bind(this, this._queueCardUpdate)) 92 - .start(); 93 - }, 94 - 95 - _setupPanHandlers: function() { 96 - var mousedown = JX.bind(this, this._onpanmousedown); 97 - var mousemove = JX.bind(this, this._onpanmousemove); 98 - var mouseup = JX.bind(this, this._onpanmouseup); 99 - 100 - JX.Stratcom.listen('mousedown', 'workboard-shadow', mousedown); 101 - JX.Stratcom.listen('mousemove', null, mousemove); 102 - JX.Stratcom.listen('mouseup', null, mouseup); 103 - }, 104 - 105 - _onpanmousedown: function(e) { 106 - if (!JX.Device.isDesktop()) { 107 - return; 108 - } 109 - 110 - if (e.getNode('workpanel')) { 111 - return; 112 - } 113 - 114 - if (JX.Stratcom.pass()) { 115 - return; 116 - } 117 - 118 - e.kill(); 119 - 120 - this._panOrigin = JX.$V(e); 121 - this._panNode = e.getNode('workboard-shadow'); 122 - this._panX = this._panNode.scrollLeft; 123 - }, 124 - 125 - _onpanmousemove: function(e) { 126 - if (!this._panOrigin) { 127 - return; 128 - } 129 - 130 - var cursor = JX.$V(e); 131 - this._panNode.scrollLeft = this._panX + (this._panOrigin.x - cursor.x); 132 - }, 133 - 134 - _onpanmouseup: function() { 135 - this._panOrigin = null; 136 - }, 137 - 138 - 139 - _setupDragHandlers: function(board_node) { 140 - var columns = this._findBoardColumns(board_node); 141 - var column; 142 - var ii; 143 - var lists = []; 144 - 145 - for (ii = 0; ii < columns.length; ii++) { 146 - column = columns[ii]; 147 - 148 - var list = new JX.DraggableList('project-card', column) 149 - .setOuterContainer(board_node) 150 - .setFindItemsHandler(JX.bind(this, this._findCardsInColumn, column)) 151 - .setCanDragX(true) 152 - .setHasInfiniteHeight(true); 153 - 154 - // TODO: Restore these behaviors. 155 - // list.listen('didSend', JX.bind(list, onupdate, cols[ii])); 156 - // list.listen('didReceive', JX.bind(list, onupdate, cols[ii])); 157 - // onupdate(cols[ii]); 158 - 159 - list.listen('didDrop', JX.bind(this, this._onmovecard, list)); 160 - 161 - lists.push(list); 162 - } 163 - 164 - for (ii = 0; ii < lists.length; ii++) { 165 - lists[ii].setGroup(lists); 166 - } 167 - }, 168 - 169 - _findBoardColumns: function(board_node) { 170 - return JX.DOM.scry(board_node, 'ul', 'project-column'); 171 - }, 172 - 173 - _findCardsInColumn: function(column_node) { 174 - return JX.DOM.scry(column_node, 'li', 'project-card'); 175 - }, 176 - 177 - _onmovecard: function(list, item, after_node) { 178 - list.lock(); 179 - JX.DOM.alterClass(item, 'drag-sending', true); 180 - 181 - var item_phid = JX.Stratcom.getData(item).objectPHID; 182 - var data = { 183 - objectPHID: item_phid, 184 - columnPHID: JX.Stratcom.getData(list.getRootNode()).columnPHID 185 - }; 186 - 187 - if (after_node) { 188 - data.afterPHID = JX.Stratcom.getData(after_node).objectPHID; 189 - } 190 - 191 - var before_node = item.nextSibling; 192 - if (before_node) { 193 - var before_phid = JX.Stratcom.getData(before_node).objectPHID; 194 - if (before_phid) { 195 - data.beforePHID = before_phid; 196 - } 197 - } 198 - 199 - // TODO: This should be managed per-board. 200 - var config = this._getConfig(); 201 - data.order = config.order; 202 - 203 - new JX.Workflow(this.getMoveURI(), data) 204 - .setHandler(JX.bind(this, this._oncardupdate, item, list)) 205 - .start(); 206 - }, 207 - 208 - _oncardupdate: function(item, list, response) { 209 - list.unlock(); 210 - JX.DOM.alterClass(item, 'drag-sending', false); 211 - 212 - this._queueCardUpdate(response); 213 - }, 214 - 215 - _queueCardUpdate: function(response) { 216 - var board_node = this._boardNodes[this._currentBoard]; 217 - 218 - var columns = this._findBoardColumns(board_node); 219 - var cards; 220 - var ii; 221 - var jj; 222 - var data; 223 - 224 - for (ii = 0; ii < columns.length; ii++) { 225 - cards = this._findCardsInColumn(columns[ii]); 226 - for (jj = 0; jj < cards.length; jj++) { 227 - data = JX.Stratcom.getData(cards[jj]); 228 - if (data.objectPHID == response.objectPHID) { 229 - this._replaceCard(cards[jj], JX.$H(response.cardHTML)); 230 - } 231 - } 232 - } 233 - 234 - }, 235 - 236 - _replaceCard: function(old_node, new_node) { 237 - JX.DOM.replace(old_node, new_node); 238 - } 239 - 240 - } 241 - 242 - });
+221
webroot/rsrc/js/application/projects/WorkboardBoard.js
··· 1 + /** 2 + * @provides javelin-workboard-board 3 + * @requires javelin-install 4 + * javelin-dom 5 + * javelin-util 6 + * javelin-stratcom 7 + * javelin-workflow 8 + * phabricator-draggable-list 9 + * javelin-workboard-column 10 + * @javelin 11 + */ 12 + 13 + JX.install('WorkboardBoard', { 14 + 15 + construct: function(controller, phid, root) { 16 + this._controller = controller; 17 + this._phid = phid; 18 + this._root = root; 19 + 20 + this._templates = {}; 21 + this._orderMaps = {}; 22 + this._buildColumns(); 23 + }, 24 + 25 + properties: { 26 + order: null, 27 + }, 28 + 29 + members: { 30 + _controller: null, 31 + _phid: null, 32 + _root: null, 33 + _columns: null, 34 + _templates: null, 35 + _orderMaps: null, 36 + 37 + getRoot: function() { 38 + return this._root; 39 + }, 40 + 41 + getColumns: function() { 42 + return this._columns; 43 + }, 44 + 45 + getColumn: function(k) { 46 + return this._columns[k]; 47 + }, 48 + 49 + getPHID: function() { 50 + return this._phid; 51 + }, 52 + 53 + setCardTemplate: function(phid, template) { 54 + this._templates[phid] = template; 55 + return this; 56 + }, 57 + 58 + getCardTemplate: function(phid) { 59 + return this._templates[phid]; 60 + }, 61 + 62 + getController: function() { 63 + return this._controller; 64 + }, 65 + 66 + setOrderMap: function(phid, map) { 67 + this._orderMaps[phid] = map; 68 + return this; 69 + }, 70 + 71 + getOrderVector: function(phid, key) { 72 + return this._orderMaps[phid][key]; 73 + }, 74 + 75 + start: function() { 76 + this._setupDragHandlers(); 77 + 78 + for (var k in this._columns) { 79 + this._columns[k].redraw(); 80 + } 81 + }, 82 + 83 + _buildColumns: function() { 84 + var nodes = JX.DOM.scry(this.getRoot(), 'ul', 'project-column'); 85 + 86 + this._columns = {}; 87 + for (var ii = 0; ii < nodes.length; ii++) { 88 + var node = nodes[ii]; 89 + var data = JX.Stratcom.getData(node); 90 + var phid = data.columnPHID; 91 + 92 + this._columns[phid] = new JX.WorkboardColumn(this, phid, node); 93 + } 94 + }, 95 + 96 + _setupDragHandlers: function() { 97 + var columns = this.getColumns(); 98 + 99 + var lists = []; 100 + for (var k in columns) { 101 + var column = columns[k]; 102 + 103 + var list = new JX.DraggableList('project-card', column.getRoot()) 104 + .setOuterContainer(this.getRoot()) 105 + .setFindItemsHandler(JX.bind(column, column.getCardNodes)) 106 + .setCanDragX(true) 107 + .setHasInfiniteHeight(true); 108 + 109 + list.listen('didDrop', JX.bind(this, this._onmovecard, list)); 110 + 111 + lists.push(list); 112 + } 113 + 114 + for (var ii = 0; ii < lists.length; ii++) { 115 + lists[ii].setGroup(lists); 116 + } 117 + }, 118 + 119 + _findCardsInColumn: function(column_node) { 120 + return JX.DOM.scry(column_node, 'li', 'project-card'); 121 + }, 122 + 123 + _onmovecard: function(list, item, after_node, src_list) { 124 + list.lock(); 125 + JX.DOM.alterClass(item, 'drag-sending', true); 126 + 127 + var src_phid = JX.Stratcom.getData(src_list.getRootNode()).columnPHID; 128 + var dst_phid = JX.Stratcom.getData(list.getRootNode()).columnPHID; 129 + 130 + var item_phid = JX.Stratcom.getData(item).objectPHID; 131 + var data = { 132 + objectPHID: item_phid, 133 + columnPHID: dst_phid, 134 + order: this.getOrder() 135 + }; 136 + 137 + if (after_node) { 138 + data.afterPHID = JX.Stratcom.getData(after_node).objectPHID; 139 + } 140 + 141 + var before_node = item.nextSibling; 142 + if (before_node) { 143 + var before_phid = JX.Stratcom.getData(before_node).objectPHID; 144 + if (before_phid) { 145 + data.beforePHID = before_phid; 146 + } 147 + } 148 + 149 + var visible_phids = []; 150 + var column = this.getColumn(dst_phid); 151 + for (var object_phid in column.getCards()) { 152 + visible_phids.push(object_phid); 153 + } 154 + 155 + data.visiblePHIDs = visible_phids.join(','); 156 + 157 + var onupdate = JX.bind( 158 + this, 159 + this._oncardupdate, 160 + list, 161 + src_phid, 162 + dst_phid, 163 + data.afterPHID); 164 + 165 + new JX.Workflow(this.getController().getMoveURI(), data) 166 + .setHandler(onupdate) 167 + .start(); 168 + }, 169 + 170 + _oncardupdate: function(list, src_phid, dst_phid, after_phid, response) { 171 + var src_column = this.getColumn(src_phid); 172 + var dst_column = this.getColumn(dst_phid); 173 + 174 + var card = src_column.removeCard(response.objectPHID); 175 + dst_column.addCard(card, after_phid); 176 + 177 + this.updateCard(response); 178 + 179 + list.unlock(); 180 + }, 181 + 182 + updateCard: function(response) { 183 + var columns = this.getColumns(); 184 + 185 + var phid = response.objectPHID; 186 + 187 + if (!this._templates[phid]) { 188 + for (var add_phid in response.columnMaps) { 189 + this.getColumn(add_phid).newCard(phid); 190 + } 191 + } 192 + 193 + this.setCardTemplate(phid, response.cardHTML); 194 + 195 + var order_maps = response.orderMaps; 196 + for (var order_phid in order_maps) { 197 + this.setOrderMap(order_phid, order_maps[order_phid]); 198 + } 199 + 200 + var column_maps = response.columnMaps; 201 + for (var natural_phid in column_maps) { 202 + this.getColumn(natural_phid).setNaturalOrder(column_maps[natural_phid]); 203 + } 204 + 205 + for (var column_phid in columns) { 206 + var cards = columns[column_phid].getCards(); 207 + for (var object_phid in cards) { 208 + if (object_phid !== phid) { 209 + continue; 210 + } 211 + 212 + var card = cards[object_phid]; 213 + card.redraw(); 214 + } 215 + columns[column_phid].redraw(); 216 + } 217 + } 218 + 219 + } 220 + 221 + });
+56
webroot/rsrc/js/application/projects/WorkboardCard.js
··· 1 + /** 2 + * @provides javelin-workboard-card 3 + * @requires javelin-install 4 + * @javelin 5 + */ 6 + 7 + JX.install('WorkboardCard', { 8 + 9 + construct: function(column, phid) { 10 + this._column = column; 11 + this._phid = phid; 12 + }, 13 + 14 + members: { 15 + _column: null, 16 + _phid: null, 17 + _root: null, 18 + 19 + getPHID: function() { 20 + return this._phid; 21 + }, 22 + 23 + getColumn: function() { 24 + return this._column; 25 + }, 26 + 27 + setColumn: function(column) { 28 + this._column = column; 29 + }, 30 + 31 + getNode: function() { 32 + if (!this._root) { 33 + var phid = this.getPHID(); 34 + var template = this.getColumn().getBoard().getCardTemplate(phid); 35 + this._root = JX.$H(template).getFragment().firstChild; 36 + 37 + JX.Stratcom.getData(this._root).objectPHID = this.getPHID(); 38 + } 39 + return this._root; 40 + }, 41 + 42 + redraw: function() { 43 + var old_node = this._root; 44 + this._root = null; 45 + var new_node = this.getNode(); 46 + 47 + if (old_node && old_node.parentNode) { 48 + JX.DOM.replace(old_node, new_node); 49 + } 50 + 51 + return this; 52 + } 53 + 54 + } 55 + 56 + });
+177
webroot/rsrc/js/application/projects/WorkboardColumn.js
··· 1 + /** 2 + * @provides javelin-workboard-column 3 + * @requires javelin-install 4 + * javelin-workboard-card 5 + * @javelin 6 + */ 7 + 8 + JX.install('WorkboardColumn', { 9 + 10 + construct: function(board, phid, root) { 11 + this._board = board; 12 + this._phid = phid; 13 + this._root = root; 14 + 15 + this._cards = {}; 16 + this._naturalOrder = []; 17 + }, 18 + 19 + members: { 20 + _phid: null, 21 + _root: null, 22 + _board: null, 23 + _cards: null, 24 + _naturalOrder: null, 25 + 26 + getPHID: function() { 27 + return this._phid; 28 + }, 29 + 30 + getRoot: function() { 31 + return this._root; 32 + }, 33 + 34 + getCards: function() { 35 + return this._cards; 36 + }, 37 + 38 + getCard: function(phid) { 39 + return this._cards[phid]; 40 + }, 41 + 42 + getBoard: function() { 43 + return this._board; 44 + }, 45 + 46 + setNaturalOrder: function(order) { 47 + this._naturalOrder = order; 48 + return this; 49 + }, 50 + 51 + newCard: function(phid) { 52 + var card = new JX.WorkboardCard(this, phid); 53 + 54 + this._cards[phid] = card; 55 + this._naturalOrder.push(phid); 56 + 57 + return card; 58 + }, 59 + 60 + removeCard: function(phid) { 61 + var card = this._cards[phid]; 62 + delete this._cards[phid]; 63 + 64 + for (var ii = 0; ii < this._naturalOrder.length; ii++) { 65 + if (this._naturalOrder[ii] == phid) { 66 + this._naturalOrder.splice(ii, 1); 67 + break; 68 + } 69 + } 70 + 71 + return card; 72 + }, 73 + 74 + addCard: function(card, after) { 75 + var phid = card.getPHID(); 76 + 77 + card.setColumn(this); 78 + this._cards[phid] = card; 79 + 80 + var index = 0; 81 + 82 + if (after) { 83 + for (var ii = 0; ii < this._naturalOrder.length; ii++) { 84 + if (this._naturalOrder[ii] == after) { 85 + index = ii + 1; 86 + break; 87 + } 88 + } 89 + } 90 + 91 + if (index > this._naturalOrder.length) { 92 + this._naturalOrder.push(phid); 93 + } else { 94 + this._naturalOrder.splice(index, 0, phid); 95 + } 96 + 97 + return this; 98 + }, 99 + 100 + getCardNodes: function() { 101 + var cards = this.getCards(); 102 + 103 + var nodes = []; 104 + for (var k in cards) { 105 + nodes.push(cards[k].getNode()); 106 + } 107 + 108 + return nodes; 109 + }, 110 + 111 + getCardPHIDs: function() { 112 + return JX.keys(this.getCards()); 113 + }, 114 + 115 + redraw: function() { 116 + var order = this.getBoard().getOrder(); 117 + 118 + var list; 119 + if (order == 'natural') { 120 + list = this._getCardsSortedNaturally(); 121 + } else { 122 + list = this._getCardsSortedByKey(order); 123 + } 124 + 125 + var content = []; 126 + for (var ii = 0; ii < list.length; ii++) { 127 + var node = list[ii].getNode(); 128 + content.push(node); 129 + } 130 + 131 + JX.DOM.setContent(this.getRoot(), content); 132 + }, 133 + 134 + _getCardsSortedNaturally: function() { 135 + var list = []; 136 + 137 + for (var ii = 0; ii < this._naturalOrder.length; ii++) { 138 + var phid = this._naturalOrder[ii]; 139 + list.push(this.getCard(phid)); 140 + } 141 + 142 + return list; 143 + }, 144 + 145 + _getCardsSortedByKey: function(order) { 146 + var cards = this.getCards(); 147 + 148 + var list = []; 149 + for (var k in cards) { 150 + list.push(cards[k]); 151 + } 152 + 153 + list.sort(JX.bind(this, this._sortCards, order)); 154 + 155 + return list; 156 + }, 157 + 158 + _sortCards: function(order, u, v) { 159 + var ud = this.getBoard().getOrderVector(u.getPHID(), order); 160 + var vd = this.getBoard().getOrderVector(v.getPHID(), order); 161 + 162 + for (var ii = 0; ii < ud.length; ii++) { 163 + if (ud[ii] > vd[ii]) { 164 + return 1; 165 + } 166 + 167 + if (ud[ii] < vd[ii]) { 168 + return -1; 169 + } 170 + } 171 + 172 + return 0; 173 + } 174 + 175 + } 176 + 177 + });
+201
webroot/rsrc/js/application/projects/WorkboardController.js
··· 1 + /** 2 + * @provides javelin-workboard-controller 3 + * @requires javelin-install 4 + * javelin-dom 5 + * javelin-util 6 + * javelin-vector 7 + * javelin-stratcom 8 + * javelin-workflow 9 + * phabricator-drag-and-drop-file-upload 10 + * javelin-workboard-board 11 + * @javelin 12 + */ 13 + 14 + JX.install('WorkboardController', { 15 + 16 + construct: function() { 17 + this._boards = {}; 18 + }, 19 + 20 + properties: { 21 + uploadURI: null, 22 + coverURI: null, 23 + moveURI: null, 24 + createURI: null, 25 + chunkThreshold: null 26 + }, 27 + 28 + members: { 29 + _boards: null, 30 + 31 + _panOrigin: null, 32 + _panNode: null, 33 + _panX: null, 34 + 35 + start: function() { 36 + this._setupCoverImageHandlers(); 37 + this._setupPanHandlers(); 38 + this._setupEditHandlers(); 39 + 40 + return this; 41 + }, 42 + 43 + newBoard: function(phid, node) { 44 + var board = new JX.WorkboardBoard(this, phid, node); 45 + this._boards[phid] = board; 46 + return board; 47 + }, 48 + 49 + _getBoard: function(board_phid) { 50 + return this._boards[board_phid]; 51 + }, 52 + 53 + _setupCoverImageHandlers: function() { 54 + if (!JX.PhabricatorDragAndDropFileUpload.isSupported()) { 55 + return; 56 + } 57 + 58 + var drop = new JX.PhabricatorDragAndDropFileUpload('project-card') 59 + .setURI(this.getUploadURI()) 60 + .setChunkThreshold(this.getChunkThreshold()); 61 + 62 + drop.listen('didBeginDrag', function(node) { 63 + JX.DOM.alterClass(node, 'phui-workcard-upload-target', true); 64 + }); 65 + 66 + drop.listen('didEndDrag', function(node) { 67 + JX.DOM.alterClass(node, 'phui-workcard-upload-target', false); 68 + }); 69 + 70 + drop.listen('didUpload', JX.bind(this, this._oncoverupload)); 71 + 72 + drop.start(); 73 + }, 74 + 75 + _oncoverupload: function(file) { 76 + var node = file.getTargetNode(); 77 + 78 + var board = this._getBoardFromNode(node); 79 + 80 + var column_node = JX.DOM.findAbove(node, 'ul', 'project-column'); 81 + var column_phid = JX.Stratcom.getData(column_node).columnPHID; 82 + var column = board.getColumn(column_phid); 83 + 84 + var data = { 85 + boardPHID: board.getPHID(), 86 + objectPHID: JX.Stratcom.getData(node).objectPHID, 87 + filePHID: file.getPHID(), 88 + visiblePHIDs: column.getCardPHIDs() 89 + }; 90 + 91 + new JX.Workflow(this.getCoverURI(), data) 92 + .setHandler(JX.bind(board, board.updateCard)) 93 + .start(); 94 + }, 95 + 96 + _getBoardFromNode: function(node) { 97 + var board_node = JX.DOM.findAbove(node, 'div', 'jx-workboard'); 98 + var board_phid = JX.Stratcom.getData(board_node).boardPHID; 99 + return this._getBoard(board_phid); 100 + }, 101 + 102 + _setupPanHandlers: function() { 103 + var mousedown = JX.bind(this, this._onpanmousedown); 104 + var mousemove = JX.bind(this, this._onpanmousemove); 105 + var mouseup = JX.bind(this, this._onpanmouseup); 106 + 107 + JX.Stratcom.listen('mousedown', 'workboard-shadow', mousedown); 108 + JX.Stratcom.listen('mousemove', null, mousemove); 109 + JX.Stratcom.listen('mouseup', null, mouseup); 110 + }, 111 + 112 + _onpanmousedown: function(e) { 113 + if (!JX.Device.isDesktop()) { 114 + return; 115 + } 116 + 117 + if (e.getNode('workpanel')) { 118 + return; 119 + } 120 + 121 + if (JX.Stratcom.pass()) { 122 + return; 123 + } 124 + 125 + e.kill(); 126 + 127 + this._panOrigin = JX.$V(e); 128 + this._panNode = e.getNode('workboard-shadow'); 129 + this._panX = this._panNode.scrollLeft; 130 + }, 131 + 132 + _onpanmousemove: function(e) { 133 + if (!this._panOrigin) { 134 + return; 135 + } 136 + 137 + var cursor = JX.$V(e); 138 + this._panNode.scrollLeft = this._panX + (this._panOrigin.x - cursor.x); 139 + }, 140 + 141 + _onpanmouseup: function() { 142 + this._panOrigin = null; 143 + }, 144 + 145 + _setupEditHandlers: function() { 146 + var onadd = JX.bind(this, this._onaddcard); 147 + var onedit = JX.bind(this, this._oneditcard); 148 + 149 + JX.Stratcom.listen('click', 'column-add-task', onadd); 150 + JX.Stratcom.listen('click', 'edit-project-card', onedit); 151 + }, 152 + 153 + _onaddcard: function(e) { 154 + // We want the 'boards-dropdown-menu' behavior to see this event and 155 + // close the dropdown, but don't want to follow the link. 156 + e.prevent(); 157 + 158 + var column_data = e.getNodeData('column-add-task'); 159 + var column_phid = column_data.columnPHID; 160 + 161 + var board_phid = column_data.projectPHID; 162 + var board = this._getBoard(board_phid); 163 + var column = board.getColumn(column_phid); 164 + 165 + var request_data = { 166 + responseType: 'card', 167 + columnPHID: column.getPHID(), 168 + projects: board.getPHID(), 169 + visiblePHIDs: column.getCardPHIDs(), 170 + order: board.getOrder() 171 + }; 172 + 173 + new JX.Workflow(this.getCreateURI(), request_data) 174 + .setHandler(JX.bind(board, board.updateCard)) 175 + .start(); 176 + }, 177 + 178 + _oneditcard: function(e) { 179 + e.kill(); 180 + 181 + var column_node = e.getNode('project-column'); 182 + var column_phid = JX.Stratcom.getData(column_node).columnPHID; 183 + 184 + var board = this._getBoardFromNode(column_node); 185 + var column = board.getColumn(column_phid); 186 + 187 + var request_data = { 188 + responseType: 'card', 189 + columnPHID: column.getPHID(), 190 + visiblePHIDs: column.getCardPHIDs(), 191 + order: board.getOrder() 192 + }; 193 + 194 + new JX.Workflow(e.getNode('tag:a').href, request_data) 195 + .setHandler(JX.bind(board, board.updateCard)) 196 + .start(); 197 + } 198 + 199 + } 200 + 201 + });
+29 -115
webroot/rsrc/js/application/projects/behavior-project-boards.js
··· 6 6 * javelin-vector 7 7 * javelin-stratcom 8 8 * javelin-workflow 9 - * phabricator-draggable-list 10 - * phabricator-drag-and-drop-file-upload 11 - * javelin-workboard 9 + * javelin-workboard-controller 12 10 */ 13 11 14 12 JX.behavior('project-boards', function(config, statics) { ··· 61 59 } 62 60 } 63 61 64 - 65 - function colsort(u, v) { 66 - var ud = JX.Stratcom.getData(u).sort || []; 67 - var vd = JX.Stratcom.getData(v).sort || []; 68 - 69 - for (var ii = 0; ii < ud.length; ii++) { 70 - 71 - if (parseInt(ud[ii]) < parseInt(vd[ii])) { 72 - return 1; 73 - } 74 - if (parseInt(ud[ii]) > parseInt(vd[ii])) { 75 - return -1; 76 - } 77 - } 78 - 79 - return 0; 80 - } 81 - 82 - function onedit(column, r) { 83 - var new_card = JX.$H(r.tasks).getNode(); 84 - var new_data = JX.Stratcom.getData(new_card); 85 - var items = finditems(column); 86 - var edited = false; 87 - var remove_index = null; 88 - 89 - for (var ii = 0; ii < items.length; ii++) { 90 - var item = items[ii]; 91 - 92 - var data = JX.Stratcom.getData(item); 93 - var phid = data.objectPHID; 94 - 95 - if (phid == new_data.objectPHID) { 96 - if (r.data.removeFromBoard) { 97 - remove_index = ii; 98 - } 99 - items[ii] = new_card; 100 - data = new_data; 101 - edited = true; 102 - } 103 - 104 - data.sort = r.data.sortMap[data.objectPHID] || data.sort; 105 - } 106 - 107 - // this is an add then...! 108 - if (!edited) { 109 - items[items.length + 1] = new_card; 110 - new_data.sort = r.data.sortMap[new_data.objectPHID] || new_data.sort; 111 - } 112 - 113 - if (remove_index !== null) { 114 - items.splice(remove_index, 1); 115 - } 116 - 117 - items.sort(colsort); 118 - 119 - JX.DOM.setContent(column, items); 120 - 121 - onupdate(column); 122 - }; 123 - 124 62 function update_statics(update_config) { 125 63 statics.boardID = update_config.boardID; 126 64 statics.projectPHID = update_config.projectPHID; ··· 130 68 } 131 69 132 70 function setup() { 133 - 134 - JX.Stratcom.listen( 135 - 'click', 136 - ['edit-project-card'], 137 - function(e) { 138 - e.kill(); 139 - var column = e.getNode('project-column'); 140 - var request_data = { 141 - responseType: 'card', 142 - columnPHID: JX.Stratcom.getData(column).columnPHID, 143 - order: statics.order 144 - }; 145 - new JX.Workflow(e.getNode('tag:a').href, request_data) 146 - .setHandler(JX.bind(null, onedit, column)) 147 - .start(); 148 - }); 149 - 150 - JX.Stratcom.listen( 151 - 'click', 152 - ['column-add-task'], 153 - function (e) { 154 - 155 - // We want the 'boards-dropdown-menu' behavior to see this event and 156 - // close the dropdown, but don't want to follow the link. 157 - e.prevent(); 158 - 159 - var column_data = e.getNodeData('column-add-task'); 160 - var column_phid = column_data.columnPHID; 161 - 162 - var request_data = { 163 - responseType: 'card', 164 - columnPHID: column_phid, 165 - projects: column_data.projectPHID, 166 - order: statics.order 167 - }; 168 - 169 - var cols = getcolumns(); 170 - var ii; 171 - var column; 172 - for (ii = 0; ii < cols.length; ii++) { 173 - if (JX.Stratcom.getData(cols[ii]).columnPHID == column_phid) { 174 - column = cols[ii]; 175 - break; 176 - } 177 - } 178 - new JX.Workflow(statics.createURI, request_data) 179 - .setHandler(JX.bind(null, onedit, column)) 180 - .start(); 181 - }); 182 - 183 71 JX.Stratcom.listen('click', 'boards-dropdown-menu', function(e) { 184 72 var data = e.getNodeData('boards-dropdown-menu'); 185 73 if (data.menu) { ··· 234 122 } 235 123 236 124 if (!statics.workboard) { 237 - statics.workboard = new JX.Workboard(config) 125 + statics.workboard = new JX.WorkboardController() 238 126 .setUploadURI(config.uploadURI) 239 127 .setCoverURI(config.coverURI) 240 128 .setMoveURI(config.moveURI) 129 + .setCreateURI(config.createURI) 241 130 .setChunkThreshold(config.chunkThreshold) 242 131 .start(); 243 132 } 244 133 245 - statics.workboard.addBoard(config.projectPHID, JX.$(config.boardID)); 134 + var board_phid = config.projectPHID; 135 + var board_node = JX.$(config.boardID); 136 + 137 + var board = statics.workboard.newBoard(board_phid, board_node) 138 + .setOrder(config.order); 139 + 140 + var templates = config.templateMap; 141 + for (var k in templates) { 142 + board.setCardTemplate(k, templates[k]); 143 + } 144 + 145 + var column_maps = config.columnMaps; 146 + for (var column_phid in column_maps) { 147 + var column = board.getColumn(column_phid); 148 + var column_map = column_maps[column_phid]; 149 + for (var ii = 0; ii < column_map.length; ii++) { 150 + column.newCard(column_map[ii]); 151 + } 152 + } 153 + 154 + var order_maps = config.orderMaps; 155 + for (var object_phid in order_maps) { 156 + board.setOrderMap(object_phid, order_maps[object_phid]); 157 + } 158 + 159 + board.start(); 246 160 247 161 });