diff --git a/bin/bulk b/bin/bulk new file mode 120000 --- /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 --- /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 --- /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 --- /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 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -2206,6 +2206,8 @@ '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 @@ '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 --- a/src/applications/daemon/controller/PhabricatorDaemonBulkJobMonitorController.php +++ b/src/applications/daemon/controller/PhabricatorDaemonBulkJobMonitorController.php @@ -49,13 +49,22 @@ 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 --- a/src/applications/transactions/bulk/PhabricatorEditEngineBulkJobType.php +++ b/src/applications/transactions/bulk/PhabricatorEditEngineBulkJobType.php @@ -12,10 +12,36 @@ } 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 @@ $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 --- /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 --- /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 @@ ->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 --- a/src/infrastructure/internationalization/translation/PhabricatorUSEnglishTranslation.php +++ b/src/infrastructure/internationalization/translation/PhabricatorUSEnglishTranslation.php @@ -1639,6 +1639,13 @@ ), ), + '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 --- a/src/view/phui/PHUITimelineEventView.php +++ b/src/view/phui/PHUITimelineEventView.php @@ -584,6 +584,14 @@ } $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(