mirror of
https://we.phorge.it/source/phorge.git
synced 2024-11-26 00:32:42 +01:00
Define bulk edits in terms of EditEngine, not hard-coded ad-hoc definitions
Summary: Depends on D18862. See PHI173. Ref T13025. Fixes T10005. This redefines bulk edits in terms of EditEngine fields, rather than hard-coding the whole thing. Only text fields -- and, specifically, only the "Title" field -- are supported after this change. Followup changes will add more bulk edit parameter types and broader field support. However, the title field now works without any Maniphest-specific code, outside of the small amount of binding code in the `ManiphestBulkEditor` subclass. Test Plan: Used the bulk edit workflow to change the titles of tasks. Reviewers: amckinley Reviewed By: amckinley Maniphest Tasks: T13025, T10005 Differential Revision: https://secure.phabricator.com/D18863
This commit is contained in:
parent
6ef45d8245
commit
09e71a4082
17 changed files with 455 additions and 288 deletions
|
@ -81,7 +81,6 @@ return array(
|
||||||
'rsrc/css/application/harbormaster/harbormaster.css' => 'f491c9f4',
|
'rsrc/css/application/harbormaster/harbormaster.css' => 'f491c9f4',
|
||||||
'rsrc/css/application/herald/herald-test.css' => 'a52e323e',
|
'rsrc/css/application/herald/herald-test.css' => 'a52e323e',
|
||||||
'rsrc/css/application/herald/herald.css' => 'cd8d0134',
|
'rsrc/css/application/herald/herald.css' => 'cd8d0134',
|
||||||
'rsrc/css/application/maniphest/batch-editor.css' => 'b0f0b6d5',
|
|
||||||
'rsrc/css/application/maniphest/report.css' => '9b9580b7',
|
'rsrc/css/application/maniphest/report.css' => '9b9580b7',
|
||||||
'rsrc/css/application/maniphest/task-edit.css' => 'fda62a9b',
|
'rsrc/css/application/maniphest/task-edit.css' => 'fda62a9b',
|
||||||
'rsrc/css/application/maniphest/task-summary.css' => '11cc5344',
|
'rsrc/css/application/maniphest/task-summary.css' => '11cc5344',
|
||||||
|
@ -143,6 +142,7 @@ return array(
|
||||||
'rsrc/css/phui/phui-basic-nav-view.css' => '98c11ab3',
|
'rsrc/css/phui/phui-basic-nav-view.css' => '98c11ab3',
|
||||||
'rsrc/css/phui/phui-big-info-view.css' => 'acc3492c',
|
'rsrc/css/phui/phui-big-info-view.css' => 'acc3492c',
|
||||||
'rsrc/css/phui/phui-box.css' => '4bd6cdb9',
|
'rsrc/css/phui/phui-box.css' => '4bd6cdb9',
|
||||||
|
'rsrc/css/phui/phui-bulk-editor.css' => '1fe728a8',
|
||||||
'rsrc/css/phui/phui-chart.css' => '6bf6f78e',
|
'rsrc/css/phui/phui-chart.css' => '6bf6f78e',
|
||||||
'rsrc/css/phui/phui-cms.css' => '504b4b23',
|
'rsrc/css/phui/phui-cms.css' => '504b4b23',
|
||||||
'rsrc/css/phui/phui-comment-form.css' => 'ac68149f',
|
'rsrc/css/phui/phui-comment-form.css' => 'ac68149f',
|
||||||
|
@ -419,7 +419,6 @@ return array(
|
||||||
'rsrc/js/application/herald/HeraldRuleEditor.js' => '2dff5579',
|
'rsrc/js/application/herald/HeraldRuleEditor.js' => '2dff5579',
|
||||||
'rsrc/js/application/herald/PathTypeahead.js' => 'f7fc67ec',
|
'rsrc/js/application/herald/PathTypeahead.js' => 'f7fc67ec',
|
||||||
'rsrc/js/application/herald/herald-rule-editor.js' => '7ebaeed3',
|
'rsrc/js/application/herald/herald-rule-editor.js' => '7ebaeed3',
|
||||||
'rsrc/js/application/maniphest/behavior-batch-editor.js' => '782ab6e7',
|
|
||||||
'rsrc/js/application/maniphest/behavior-batch-selector.js' => 'ad54037e',
|
'rsrc/js/application/maniphest/behavior-batch-selector.js' => 'ad54037e',
|
||||||
'rsrc/js/application/maniphest/behavior-line-chart.js' => 'e4232876',
|
'rsrc/js/application/maniphest/behavior-line-chart.js' => 'e4232876',
|
||||||
'rsrc/js/application/maniphest/behavior-list-edit.js' => 'a9f88de2',
|
'rsrc/js/application/maniphest/behavior-list-edit.js' => 'a9f88de2',
|
||||||
|
@ -477,6 +476,7 @@ return array(
|
||||||
'rsrc/js/core/behavior-audio-source.js' => '59b251eb',
|
'rsrc/js/core/behavior-audio-source.js' => '59b251eb',
|
||||||
'rsrc/js/core/behavior-autofocus.js' => '7319e029',
|
'rsrc/js/core/behavior-autofocus.js' => '7319e029',
|
||||||
'rsrc/js/core/behavior-badge-view.js' => '8ff5e24c',
|
'rsrc/js/core/behavior-badge-view.js' => '8ff5e24c',
|
||||||
|
'rsrc/js/core/behavior-bulk-editor.js' => '5e178556',
|
||||||
'rsrc/js/core/behavior-choose-control.js' => '327a00d1',
|
'rsrc/js/core/behavior-choose-control.js' => '327a00d1',
|
||||||
'rsrc/js/core/behavior-copy.js' => 'b0b8f86d',
|
'rsrc/js/core/behavior-copy.js' => 'b0b8f86d',
|
||||||
'rsrc/js/core/behavior-detect-timezone.js' => '4c193c96',
|
'rsrc/js/core/behavior-detect-timezone.js' => '4c193c96',
|
||||||
|
@ -532,7 +532,7 @@ return array(
|
||||||
'rsrc/js/phuix/PHUIXButtonView.js' => '8a91e1ac',
|
'rsrc/js/phuix/PHUIXButtonView.js' => '8a91e1ac',
|
||||||
'rsrc/js/phuix/PHUIXDropdownMenu.js' => '04b2ae03',
|
'rsrc/js/phuix/PHUIXDropdownMenu.js' => '04b2ae03',
|
||||||
'rsrc/js/phuix/PHUIXExample.js' => '68af71ca',
|
'rsrc/js/phuix/PHUIXExample.js' => '68af71ca',
|
||||||
'rsrc/js/phuix/PHUIXFormControl.js' => '83e03671',
|
'rsrc/js/phuix/PHUIXFormControl.js' => '68bb05aa',
|
||||||
'rsrc/js/phuix/PHUIXIconView.js' => 'bff6884b',
|
'rsrc/js/phuix/PHUIXIconView.js' => 'bff6884b',
|
||||||
),
|
),
|
||||||
'symbols' => array(
|
'symbols' => array(
|
||||||
|
@ -595,6 +595,7 @@ return array(
|
||||||
'javelin-behavior-audio-source' => '59b251eb',
|
'javelin-behavior-audio-source' => '59b251eb',
|
||||||
'javelin-behavior-audit-preview' => 'd835b03a',
|
'javelin-behavior-audit-preview' => 'd835b03a',
|
||||||
'javelin-behavior-badge-view' => '8ff5e24c',
|
'javelin-behavior-badge-view' => '8ff5e24c',
|
||||||
|
'javelin-behavior-bulk-editor' => '5e178556',
|
||||||
'javelin-behavior-bulk-job-reload' => 'edf8a145',
|
'javelin-behavior-bulk-job-reload' => 'edf8a145',
|
||||||
'javelin-behavior-calendar-month-view' => 'fe33e256',
|
'javelin-behavior-calendar-month-view' => 'fe33e256',
|
||||||
'javelin-behavior-choose-control' => '327a00d1',
|
'javelin-behavior-choose-control' => '327a00d1',
|
||||||
|
@ -642,7 +643,6 @@ return array(
|
||||||
'javelin-behavior-lightbox-attachments' => '560f41da',
|
'javelin-behavior-lightbox-attachments' => '560f41da',
|
||||||
'javelin-behavior-line-chart' => 'e4232876',
|
'javelin-behavior-line-chart' => 'e4232876',
|
||||||
'javelin-behavior-load-blame' => '42126667',
|
'javelin-behavior-load-blame' => '42126667',
|
||||||
'javelin-behavior-maniphest-batch-editor' => '782ab6e7',
|
|
||||||
'javelin-behavior-maniphest-batch-selector' => 'ad54037e',
|
'javelin-behavior-maniphest-batch-selector' => 'ad54037e',
|
||||||
'javelin-behavior-maniphest-list-editor' => 'a9f88de2',
|
'javelin-behavior-maniphest-list-editor' => 'a9f88de2',
|
||||||
'javelin-behavior-maniphest-subpriority-editor' => '71237763',
|
'javelin-behavior-maniphest-subpriority-editor' => '71237763',
|
||||||
|
@ -756,7 +756,6 @@ return array(
|
||||||
'javelin-workboard-column' => '758b4758',
|
'javelin-workboard-column' => '758b4758',
|
||||||
'javelin-workboard-controller' => '26167537',
|
'javelin-workboard-controller' => '26167537',
|
||||||
'javelin-workflow' => '1e911d0f',
|
'javelin-workflow' => '1e911d0f',
|
||||||
'maniphest-batch-editor' => 'b0f0b6d5',
|
|
||||||
'maniphest-report-css' => '9b9580b7',
|
'maniphest-report-css' => '9b9580b7',
|
||||||
'maniphest-task-edit-css' => 'fda62a9b',
|
'maniphest-task-edit-css' => 'fda62a9b',
|
||||||
'maniphest-task-summary-css' => '11cc5344',
|
'maniphest-task-summary-css' => '11cc5344',
|
||||||
|
@ -823,6 +822,7 @@ return array(
|
||||||
'phui-basic-nav-view-css' => '98c11ab3',
|
'phui-basic-nav-view-css' => '98c11ab3',
|
||||||
'phui-big-info-view-css' => 'acc3492c',
|
'phui-big-info-view-css' => 'acc3492c',
|
||||||
'phui-box-css' => '4bd6cdb9',
|
'phui-box-css' => '4bd6cdb9',
|
||||||
|
'phui-bulk-editor-css' => '1fe728a8',
|
||||||
'phui-button-bar-css' => 'f1ff5494',
|
'phui-button-bar-css' => 'f1ff5494',
|
||||||
'phui-button-css' => '1863cc6e',
|
'phui-button-css' => '1863cc6e',
|
||||||
'phui-button-simple-css' => '8e1baf68',
|
'phui-button-simple-css' => '8e1baf68',
|
||||||
|
@ -884,7 +884,7 @@ return array(
|
||||||
'phuix-autocomplete' => 'e0731603',
|
'phuix-autocomplete' => 'e0731603',
|
||||||
'phuix-button-view' => '8a91e1ac',
|
'phuix-button-view' => '8a91e1ac',
|
||||||
'phuix-dropdown-menu' => '04b2ae03',
|
'phuix-dropdown-menu' => '04b2ae03',
|
||||||
'phuix-form-control-view' => '83e03671',
|
'phuix-form-control-view' => '68bb05aa',
|
||||||
'phuix-icon-view' => 'bff6884b',
|
'phuix-icon-view' => 'bff6884b',
|
||||||
'policy-css' => '957ea14c',
|
'policy-css' => '957ea14c',
|
||||||
'policy-edit-css' => '815c66f7',
|
'policy-edit-css' => '815c66f7',
|
||||||
|
@ -1387,6 +1387,15 @@ return array(
|
||||||
'javelin-stratcom',
|
'javelin-stratcom',
|
||||||
'javelin-dom',
|
'javelin-dom',
|
||||||
),
|
),
|
||||||
|
'5e178556' => array(
|
||||||
|
'javelin-behavior',
|
||||||
|
'javelin-dom',
|
||||||
|
'javelin-util',
|
||||||
|
'phabricator-prefab',
|
||||||
|
'multirow-row-manager',
|
||||||
|
'javelin-json',
|
||||||
|
'phuix-form-control-view',
|
||||||
|
),
|
||||||
'5e2634b9' => array(
|
'5e2634b9' => array(
|
||||||
'javelin-behavior',
|
'javelin-behavior',
|
||||||
'javelin-aphlict',
|
'javelin-aphlict',
|
||||||
|
@ -1436,6 +1445,10 @@ return array(
|
||||||
'javelin-dom',
|
'javelin-dom',
|
||||||
'phuix-button-view',
|
'phuix-button-view',
|
||||||
),
|
),
|
||||||
|
'68bb05aa' => array(
|
||||||
|
'javelin-install',
|
||||||
|
'javelin-dom',
|
||||||
|
),
|
||||||
'69adf288' => array(
|
'69adf288' => array(
|
||||||
'javelin-install',
|
'javelin-install',
|
||||||
),
|
),
|
||||||
|
@ -1524,14 +1537,6 @@ return array(
|
||||||
'javelin-request',
|
'javelin-request',
|
||||||
'javelin-util',
|
'javelin-util',
|
||||||
),
|
),
|
||||||
'782ab6e7' => array(
|
|
||||||
'javelin-behavior',
|
|
||||||
'javelin-dom',
|
|
||||||
'javelin-util',
|
|
||||||
'phabricator-prefab',
|
|
||||||
'multirow-row-manager',
|
|
||||||
'javelin-json',
|
|
||||||
),
|
|
||||||
'7927a7d3' => array(
|
'7927a7d3' => array(
|
||||||
'javelin-behavior',
|
'javelin-behavior',
|
||||||
'javelin-quicksand',
|
'javelin-quicksand',
|
||||||
|
@ -1570,10 +1575,6 @@ return array(
|
||||||
'javelin-behavior',
|
'javelin-behavior',
|
||||||
'javelin-scrollbar',
|
'javelin-scrollbar',
|
||||||
),
|
),
|
||||||
'83e03671' => array(
|
|
||||||
'javelin-install',
|
|
||||||
'javelin-dom',
|
|
||||||
),
|
|
||||||
'8499b6ab' => array(
|
'8499b6ab' => array(
|
||||||
'javelin-behavior',
|
'javelin-behavior',
|
||||||
'javelin-dom',
|
'javelin-dom',
|
||||||
|
|
|
@ -222,6 +222,8 @@ phutil_register_library_map(array(
|
||||||
'AuditConduitAPIMethod' => 'applications/audit/conduit/AuditConduitAPIMethod.php',
|
'AuditConduitAPIMethod' => 'applications/audit/conduit/AuditConduitAPIMethod.php',
|
||||||
'AuditQueryConduitAPIMethod' => 'applications/audit/conduit/AuditQueryConduitAPIMethod.php',
|
'AuditQueryConduitAPIMethod' => 'applications/audit/conduit/AuditQueryConduitAPIMethod.php',
|
||||||
'AuthManageProvidersCapability' => 'applications/auth/capability/AuthManageProvidersCapability.php',
|
'AuthManageProvidersCapability' => 'applications/auth/capability/AuthManageProvidersCapability.php',
|
||||||
|
'BulkParameterType' => 'applications/transactions/bulk/type/BulkParameterType.php',
|
||||||
|
'BulkStringParameterType' => 'applications/transactions/bulk/type/BulkStringParameterType.php',
|
||||||
'CalendarTimeUtil' => 'applications/calendar/util/CalendarTimeUtil.php',
|
'CalendarTimeUtil' => 'applications/calendar/util/CalendarTimeUtil.php',
|
||||||
'CalendarTimeUtilTestCase' => 'applications/calendar/__tests__/CalendarTimeUtilTestCase.php',
|
'CalendarTimeUtilTestCase' => 'applications/calendar/__tests__/CalendarTimeUtilTestCase.php',
|
||||||
'CelerityAPI' => 'applications/celerity/CelerityAPI.php',
|
'CelerityAPI' => 'applications/celerity/CelerityAPI.php',
|
||||||
|
@ -5242,6 +5244,8 @@ phutil_register_library_map(array(
|
||||||
'AuditConduitAPIMethod' => 'ConduitAPIMethod',
|
'AuditConduitAPIMethod' => 'ConduitAPIMethod',
|
||||||
'AuditQueryConduitAPIMethod' => 'AuditConduitAPIMethod',
|
'AuditQueryConduitAPIMethod' => 'AuditConduitAPIMethod',
|
||||||
'AuthManageProvidersCapability' => 'PhabricatorPolicyCapability',
|
'AuthManageProvidersCapability' => 'PhabricatorPolicyCapability',
|
||||||
|
'BulkParameterType' => 'Phobject',
|
||||||
|
'BulkStringParameterType' => 'BulkParameterType',
|
||||||
'CalendarTimeUtil' => 'Phobject',
|
'CalendarTimeUtil' => 'Phobject',
|
||||||
'CalendarTimeUtilTestCase' => 'PhabricatorTestCase',
|
'CalendarTimeUtilTestCase' => 'PhabricatorTestCase',
|
||||||
'CelerityAPI' => 'Phobject',
|
'CelerityAPI' => 'Phobject',
|
||||||
|
|
|
@ -18,6 +18,10 @@ final class ManiphestTaskBulkEngine
|
||||||
return new ManiphestTaskSearchEngine();
|
return new ManiphestTaskSearchEngine();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function newEditEngine() {
|
||||||
|
return new ManiphestEditEngine();
|
||||||
|
}
|
||||||
|
|
||||||
public function getDoneURI() {
|
public function getDoneURI() {
|
||||||
$board_uri = $this->getBoardURI();
|
$board_uri = $this->getBoardURI();
|
||||||
if ($board_uri) {
|
if ($board_uri) {
|
||||||
|
|
|
@ -178,6 +178,7 @@ EODOCS
|
||||||
id(new PhabricatorTextEditField())
|
id(new PhabricatorTextEditField())
|
||||||
->setKey('title')
|
->setKey('title')
|
||||||
->setLabel(pht('Title'))
|
->setLabel(pht('Title'))
|
||||||
|
->setBulkEditLabel(pht('Set title to'))
|
||||||
->setDescription(pht('Name of the task.'))
|
->setDescription(pht('Name of the task.'))
|
||||||
->setConduitDescription(pht('Rename the task.'))
|
->setConduitDescription(pht('Rename the task.'))
|
||||||
->setConduitTypeDescription(pht('New task name.'))
|
->setConduitTypeDescription(pht('New task name.'))
|
||||||
|
|
|
@ -10,7 +10,10 @@ abstract class PhabricatorBulkEngine extends Phobject {
|
||||||
private $editableList;
|
private $editableList;
|
||||||
private $targetList;
|
private $targetList;
|
||||||
|
|
||||||
|
private $rootFormID;
|
||||||
|
|
||||||
abstract public function newSearchEngine();
|
abstract public function newSearchEngine();
|
||||||
|
abstract public function newEditEngine();
|
||||||
|
|
||||||
public function getCancelURI() {
|
public function getCancelURI() {
|
||||||
$saved_query = $this->savedQuery;
|
$saved_query = $this->savedQuery;
|
||||||
|
@ -118,7 +121,7 @@ abstract class PhabricatorBulkEngine extends Phobject {
|
||||||
array(
|
array(
|
||||||
'action' => $this->getBulkURI(),
|
'action' => $this->getBulkURI(),
|
||||||
'method' => 'POST',
|
'method' => 'POST',
|
||||||
'id' => 'maniphest-batch-edit-form',
|
'id' => $this->getRootFormID(),
|
||||||
),
|
),
|
||||||
array(
|
array(
|
||||||
$this->newContextInputs(),
|
$this->newContextInputs(),
|
||||||
|
@ -290,95 +293,60 @@ abstract class PhabricatorBulkEngine extends Phobject {
|
||||||
|
|
||||||
private function newBulkActionForm() {
|
private function newBulkActionForm() {
|
||||||
$viewer = $this->getViewer();
|
$viewer = $this->getViewer();
|
||||||
|
$input_id = celerity_generate_unique_node_id();
|
||||||
|
|
||||||
|
$edit_engine = id($this->newEditEngine())
|
||||||
|
->setViewer($viewer);
|
||||||
|
|
||||||
|
$edit_map = $edit_engine->newBulkEditMap();
|
||||||
|
|
||||||
|
require_celerity_resource('phui-bulk-editor-css');
|
||||||
|
|
||||||
|
Javelin::initBehavior(
|
||||||
|
'bulk-editor',
|
||||||
|
array(
|
||||||
|
'rootNodeID' => $this->getRootFormID(),
|
||||||
|
'inputNodeID' => $input_id,
|
||||||
|
'edits' => $edit_map,
|
||||||
|
));
|
||||||
|
|
||||||
$cancel_uri = $this->getCancelURI();
|
$cancel_uri = $this->getCancelURI();
|
||||||
|
|
||||||
$template = new AphrontTokenizerTemplateView();
|
return id(new PHUIFormLayoutView())
|
||||||
$template = $template->render();
|
->setViewer($viewer)
|
||||||
|
->appendChild(
|
||||||
$projects_source = new PhabricatorProjectDatasource();
|
phutil_tag(
|
||||||
$mailable_source = new PhabricatorMetaMTAMailableDatasource();
|
'input',
|
||||||
$mailable_source->setViewer($viewer);
|
array(
|
||||||
$owner_source = new ManiphestAssigneeDatasource();
|
'type' => 'hidden',
|
||||||
$owner_source->setViewer($viewer);
|
'name' => 'xactions',
|
||||||
$spaces_source = id(new PhabricatorSpacesNamespaceDatasource())
|
'id' => $input_id,
|
||||||
->setViewer($viewer);
|
)))
|
||||||
|
->appendChild(
|
||||||
require_celerity_resource('maniphest-batch-editor');
|
id(new PHUIFormInsetView())
|
||||||
|
->setTitle(pht('Bulk Edit Actions'))
|
||||||
Javelin::initBehavior(
|
->setRightButton(
|
||||||
'maniphest-batch-editor',
|
javelin_tag(
|
||||||
array(
|
'a',
|
||||||
'root' => 'maniphest-batch-edit-form',
|
array(
|
||||||
'tokenizerTemplate' => $template,
|
'href' => '#',
|
||||||
'sources' => array(
|
'class' => 'button button-green',
|
||||||
'project' => array(
|
'sigil' => 'add-action',
|
||||||
'src' => $projects_source->getDatasourceURI(),
|
'mustcapture' => true,
|
||||||
'placeholder' => $projects_source->getPlaceholderText(),
|
),
|
||||||
'browseURI' => $projects_source->getBrowseURI(),
|
pht('Add Another Action')))
|
||||||
),
|
->setContent(
|
||||||
'owner' => array(
|
javelin_tag(
|
||||||
'src' => $owner_source->getDatasourceURI(),
|
'table',
|
||||||
'placeholder' => $owner_source->getPlaceholderText(),
|
array(
|
||||||
'browseURI' => $owner_source->getBrowseURI(),
|
'sigil' => 'bulk-actions',
|
||||||
'limit' => 1,
|
'class' => 'bulk-edit-table',
|
||||||
),
|
),
|
||||||
'cc' => array(
|
'')))
|
||||||
'src' => $mailable_source->getDatasourceURI(),
|
|
||||||
'placeholder' => $mailable_source->getPlaceholderText(),
|
|
||||||
'browseURI' => $mailable_source->getBrowseURI(),
|
|
||||||
),
|
|
||||||
'spaces' => array(
|
|
||||||
'src' => $spaces_source->getDatasourceURI(),
|
|
||||||
'placeholder' => $spaces_source->getPlaceholderText(),
|
|
||||||
'browseURI' => $spaces_source->getBrowseURI(),
|
|
||||||
'limit' => 1,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
'input' => 'batch-form-actions',
|
|
||||||
'priorityMap' => ManiphestTaskPriority::getTaskPriorityMap(),
|
|
||||||
'statusMap' => ManiphestTaskStatus::getTaskStatusMap(),
|
|
||||||
));
|
|
||||||
|
|
||||||
$form = id(new PHUIFormLayoutView())
|
|
||||||
->setUser($viewer);
|
|
||||||
|
|
||||||
$form->appendChild(
|
|
||||||
phutil_tag(
|
|
||||||
'input',
|
|
||||||
array(
|
|
||||||
'type' => 'hidden',
|
|
||||||
'name' => 'actions',
|
|
||||||
'id' => 'batch-form-actions',
|
|
||||||
)));
|
|
||||||
|
|
||||||
$form->appendChild(
|
|
||||||
id(new PHUIFormInsetView())
|
|
||||||
->setTitle(pht('Bulk Edit Actions'))
|
|
||||||
->setRightButton(
|
|
||||||
javelin_tag(
|
|
||||||
'a',
|
|
||||||
array(
|
|
||||||
'href' => '#',
|
|
||||||
'class' => 'button button-green',
|
|
||||||
'sigil' => 'add-action',
|
|
||||||
'mustcapture' => true,
|
|
||||||
),
|
|
||||||
pht('Add Another Action')))
|
|
||||||
->setContent(
|
|
||||||
javelin_tag(
|
|
||||||
'table',
|
|
||||||
array(
|
|
||||||
'sigil' => 'maniphest-batch-actions',
|
|
||||||
'class' => 'maniphest-batch-actions-table',
|
|
||||||
),
|
|
||||||
'')))
|
|
||||||
->appendChild(
|
->appendChild(
|
||||||
id(new AphrontFormSubmitControl())
|
id(new AphrontFormSubmitControl())
|
||||||
->setValue(pht('Apply Bulk Edit'))
|
->setValue(pht('Apply Bulk Edit'))
|
||||||
->addCancelButton($cancel_uri));
|
->addCancelButton($cancel_uri));
|
||||||
|
|
||||||
return $form;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private function buildEditResponse() {
|
private function buildEditResponse() {
|
||||||
|
@ -405,31 +373,33 @@ abstract class PhabricatorBulkEngine extends Phobject {
|
||||||
'You have not selected any objects to edit.'));
|
'You have not selected any objects to edit.'));
|
||||||
}
|
}
|
||||||
|
|
||||||
$raw_actions = $request->getStr('actions');
|
$raw_xactions = $request->getStr('xactions');
|
||||||
if ($raw_actions) {
|
if ($raw_xactions) {
|
||||||
$actions = phutil_json_decode($raw_actions);
|
$raw_xactions = phutil_json_decode($raw_xactions);
|
||||||
} else {
|
} else {
|
||||||
$actions = array();
|
$raw_xactions = array();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!$actions) {
|
if (!$raw_xactions) {
|
||||||
throw new Exception(
|
throw new Exception(
|
||||||
pht(
|
pht(
|
||||||
'You have not chosen any edits to apply.'));
|
'You have not chosen any edits to apply.'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$edit_engine = id($this->newEditEngine())
|
||||||
|
->setViewer($viewer);
|
||||||
|
|
||||||
|
$xactions = $edit_engine->newRawBulkTransactions($raw_xactions);
|
||||||
|
|
||||||
$cancel_uri = $this->getCancelURI();
|
$cancel_uri = $this->getCancelURI();
|
||||||
$done_uri = $this->getDoneURI();
|
$done_uri = $this->getDoneURI();
|
||||||
|
|
||||||
$job = PhabricatorWorkerBulkJob::initializeNewJob(
|
$job = PhabricatorWorkerBulkJob::initializeNewJob(
|
||||||
$viewer,
|
$viewer,
|
||||||
// TODO: This is a Maniphest-specific job type for now, but will become
|
new PhabricatorEditEngineBulkJobType(),
|
||||||
// a generic one so it gets to live here for now instead of in the task
|
|
||||||
// specific BulkEngine subclass.
|
|
||||||
new ManiphestTaskEditBulkJobType(),
|
|
||||||
array(
|
array(
|
||||||
'taskPHIDs' => mpull($objects, 'getPHID'),
|
'objectPHIDs' => mpull($objects, 'getPHID'),
|
||||||
'actions' => $actions,
|
'xactions' => $xactions,
|
||||||
'cancelURI' => $cancel_uri,
|
'cancelURI' => $cancel_uri,
|
||||||
'doneURI' => $done_uri,
|
'doneURI' => $done_uri,
|
||||||
));
|
));
|
||||||
|
@ -451,4 +421,12 @@ abstract class PhabricatorBulkEngine extends Phobject {
|
||||||
->setURI($job->getMonitorURI());
|
->setURI($job->getMonitorURI());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function getRootFormID() {
|
||||||
|
if (!$this->rootFormID) {
|
||||||
|
$this->rootFormID = celerity_generate_unique_node_id();
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->rootFormID;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
abstract class BulkParameterType extends Phobject {
|
||||||
|
|
||||||
|
private $viewer;
|
||||||
|
|
||||||
|
final public function setViewer(PhabricatorUser $viewer) {
|
||||||
|
$this->viewer = $viewer;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
final public function getViewer() {
|
||||||
|
return $this->viewer;
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract public function getPHUIXControlType();
|
||||||
|
|
||||||
|
public function getPHUIXControlSpecification() {
|
||||||
|
return array(
|
||||||
|
'value' => null,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class BulkStringParameterType
|
||||||
|
extends BulkParameterType {
|
||||||
|
|
||||||
|
public function getPHUIXControlType() {
|
||||||
|
return 'text';
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -2421,6 +2421,71 @@ abstract class PhabricatorEditEngine
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* -( Bulk Edits )--------------------------------------------------------- */
|
||||||
|
|
||||||
|
|
||||||
|
final public function newBulkEditMap() {
|
||||||
|
$config = $this->loadDefaultConfiguration();
|
||||||
|
if (!$config) {
|
||||||
|
throw new Exception(
|
||||||
|
pht('No default edit engine configuration for bulk edit.'));
|
||||||
|
}
|
||||||
|
|
||||||
|
$object = $this->newEditableObject();
|
||||||
|
$fields = $this->buildEditFields($object);
|
||||||
|
|
||||||
|
$edit_types = $this->getBulkEditTypesFromFields($fields);
|
||||||
|
|
||||||
|
$map = array();
|
||||||
|
foreach ($edit_types as $key => $type) {
|
||||||
|
$bulk_type = $type->getBulkParameterType();
|
||||||
|
if ($bulk_type === null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$bulk_label = $type->getBulkEditLabel();
|
||||||
|
if ($bulk_label === null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$map[] = array(
|
||||||
|
'label' => $bulk_label,
|
||||||
|
'xaction' => $type->getTransactionType(),
|
||||||
|
'control' => array(
|
||||||
|
'type' => $bulk_type->getPHUIXControlType(),
|
||||||
|
'spec' => (object)$bulk_type->getPHUIXControlSpecification(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $map;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
final public function newRawBulkTransactions(array $xactions) {
|
||||||
|
return $xactions;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getBulkEditTypesFromFields(array $fields) {
|
||||||
|
$types = array();
|
||||||
|
|
||||||
|
foreach ($fields as $field) {
|
||||||
|
$field_types = $field->getBulkEditTypes();
|
||||||
|
|
||||||
|
if ($field_types === null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($field_types as $field_type) {
|
||||||
|
$field_type->setField($field);
|
||||||
|
$types[$field_type->getEditType()] = $field_type;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $types;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* -( PhabricatorPolicyInterface )----------------------------------------- */
|
/* -( PhabricatorPolicyInterface )----------------------------------------- */
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -17,6 +17,7 @@ abstract class PhabricatorEditField extends Phobject {
|
||||||
private $previewPanel;
|
private $previewPanel;
|
||||||
private $controlID;
|
private $controlID;
|
||||||
private $controlInstructions;
|
private $controlInstructions;
|
||||||
|
private $bulkEditLabel;
|
||||||
|
|
||||||
private $description;
|
private $description;
|
||||||
private $conduitDescription;
|
private $conduitDescription;
|
||||||
|
@ -45,6 +46,7 @@ abstract class PhabricatorEditField extends Phobject {
|
||||||
private $isConduitOnly = false;
|
private $isConduitOnly = false;
|
||||||
|
|
||||||
private $conduitEditTypes;
|
private $conduitEditTypes;
|
||||||
|
private $bulkEditTypes;
|
||||||
|
|
||||||
public function setKey($key) {
|
public function setKey($key) {
|
||||||
$this->key = $key;
|
$this->key = $key;
|
||||||
|
@ -64,6 +66,15 @@ abstract class PhabricatorEditField extends Phobject {
|
||||||
return $this->label;
|
return $this->label;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function setBulkEditLabel($bulk_edit_label) {
|
||||||
|
$this->bulkEditLabel = $bulk_edit_label;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getBulkEditLabel() {
|
||||||
|
return $this->bulkEditLabel;
|
||||||
|
}
|
||||||
|
|
||||||
public function setViewer(PhabricatorUser $viewer) {
|
public function setViewer(PhabricatorUser $viewer) {
|
||||||
$this->viewer = $viewer;
|
$this->viewer = $viewer;
|
||||||
return $this;
|
return $this;
|
||||||
|
@ -625,6 +636,22 @@ abstract class PhabricatorEditField extends Phobject {
|
||||||
return new AphrontStringHTTPParameterType();
|
return new AphrontStringHTTPParameterType();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function getBulkParameterType() {
|
||||||
|
$type = $this->newBulkParameterType();
|
||||||
|
|
||||||
|
if (!$type) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$type->setViewer($this->getViewer());
|
||||||
|
|
||||||
|
return $type;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function newBulkParameterType() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
public function getConduitParameterType() {
|
public function getConduitParameterType() {
|
||||||
$type = $this->newConduitParameterType();
|
$type = $this->newConduitParameterType();
|
||||||
|
|
||||||
|
@ -657,8 +684,15 @@ abstract class PhabricatorEditField extends Phobject {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return id(new PhabricatorSimpleEditType())
|
$edit_type = id(new PhabricatorSimpleEditType())
|
||||||
->setConduitParameterType($parameter_type);
|
->setConduitParameterType($parameter_type);
|
||||||
|
|
||||||
|
$bulk_type = $this->getBulkParameterType();
|
||||||
|
if ($bulk_type) {
|
||||||
|
$edit_type->setBulkParameterType($bulk_type);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $edit_type;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function getEditType() {
|
protected function getEditType() {
|
||||||
|
@ -718,6 +752,31 @@ abstract class PhabricatorEditField extends Phobject {
|
||||||
return array($edit_type);
|
return array($edit_type);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final public function getBulkEditTypes() {
|
||||||
|
if ($this->bulkEditTypes === null) {
|
||||||
|
$edit_types = $this->newBulkEditTypes();
|
||||||
|
$edit_types = mpull($edit_types, null, 'getEditType');
|
||||||
|
|
||||||
|
foreach ($edit_types as $edit_type) {
|
||||||
|
$edit_type->setEditField($this);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->bulkEditTypes = $edit_types;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->bulkEditTypes;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function newBulkEditTypes() {
|
||||||
|
$edit_type = $this->getEditType();
|
||||||
|
|
||||||
|
if (!$edit_type) {
|
||||||
|
return array();
|
||||||
|
}
|
||||||
|
|
||||||
|
return array($edit_type);
|
||||||
|
}
|
||||||
|
|
||||||
public function getCommentAction() {
|
public function getCommentAction() {
|
||||||
$label = $this->getCommentActionLabel();
|
$label = $this->getCommentActionLabel();
|
||||||
if ($label === null) {
|
if ($label === null) {
|
||||||
|
|
|
@ -104,6 +104,10 @@ abstract class PhabricatorPHIDListEditField
|
||||||
return $type;
|
return $type;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function newBulkEditTypes() {
|
||||||
|
return $this->newConduitEditTypes();
|
||||||
|
}
|
||||||
|
|
||||||
protected function newConduitEditTypes() {
|
protected function newConduitEditTypes() {
|
||||||
if (!$this->getUseEdgeTransactions()) {
|
if (!$this->getUseEdgeTransactions()) {
|
||||||
return parent::newConduitEditTypes();
|
return parent::newConduitEditTypes();
|
||||||
|
|
|
@ -29,4 +29,8 @@ final class PhabricatorTextEditField
|
||||||
return new ConduitStringParameterType();
|
return new ConduitStringParameterType();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function newBulkParameterType() {
|
||||||
|
return new BulkStringParameterType();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,6 +14,9 @@ abstract class PhabricatorEditType extends Phobject {
|
||||||
private $conduitTypeDescription;
|
private $conduitTypeDescription;
|
||||||
private $conduitParameterType;
|
private $conduitParameterType;
|
||||||
|
|
||||||
|
private $bulkParameterType;
|
||||||
|
private $bulkEditLabel;
|
||||||
|
|
||||||
public function setLabel($label) {
|
public function setLabel($label) {
|
||||||
$this->label = $label;
|
$this->label = $label;
|
||||||
return $this;
|
return $this;
|
||||||
|
@ -23,6 +26,19 @@ abstract class PhabricatorEditType extends Phobject {
|
||||||
return $this->label;
|
return $this->label;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function setBulkEditLabel($bulk_edit_label) {
|
||||||
|
$this->bulkEditLabel = $bulk_edit_label;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getBulkEditLabel() {
|
||||||
|
if ($this->bulkEditLabel !== null) {
|
||||||
|
return $this->bulkEditLabel;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->getField()->getBulkEditLabel();
|
||||||
|
}
|
||||||
|
|
||||||
public function setField(PhabricatorEditField $field) {
|
public function setField(PhabricatorEditField $field) {
|
||||||
$this->field = $field;
|
$this->field = $field;
|
||||||
return $this;
|
return $this;
|
||||||
|
@ -85,6 +101,30 @@ abstract class PhabricatorEditType extends Phobject {
|
||||||
return $this->editField;
|
return $this->editField;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* -( Bulk )--------------------------------------------------------------- */
|
||||||
|
|
||||||
|
|
||||||
|
protected function newBulkParameterType() {
|
||||||
|
if ($this->bulkParameterType) {
|
||||||
|
return clone $this->bulkParameterType;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function setBulkParameterType(BulkParameterType $type) {
|
||||||
|
$this->bulkParameterType = $type;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function getBulkParameterType() {
|
||||||
|
return $this->newBulkParameterType();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* -( Conduit )------------------------------------------------------------ */
|
/* -( Conduit )------------------------------------------------------------ */
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,17 +0,0 @@
|
||||||
/**
|
|
||||||
* @provides maniphest-batch-editor
|
|
||||||
*/
|
|
||||||
.maniphest-batch-actions-table {
|
|
||||||
width: 100%;
|
|
||||||
margin: 12px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.maniphest-batch-actions-table td {
|
|
||||||
padding: 4px 8px;
|
|
||||||
vertical-align: middle;
|
|
||||||
}
|
|
||||||
|
|
||||||
.batch-editor-input {
|
|
||||||
width: 100%;
|
|
||||||
text-align: left;
|
|
||||||
}
|
|
22
webroot/rsrc/css/phui/phui-bulk-editor.css
Normal file
22
webroot/rsrc/css/phui/phui-bulk-editor.css
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
/**
|
||||||
|
* @provides phui-bulk-editor-css
|
||||||
|
*/
|
||||||
|
|
||||||
|
.bulk-edit-table {
|
||||||
|
width: 100%;
|
||||||
|
margin: 12px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bulk-edit-table td {
|
||||||
|
padding: 4px 8px;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bulk-edit-input {
|
||||||
|
width: 100%;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bulk-edit-input input {
|
||||||
|
width: 100%;
|
||||||
|
}
|
|
@ -1,158 +0,0 @@
|
||||||
/**
|
|
||||||
* @provides javelin-behavior-maniphest-batch-editor
|
|
||||||
* @requires javelin-behavior
|
|
||||||
* javelin-dom
|
|
||||||
* javelin-util
|
|
||||||
* phabricator-prefab
|
|
||||||
* multirow-row-manager
|
|
||||||
* javelin-json
|
|
||||||
*/
|
|
||||||
|
|
||||||
JX.behavior('maniphest-batch-editor', function(config) {
|
|
||||||
var root = JX.$(config.root);
|
|
||||||
var editor_table = JX.DOM.find(root, 'table', 'maniphest-batch-actions');
|
|
||||||
var manager = new JX.MultirowRowManager(editor_table);
|
|
||||||
var action_rows = [];
|
|
||||||
|
|
||||||
function renderRow() {
|
|
||||||
var action_select = JX.Prefab.renderSelect(
|
|
||||||
{
|
|
||||||
'add_project': 'Add Projects',
|
|
||||||
'remove_project' : 'Remove Projects',
|
|
||||||
'priority': 'Change Priority',
|
|
||||||
'status': 'Change Status',
|
|
||||||
'add_comment': 'Comment',
|
|
||||||
'assign': 'Assign',
|
|
||||||
'add_ccs' : 'Add CCs',
|
|
||||||
'remove_ccs' : 'Remove CCs',
|
|
||||||
'space': 'Shift to Space'
|
|
||||||
});
|
|
||||||
|
|
||||||
var proj_tokenizer = build_tokenizer(config.sources.project);
|
|
||||||
var owner_tokenizer = build_tokenizer(config.sources.owner);
|
|
||||||
var cc_tokenizer = build_tokenizer(config.sources.cc);
|
|
||||||
var space_tokenizer = build_tokenizer(config.sources.spaces);
|
|
||||||
|
|
||||||
var priority_select = JX.Prefab.renderSelect(config.priorityMap);
|
|
||||||
var status_select = JX.Prefab.renderSelect(config.statusMap);
|
|
||||||
var comment_input = JX.$N('input', {style: {width: '100%'}});
|
|
||||||
|
|
||||||
var cell = JX.$N('td', {className: 'batch-editor-input'});
|
|
||||||
var vfunc = null;
|
|
||||||
|
|
||||||
function update() {
|
|
||||||
switch (action_select.value) {
|
|
||||||
case 'add_project':
|
|
||||||
case 'remove_project':
|
|
||||||
JX.DOM.setContent(cell, proj_tokenizer.template);
|
|
||||||
vfunc = function() {
|
|
||||||
return JX.keys(proj_tokenizer.object.getTokens());
|
|
||||||
};
|
|
||||||
break;
|
|
||||||
case 'add_ccs':
|
|
||||||
case 'remove_ccs':
|
|
||||||
JX.DOM.setContent(cell, cc_tokenizer.template);
|
|
||||||
vfunc = function() {
|
|
||||||
return JX.keys(cc_tokenizer.object.getTokens());
|
|
||||||
};
|
|
||||||
break;
|
|
||||||
case 'assign':
|
|
||||||
JX.DOM.setContent(cell, owner_tokenizer.template);
|
|
||||||
vfunc = function() {
|
|
||||||
return JX.keys(owner_tokenizer.object.getTokens());
|
|
||||||
};
|
|
||||||
break;
|
|
||||||
case 'space':
|
|
||||||
JX.DOM.setContent(cell, space_tokenizer.template);
|
|
||||||
vfunc = function() {
|
|
||||||
return JX.keys(space_tokenizer.object.getTokens());
|
|
||||||
};
|
|
||||||
break;
|
|
||||||
case 'add_comment':
|
|
||||||
JX.DOM.setContent(cell, comment_input);
|
|
||||||
vfunc = function() {
|
|
||||||
return comment_input.value;
|
|
||||||
};
|
|
||||||
break;
|
|
||||||
case 'priority':
|
|
||||||
JX.DOM.setContent(cell, priority_select);
|
|
||||||
vfunc = function() { return priority_select.value; };
|
|
||||||
break;
|
|
||||||
case 'status':
|
|
||||||
JX.DOM.setContent(cell, status_select);
|
|
||||||
vfunc = function() { return status_select.value; };
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
JX.DOM.listen(action_select, 'change', null, update);
|
|
||||||
update();
|
|
||||||
|
|
||||||
return {
|
|
||||||
nodes : [JX.$N('td', {}, action_select), cell],
|
|
||||||
dataCallback : function() {
|
|
||||||
return {
|
|
||||||
action: action_select.value,
|
|
||||||
value: vfunc()
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function onaddaction(e) {
|
|
||||||
e.kill();
|
|
||||||
addRow({});
|
|
||||||
}
|
|
||||||
|
|
||||||
function addRow(info) {
|
|
||||||
var data = renderRow(info);
|
|
||||||
var row = manager.addRow(data.nodes);
|
|
||||||
var id = manager.getRowID(row);
|
|
||||||
|
|
||||||
action_rows[id] = data.dataCallback;
|
|
||||||
}
|
|
||||||
|
|
||||||
function onsubmit() {
|
|
||||||
var input = JX.$(config.input);
|
|
||||||
|
|
||||||
var actions = [];
|
|
||||||
for (var k in action_rows) {
|
|
||||||
actions.push(action_rows[k]());
|
|
||||||
}
|
|
||||||
|
|
||||||
input.value = JX.JSON.stringify(actions);
|
|
||||||
}
|
|
||||||
|
|
||||||
addRow({});
|
|
||||||
|
|
||||||
JX.DOM.listen(
|
|
||||||
root,
|
|
||||||
'click',
|
|
||||||
'add-action',
|
|
||||||
onaddaction);
|
|
||||||
|
|
||||||
JX.DOM.listen(
|
|
||||||
root,
|
|
||||||
'submit',
|
|
||||||
null,
|
|
||||||
onsubmit);
|
|
||||||
|
|
||||||
manager.listen(
|
|
||||||
'row-removed',
|
|
||||||
function(row_id) {
|
|
||||||
delete action_rows[row_id];
|
|
||||||
});
|
|
||||||
|
|
||||||
function build_tokenizer(tconfig) {
|
|
||||||
var built = JX.Prefab.newTokenizerFromTemplate(
|
|
||||||
config.tokenizerTemplate,
|
|
||||||
JX.copy({}, tconfig));
|
|
||||||
built.tokenizer.start();
|
|
||||||
|
|
||||||
return {
|
|
||||||
object: built.tokenizer,
|
|
||||||
template: built.node
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
});
|
|
113
webroot/rsrc/js/core/behavior-bulk-editor.js
Normal file
113
webroot/rsrc/js/core/behavior-bulk-editor.js
Normal file
|
@ -0,0 +1,113 @@
|
||||||
|
/**
|
||||||
|
* @provides javelin-behavior-bulk-editor
|
||||||
|
* @requires javelin-behavior
|
||||||
|
* javelin-dom
|
||||||
|
* javelin-util
|
||||||
|
* phabricator-prefab
|
||||||
|
* multirow-row-manager
|
||||||
|
* javelin-json
|
||||||
|
* phuix-form-control-view
|
||||||
|
*/
|
||||||
|
|
||||||
|
JX.behavior('bulk-editor', function(config) {
|
||||||
|
|
||||||
|
var root = JX.$(config.rootNodeID);
|
||||||
|
var editor_table = JX.DOM.find(root, 'table', 'bulk-actions');
|
||||||
|
|
||||||
|
var manager = new JX.MultirowRowManager(editor_table);
|
||||||
|
var action_rows = [];
|
||||||
|
|
||||||
|
var option_map = {};
|
||||||
|
var option_order = [];
|
||||||
|
var spec_map = {};
|
||||||
|
|
||||||
|
for (var ii = 0; ii < config.edits.length; ii++) {
|
||||||
|
var edit = config.edits[ii];
|
||||||
|
|
||||||
|
option_map[edit.xaction] = edit.label;
|
||||||
|
option_order.push(edit.xaction);
|
||||||
|
|
||||||
|
spec_map[edit.xaction] = edit;
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderRow() {
|
||||||
|
var action_select = JX.Prefab.renderSelect(
|
||||||
|
option_map,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
option_order);
|
||||||
|
|
||||||
|
var cell = JX.$N('td', {className: 'bulk-edit-input'});
|
||||||
|
var vfunc = null;
|
||||||
|
|
||||||
|
function update() {
|
||||||
|
var spec = spec_map[action_select.value];
|
||||||
|
var control = spec.control;
|
||||||
|
|
||||||
|
var phuix = new JX.PHUIXFormControl()
|
||||||
|
.setControl(control.type, control.spec);
|
||||||
|
|
||||||
|
JX.DOM.setContent(cell, phuix.getRawInputNode());
|
||||||
|
|
||||||
|
vfunc = JX.bind(phuix, phuix.getValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
JX.DOM.listen(action_select, 'change', null, update);
|
||||||
|
update();
|
||||||
|
|
||||||
|
return {
|
||||||
|
nodes : [JX.$N('td', {}, action_select), cell],
|
||||||
|
dataCallback : function() {
|
||||||
|
return {
|
||||||
|
type: action_select.value,
|
||||||
|
value: vfunc()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function onaddaction(e) {
|
||||||
|
e.kill();
|
||||||
|
addRow({});
|
||||||
|
}
|
||||||
|
|
||||||
|
function addRow(info) {
|
||||||
|
var data = renderRow(info);
|
||||||
|
var row = manager.addRow(data.nodes);
|
||||||
|
var id = manager.getRowID(row);
|
||||||
|
|
||||||
|
action_rows[id] = data.dataCallback;
|
||||||
|
}
|
||||||
|
|
||||||
|
function onsubmit() {
|
||||||
|
var input = JX.$(config.inputNodeID);
|
||||||
|
|
||||||
|
var actions = [];
|
||||||
|
for (var k in action_rows) {
|
||||||
|
actions.push(action_rows[k]());
|
||||||
|
}
|
||||||
|
|
||||||
|
input.value = JX.JSON.stringify(actions);
|
||||||
|
}
|
||||||
|
|
||||||
|
addRow({});
|
||||||
|
|
||||||
|
JX.DOM.listen(
|
||||||
|
root,
|
||||||
|
'click',
|
||||||
|
'add-action',
|
||||||
|
onaddaction);
|
||||||
|
|
||||||
|
JX.DOM.listen(
|
||||||
|
root,
|
||||||
|
'submit',
|
||||||
|
null,
|
||||||
|
onsubmit);
|
||||||
|
|
||||||
|
manager.listen(
|
||||||
|
'row-removed',
|
||||||
|
function(row_id) {
|
||||||
|
delete action_rows[row_id];
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
|
@ -14,6 +14,7 @@ JX.install('PHUIXFormControl', {
|
||||||
_className: null,
|
_className: null,
|
||||||
_valueSetCallback: null,
|
_valueSetCallback: null,
|
||||||
_valueGetCallback: null,
|
_valueGetCallback: null,
|
||||||
|
_rawInputNode: null,
|
||||||
|
|
||||||
setLabel: function(label) {
|
setLabel: function(label) {
|
||||||
JX.DOM.setContent(this._getLabelNode(), label);
|
JX.DOM.setContent(this._getLabelNode(), label);
|
||||||
|
@ -53,6 +54,9 @@ JX.install('PHUIXFormControl', {
|
||||||
case 'checkboxes':
|
case 'checkboxes':
|
||||||
input = this._newCheckboxes(spec);
|
input = this._newCheckboxes(spec);
|
||||||
break;
|
break;
|
||||||
|
case 'text':
|
||||||
|
input = this._newText(spec);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
// TODO: Default or better error?
|
// TODO: Default or better error?
|
||||||
JX.$E('Bad Input Type');
|
JX.$E('Bad Input Type');
|
||||||
|
@ -62,6 +66,7 @@ JX.install('PHUIXFormControl', {
|
||||||
JX.DOM.setContent(node, input.node);
|
JX.DOM.setContent(node, input.node);
|
||||||
this._valueGetCallback = input.get;
|
this._valueGetCallback = input.get;
|
||||||
this._valueSetCallback = input.set;
|
this._valueSetCallback = input.set;
|
||||||
|
this._rawInputNode = input.node;
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
},
|
},
|
||||||
|
@ -75,6 +80,10 @@ JX.install('PHUIXFormControl', {
|
||||||
return this._valueGetCallback();
|
return this._valueGetCallback();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
getRawInputNode: function() {
|
||||||
|
return this._rawInputNode;
|
||||||
|
},
|
||||||
|
|
||||||
getNode: function() {
|
getNode: function() {
|
||||||
if (!this._node) {
|
if (!this._node) {
|
||||||
|
|
||||||
|
@ -281,6 +290,10 @@ JX.install('PHUIXFormControl', {
|
||||||
},
|
},
|
||||||
|
|
||||||
_newPoints: function(spec) {
|
_newPoints: function(spec) {
|
||||||
|
return this._newText();
|
||||||
|
},
|
||||||
|
|
||||||
|
_newText: function(spec) {
|
||||||
var attrs = {
|
var attrs = {
|
||||||
type: 'text',
|
type: 'text',
|
||||||
value: spec.value
|
value: spec.value
|
||||||
|
|
Loading…
Reference in a new issue