From 3038d564a6d8a2194fdf0cd716436e44ee23efd5 Mon Sep 17 00:00:00 2001 From: epriestley Date: Fri, 19 Jan 2018 10:08:02 -0800 Subject: [PATCH] Allow bulk edits to be made silently if you have CLI access Summary: Fixes T13042. This hooks up the new "silent" mode from D18882 and makes it actually work. The UI (where we tell you to go run some command and then reload the page) is pretty clumsy, but should solve some problems for now and can be cleaned up eventually. The actual mechanics (timeline aggregation, Herald interaction, etc.) are on firmer ground. Test Plan: - Made a normal bulk edit, got mail and feed stories. - Made a silent bulk edit, no mail and no feed. - Saw "Silent Edit" marker in timeline for silent edits: {F5386245} Reviewers: amckinley Reviewed By: amckinley Maniphest Tasks: T13042 Differential Revision: https://secure.phabricator.com/D18883 --- bin/bulk | 1 + .../autopatches/20180119.bulk.01.silent.sql | 2 + scripts/manage_bulk.php | 1 + scripts/setup/manage_bulk.php | 21 ++++++ src/__phutil_library_map__.php | 4 ++ ...bricatorDaemonBulkJobMonitorController.php | 15 +++- .../bulk/PhabricatorEditEngineBulkJobType.php | 30 +++++++- ...icatorBulkManagementMakeSilentWorkflow.php | 72 +++++++++++++++++++ .../PhabricatorBulkManagementWorkflow.php | 4 ++ .../storage/PhabricatorWorkerBulkJob.php | 5 +- .../PhabricatorUSEnglishTranslation.php | 7 ++ src/view/phui/PHUITimelineEventView.php | 8 +++ 12 files changed, 165 insertions(+), 5 deletions(-) create mode 120000 bin/bulk create mode 100644 resources/sql/autopatches/20180119.bulk.01.silent.sql create mode 120000 scripts/manage_bulk.php create mode 100755 scripts/setup/manage_bulk.php create mode 100644 src/applications/transactions/bulk/management/PhabricatorBulkManagementMakeSilentWorkflow.php create mode 100644 src/applications/transactions/bulk/management/PhabricatorBulkManagementWorkflow.php diff --git a/bin/bulk b/bin/bulk new file mode 120000 index 0000000000..04d0550497 --- /dev/null +++ b/bin/bulk @@ -0,0 +1 @@ +../scripts/setup/manage_bulk.php \ No newline at end of file diff --git a/resources/sql/autopatches/20180119.bulk.01.silent.sql b/resources/sql/autopatches/20180119.bulk.01.silent.sql new file mode 100644 index 0000000000..b426de953d --- /dev/null +++ b/resources/sql/autopatches/20180119.bulk.01.silent.sql @@ -0,0 +1,2 @@ +ALTER TABLE {$NAMESPACE}_worker.worker_bulkjob + ADD isSilent BOOL NOT NULL; diff --git a/scripts/manage_bulk.php b/scripts/manage_bulk.php new file mode 120000 index 0000000000..04d0550497 --- /dev/null +++ b/scripts/manage_bulk.php @@ -0,0 +1 @@ +../scripts/setup/manage_bulk.php \ No newline at end of file diff --git a/scripts/setup/manage_bulk.php b/scripts/setup/manage_bulk.php new file mode 100755 index 0000000000..9786f9b078 --- /dev/null +++ b/scripts/setup/manage_bulk.php @@ -0,0 +1,21 @@ +#!/usr/bin/env php +setTagline(pht('manage bulk jobs')); +$args->setSynopsis(<<parseStandardArguments(); + +$workflows = id(new PhutilClassMapQuery()) + ->setAncestorClass('PhabricatorBulkManagementWorkflow') + ->execute(); +$workflows[] = new PhutilHelpArgumentWorkflow(); +$args->parseWorkflows($workflows); diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index 108c73b044..55fe8ada3d 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -2206,6 +2206,8 @@ phutil_register_library_map(array( 'PhabricatorBulkContentSource' => 'infrastructure/daemon/contentsource/PhabricatorBulkContentSource.php', 'PhabricatorBulkEditGroup' => 'applications/transactions/bulk/PhabricatorBulkEditGroup.php', 'PhabricatorBulkEngine' => 'applications/transactions/bulk/PhabricatorBulkEngine.php', + 'PhabricatorBulkManagementMakeSilentWorkflow' => 'applications/transactions/bulk/management/PhabricatorBulkManagementMakeSilentWorkflow.php', + 'PhabricatorBulkManagementWorkflow' => 'applications/transactions/bulk/management/PhabricatorBulkManagementWorkflow.php', 'PhabricatorCacheDAO' => 'applications/cache/storage/PhabricatorCacheDAO.php', 'PhabricatorCacheEngine' => 'applications/system/engine/PhabricatorCacheEngine.php', 'PhabricatorCacheEngineExtension' => 'applications/system/engine/PhabricatorCacheEngineExtension.php', @@ -7504,6 +7506,8 @@ phutil_register_library_map(array( 'PhabricatorBulkContentSource' => 'PhabricatorContentSource', 'PhabricatorBulkEditGroup' => 'Phobject', 'PhabricatorBulkEngine' => 'Phobject', + 'PhabricatorBulkManagementMakeSilentWorkflow' => 'PhabricatorBulkManagementWorkflow', + 'PhabricatorBulkManagementWorkflow' => 'PhabricatorManagementWorkflow', 'PhabricatorCacheDAO' => 'PhabricatorLiskDAO', 'PhabricatorCacheEngine' => 'Phobject', 'PhabricatorCacheEngineExtension' => 'Phobject', diff --git a/src/applications/daemon/controller/PhabricatorDaemonBulkJobMonitorController.php b/src/applications/daemon/controller/PhabricatorDaemonBulkJobMonitorController.php index 79d509f1ea..fd3e61d869 100644 --- a/src/applications/daemon/controller/PhabricatorDaemonBulkJobMonitorController.php +++ b/src/applications/daemon/controller/PhabricatorDaemonBulkJobMonitorController.php @@ -49,13 +49,22 @@ final class PhabricatorDaemonBulkJobMonitorController return id(new AphrontRedirectResponse()) ->setURI($job->getMonitorURI()); } else { - return $this->newDialog() - ->setTitle(pht('Confirm Bulk Job')) - ->appendParagraph($job->getDescriptionForConfirm()) + $dialog = $this->newDialog() + ->setTitle(pht('Confirm Bulk Job')); + + $confirm = $job->getDescriptionForConfirm(); + $confirm = (array)$confirm; + foreach ($confirm as $paragraph) { + $dialog->appendParagraph($paragraph); + } + + $dialog ->appendParagraph( pht('Start work on this bulk job?')) ->addCancelButton($job->getManageURI(), pht('Details')) ->addSubmitButton(pht('Start Work')); + + return $dialog; } } else { return $this->newDialog() diff --git a/src/applications/transactions/bulk/PhabricatorEditEngineBulkJobType.php b/src/applications/transactions/bulk/PhabricatorEditEngineBulkJobType.php index e7ff5d3414..758e7f3439 100644 --- a/src/applications/transactions/bulk/PhabricatorEditEngineBulkJobType.php +++ b/src/applications/transactions/bulk/PhabricatorEditEngineBulkJobType.php @@ -12,10 +12,36 @@ final class PhabricatorEditEngineBulkJobType } public function getDescriptionForConfirm(PhabricatorWorkerBulkJob $job) { - return pht( + $parts = array(); + + $parts[] = pht( 'You are about to apply a bulk edit which will affect '. '%s object(s).', new PhutilNumber($job->getSize())); + + if ($job->getIsSilent()) { + $parts[] = pht( + 'If you start work now, this edit will be applied silently: it will '. + 'not send mail or publish notifications.'); + } else { + $parts[] = pht( + 'If you start work now, this edit will send mail and publish '. + 'notifications normally.'); + + $parts[] = pht('To silence this edit, run this command:'); + + $command = csprintf( + 'phabricator/ $ ./bin/bulk make-silent --id %R', + $job->getID()); + $command = (string)$command; + + $parts[] = phutil_tag('tt', array(), $command); + + $parts[] = pht( + 'After running this command, reload this page to see the new setting.'); + } + + return $parts; } public function getJobSize(PhabricatorWorkerBulkJob $job) { @@ -56,12 +82,14 @@ final class PhabricatorEditEngineBulkJobType $raw_xactions = $job->getParameter('xactions'); $xactions = $this->buildTransactions($object, $raw_xactions); + $is_silent = $job->getIsSilent(); $editor = $object->getApplicationTransactionEditor() ->setActor($actor) ->setContentSource($job->newContentSource()) ->setContinueOnNoEffect(true) ->setContinueOnMissingFields(true) + ->setIsSilent($is_silent) ->applyTransactions($object, $xactions); } diff --git a/src/applications/transactions/bulk/management/PhabricatorBulkManagementMakeSilentWorkflow.php b/src/applications/transactions/bulk/management/PhabricatorBulkManagementMakeSilentWorkflow.php new file mode 100644 index 0000000000..ca13a8f0ea --- /dev/null +++ b/src/applications/transactions/bulk/management/PhabricatorBulkManagementMakeSilentWorkflow.php @@ -0,0 +1,72 @@ +setName('make-silent') + ->setExamples('**make-silent** [options]') + ->setSynopsis( + pht('Configure a bulk job to execute silently.')) + ->setArguments( + array( + array( + 'name' => 'id', + 'param' => 'id', + 'help' => pht( + 'Configure bulk job __id__ to run silently (without sending '. + 'mail or publishing notifications).'), + ), + )); + } + + public function execute(PhutilArgumentParser $args) { + $viewer = $this->getViewer(); + + $id = $args->getArg('id'); + if (!$id) { + throw new PhutilArgumentUsageException( + pht('Use "--id" to choose a bulk job to make silent.')); + } + + $job = id(new PhabricatorWorkerBulkJobQuery()) + ->setViewer($viewer) + ->withIDs(array($id)) + ->executeOne(); + if (!$job) { + throw new PhutilArgumentUsageException( + pht( + 'Unable to load bulk job with ID "%s".', + $id)); + } + + if ($job->getIsSilent()) { + echo tsprintf( + "%s\n", + pht('This job is already configured to run silently.')); + return 0; + } + + if ($job->getStatus() !== PhabricatorWorkerBulkJob::STATUS_CONFIRM) { + throw new PhutilArgumentUsageException( + pht( + 'Work has already started on job "%s". Jobs can not be '. + 'reconfigured after they have been started.', + $id)); + } + + $job + ->setIsSilent(true) + ->save(); + + echo tsprintf( + "%s\n", + pht( + 'Configured job "%s" to run silently.', + $id)); + + return 0; + } + +} diff --git a/src/applications/transactions/bulk/management/PhabricatorBulkManagementWorkflow.php b/src/applications/transactions/bulk/management/PhabricatorBulkManagementWorkflow.php new file mode 100644 index 0000000000..6533fedd80 --- /dev/null +++ b/src/applications/transactions/bulk/management/PhabricatorBulkManagementWorkflow.php @@ -0,0 +1,4 @@ + 'text32', 'status' => 'text32', 'size' => 'uint32', + 'isSilent' => 'bool', ), self::CONFIG_KEY_SCHEMA => array( 'key_type' => array( @@ -58,7 +60,8 @@ final class PhabricatorWorkerBulkJob ->setAuthorPHID($actor->getPHID()) ->setJobTypeKey($type->getBulkJobTypeKey()) ->setParameters($parameters) - ->attachJobImplementation($type); + ->attachJobImplementation($type) + ->setIsSilent(0); $job->setSize($job->computeSize()); diff --git a/src/infrastructure/internationalization/translation/PhabricatorUSEnglishTranslation.php b/src/infrastructure/internationalization/translation/PhabricatorUSEnglishTranslation.php index 184c2d5151..2cb44fe169 100644 --- a/src/infrastructure/internationalization/translation/PhabricatorUSEnglishTranslation.php +++ b/src/infrastructure/internationalization/translation/PhabricatorUSEnglishTranslation.php @@ -1639,6 +1639,13 @@ final class PhabricatorUSEnglishTranslation ), ), + 'You are about to apply a bulk edit which will affect '. + '%s object(s).' => array( + 'You are about to apply a bulk edit to a single object.', + 'You are about to apply a bulk edit which will affect '. + '%s objects.', + ), + ); } diff --git a/src/view/phui/PHUITimelineEventView.php b/src/view/phui/PHUITimelineEventView.php index f07b40cb39..161dd0c944 100644 --- a/src/view/phui/PHUITimelineEventView.php +++ b/src/view/phui/PHUITimelineEventView.php @@ -584,6 +584,14 @@ final class PHUITimelineEventView extends AphrontView { } $extra[] = $date; } + + // If this edit was applied silently, give user a hint that they should + // not expect to have received any mail or notifications. + if ($this->getIsSilent()) { + $extra[] = id(new PHUIIconView()) + ->setIcon('fa-bell-slash', 'red') + ->setTooltip(pht('Silent Edit')); + } } $extra = javelin_tag(