diff --git a/core/includes/batch.inc b/core/includes/batch.inc index 3a835e5..c8db02a 100644 --- a/core/includes/batch.inc +++ b/core/includes/batch.inc @@ -100,7 +100,7 @@ function _batch_do() { // Perform actual processing. /** @var \Drupal\Core\Batch\BatchProcessorInterface */ $batch_processor = \Drupal::service('batch.processor'); - list($percentage, $message, $label) = $batch_processor->process(); + list($percentage, $message, $label) = $batch_processor->processBatchSet(); return new JsonResponse(array('status' => TRUE, 'percentage' => $percentage, 'message' => $message, 'label' => $label)); } @@ -151,7 +151,7 @@ function _batch_progress_page() { print $fallback; // Perform actual processing. - list($percentage, $message, $label) = $batch_processor->process($batch); + list($percentage, $message, $label) = $batch_processor->processBatchSet($batch); if ($percentage == 100) { $new_op = 'finished'; } diff --git a/core/includes/form.inc b/core/includes/form.inc index 8f381ae..945f2b4 100644 --- a/core/includes/form.inc +++ b/core/includes/form.inc @@ -795,9 +795,91 @@ function batch_set($batch_definition) { * A redirect response if the batch is progressive. No return value otherwise. */ function batch_process($redirect = NULL, Url $url = NULL, $redirect_callback = NULL) { - /** @var \Drupal\Core\Batch\BatchProcessorInterface */ - $batch_processor = \Drupal::service('batch.processor'); - return $batch_processor->process($redirect, $url, $redirect_callback); + $batch =& batch_get(); + + if (isset($batch)) { + // Add process information + $process_info = array( + 'current_set' => 0, + 'progressive' => TRUE, + 'url' => isset($url) ? $url : Url::fromRoute('system.batch_page.html'), + 'source_url' => Url::fromRouteMatch(\Drupal::routeMatch()), + 'batch_redirect' => $redirect, + 'theme' => \Drupal::theme()->getActiveTheme()->getName(), + 'redirect_callback' => $redirect_callback, + ); + $batch += $process_info; + + // The batch is now completely built. Allow other modules to make changes + // to the batch so that it is easier to reuse batch processes in other + // environments. + \Drupal::moduleHandler()->alter('batch', $batch); + + // Assign an arbitrary id: don't rely on a serial column in the 'batch' + // table, since non-progressive batches skip database storage completely. + $batch['id'] = db_next_id(); + + // Move operations to a job queue. Non-progressive batches will use a + // memory-based queue. + foreach ($batch['sets'] as $key => $batch_set) { + _batch_populate_queue($batch, $key); + } + + // Initiate processing. + if ($batch['progressive']) { + // Now that we have a batch id, we can generate the redirection link in + // the generic error message. + /** @var \Drupal\Core\Url $batch_url */ + $batch_url = $batch['url']; + /** @var \Drupal\Core\Url $error_url */ + $error_url = clone $batch_url; + $query_options = $error_url->getOption('query'); + $query_options['id'] = $batch['id']; + $query_options['op'] = 'finished'; + $error_url->setOption('query', $query_options); + + $batch['error_message'] = t('Please continue to the error page', array('@error_url' => $error_url->toString(TRUE)->getGeneratedUrl())); + + // Clear the way for the redirection to the batch processing page, by + // saving and unsetting the 'destination', if there is any. + $request = \Drupal::request(); + if ($request->query->has('destination')) { + $batch['destination'] = $request->query->get('destination'); + $request->query->remove('destination'); + } + + // Store safe strings. + // @todo Ensure we are not storing an excessively large string list in: + // https://www.drupal.org/node/2295823 + $batch['safe_strings'] = SafeMarkup::getAll(); + + // Store the batch. + \Drupal::service('batch.storage')->create($batch); + + // Set the batch number in the session to guarantee that it will stay alive. + $_SESSION['batches'][$batch['id']] = TRUE; + + // Redirect for processing. + $query_options = $error_url->getOption('query'); + $query_options['op'] = 'start'; + $query_options['id'] = $batch['id']; + $batch_url->setOption('query', $query_options); + if (($function = $batch['redirect_callback']) && function_exists($function)) { + $function($batch_url->toString(), ['query' => $query_options]); + } + else { + return new RedirectResponse($batch_url->setAbsolute()->toString(TRUE)->getGeneratedUrl()); + } + } + else { + // Non-progressive execution: bypass the whole progressbar workflow + // and execute the batch in one pass. + require_once __DIR__ . '/batch.inc'; + /** @var \Drupal\Core\Batch\BatchProcessorInterface $batch_processor */ + $batch_processor = \Drupal::service('batch.processor'); + $batch_processor->processBatchSet(); + } + } } /** diff --git a/core/lib/Drupal/Core/Batch/BatchProcessor.php b/core/lib/Drupal/Core/Batch/BatchProcessor.php index e1507ba..29319cb 100644 --- a/core/lib/Drupal/Core/Batch/BatchProcessor.php +++ b/core/lib/Drupal/Core/Batch/BatchProcessor.php @@ -10,9 +10,14 @@ use Drupal\Component\Utility\SafeMarkup; use Drupal\Component\Utility\Timer; use Drupal\Component\Utility\UrlHelper; +use Drupal\Core\Database\Database; use Drupal\Core\Datetime\DateFormatter; +use Drupal\Core\Extension\ModuleHandlerInterface; use Drupal\Core\Form\FormState; use Drupal\Core\Form\FormSubmitterInterface; +use Drupal\Core\Routing\RouteMatchInterface; +use Drupal\Core\StringTranslation\StringTranslationTrait; +use Drupal\Core\Theme\ThemeManagerInterface; use Drupal\Core\Url; use Symfony\Component\HttpFoundation\RedirectResponse; use Symfony\Component\HttpFoundation\RequestStack; @@ -22,6 +27,8 @@ */ class BatchProcessor implements BatchProcessorInterface { + use StringTranslationTrait; + /** * The set of batches. * @@ -98,115 +105,6 @@ public function __construct($root, BatchQueueHandlerInterface $batch_queue, Date } /** - * Processes the batch. - * - * This function is generally not needed in form submit handlers; - * Form API takes care of batches that were set during form submission. - * - * @param \Drupal\Core\Url|string $redirect - * (optional) Either path or Url object to redirect to when the batch has - * finished processing. Note that to simply force a batch to (conditionally) - * redirect to a custom location after it is finished processing but to - * otherwise allow the standard form API batch handling to occur, it is not - * necessary to call batch_process() and use this parameter. Instead, make - * the batch 'finished' callback return an instance of - * \Symfony\Component\HttpFoundation\RedirectResponse, which will be used - * automatically by the standard batch processing pipeline (and which takes - * precedence over this parameter). - * @param \Drupal\Core\Url $url - * (optional - should only be used for separate scripts like update.php) - * URL of the batch processing page. - * @param $redirect_callback - * (optional) Specify a function to be called to redirect to the progressive - * processing page. - * - * @return \Symfony\Component\HttpFoundation\RedirectResponse|null - * A redirect response if the batch is progressive. No return value otherwise. - */ - public function process($redirect = NULL, Url $url = NULL, $redirect_callback = NULL) { - if (isset($this->batch)) { - // Add process information - $process_info = array( - 'current_set' => 0, - 'progressive' => TRUE, - 'url' => isset($url) ? $url : Url::fromRoute('system.batch_page.html'), - 'source_url' => Url::fromRouteMatch(\Drupal::routeMatch() ), - 'batch_redirect' => $redirect, - 'theme' => \Drupal::theme()->getActiveTheme()->getName(), - 'redirect_callback' => $redirect_callback, - ); - $this->batch += $process_info; - - // The batch is now completely built. Allow other modules to make changes - // to the batch so that it is easier to reuse batch processes in other - // environments. - \Drupal::moduleHandler()->alter('batch', $this->batch); - - // Assign an arbitrary id: don't rely on a serial column in the 'batch' - // table, since non-progressive batches skip database storage completely. - $this->batch['id'] = db_next_id(); - - // Move operations to a job queue. Non-progressive batches will use a - // memory-based queue. - foreach ($this->batch['sets'] as $key => $batch_set) { - _batch_populate_queue($this->batch, $key); - } - - // Initiate processing. - if ($this->batch['progressive']) { - // Now that we have a batch id, we can generate the redirection link in - // the generic error message. - /** @var \Drupal\Core\Url $batch_url */ - $batch_url = $this->batch['url']; - /** @var \Drupal\Core\Url $error_url */ - $error_url = clone $batch_url; - $query_options = $error_url->getOption('query'); - $query_options['id'] = $this->batch['id']; - $query_options['op'] = 'finished'; - $error_url->setOption('query', $query_options); - - $this->batch['error_message'] = t('Please continue to the error page', array('@error_url' => $error_url->toString(TRUE)->getGeneratedUrl())); - - // Clear the way for the redirection to the batch processing page, by - // saving and unsetting the 'destination', if there is any. - $request = \Drupal::request(); - if ($request->query->has('destination')) { - $this->batch['destination'] = $request->query->get('destination'); - $request->query->remove('destination'); - } - - // Store safe strings. - // @todo Ensure we are not storing an excessively large string list in: - // https://www.drupal.org/node/2295823 - $this->batch['safe_strings'] = SafeMarkup::getAll(); - - // Store the batch. - $this->batchStorage->create($this->batch); - - // Set the batch number in the session to guarantee that it will stay alive. - $_SESSION['batches'][$this->batch['id']] = TRUE; - - // Redirect for processing. - $query_options = $error_url->getOption('query'); - $query_options['op'] = 'start'; - $query_options['id'] = $this->batch['id']; - $batch_url->setOption('query', $query_options); - if (($function = $this->batch['redirect_callback']) && function_exists($function)) { - $function($batch_url->toString(), ['query' => $query_options]); - } - else { - return new RedirectResponse($batch_url->setAbsolute()->toString(TRUE)->getGeneratedUrl()); - } - } - else { - // Non-progressive execution: bypass the whole progressbar workflow - // and execute the batch in one pass. - $this->processBatchSet(); - } - } - } - - /** * {@inheritdoc} */ public function processBatchSet() { @@ -340,14 +238,13 @@ public function processBatchSet() { * {@inheritdoc} */ public function currentSet() { - $this->batch = &batch_get(); return $this->batch['sets'][$this->batch['current_set']]; } /** * @return array */ - public function getBatch() { + public function &getBatch() { return $this->batch; } @@ -472,5 +369,23 @@ public function finished() { } } + /** + * @return mixed + */ + protected function getBatchId() { + return Database::getConnection()->nextId(); + } + + /** + * @param $error_url + * @return string + */ + protected function getErrorMessage(Url $error_url) { + return $this->t('Please continue to the error page', array( + '@error_url' => $error_url->toString(TRUE) + ->getGeneratedUrl() + )); + } + } diff --git a/core/tests/Drupal/Tests/Core/Batch/BatchProcessorTest.php b/core/tests/Drupal/Tests/Core/Batch/BatchProcessorTest.php index c4af0be..cd992de 100644 --- a/core/tests/Drupal/Tests/Core/Batch/BatchProcessorTest.php +++ b/core/tests/Drupal/Tests/Core/Batch/BatchProcessorTest.php @@ -11,9 +11,22 @@ use Drupal\Core\Batch\BatchQueueHandlerInterface; use Drupal\Core\Batch\BatchStorageInterface; use Drupal\Core\Datetime\DateFormatter; +use Drupal\Core\Extension\ModuleHandlerInterface; use Drupal\Core\Form\FormSubmitterInterface; +use Drupal\Core\GeneratedUrl; +use Drupal\Core\Routing\RouteMatchInterface; +use Drupal\Core\Routing\UrlGeneratorInterface; +use Drupal\Core\Theme\ActiveTheme; +use Drupal\Core\Theme\ThemeManagerInterface; +use Drupal\Core\Url; use Drupal\Tests\UnitTestCase; +use Prophecy\Argument; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\HttpFoundation\ParameterBag; +use Symfony\Component\HttpFoundation\RedirectResponse; +use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\RequestStack; +use Symfony\Component\Routing\Route; /** * @coversDefaultClass \Drupal\Core\Batch\BatchProcessor @@ -71,6 +84,38 @@ protected function setupBatchProcessor() { return new BatchProcessor('core', $this->batchQueue->reveal(), $this->dateFormatter->reveal(), $this->formSubmitter->reveal(), $this->batchStorage->reveal(), $this->requestStack); } + protected function setupThemeManager() { + $this->themeManager->getActiveTheme()->willReturn(new ActiveTheme(['name' => 'example'])); + } + + protected function setupRouteMatch() { + $this->routeMatch->getRouteObject()->willReturn(new Route('/test')); + $this->routeMatch->getRouteName()->willReturn('test'); + $this->routeMatch->getRawParameters()->willReturn(new ParameterBag()); + } + + protected function setupRequest() { + $request = Request::create('/test'); + $this->requestStack->push($request); + } + + protected function setupBasicBatch() { + $batch = [ + 'current_set' => 0, + 'progressive' => TRUE, + 'url' => Url::fromRoute('system.batch_page.html'), + 'source_url' => Url::fromRoute(''), + 'theme' => 'example_theme', + 'sets' => [], + ]; + return $batch; + } + + protected function setBatchWithOneSet() { + $batch = $this->setupBasicBatch(); + $batch['sets'][0] = + } + /** * @covers ::getBatch */ @@ -80,4 +125,24 @@ public function testGetEmptyBatch() { $this->assertEquals(['sets' => []], $batch->getBatch()); } +// @todo Throws an assertion ATM. +// public function testProcessBatchSetWithNoCurrentSet() { +// $batch = $this->setupBatchProcessor(); +// +// $batch->processBatchSet(); +// } + + public function testProcessBatchSetWithInitialSet() { + $this->batchStorage->load()->willReturn($this->setupBasicBatch()); + + $batch_processor = $this->setupBatchProcessor(); + + $batch = &$batch_processor->getBatch(); + $batch = $this->setupBasicBatch(); + + $result = $batch_processor->processBatchSet(); + print_r($result); + } + } +