mirror of
https://we.phorge.it/source/phorge.git
synced 2024-11-18 21:02:41 +01:00
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
This commit is contained in:
parent
01084bfe22
commit
0bf3519045
15 changed files with 936 additions and 542 deletions
|
@ -415,8 +415,11 @@ return array(
|
||||||
'rsrc/js/application/phortune/phortune-credit-card-form.js' => '2290aeef',
|
'rsrc/js/application/phortune/phortune-credit-card-form.js' => '2290aeef',
|
||||||
'rsrc/js/application/policy/behavior-policy-control.js' => 'd0c516d5',
|
'rsrc/js/application/policy/behavior-policy-control.js' => 'd0c516d5',
|
||||||
'rsrc/js/application/policy/behavior-policy-rule-editor.js' => '5e9f347c',
|
'rsrc/js/application/policy/behavior-policy-rule-editor.js' => '5e9f347c',
|
||||||
'rsrc/js/application/projects/Workboard.js' => '088b2495',
|
'rsrc/js/application/projects/WorkboardBoard.js' => '069d6dd3',
|
||||||
'rsrc/js/application/projects/behavior-project-boards.js' => '37eb99e4',
|
'rsrc/js/application/projects/WorkboardCard.js' => '2fcefa17',
|
||||||
|
'rsrc/js/application/projects/WorkboardColumn.js' => 'e8f303bb',
|
||||||
|
'rsrc/js/application/projects/WorkboardController.js' => 'fa1378c3',
|
||||||
|
'rsrc/js/application/projects/behavior-project-boards.js' => 'e1b56d72',
|
||||||
'rsrc/js/application/projects/behavior-project-create.js' => '065227cc',
|
'rsrc/js/application/projects/behavior-project-create.js' => '065227cc',
|
||||||
'rsrc/js/application/projects/behavior-reorder-columns.js' => 'e1d25dfb',
|
'rsrc/js/application/projects/behavior-reorder-columns.js' => 'e1d25dfb',
|
||||||
'rsrc/js/application/releeph/releeph-preview-branch.js' => 'b2b4fbaf',
|
'rsrc/js/application/releeph/releeph-preview-branch.js' => 'b2b4fbaf',
|
||||||
|
@ -656,7 +659,7 @@ return array(
|
||||||
'javelin-behavior-phui-profile-menu' => '12884df9',
|
'javelin-behavior-phui-profile-menu' => '12884df9',
|
||||||
'javelin-behavior-policy-control' => 'd0c516d5',
|
'javelin-behavior-policy-control' => 'd0c516d5',
|
||||||
'javelin-behavior-policy-rule-editor' => '5e9f347c',
|
'javelin-behavior-policy-rule-editor' => '5e9f347c',
|
||||||
'javelin-behavior-project-boards' => '37eb99e4',
|
'javelin-behavior-project-boards' => 'e1b56d72',
|
||||||
'javelin-behavior-project-create' => '065227cc',
|
'javelin-behavior-project-create' => '065227cc',
|
||||||
'javelin-behavior-quicksand-blacklist' => '7927a7d3',
|
'javelin-behavior-quicksand-blacklist' => '7927a7d3',
|
||||||
'javelin-behavior-recurring-edit' => '5f1c4d5f',
|
'javelin-behavior-recurring-edit' => '5f1c4d5f',
|
||||||
|
@ -723,7 +726,10 @@ return array(
|
||||||
'javelin-view-renderer' => '6c2b09a2',
|
'javelin-view-renderer' => '6c2b09a2',
|
||||||
'javelin-view-visitor' => 'efe49472',
|
'javelin-view-visitor' => 'efe49472',
|
||||||
'javelin-websocket' => 'e292eaf4',
|
'javelin-websocket' => 'e292eaf4',
|
||||||
'javelin-workboard' => '088b2495',
|
'javelin-workboard-board' => '069d6dd3',
|
||||||
|
'javelin-workboard-card' => '2fcefa17',
|
||||||
|
'javelin-workboard-column' => 'e8f303bb',
|
||||||
|
'javelin-workboard-controller' => 'fa1378c3',
|
||||||
'javelin-workflow' => '5b2e3e2b',
|
'javelin-workflow' => '5b2e3e2b',
|
||||||
'lightbox-attachment-css' => '7acac05d',
|
'lightbox-attachment-css' => '7acac05d',
|
||||||
'maniphest-batch-editor' => 'b0f0b6d5',
|
'maniphest-batch-editor' => 'b0f0b6d5',
|
||||||
|
@ -913,6 +919,15 @@ return array(
|
||||||
'javelin-stratcom',
|
'javelin-stratcom',
|
||||||
'javelin-workflow',
|
'javelin-workflow',
|
||||||
),
|
),
|
||||||
|
'069d6dd3' => array(
|
||||||
|
'javelin-install',
|
||||||
|
'javelin-dom',
|
||||||
|
'javelin-util',
|
||||||
|
'javelin-stratcom',
|
||||||
|
'javelin-workflow',
|
||||||
|
'phabricator-draggable-list',
|
||||||
|
'javelin-workboard-column',
|
||||||
|
),
|
||||||
'06c32383' => array(
|
'06c32383' => array(
|
||||||
'javelin-behavior',
|
'javelin-behavior',
|
||||||
'javelin-typeahead-ondemand-source',
|
'javelin-typeahead-ondemand-source',
|
||||||
|
@ -930,16 +945,6 @@ return array(
|
||||||
'javelin-stratcom',
|
'javelin-stratcom',
|
||||||
'javelin-vector',
|
'javelin-vector',
|
||||||
),
|
),
|
||||||
'088b2495' => array(
|
|
||||||
'javelin-install',
|
|
||||||
'javelin-dom',
|
|
||||||
'javelin-util',
|
|
||||||
'javelin-vector',
|
|
||||||
'javelin-stratcom',
|
|
||||||
'javelin-workflow',
|
|
||||||
'phabricator-draggable-list',
|
|
||||||
'phabricator-drag-and-drop-file-upload',
|
|
||||||
),
|
|
||||||
'0a3f3021' => array(
|
'0a3f3021' => array(
|
||||||
'javelin-behavior',
|
'javelin-behavior',
|
||||||
'javelin-stratcom',
|
'javelin-stratcom',
|
||||||
|
@ -1077,6 +1082,9 @@ return array(
|
||||||
'2ee659ce' => array(
|
'2ee659ce' => array(
|
||||||
'javelin-install',
|
'javelin-install',
|
||||||
),
|
),
|
||||||
|
'2fcefa17' => array(
|
||||||
|
'javelin-install',
|
||||||
|
),
|
||||||
'327a00d1' => array(
|
'327a00d1' => array(
|
||||||
'javelin-behavior',
|
'javelin-behavior',
|
||||||
'javelin-stratcom',
|
'javelin-stratcom',
|
||||||
|
@ -1096,17 +1104,6 @@ return array(
|
||||||
'javelin-vector',
|
'javelin-vector',
|
||||||
'phuix-autocomplete',
|
'phuix-autocomplete',
|
||||||
),
|
),
|
||||||
'37eb99e4' => array(
|
|
||||||
'javelin-behavior',
|
|
||||||
'javelin-dom',
|
|
||||||
'javelin-util',
|
|
||||||
'javelin-vector',
|
|
||||||
'javelin-stratcom',
|
|
||||||
'javelin-workflow',
|
|
||||||
'phabricator-draggable-list',
|
|
||||||
'phabricator-drag-and-drop-file-upload',
|
|
||||||
'javelin-workboard',
|
|
||||||
),
|
|
||||||
'3ab51e2c' => array(
|
'3ab51e2c' => array(
|
||||||
'javelin-behavior',
|
'javelin-behavior',
|
||||||
'javelin-behavior-device',
|
'javelin-behavior-device',
|
||||||
|
@ -1937,6 +1934,15 @@ return array(
|
||||||
'javelin-dom',
|
'javelin-dom',
|
||||||
'phabricator-prefab',
|
'phabricator-prefab',
|
||||||
),
|
),
|
||||||
|
'e1b56d72' => array(
|
||||||
|
'javelin-behavior',
|
||||||
|
'javelin-dom',
|
||||||
|
'javelin-util',
|
||||||
|
'javelin-vector',
|
||||||
|
'javelin-stratcom',
|
||||||
|
'javelin-workflow',
|
||||||
|
'javelin-workboard-controller',
|
||||||
|
),
|
||||||
'e1d25dfb' => array(
|
'e1d25dfb' => array(
|
||||||
'javelin-behavior',
|
'javelin-behavior',
|
||||||
'javelin-stratcom',
|
'javelin-stratcom',
|
||||||
|
@ -2004,6 +2010,10 @@ return array(
|
||||||
'e6e25838' => array(
|
'e6e25838' => array(
|
||||||
'javelin-install',
|
'javelin-install',
|
||||||
),
|
),
|
||||||
|
'e8f303bb' => array(
|
||||||
|
'javelin-install',
|
||||||
|
'javelin-workboard-card',
|
||||||
|
),
|
||||||
'e9581f08' => array(
|
'e9581f08' => array(
|
||||||
'javelin-behavior',
|
'javelin-behavior',
|
||||||
'javelin-stratcom',
|
'javelin-stratcom',
|
||||||
|
@ -2088,6 +2098,16 @@ return array(
|
||||||
'javelin-vector',
|
'javelin-vector',
|
||||||
'javelin-magical-init',
|
'javelin-magical-init',
|
||||||
),
|
),
|
||||||
|
'fa1378c3' => array(
|
||||||
|
'javelin-install',
|
||||||
|
'javelin-dom',
|
||||||
|
'javelin-util',
|
||||||
|
'javelin-vector',
|
||||||
|
'javelin-stratcom',
|
||||||
|
'javelin-workflow',
|
||||||
|
'phabricator-drag-and-drop-file-upload',
|
||||||
|
'javelin-workboard-board',
|
||||||
|
),
|
||||||
'fb20ac8d' => array(
|
'fb20ac8d' => array(
|
||||||
'javelin-behavior',
|
'javelin-behavior',
|
||||||
'javelin-aphlict',
|
'javelin-aphlict',
|
||||||
|
|
|
@ -1820,6 +1820,7 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorBitbucketAuthProvider' => 'applications/auth/provider/PhabricatorBitbucketAuthProvider.php',
|
'PhabricatorBitbucketAuthProvider' => 'applications/auth/provider/PhabricatorBitbucketAuthProvider.php',
|
||||||
'PhabricatorBoardLayoutEngine' => 'applications/project/engine/PhabricatorBoardLayoutEngine.php',
|
'PhabricatorBoardLayoutEngine' => 'applications/project/engine/PhabricatorBoardLayoutEngine.php',
|
||||||
'PhabricatorBoardRenderingEngine' => 'applications/project/engine/PhabricatorBoardRenderingEngine.php',
|
'PhabricatorBoardRenderingEngine' => 'applications/project/engine/PhabricatorBoardRenderingEngine.php',
|
||||||
|
'PhabricatorBoardResponseEngine' => 'applications/project/engine/PhabricatorBoardResponseEngine.php',
|
||||||
'PhabricatorBot' => 'infrastructure/daemon/bot/PhabricatorBot.php',
|
'PhabricatorBot' => 'infrastructure/daemon/bot/PhabricatorBot.php',
|
||||||
'PhabricatorBotChannel' => 'infrastructure/daemon/bot/target/PhabricatorBotChannel.php',
|
'PhabricatorBotChannel' => 'infrastructure/daemon/bot/target/PhabricatorBotChannel.php',
|
||||||
'PhabricatorBotDebugLogHandler' => 'infrastructure/daemon/bot/handler/PhabricatorBotDebugLogHandler.php',
|
'PhabricatorBotDebugLogHandler' => 'infrastructure/daemon/bot/handler/PhabricatorBotDebugLogHandler.php',
|
||||||
|
@ -6058,6 +6059,7 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorBitbucketAuthProvider' => 'PhabricatorOAuth1AuthProvider',
|
'PhabricatorBitbucketAuthProvider' => 'PhabricatorOAuth1AuthProvider',
|
||||||
'PhabricatorBoardLayoutEngine' => 'Phobject',
|
'PhabricatorBoardLayoutEngine' => 'Phobject',
|
||||||
'PhabricatorBoardRenderingEngine' => 'Phobject',
|
'PhabricatorBoardRenderingEngine' => 'Phobject',
|
||||||
|
'PhabricatorBoardResponseEngine' => 'Phobject',
|
||||||
'PhabricatorBot' => 'PhabricatorDaemon',
|
'PhabricatorBot' => 'PhabricatorDaemon',
|
||||||
'PhabricatorBotChannel' => 'PhabricatorBotTarget',
|
'PhabricatorBotChannel' => 'PhabricatorBotTarget',
|
||||||
'PhabricatorBotDebugLogHandler' => 'PhabricatorBotHandler',
|
'PhabricatorBotDebugLogHandler' => 'PhabricatorBotHandler',
|
||||||
|
|
|
@ -9,6 +9,7 @@ final class ManiphestTaskEditController extends ManiphestController {
|
||||||
->addContextParameter('responseType')
|
->addContextParameter('responseType')
|
||||||
->addContextParameter('columnPHID')
|
->addContextParameter('columnPHID')
|
||||||
->addContextParameter('order')
|
->addContextParameter('order')
|
||||||
|
->addContextParameter('visiblePHIDs')
|
||||||
->buildResponse();
|
->buildResponse();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -289,7 +289,11 @@ final class ManiphestEditEngine
|
||||||
$viewer = $request->getViewer();
|
$viewer = $request->getViewer();
|
||||||
|
|
||||||
$column_phid = $request->getStr('columnPHID');
|
$column_phid = $request->getStr('columnPHID');
|
||||||
$order = $request->getStr('order');
|
|
||||||
|
$visible_phids = $request->getStrList('visiblePHIDs');
|
||||||
|
if (!$visible_phids) {
|
||||||
|
$visible_phids = array();
|
||||||
|
}
|
||||||
|
|
||||||
$column = id(new PhabricatorProjectColumnQuery())
|
$column = id(new PhabricatorProjectColumnQuery())
|
||||||
->setViewer($viewer)
|
->setViewer($viewer)
|
||||||
|
@ -299,98 +303,15 @@ final class ManiphestEditEngine
|
||||||
return new Aphront404Response();
|
return new Aphront404Response();
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the workboard's project and all descendant projects have been removed
|
|
||||||
// from the card's project list, we are going to remove it from the board
|
|
||||||
// completely.
|
|
||||||
|
|
||||||
// TODO: If the user did something sneaky and changed a subproject, we'll
|
|
||||||
// currently leave the card where it was but should really move it to the
|
|
||||||
// proper new column.
|
|
||||||
|
|
||||||
$board_phid = $column->getProjectPHID();
|
$board_phid = $column->getProjectPHID();
|
||||||
|
$object_phid = $task->getPHID();
|
||||||
|
|
||||||
$descendant_projects = id(new PhabricatorProjectQuery())
|
return id(new PhabricatorBoardResponseEngine())
|
||||||
->setViewer($viewer)
|
->setViewer($viewer)
|
||||||
->withAncestorProjectPHIDs(array($column->getProjectPHID()))
|
->setBoardPHID($board_phid)
|
||||||
->execute();
|
->setObjectPHID($object_phid)
|
||||||
$board_phids = mpull($descendant_projects, 'getPHID', 'getPHID');
|
->setVisiblePHIDs($visible_phids)
|
||||||
$board_phids[$board_phid] = $board_phid;
|
->buildResponse();
|
||||||
|
|
||||||
$project_map = array_fuse($task->getProjectPHIDs());
|
|
||||||
$remove_card = !array_intersect_key($board_phids, $project_map);
|
|
||||||
|
|
||||||
// TODO: Maybe the caller should pass a list of visible task PHIDs so we
|
|
||||||
// know which ones we need to reorder? This is a HUGE overfetch.
|
|
||||||
$objects = id(new ManiphestTaskQuery())
|
|
||||||
->setViewer($viewer)
|
|
||||||
->withEdgeLogicPHIDs(
|
|
||||||
PhabricatorProjectObjectHasProjectEdgeType::EDGECONST,
|
|
||||||
PhabricatorQueryConstraint::OPERATOR_ANCESTOR,
|
|
||||||
array($board_phids))
|
|
||||||
->setViewer($viewer)
|
|
||||||
->execute();
|
|
||||||
$objects = mpull($objects, null, 'getPHID');
|
|
||||||
|
|
||||||
$layout_engine = id(new PhabricatorBoardLayoutEngine())
|
|
||||||
->setViewer($viewer)
|
|
||||||
->setBoardPHIDs(array($board_phid))
|
|
||||||
->setObjectPHIDs(array_keys($objects))
|
|
||||||
->executeLayout();
|
|
||||||
|
|
||||||
$positions = $layout_engine->getColumnObjectPositions(
|
|
||||||
$board_phid,
|
|
||||||
$column_phid);
|
|
||||||
|
|
||||||
$column_phids = $layout_engine->getColumnObjectPHIDs(
|
|
||||||
$board_phid,
|
|
||||||
$column_phid);
|
|
||||||
|
|
||||||
$column_tasks = array_select_keys($objects, $column_phids);
|
|
||||||
|
|
||||||
if ($order == PhabricatorProjectColumn::ORDER_NATURAL) {
|
|
||||||
// TODO: This is a little bit awkward, because PHP and JS use
|
|
||||||
// slightly different sort order parameters to achieve the same
|
|
||||||
// effect. It would be good to unify this a bit at some point.
|
|
||||||
$sort_map = array();
|
|
||||||
foreach ($positions as $position) {
|
|
||||||
$sort_map[$position->getObjectPHID()] = array(
|
|
||||||
-$position->getSequence(),
|
|
||||||
$position->getID(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
$sort_map = mpull(
|
|
||||||
$column_tasks,
|
|
||||||
'getPrioritySortVector',
|
|
||||||
'getPHID');
|
|
||||||
}
|
|
||||||
|
|
||||||
$data = array(
|
|
||||||
'removeFromBoard' => $remove_card,
|
|
||||||
'sortMap' => $sort_map,
|
|
||||||
);
|
|
||||||
|
|
||||||
$rendering_engine = id(new PhabricatorBoardRenderingEngine())
|
|
||||||
->setViewer($viewer)
|
|
||||||
->setObjects(array($task))
|
|
||||||
->setExcludedProjectPHIDs($board_phids);
|
|
||||||
|
|
||||||
$card = $rendering_engine->renderCard($task->getPHID());
|
|
||||||
|
|
||||||
$item = $card->getItem();
|
|
||||||
$item->addClass('phui-workcard');
|
|
||||||
|
|
||||||
$payload = array(
|
|
||||||
'tasks' => $item,
|
|
||||||
'data' => $data,
|
|
||||||
);
|
|
||||||
|
|
||||||
return id(new AphrontAjaxResponse())
|
|
||||||
->setContent(
|
|
||||||
array(
|
|
||||||
'tasks' => $item,
|
|
||||||
'data' => $data,
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -194,14 +194,6 @@ final class ManiphestTask extends ManiphestDAO
|
||||||
return ManiphestTaskStatus::isClosedStatus($this->getStatus());
|
return ManiphestTaskStatus::isClosedStatus($this->getStatus());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getPrioritySortVector() {
|
|
||||||
return array(
|
|
||||||
$this->getPriority(),
|
|
||||||
-$this->getSubpriority(),
|
|
||||||
$this->getID(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function setProperty($key, $value) {
|
public function setProperty($key, $value) {
|
||||||
$this->properties[$key] = $value;
|
$this->properties[$key] = $value;
|
||||||
return $this;
|
return $this;
|
||||||
|
@ -219,6 +211,16 @@ final class ManiphestTask extends ManiphestDAO
|
||||||
return idx($this->properties, 'cover.thumbnailPHID');
|
return idx($this->properties, 'cover.thumbnailPHID');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getWorkboardOrderVectors() {
|
||||||
|
return array(
|
||||||
|
PhabricatorProjectColumn::ORDER_PRIORITY => array(
|
||||||
|
(int)-$this->getPriority(),
|
||||||
|
(double)-$this->getSubpriority(),
|
||||||
|
(int)-$this->getID(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* -( PhabricatorSubscribableInterface )----------------------------------- */
|
/* -( PhabricatorSubscribableInterface )----------------------------------- */
|
||||||
|
|
||||||
|
|
|
@ -238,20 +238,6 @@ final class PhabricatorProjectBoardViewController
|
||||||
'boardPHID' => $project->getPHID(),
|
'boardPHID' => $project->getPHID(),
|
||||||
));
|
));
|
||||||
|
|
||||||
$behavior_config = array(
|
|
||||||
'boardID' => $board_id,
|
|
||||||
'projectPHID' => $project->getPHID(),
|
|
||||||
'moveURI' => $this->getApplicationURI('move/'.$project->getID().'/'),
|
|
||||||
'createURI' => $this->getCreateURI(),
|
|
||||||
'uploadURI' => '/file/dropupload/',
|
|
||||||
'coverURI' => $this->getApplicationURI('cover/'),
|
|
||||||
'chunkThreshold' => PhabricatorFileStorageEngine::getChunkThreshold(),
|
|
||||||
'order' => $this->sortKey,
|
|
||||||
);
|
|
||||||
$this->initBehavior(
|
|
||||||
'project-boards',
|
|
||||||
$behavior_config);
|
|
||||||
|
|
||||||
$visible_columns = array();
|
$visible_columns = array();
|
||||||
$column_phids = array();
|
$column_phids = array();
|
||||||
$visible_phids = array();
|
$visible_phids = array();
|
||||||
|
@ -297,6 +283,9 @@ final class PhabricatorProjectBoardViewController
|
||||||
->setEditMap($task_can_edit_map)
|
->setEditMap($task_can_edit_map)
|
||||||
->setExcludedProjectPHIDs($select_phids);
|
->setExcludedProjectPHIDs($select_phids);
|
||||||
|
|
||||||
|
$templates = array();
|
||||||
|
$column_maps = array();
|
||||||
|
$all_tasks = array();
|
||||||
foreach ($visible_columns as $column_phid => $column) {
|
foreach ($visible_columns as $column_phid => $column) {
|
||||||
$column_tasks = $column_phids[$column_phid];
|
$column_tasks = $column_phids[$column_phid];
|
||||||
|
|
||||||
|
@ -356,14 +345,35 @@ final class PhabricatorProjectBoardViewController
|
||||||
));
|
));
|
||||||
|
|
||||||
foreach ($column_tasks as $task) {
|
foreach ($column_tasks as $task) {
|
||||||
$card = $rendering_engine->renderCard($task->getPHID());
|
$object_phid = $task->getPHID();
|
||||||
$cards->addItem($card->getItem());
|
|
||||||
|
$card = $rendering_engine->renderCard($object_phid);
|
||||||
|
$templates[$object_phid] = hsprintf('%s', $card->getItem());
|
||||||
|
$column_maps[$column_phid][] = $object_phid;
|
||||||
|
|
||||||
|
$all_tasks[$object_phid] = $task;
|
||||||
}
|
}
|
||||||
|
|
||||||
$panel->setCards($cards);
|
$panel->setCards($cards);
|
||||||
$board->addPanel($panel);
|
$board->addPanel($panel);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$behavior_config = array(
|
||||||
|
'boardID' => $board_id,
|
||||||
|
'projectPHID' => $project->getPHID(),
|
||||||
|
'moveURI' => $this->getApplicationURI('move/'.$project->getID().'/'),
|
||||||
|
'createURI' => $this->getCreateURI(),
|
||||||
|
'uploadURI' => '/file/dropupload/',
|
||||||
|
'coverURI' => $this->getApplicationURI('cover/'),
|
||||||
|
'chunkThreshold' => PhabricatorFileStorageEngine::getChunkThreshold(),
|
||||||
|
'order' => $this->sortKey,
|
||||||
|
'templateMap' => $templates,
|
||||||
|
'columnMaps' => $column_maps,
|
||||||
|
'orderMaps' => mpull($all_tasks, 'getWorkboardOrderVectors'),
|
||||||
|
);
|
||||||
|
$this->initBehavior('project-boards', $behavior_config);
|
||||||
|
|
||||||
|
|
||||||
$sort_menu = $this->buildSortMenu(
|
$sort_menu = $this->buildSortMenu(
|
||||||
$viewer,
|
$viewer,
|
||||||
$this->sortKey);
|
$this->sortKey);
|
||||||
|
|
|
@ -150,51 +150,18 @@ abstract class PhabricatorProjectController extends PhabricatorController {
|
||||||
protected function newCardResponse($board_phid, $object_phid) {
|
protected function newCardResponse($board_phid, $object_phid) {
|
||||||
$viewer = $this->getViewer();
|
$viewer = $this->getViewer();
|
||||||
|
|
||||||
$project = id(new PhabricatorProjectQuery())
|
$request = $this->getRequest();
|
||||||
->setViewer($viewer)
|
$visible_phids = $request->getStrList('visiblePHIDs');
|
||||||
->withPHIDs(array($board_phid))
|
if (!$visible_phids) {
|
||||||
->executeOne();
|
$visible_phids = array();
|
||||||
if (!$project) {
|
|
||||||
return new Aphront404Response();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reload the object so it reflects edits which have been applied.
|
return id(new PhabricatorBoardResponseEngine())
|
||||||
$object = id(new ManiphestTaskQuery())
|
|
||||||
->setViewer($viewer)
|
->setViewer($viewer)
|
||||||
->withPHIDs(array($object_phid))
|
->setBoardPHID($board_phid)
|
||||||
->needProjectPHIDs(true)
|
->setObjectPHID($object_phid)
|
||||||
->executeOne();
|
->setVisiblePHIDs($visible_phids)
|
||||||
if (!$object) {
|
->buildResponse();
|
||||||
return new Aphront404Response();
|
|
||||||
}
|
|
||||||
|
|
||||||
$except_phids = array($board_phid);
|
|
||||||
if ($project->getHasSubprojects() || $project->getHasMilestones()) {
|
|
||||||
$descendants = id(new PhabricatorProjectQuery())
|
|
||||||
->setViewer($viewer)
|
|
||||||
->withAncestorProjectPHIDs($except_phids)
|
|
||||||
->execute();
|
|
||||||
foreach ($descendants as $descendant) {
|
|
||||||
$except_phids[] = $descendant->getPHID();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$rendering_engine = id(new PhabricatorBoardRenderingEngine())
|
|
||||||
->setViewer($viewer)
|
|
||||||
->setObjects(array($object))
|
|
||||||
->setExcludedProjectPHIDs($except_phids);
|
|
||||||
|
|
||||||
$card = $rendering_engine->renderCard($object->getPHID());
|
|
||||||
|
|
||||||
$item = $card->getItem();
|
|
||||||
$item->addClass('phui-workcard');
|
|
||||||
|
|
||||||
return id(new AphrontAjaxResponse())
|
|
||||||
->setContent(
|
|
||||||
array(
|
|
||||||
'objectPHID' => $object->getPHID(),
|
|
||||||
'cardHTML' => $item,
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,146 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class PhabricatorBoardResponseEngine extends Phobject {
|
||||||
|
|
||||||
|
private $viewer;
|
||||||
|
private $boardPHID;
|
||||||
|
private $objectPHID;
|
||||||
|
private $visiblePHIDs;
|
||||||
|
|
||||||
|
public function setViewer(PhabricatorUser $viewer) {
|
||||||
|
$this->viewer = $viewer;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getViewer() {
|
||||||
|
return $this->viewer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setBoardPHID($board_phid) {
|
||||||
|
$this->boardPHID = $board_phid;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getBoardPHID() {
|
||||||
|
return $this->boardPHID;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setObjectPHID($object_phid) {
|
||||||
|
$this->objectPHID = $object_phid;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getObjectPHID() {
|
||||||
|
return $this->objectPHID;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setVisiblePHIDs(array $visible_phids) {
|
||||||
|
$this->visiblePHIDs = $visible_phids;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getVisiblePHIDs() {
|
||||||
|
return $this->visiblePHIDs;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function buildResponse() {
|
||||||
|
$viewer = $this->getViewer();
|
||||||
|
$object_phid = $this->getObjectPHID();
|
||||||
|
$board_phid = $this->getBoardPHID();
|
||||||
|
|
||||||
|
// Load all the other tasks that are visible in the affected columns and
|
||||||
|
// perform layout for them.
|
||||||
|
$visible_phids = $this->getAllVisiblePHIDs();
|
||||||
|
|
||||||
|
$layout_engine = id(new PhabricatorBoardLayoutEngine())
|
||||||
|
->setViewer($viewer)
|
||||||
|
->setBoardPHIDs(array($board_phid))
|
||||||
|
->setObjectPHIDs($visible_phids)
|
||||||
|
->executeLayout();
|
||||||
|
|
||||||
|
$object_columns = $layout_engine->getObjectColumns(
|
||||||
|
$board_phid,
|
||||||
|
$object_phid);
|
||||||
|
|
||||||
|
$natural = array();
|
||||||
|
foreach ($object_columns as $column_phid => $column) {
|
||||||
|
$column_object_phids = $layout_engine->getColumnObjectPHIDs(
|
||||||
|
$board_phid,
|
||||||
|
$column_phid);
|
||||||
|
$natural[$column_phid] = array_values($column_object_phids);
|
||||||
|
}
|
||||||
|
|
||||||
|
$all_visible = id(new ManiphestTaskQuery())
|
||||||
|
->setViewer($viewer)
|
||||||
|
->withPHIDs($visible_phids)
|
||||||
|
->execute();
|
||||||
|
|
||||||
|
$order_maps = array();
|
||||||
|
foreach ($all_visible as $visible) {
|
||||||
|
$order_maps[$visible->getPHID()] = $visible->getWorkboardOrderVectors();
|
||||||
|
}
|
||||||
|
|
||||||
|
$template = $this->buildTemplate();
|
||||||
|
|
||||||
|
$payload = array(
|
||||||
|
'objectPHID' => $object_phid,
|
||||||
|
'cardHTML' => $template,
|
||||||
|
'columnMaps' => $natural,
|
||||||
|
'orderMaps' => $order_maps,
|
||||||
|
);
|
||||||
|
|
||||||
|
return id(new AphrontAjaxResponse())
|
||||||
|
->setContent($payload);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function buildTemplate() {
|
||||||
|
$viewer = $this->getViewer();
|
||||||
|
$object_phid = $this->getObjectPHID();
|
||||||
|
|
||||||
|
$excluded_phids = $this->loadExcludedProjectPHIDs();
|
||||||
|
|
||||||
|
$object = id(new ManiphestTaskQuery())
|
||||||
|
->setViewer($viewer)
|
||||||
|
->withPHIDs(array($object_phid))
|
||||||
|
->needProjectPHIDs(true)
|
||||||
|
->executeOne();
|
||||||
|
if (!$object) {
|
||||||
|
return new Aphront404Response();
|
||||||
|
}
|
||||||
|
|
||||||
|
$rendering_engine = id(new PhabricatorBoardRenderingEngine())
|
||||||
|
->setViewer($viewer)
|
||||||
|
->setObjects(array($object))
|
||||||
|
->setExcludedProjectPHIDs($excluded_phids);
|
||||||
|
|
||||||
|
$card = $rendering_engine->renderCard($object_phid);
|
||||||
|
|
||||||
|
return hsprintf('%s', $card->getItem());
|
||||||
|
}
|
||||||
|
|
||||||
|
private function loadExcludedProjectPHIDs() {
|
||||||
|
$viewer = $this->getViewer();
|
||||||
|
$board_phid = $this->getBoardPHID();
|
||||||
|
|
||||||
|
$exclude_phids = array($board_phid);
|
||||||
|
|
||||||
|
$descendants = id(new PhabricatorProjectQuery())
|
||||||
|
->setViewer($viewer)
|
||||||
|
->withAncestorProjectPHIDs($exclude_phids)
|
||||||
|
->execute();
|
||||||
|
|
||||||
|
foreach ($descendants as $descendant) {
|
||||||
|
$exclude_phids[] = $descendant->getPHID();
|
||||||
|
}
|
||||||
|
|
||||||
|
return array_fuse($exclude_phids);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getAllVisiblePHIDs() {
|
||||||
|
$visible_phids = $this->getVisiblePHIDs();
|
||||||
|
$visible_phids[] = $this->getObjectPHID();
|
||||||
|
$visible_phids = array_fuse($visible_phids);
|
||||||
|
return $visible_phids;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -78,10 +78,6 @@ final class ProjectBoardTaskCard extends Phobject {
|
||||||
->setHref('/T'.$task->getID())
|
->setHref('/T'.$task->getID())
|
||||||
->addSigil('project-card')
|
->addSigil('project-card')
|
||||||
->setDisabled($task->isClosed())
|
->setDisabled($task->isClosed())
|
||||||
->setMetadata(
|
|
||||||
array(
|
|
||||||
'objectPHID' => $task->getPHID(),
|
|
||||||
))
|
|
||||||
->addAction(
|
->addAction(
|
||||||
id(new PHUIListItemView())
|
id(new PHUIListItemView())
|
||||||
->setName(pht('Edit'))
|
->setName(pht('Edit'))
|
||||||
|
@ -115,6 +111,8 @@ final class ProjectBoardTaskCard extends Phobject {
|
||||||
$card->addAttribute($tag_list);
|
$card->addAttribute($tag_list);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$card->addClass('phui-workcard');
|
||||||
|
|
||||||
return $card;
|
return $card;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,242 +0,0 @@
|
||||||
/**
|
|
||||||
* @provides javelin-workboard
|
|
||||||
* @requires javelin-install
|
|
||||||
* javelin-dom
|
|
||||||
* javelin-util
|
|
||||||
* javelin-vector
|
|
||||||
* javelin-stratcom
|
|
||||||
* javelin-workflow
|
|
||||||
* phabricator-draggable-list
|
|
||||||
* phabricator-drag-and-drop-file-upload
|
|
||||||
* @javelin
|
|
||||||
*/
|
|
||||||
|
|
||||||
JX.install('Workboard', {
|
|
||||||
|
|
||||||
construct: function(config) {
|
|
||||||
this._config = config;
|
|
||||||
|
|
||||||
this._boardNodes = {};
|
|
||||||
this._columnMap = {};
|
|
||||||
},
|
|
||||||
|
|
||||||
properties: {
|
|
||||||
uploadURI: null,
|
|
||||||
coverURI: null,
|
|
||||||
moveURI: null,
|
|
||||||
chunkThreshold: null
|
|
||||||
},
|
|
||||||
|
|
||||||
members: {
|
|
||||||
_config: null,
|
|
||||||
_boardNodes: null,
|
|
||||||
_currentBoard: null,
|
|
||||||
|
|
||||||
_panOrigin: null,
|
|
||||||
_panNode: null,
|
|
||||||
_panX: null,
|
|
||||||
|
|
||||||
_columnMap: null,
|
|
||||||
|
|
||||||
start: function() {
|
|
||||||
this._setupCoverImageHandlers();
|
|
||||||
this._setupPanHandlers();
|
|
||||||
|
|
||||||
return this;
|
|
||||||
},
|
|
||||||
|
|
||||||
addBoard: function(board_phid, board_node) {
|
|
||||||
this._currentBoard = board_phid;
|
|
||||||
this._boardNodes[board_phid] = board_node;
|
|
||||||
this._setupDragHandlers(board_node);
|
|
||||||
},
|
|
||||||
|
|
||||||
_getConfig: function() {
|
|
||||||
return this._config;
|
|
||||||
},
|
|
||||||
|
|
||||||
_setupCoverImageHandlers: function() {
|
|
||||||
if (!JX.PhabricatorDragAndDropFileUpload.isSupported()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var drop = new JX.PhabricatorDragAndDropFileUpload('project-card')
|
|
||||||
.setURI(this.getUploadURI())
|
|
||||||
.setChunkThreshold(this.getChunkThreshold());
|
|
||||||
|
|
||||||
drop.listen('didBeginDrag', function(node) {
|
|
||||||
JX.DOM.alterClass(node, 'phui-workcard-upload-target', true);
|
|
||||||
});
|
|
||||||
|
|
||||||
drop.listen('didEndDrag', function(node) {
|
|
||||||
JX.DOM.alterClass(node, 'phui-workcard-upload-target', false);
|
|
||||||
});
|
|
||||||
|
|
||||||
drop.listen('didUpload', JX.bind(this, this._oncoverupload));
|
|
||||||
|
|
||||||
drop.start();
|
|
||||||
},
|
|
||||||
|
|
||||||
_oncoverupload: function(file) {
|
|
||||||
var node = file.getTargetNode();
|
|
||||||
var board = JX.DOM.findAbove(node, 'div', 'jx-workboard');
|
|
||||||
|
|
||||||
var data = {
|
|
||||||
boardPHID: JX.Stratcom.getData(board).boardPHID,
|
|
||||||
objectPHID: JX.Stratcom.getData(node).objectPHID,
|
|
||||||
filePHID: file.getPHID()
|
|
||||||
};
|
|
||||||
|
|
||||||
new JX.Workflow(this.getCoverURI(), data)
|
|
||||||
.setHandler(JX.bind(this, this._queueCardUpdate))
|
|
||||||
.start();
|
|
||||||
},
|
|
||||||
|
|
||||||
_setupPanHandlers: function() {
|
|
||||||
var mousedown = JX.bind(this, this._onpanmousedown);
|
|
||||||
var mousemove = JX.bind(this, this._onpanmousemove);
|
|
||||||
var mouseup = JX.bind(this, this._onpanmouseup);
|
|
||||||
|
|
||||||
JX.Stratcom.listen('mousedown', 'workboard-shadow', mousedown);
|
|
||||||
JX.Stratcom.listen('mousemove', null, mousemove);
|
|
||||||
JX.Stratcom.listen('mouseup', null, mouseup);
|
|
||||||
},
|
|
||||||
|
|
||||||
_onpanmousedown: function(e) {
|
|
||||||
if (!JX.Device.isDesktop()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (e.getNode('workpanel')) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (JX.Stratcom.pass()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
e.kill();
|
|
||||||
|
|
||||||
this._panOrigin = JX.$V(e);
|
|
||||||
this._panNode = e.getNode('workboard-shadow');
|
|
||||||
this._panX = this._panNode.scrollLeft;
|
|
||||||
},
|
|
||||||
|
|
||||||
_onpanmousemove: function(e) {
|
|
||||||
if (!this._panOrigin) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var cursor = JX.$V(e);
|
|
||||||
this._panNode.scrollLeft = this._panX + (this._panOrigin.x - cursor.x);
|
|
||||||
},
|
|
||||||
|
|
||||||
_onpanmouseup: function() {
|
|
||||||
this._panOrigin = null;
|
|
||||||
},
|
|
||||||
|
|
||||||
|
|
||||||
_setupDragHandlers: function(board_node) {
|
|
||||||
var columns = this._findBoardColumns(board_node);
|
|
||||||
var column;
|
|
||||||
var ii;
|
|
||||||
var lists = [];
|
|
||||||
|
|
||||||
for (ii = 0; ii < columns.length; ii++) {
|
|
||||||
column = columns[ii];
|
|
||||||
|
|
||||||
var list = new JX.DraggableList('project-card', column)
|
|
||||||
.setOuterContainer(board_node)
|
|
||||||
.setFindItemsHandler(JX.bind(this, this._findCardsInColumn, column))
|
|
||||||
.setCanDragX(true)
|
|
||||||
.setHasInfiniteHeight(true);
|
|
||||||
|
|
||||||
// TODO: Restore these behaviors.
|
|
||||||
// list.listen('didSend', JX.bind(list, onupdate, cols[ii]));
|
|
||||||
// list.listen('didReceive', JX.bind(list, onupdate, cols[ii]));
|
|
||||||
// onupdate(cols[ii]);
|
|
||||||
|
|
||||||
list.listen('didDrop', JX.bind(this, this._onmovecard, list));
|
|
||||||
|
|
||||||
lists.push(list);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (ii = 0; ii < lists.length; ii++) {
|
|
||||||
lists[ii].setGroup(lists);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
_findBoardColumns: function(board_node) {
|
|
||||||
return JX.DOM.scry(board_node, 'ul', 'project-column');
|
|
||||||
},
|
|
||||||
|
|
||||||
_findCardsInColumn: function(column_node) {
|
|
||||||
return JX.DOM.scry(column_node, 'li', 'project-card');
|
|
||||||
},
|
|
||||||
|
|
||||||
_onmovecard: function(list, item, after_node) {
|
|
||||||
list.lock();
|
|
||||||
JX.DOM.alterClass(item, 'drag-sending', true);
|
|
||||||
|
|
||||||
var item_phid = JX.Stratcom.getData(item).objectPHID;
|
|
||||||
var data = {
|
|
||||||
objectPHID: item_phid,
|
|
||||||
columnPHID: JX.Stratcom.getData(list.getRootNode()).columnPHID
|
|
||||||
};
|
|
||||||
|
|
||||||
if (after_node) {
|
|
||||||
data.afterPHID = JX.Stratcom.getData(after_node).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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: This should be managed per-board.
|
|
||||||
var config = this._getConfig();
|
|
||||||
data.order = config.order;
|
|
||||||
|
|
||||||
new JX.Workflow(this.getMoveURI(), data)
|
|
||||||
.setHandler(JX.bind(this, this._oncardupdate, item, list))
|
|
||||||
.start();
|
|
||||||
},
|
|
||||||
|
|
||||||
_oncardupdate: function(item, list, response) {
|
|
||||||
list.unlock();
|
|
||||||
JX.DOM.alterClass(item, 'drag-sending', false);
|
|
||||||
|
|
||||||
this._queueCardUpdate(response);
|
|
||||||
},
|
|
||||||
|
|
||||||
_queueCardUpdate: function(response) {
|
|
||||||
var board_node = this._boardNodes[this._currentBoard];
|
|
||||||
|
|
||||||
var columns = this._findBoardColumns(board_node);
|
|
||||||
var cards;
|
|
||||||
var ii;
|
|
||||||
var jj;
|
|
||||||
var data;
|
|
||||||
|
|
||||||
for (ii = 0; ii < columns.length; ii++) {
|
|
||||||
cards = this._findCardsInColumn(columns[ii]);
|
|
||||||
for (jj = 0; jj < cards.length; jj++) {
|
|
||||||
data = JX.Stratcom.getData(cards[jj]);
|
|
||||||
if (data.objectPHID == response.objectPHID) {
|
|
||||||
this._replaceCard(cards[jj], JX.$H(response.cardHTML));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
},
|
|
||||||
|
|
||||||
_replaceCard: function(old_node, new_node) {
|
|
||||||
JX.DOM.replace(old_node, new_node);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
});
|
|
221
webroot/rsrc/js/application/projects/WorkboardBoard.js
Normal file
221
webroot/rsrc/js/application/projects/WorkboardBoard.js
Normal file
|
@ -0,0 +1,221 @@
|
||||||
|
/**
|
||||||
|
* @provides javelin-workboard-board
|
||||||
|
* @requires javelin-install
|
||||||
|
* javelin-dom
|
||||||
|
* javelin-util
|
||||||
|
* javelin-stratcom
|
||||||
|
* javelin-workflow
|
||||||
|
* phabricator-draggable-list
|
||||||
|
* javelin-workboard-column
|
||||||
|
* @javelin
|
||||||
|
*/
|
||||||
|
|
||||||
|
JX.install('WorkboardBoard', {
|
||||||
|
|
||||||
|
construct: function(controller, phid, root) {
|
||||||
|
this._controller = controller;
|
||||||
|
this._phid = phid;
|
||||||
|
this._root = root;
|
||||||
|
|
||||||
|
this._templates = {};
|
||||||
|
this._orderMaps = {};
|
||||||
|
this._buildColumns();
|
||||||
|
},
|
||||||
|
|
||||||
|
properties: {
|
||||||
|
order: null,
|
||||||
|
},
|
||||||
|
|
||||||
|
members: {
|
||||||
|
_controller: null,
|
||||||
|
_phid: null,
|
||||||
|
_root: null,
|
||||||
|
_columns: null,
|
||||||
|
_templates: null,
|
||||||
|
_orderMaps: null,
|
||||||
|
|
||||||
|
getRoot: function() {
|
||||||
|
return this._root;
|
||||||
|
},
|
||||||
|
|
||||||
|
getColumns: function() {
|
||||||
|
return this._columns;
|
||||||
|
},
|
||||||
|
|
||||||
|
getColumn: function(k) {
|
||||||
|
return this._columns[k];
|
||||||
|
},
|
||||||
|
|
||||||
|
getPHID: function() {
|
||||||
|
return this._phid;
|
||||||
|
},
|
||||||
|
|
||||||
|
setCardTemplate: function(phid, template) {
|
||||||
|
this._templates[phid] = template;
|
||||||
|
return this;
|
||||||
|
},
|
||||||
|
|
||||||
|
getCardTemplate: function(phid) {
|
||||||
|
return this._templates[phid];
|
||||||
|
},
|
||||||
|
|
||||||
|
getController: function() {
|
||||||
|
return this._controller;
|
||||||
|
},
|
||||||
|
|
||||||
|
setOrderMap: function(phid, map) {
|
||||||
|
this._orderMaps[phid] = map;
|
||||||
|
return this;
|
||||||
|
},
|
||||||
|
|
||||||
|
getOrderVector: function(phid, key) {
|
||||||
|
return this._orderMaps[phid][key];
|
||||||
|
},
|
||||||
|
|
||||||
|
start: function() {
|
||||||
|
this._setupDragHandlers();
|
||||||
|
|
||||||
|
for (var k in this._columns) {
|
||||||
|
this._columns[k].redraw();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_buildColumns: function() {
|
||||||
|
var nodes = JX.DOM.scry(this.getRoot(), 'ul', 'project-column');
|
||||||
|
|
||||||
|
this._columns = {};
|
||||||
|
for (var ii = 0; ii < nodes.length; ii++) {
|
||||||
|
var node = nodes[ii];
|
||||||
|
var data = JX.Stratcom.getData(node);
|
||||||
|
var phid = data.columnPHID;
|
||||||
|
|
||||||
|
this._columns[phid] = new JX.WorkboardColumn(this, phid, node);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_setupDragHandlers: function() {
|
||||||
|
var columns = this.getColumns();
|
||||||
|
|
||||||
|
var lists = [];
|
||||||
|
for (var k in columns) {
|
||||||
|
var column = columns[k];
|
||||||
|
|
||||||
|
var list = new JX.DraggableList('project-card', column.getRoot())
|
||||||
|
.setOuterContainer(this.getRoot())
|
||||||
|
.setFindItemsHandler(JX.bind(column, column.getCardNodes))
|
||||||
|
.setCanDragX(true)
|
||||||
|
.setHasInfiniteHeight(true);
|
||||||
|
|
||||||
|
list.listen('didDrop', JX.bind(this, this._onmovecard, list));
|
||||||
|
|
||||||
|
lists.push(list);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var ii = 0; ii < lists.length; ii++) {
|
||||||
|
lists[ii].setGroup(lists);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_findCardsInColumn: function(column_node) {
|
||||||
|
return JX.DOM.scry(column_node, 'li', 'project-card');
|
||||||
|
},
|
||||||
|
|
||||||
|
_onmovecard: function(list, item, after_node, src_list) {
|
||||||
|
list.lock();
|
||||||
|
JX.DOM.alterClass(item, 'drag-sending', true);
|
||||||
|
|
||||||
|
var src_phid = JX.Stratcom.getData(src_list.getRootNode()).columnPHID;
|
||||||
|
var dst_phid = JX.Stratcom.getData(list.getRootNode()).columnPHID;
|
||||||
|
|
||||||
|
var item_phid = JX.Stratcom.getData(item).objectPHID;
|
||||||
|
var data = {
|
||||||
|
objectPHID: item_phid,
|
||||||
|
columnPHID: dst_phid,
|
||||||
|
order: this.getOrder()
|
||||||
|
};
|
||||||
|
|
||||||
|
if (after_node) {
|
||||||
|
data.afterPHID = JX.Stratcom.getData(after_node).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 visible_phids = [];
|
||||||
|
var column = this.getColumn(dst_phid);
|
||||||
|
for (var object_phid in column.getCards()) {
|
||||||
|
visible_phids.push(object_phid);
|
||||||
|
}
|
||||||
|
|
||||||
|
data.visiblePHIDs = visible_phids.join(',');
|
||||||
|
|
||||||
|
var onupdate = JX.bind(
|
||||||
|
this,
|
||||||
|
this._oncardupdate,
|
||||||
|
list,
|
||||||
|
src_phid,
|
||||||
|
dst_phid,
|
||||||
|
data.afterPHID);
|
||||||
|
|
||||||
|
new JX.Workflow(this.getController().getMoveURI(), data)
|
||||||
|
.setHandler(onupdate)
|
||||||
|
.start();
|
||||||
|
},
|
||||||
|
|
||||||
|
_oncardupdate: function(list, src_phid, dst_phid, after_phid, response) {
|
||||||
|
var src_column = this.getColumn(src_phid);
|
||||||
|
var dst_column = this.getColumn(dst_phid);
|
||||||
|
|
||||||
|
var card = src_column.removeCard(response.objectPHID);
|
||||||
|
dst_column.addCard(card, after_phid);
|
||||||
|
|
||||||
|
this.updateCard(response);
|
||||||
|
|
||||||
|
list.unlock();
|
||||||
|
},
|
||||||
|
|
||||||
|
updateCard: function(response) {
|
||||||
|
var columns = this.getColumns();
|
||||||
|
|
||||||
|
var phid = response.objectPHID;
|
||||||
|
|
||||||
|
if (!this._templates[phid]) {
|
||||||
|
for (var add_phid in response.columnMaps) {
|
||||||
|
this.getColumn(add_phid).newCard(phid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setCardTemplate(phid, response.cardHTML);
|
||||||
|
|
||||||
|
var order_maps = response.orderMaps;
|
||||||
|
for (var order_phid in order_maps) {
|
||||||
|
this.setOrderMap(order_phid, order_maps[order_phid]);
|
||||||
|
}
|
||||||
|
|
||||||
|
var column_maps = response.columnMaps;
|
||||||
|
for (var natural_phid in column_maps) {
|
||||||
|
this.getColumn(natural_phid).setNaturalOrder(column_maps[natural_phid]);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var column_phid in columns) {
|
||||||
|
var cards = columns[column_phid].getCards();
|
||||||
|
for (var object_phid in cards) {
|
||||||
|
if (object_phid !== phid) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var card = cards[object_phid];
|
||||||
|
card.redraw();
|
||||||
|
}
|
||||||
|
columns[column_phid].redraw();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
56
webroot/rsrc/js/application/projects/WorkboardCard.js
Normal file
56
webroot/rsrc/js/application/projects/WorkboardCard.js
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
/**
|
||||||
|
* @provides javelin-workboard-card
|
||||||
|
* @requires javelin-install
|
||||||
|
* @javelin
|
||||||
|
*/
|
||||||
|
|
||||||
|
JX.install('WorkboardCard', {
|
||||||
|
|
||||||
|
construct: function(column, phid) {
|
||||||
|
this._column = column;
|
||||||
|
this._phid = phid;
|
||||||
|
},
|
||||||
|
|
||||||
|
members: {
|
||||||
|
_column: null,
|
||||||
|
_phid: null,
|
||||||
|
_root: null,
|
||||||
|
|
||||||
|
getPHID: function() {
|
||||||
|
return this._phid;
|
||||||
|
},
|
||||||
|
|
||||||
|
getColumn: function() {
|
||||||
|
return this._column;
|
||||||
|
},
|
||||||
|
|
||||||
|
setColumn: function(column) {
|
||||||
|
this._column = column;
|
||||||
|
},
|
||||||
|
|
||||||
|
getNode: function() {
|
||||||
|
if (!this._root) {
|
||||||
|
var phid = this.getPHID();
|
||||||
|
var template = this.getColumn().getBoard().getCardTemplate(phid);
|
||||||
|
this._root = JX.$H(template).getFragment().firstChild;
|
||||||
|
|
||||||
|
JX.Stratcom.getData(this._root).objectPHID = this.getPHID();
|
||||||
|
}
|
||||||
|
return this._root;
|
||||||
|
},
|
||||||
|
|
||||||
|
redraw: function() {
|
||||||
|
var old_node = this._root;
|
||||||
|
this._root = null;
|
||||||
|
var new_node = this.getNode();
|
||||||
|
|
||||||
|
if (old_node && old_node.parentNode) {
|
||||||
|
JX.DOM.replace(old_node, new_node);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
177
webroot/rsrc/js/application/projects/WorkboardColumn.js
Normal file
177
webroot/rsrc/js/application/projects/WorkboardColumn.js
Normal file
|
@ -0,0 +1,177 @@
|
||||||
|
/**
|
||||||
|
* @provides javelin-workboard-column
|
||||||
|
* @requires javelin-install
|
||||||
|
* javelin-workboard-card
|
||||||
|
* @javelin
|
||||||
|
*/
|
||||||
|
|
||||||
|
JX.install('WorkboardColumn', {
|
||||||
|
|
||||||
|
construct: function(board, phid, root) {
|
||||||
|
this._board = board;
|
||||||
|
this._phid = phid;
|
||||||
|
this._root = root;
|
||||||
|
|
||||||
|
this._cards = {};
|
||||||
|
this._naturalOrder = [];
|
||||||
|
},
|
||||||
|
|
||||||
|
members: {
|
||||||
|
_phid: null,
|
||||||
|
_root: null,
|
||||||
|
_board: null,
|
||||||
|
_cards: null,
|
||||||
|
_naturalOrder: null,
|
||||||
|
|
||||||
|
getPHID: function() {
|
||||||
|
return this._phid;
|
||||||
|
},
|
||||||
|
|
||||||
|
getRoot: function() {
|
||||||
|
return this._root;
|
||||||
|
},
|
||||||
|
|
||||||
|
getCards: function() {
|
||||||
|
return this._cards;
|
||||||
|
},
|
||||||
|
|
||||||
|
getCard: function(phid) {
|
||||||
|
return this._cards[phid];
|
||||||
|
},
|
||||||
|
|
||||||
|
getBoard: function() {
|
||||||
|
return this._board;
|
||||||
|
},
|
||||||
|
|
||||||
|
setNaturalOrder: function(order) {
|
||||||
|
this._naturalOrder = order;
|
||||||
|
return this;
|
||||||
|
},
|
||||||
|
|
||||||
|
newCard: function(phid) {
|
||||||
|
var card = new JX.WorkboardCard(this, phid);
|
||||||
|
|
||||||
|
this._cards[phid] = card;
|
||||||
|
this._naturalOrder.push(phid);
|
||||||
|
|
||||||
|
return card;
|
||||||
|
},
|
||||||
|
|
||||||
|
removeCard: function(phid) {
|
||||||
|
var card = this._cards[phid];
|
||||||
|
delete this._cards[phid];
|
||||||
|
|
||||||
|
for (var ii = 0; ii < this._naturalOrder.length; ii++) {
|
||||||
|
if (this._naturalOrder[ii] == phid) {
|
||||||
|
this._naturalOrder.splice(ii, 1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return card;
|
||||||
|
},
|
||||||
|
|
||||||
|
addCard: function(card, after) {
|
||||||
|
var phid = card.getPHID();
|
||||||
|
|
||||||
|
card.setColumn(this);
|
||||||
|
this._cards[phid] = card;
|
||||||
|
|
||||||
|
var index = 0;
|
||||||
|
|
||||||
|
if (after) {
|
||||||
|
for (var ii = 0; ii < this._naturalOrder.length; ii++) {
|
||||||
|
if (this._naturalOrder[ii] == after) {
|
||||||
|
index = ii + 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (index > this._naturalOrder.length) {
|
||||||
|
this._naturalOrder.push(phid);
|
||||||
|
} else {
|
||||||
|
this._naturalOrder.splice(index, 0, phid);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this;
|
||||||
|
},
|
||||||
|
|
||||||
|
getCardNodes: function() {
|
||||||
|
var cards = this.getCards();
|
||||||
|
|
||||||
|
var nodes = [];
|
||||||
|
for (var k in cards) {
|
||||||
|
nodes.push(cards[k].getNode());
|
||||||
|
}
|
||||||
|
|
||||||
|
return nodes;
|
||||||
|
},
|
||||||
|
|
||||||
|
getCardPHIDs: function() {
|
||||||
|
return JX.keys(this.getCards());
|
||||||
|
},
|
||||||
|
|
||||||
|
redraw: function() {
|
||||||
|
var order = this.getBoard().getOrder();
|
||||||
|
|
||||||
|
var list;
|
||||||
|
if (order == 'natural') {
|
||||||
|
list = this._getCardsSortedNaturally();
|
||||||
|
} else {
|
||||||
|
list = this._getCardsSortedByKey(order);
|
||||||
|
}
|
||||||
|
|
||||||
|
var content = [];
|
||||||
|
for (var ii = 0; ii < list.length; ii++) {
|
||||||
|
var node = list[ii].getNode();
|
||||||
|
content.push(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
JX.DOM.setContent(this.getRoot(), content);
|
||||||
|
},
|
||||||
|
|
||||||
|
_getCardsSortedNaturally: function() {
|
||||||
|
var list = [];
|
||||||
|
|
||||||
|
for (var ii = 0; ii < this._naturalOrder.length; ii++) {
|
||||||
|
var phid = this._naturalOrder[ii];
|
||||||
|
list.push(this.getCard(phid));
|
||||||
|
}
|
||||||
|
|
||||||
|
return list;
|
||||||
|
},
|
||||||
|
|
||||||
|
_getCardsSortedByKey: function(order) {
|
||||||
|
var cards = this.getCards();
|
||||||
|
|
||||||
|
var list = [];
|
||||||
|
for (var k in cards) {
|
||||||
|
list.push(cards[k]);
|
||||||
|
}
|
||||||
|
|
||||||
|
list.sort(JX.bind(this, this._sortCards, order));
|
||||||
|
|
||||||
|
return list;
|
||||||
|
},
|
||||||
|
|
||||||
|
_sortCards: function(order, u, v) {
|
||||||
|
var ud = this.getBoard().getOrderVector(u.getPHID(), order);
|
||||||
|
var vd = this.getBoard().getOrderVector(v.getPHID(), order);
|
||||||
|
|
||||||
|
for (var ii = 0; ii < ud.length; ii++) {
|
||||||
|
if (ud[ii] > vd[ii]) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ud[ii] < vd[ii]) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
201
webroot/rsrc/js/application/projects/WorkboardController.js
Normal file
201
webroot/rsrc/js/application/projects/WorkboardController.js
Normal file
|
@ -0,0 +1,201 @@
|
||||||
|
/**
|
||||||
|
* @provides javelin-workboard-controller
|
||||||
|
* @requires javelin-install
|
||||||
|
* javelin-dom
|
||||||
|
* javelin-util
|
||||||
|
* javelin-vector
|
||||||
|
* javelin-stratcom
|
||||||
|
* javelin-workflow
|
||||||
|
* phabricator-drag-and-drop-file-upload
|
||||||
|
* javelin-workboard-board
|
||||||
|
* @javelin
|
||||||
|
*/
|
||||||
|
|
||||||
|
JX.install('WorkboardController', {
|
||||||
|
|
||||||
|
construct: function() {
|
||||||
|
this._boards = {};
|
||||||
|
},
|
||||||
|
|
||||||
|
properties: {
|
||||||
|
uploadURI: null,
|
||||||
|
coverURI: null,
|
||||||
|
moveURI: null,
|
||||||
|
createURI: null,
|
||||||
|
chunkThreshold: null
|
||||||
|
},
|
||||||
|
|
||||||
|
members: {
|
||||||
|
_boards: null,
|
||||||
|
|
||||||
|
_panOrigin: null,
|
||||||
|
_panNode: null,
|
||||||
|
_panX: null,
|
||||||
|
|
||||||
|
start: function() {
|
||||||
|
this._setupCoverImageHandlers();
|
||||||
|
this._setupPanHandlers();
|
||||||
|
this._setupEditHandlers();
|
||||||
|
|
||||||
|
return this;
|
||||||
|
},
|
||||||
|
|
||||||
|
newBoard: function(phid, node) {
|
||||||
|
var board = new JX.WorkboardBoard(this, phid, node);
|
||||||
|
this._boards[phid] = board;
|
||||||
|
return board;
|
||||||
|
},
|
||||||
|
|
||||||
|
_getBoard: function(board_phid) {
|
||||||
|
return this._boards[board_phid];
|
||||||
|
},
|
||||||
|
|
||||||
|
_setupCoverImageHandlers: function() {
|
||||||
|
if (!JX.PhabricatorDragAndDropFileUpload.isSupported()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var drop = new JX.PhabricatorDragAndDropFileUpload('project-card')
|
||||||
|
.setURI(this.getUploadURI())
|
||||||
|
.setChunkThreshold(this.getChunkThreshold());
|
||||||
|
|
||||||
|
drop.listen('didBeginDrag', function(node) {
|
||||||
|
JX.DOM.alterClass(node, 'phui-workcard-upload-target', true);
|
||||||
|
});
|
||||||
|
|
||||||
|
drop.listen('didEndDrag', function(node) {
|
||||||
|
JX.DOM.alterClass(node, 'phui-workcard-upload-target', false);
|
||||||
|
});
|
||||||
|
|
||||||
|
drop.listen('didUpload', JX.bind(this, this._oncoverupload));
|
||||||
|
|
||||||
|
drop.start();
|
||||||
|
},
|
||||||
|
|
||||||
|
_oncoverupload: function(file) {
|
||||||
|
var node = file.getTargetNode();
|
||||||
|
|
||||||
|
var board = this._getBoardFromNode(node);
|
||||||
|
|
||||||
|
var column_node = JX.DOM.findAbove(node, 'ul', 'project-column');
|
||||||
|
var column_phid = JX.Stratcom.getData(column_node).columnPHID;
|
||||||
|
var column = board.getColumn(column_phid);
|
||||||
|
|
||||||
|
var data = {
|
||||||
|
boardPHID: board.getPHID(),
|
||||||
|
objectPHID: JX.Stratcom.getData(node).objectPHID,
|
||||||
|
filePHID: file.getPHID(),
|
||||||
|
visiblePHIDs: column.getCardPHIDs()
|
||||||
|
};
|
||||||
|
|
||||||
|
new JX.Workflow(this.getCoverURI(), data)
|
||||||
|
.setHandler(JX.bind(board, board.updateCard))
|
||||||
|
.start();
|
||||||
|
},
|
||||||
|
|
||||||
|
_getBoardFromNode: function(node) {
|
||||||
|
var board_node = JX.DOM.findAbove(node, 'div', 'jx-workboard');
|
||||||
|
var board_phid = JX.Stratcom.getData(board_node).boardPHID;
|
||||||
|
return this._getBoard(board_phid);
|
||||||
|
},
|
||||||
|
|
||||||
|
_setupPanHandlers: function() {
|
||||||
|
var mousedown = JX.bind(this, this._onpanmousedown);
|
||||||
|
var mousemove = JX.bind(this, this._onpanmousemove);
|
||||||
|
var mouseup = JX.bind(this, this._onpanmouseup);
|
||||||
|
|
||||||
|
JX.Stratcom.listen('mousedown', 'workboard-shadow', mousedown);
|
||||||
|
JX.Stratcom.listen('mousemove', null, mousemove);
|
||||||
|
JX.Stratcom.listen('mouseup', null, mouseup);
|
||||||
|
},
|
||||||
|
|
||||||
|
_onpanmousedown: function(e) {
|
||||||
|
if (!JX.Device.isDesktop()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (e.getNode('workpanel')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (JX.Stratcom.pass()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
e.kill();
|
||||||
|
|
||||||
|
this._panOrigin = JX.$V(e);
|
||||||
|
this._panNode = e.getNode('workboard-shadow');
|
||||||
|
this._panX = this._panNode.scrollLeft;
|
||||||
|
},
|
||||||
|
|
||||||
|
_onpanmousemove: function(e) {
|
||||||
|
if (!this._panOrigin) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var cursor = JX.$V(e);
|
||||||
|
this._panNode.scrollLeft = this._panX + (this._panOrigin.x - cursor.x);
|
||||||
|
},
|
||||||
|
|
||||||
|
_onpanmouseup: function() {
|
||||||
|
this._panOrigin = null;
|
||||||
|
},
|
||||||
|
|
||||||
|
_setupEditHandlers: function() {
|
||||||
|
var onadd = JX.bind(this, this._onaddcard);
|
||||||
|
var onedit = JX.bind(this, this._oneditcard);
|
||||||
|
|
||||||
|
JX.Stratcom.listen('click', 'column-add-task', onadd);
|
||||||
|
JX.Stratcom.listen('click', 'edit-project-card', onedit);
|
||||||
|
},
|
||||||
|
|
||||||
|
_onaddcard: function(e) {
|
||||||
|
// We want the 'boards-dropdown-menu' behavior to see this event and
|
||||||
|
// close the dropdown, but don't want to follow the link.
|
||||||
|
e.prevent();
|
||||||
|
|
||||||
|
var column_data = e.getNodeData('column-add-task');
|
||||||
|
var column_phid = column_data.columnPHID;
|
||||||
|
|
||||||
|
var board_phid = column_data.projectPHID;
|
||||||
|
var board = this._getBoard(board_phid);
|
||||||
|
var column = board.getColumn(column_phid);
|
||||||
|
|
||||||
|
var request_data = {
|
||||||
|
responseType: 'card',
|
||||||
|
columnPHID: column.getPHID(),
|
||||||
|
projects: board.getPHID(),
|
||||||
|
visiblePHIDs: column.getCardPHIDs(),
|
||||||
|
order: board.getOrder()
|
||||||
|
};
|
||||||
|
|
||||||
|
new JX.Workflow(this.getCreateURI(), request_data)
|
||||||
|
.setHandler(JX.bind(board, board.updateCard))
|
||||||
|
.start();
|
||||||
|
},
|
||||||
|
|
||||||
|
_oneditcard: function(e) {
|
||||||
|
e.kill();
|
||||||
|
|
||||||
|
var column_node = e.getNode('project-column');
|
||||||
|
var column_phid = JX.Stratcom.getData(column_node).columnPHID;
|
||||||
|
|
||||||
|
var board = this._getBoardFromNode(column_node);
|
||||||
|
var column = board.getColumn(column_phid);
|
||||||
|
|
||||||
|
var request_data = {
|
||||||
|
responseType: 'card',
|
||||||
|
columnPHID: column.getPHID(),
|
||||||
|
visiblePHIDs: column.getCardPHIDs(),
|
||||||
|
order: board.getOrder()
|
||||||
|
};
|
||||||
|
|
||||||
|
new JX.Workflow(e.getNode('tag:a').href, request_data)
|
||||||
|
.setHandler(JX.bind(board, board.updateCard))
|
||||||
|
.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
|
@ -6,9 +6,7 @@
|
||||||
* javelin-vector
|
* javelin-vector
|
||||||
* javelin-stratcom
|
* javelin-stratcom
|
||||||
* javelin-workflow
|
* javelin-workflow
|
||||||
* phabricator-draggable-list
|
* javelin-workboard-controller
|
||||||
* phabricator-drag-and-drop-file-upload
|
|
||||||
* javelin-workboard
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
JX.behavior('project-boards', function(config, statics) {
|
JX.behavior('project-boards', function(config, statics) {
|
||||||
|
@ -61,66 +59,6 @@ JX.behavior('project-boards', function(config, statics) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function colsort(u, v) {
|
|
||||||
var ud = JX.Stratcom.getData(u).sort || [];
|
|
||||||
var vd = JX.Stratcom.getData(v).sort || [];
|
|
||||||
|
|
||||||
for (var ii = 0; ii < ud.length; ii++) {
|
|
||||||
|
|
||||||
if (parseInt(ud[ii]) < parseInt(vd[ii])) {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
if (parseInt(ud[ii]) > parseInt(vd[ii])) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
function onedit(column, r) {
|
|
||||||
var new_card = JX.$H(r.tasks).getNode();
|
|
||||||
var new_data = JX.Stratcom.getData(new_card);
|
|
||||||
var items = finditems(column);
|
|
||||||
var edited = false;
|
|
||||||
var remove_index = null;
|
|
||||||
|
|
||||||
for (var ii = 0; ii < items.length; ii++) {
|
|
||||||
var item = items[ii];
|
|
||||||
|
|
||||||
var data = JX.Stratcom.getData(item);
|
|
||||||
var phid = data.objectPHID;
|
|
||||||
|
|
||||||
if (phid == new_data.objectPHID) {
|
|
||||||
if (r.data.removeFromBoard) {
|
|
||||||
remove_index = ii;
|
|
||||||
}
|
|
||||||
items[ii] = new_card;
|
|
||||||
data = new_data;
|
|
||||||
edited = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
data.sort = r.data.sortMap[data.objectPHID] || data.sort;
|
|
||||||
}
|
|
||||||
|
|
||||||
// this is an add then...!
|
|
||||||
if (!edited) {
|
|
||||||
items[items.length + 1] = new_card;
|
|
||||||
new_data.sort = r.data.sortMap[new_data.objectPHID] || new_data.sort;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (remove_index !== null) {
|
|
||||||
items.splice(remove_index, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
items.sort(colsort);
|
|
||||||
|
|
||||||
JX.DOM.setContent(column, items);
|
|
||||||
|
|
||||||
onupdate(column);
|
|
||||||
};
|
|
||||||
|
|
||||||
function update_statics(update_config) {
|
function update_statics(update_config) {
|
||||||
statics.boardID = update_config.boardID;
|
statics.boardID = update_config.boardID;
|
||||||
statics.projectPHID = update_config.projectPHID;
|
statics.projectPHID = update_config.projectPHID;
|
||||||
|
@ -130,56 +68,6 @@ JX.behavior('project-boards', function(config, statics) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function setup() {
|
function setup() {
|
||||||
|
|
||||||
JX.Stratcom.listen(
|
|
||||||
'click',
|
|
||||||
['edit-project-card'],
|
|
||||||
function(e) {
|
|
||||||
e.kill();
|
|
||||||
var column = e.getNode('project-column');
|
|
||||||
var request_data = {
|
|
||||||
responseType: 'card',
|
|
||||||
columnPHID: JX.Stratcom.getData(column).columnPHID,
|
|
||||||
order: statics.order
|
|
||||||
};
|
|
||||||
new JX.Workflow(e.getNode('tag:a').href, request_data)
|
|
||||||
.setHandler(JX.bind(null, onedit, column))
|
|
||||||
.start();
|
|
||||||
});
|
|
||||||
|
|
||||||
JX.Stratcom.listen(
|
|
||||||
'click',
|
|
||||||
['column-add-task'],
|
|
||||||
function (e) {
|
|
||||||
|
|
||||||
// We want the 'boards-dropdown-menu' behavior to see this event and
|
|
||||||
// close the dropdown, but don't want to follow the link.
|
|
||||||
e.prevent();
|
|
||||||
|
|
||||||
var column_data = e.getNodeData('column-add-task');
|
|
||||||
var column_phid = column_data.columnPHID;
|
|
||||||
|
|
||||||
var request_data = {
|
|
||||||
responseType: 'card',
|
|
||||||
columnPHID: column_phid,
|
|
||||||
projects: column_data.projectPHID,
|
|
||||||
order: statics.order
|
|
||||||
};
|
|
||||||
|
|
||||||
var cols = getcolumns();
|
|
||||||
var ii;
|
|
||||||
var column;
|
|
||||||
for (ii = 0; ii < cols.length; ii++) {
|
|
||||||
if (JX.Stratcom.getData(cols[ii]).columnPHID == column_phid) {
|
|
||||||
column = cols[ii];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
new JX.Workflow(statics.createURI, request_data)
|
|
||||||
.setHandler(JX.bind(null, onedit, column))
|
|
||||||
.start();
|
|
||||||
});
|
|
||||||
|
|
||||||
JX.Stratcom.listen('click', 'boards-dropdown-menu', function(e) {
|
JX.Stratcom.listen('click', 'boards-dropdown-menu', function(e) {
|
||||||
var data = e.getNodeData('boards-dropdown-menu');
|
var data = e.getNodeData('boards-dropdown-menu');
|
||||||
if (data.menu) {
|
if (data.menu) {
|
||||||
|
@ -234,14 +122,40 @@ JX.behavior('project-boards', function(config, statics) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!statics.workboard) {
|
if (!statics.workboard) {
|
||||||
statics.workboard = new JX.Workboard(config)
|
statics.workboard = new JX.WorkboardController()
|
||||||
.setUploadURI(config.uploadURI)
|
.setUploadURI(config.uploadURI)
|
||||||
.setCoverURI(config.coverURI)
|
.setCoverURI(config.coverURI)
|
||||||
.setMoveURI(config.moveURI)
|
.setMoveURI(config.moveURI)
|
||||||
|
.setCreateURI(config.createURI)
|
||||||
.setChunkThreshold(config.chunkThreshold)
|
.setChunkThreshold(config.chunkThreshold)
|
||||||
.start();
|
.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
statics.workboard.addBoard(config.projectPHID, JX.$(config.boardID));
|
var board_phid = config.projectPHID;
|
||||||
|
var board_node = JX.$(config.boardID);
|
||||||
|
|
||||||
|
var board = statics.workboard.newBoard(board_phid, board_node)
|
||||||
|
.setOrder(config.order);
|
||||||
|
|
||||||
|
var templates = config.templateMap;
|
||||||
|
for (var k in templates) {
|
||||||
|
board.setCardTemplate(k, templates[k]);
|
||||||
|
}
|
||||||
|
|
||||||
|
var column_maps = config.columnMaps;
|
||||||
|
for (var column_phid in column_maps) {
|
||||||
|
var column = board.getColumn(column_phid);
|
||||||
|
var column_map = column_maps[column_phid];
|
||||||
|
for (var ii = 0; ii < column_map.length; ii++) {
|
||||||
|
column.newCard(column_map[ii]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var order_maps = config.orderMaps;
|
||||||
|
for (var object_phid in order_maps) {
|
||||||
|
board.setOrderMap(object_phid, order_maps[object_phid]);
|
||||||
|
}
|
||||||
|
|
||||||
|
board.start();
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in a new issue