diff -u b/core/lib/Drupal/Core/Config/BatchConfigImporter.php b/core/lib/Drupal/Core/Config/BatchConfigImporter.php --- b/core/lib/Drupal/Core/Config/BatchConfigImporter.php +++ b/core/lib/Drupal/Core/Config/BatchConfigImporter.php @@ -14,12 +14,32 @@ */ class BatchConfigImporter extends ConfigImporter { - protected $totalToProcess = 0; + /** + * The total number of extensions to process. + * + * @var int + */ + protected $totalExtensionsToProcess = 0; + + /** + * The total number of configuration objects to process. + * + * @var int + */ + protected $totalConfigurationToProcess = 0; /** * Initializes the config importer in preparation for processing a batch. + * + * @return array + * An array of method names that to be called by the batch. If there are + * modules or themes to process then an extra step is added. + * + * @throws ConfigImporterException + * If the configuration is already importing. */ public function initialize() { + $batch_operations = array(); $this->createExtensionChangelist(); // Ensure that the changes have been validated. @@ -32,15 +52,21 @@ $modules = $this->getUnprocessedExtensions('module'); foreach (array('install', 'uninstall') as $op) { - $this->totalToProcess += count($modules[$op]); + $this->totalExtensionsToProcess += count($modules[$op]); } $themes = $this->getUnprocessedExtensions('theme'); foreach (array('enable', 'disable') as $op) { - $this->totalToProcess += count($themes[$op]); + $this->totalExtensionsToProcess += count($themes[$op]); } - foreach (array('create', 'delete', 'update') as $op) { - $this->totalToProcess += count($this->getUnprocessedConfiguration($op)); + + // We have extensions to process. + if ($this->totalExtensionsToProcess > 0) { + $batch_operations[] = 'processExtensionBatch'; } + + $batch_operations[] = 'processConfigurationBatch'; + $batch_operations[] = 'finishBatch'; + return $batch_operations; } /** @@ -49,71 +75,60 @@ * @param array $context. * The batch context. */ - public function processBatch(array &$context) { - $operation = $this->getNextOperation(); + public function processExtensionBatch(array &$context) { + $operation = $this->getNextExtensionOperation(); if (!empty($operation)) { - if (!empty($operation['type'])) { - $this->processExtension($operation['type'], $operation['op'], $operation['name']); - $this->recalculateChangelist = TRUE; - } - else { - if ($this->recalculateChangelist) { - $current_total = 0; - foreach (array('create', 'delete', 'update') as $op) { - $current_total += count($this->getUnprocessedConfiguration($op)); - } - $this->storageComparer->reset(); - // This will cause the changelist to be calculated. - $new_total = 0; - foreach (array('create', 'delete', 'update') as $op) { - $new_total += count($this->getUnprocessedConfiguration($op)); - } - $this->totalToProcess = $this->totalToProcess = $current_total + $new_total; - $operation = $this->getNextOperation(); - $this->recalculateChangelist = FALSE; - } - // Rebuilding the changelist change remove all changes. - if ($operation !== FALSE) { - $this->processConfiguration($operation['op'], $operation['name']); - } - } - $context['message'] = t('Synchronizing @name.', array('@name' => $operation['name'])); - $context['finished'] = $this->batchProgress(); + $this->processExtension($operation['type'], $operation['op'], $operation['name']); + $context['message'] = t('Synchronising extensions: @op @name.', array('@op' => $operation['op'], '@name' => $operation['name'])); + $processed_count = count($this->processedExtensions['module']['install']) + count($this->processedExtensions['module']['uninstall']); + $processed_count += count($this->processedExtensions['theme']['disable']) + count($this->processedExtensions['theme']['enable']); + $context['finished'] = $processed_count / $this->totalExtensionsToProcess; } else { $context['finished'] = 1; } - if ($context['finished'] >= 1) { - $this->eventDispatcher->dispatch(ConfigEvents::IMPORT, new ConfigImporterEvent($this)); - // The import is now complete. - $this->lock->release(static::LOCK_ID); - $this->reset(); + } + + public function processConfigurationBatch(array &$context) { + // The first time this is called we need to calculate the total to process. + // This involves recalculating the changelist which will ensure that if + // extensions have been processed any configuration affected will be taken + // into account. + if ($this->totalConfigurationToProcess == 0) { + $this->storageComparer->reset(); + foreach (array('delete', 'create', 'update') as $op) { + $this->totalConfigurationToProcess += count($this->getUnprocessedConfiguration($op)); + } + } + $operation = $this->getNextConfigurationOperation(); + if (!empty($operation)) { + $this->processConfiguration($operation['op'], $operation['name']); + $context['message'] = t('Synchronizing configuration: @op @name.', array('@op' => $operation['op'], '@name' => $operation['name'])); + $processed_count = count($this->processedConfiguration['create']) + count($this->processedConfiguration['delete']) + count($this->processedConfiguration['update']); + $context['finished'] = $processed_count / $this->totalConfigurationToProcess; + } + else { + $context['finished'] = 1; } } - /** - * Gets percentage of progress made. - * - * @return float - * The percentage of progress made expressed as a float between 0 and 1. - */ - protected function batchProgress() { - $processed_count = count($this->processedExtensions['module']['install']) + count($this->processedExtensions['module']['uninstall']); - $processed_count += count($this->processedExtensions['theme']['disable']) + count($this->processedExtensions['theme']['enable']); - $processed_count += count($this->processedConfiguration['create']) + count($this->processedConfiguration['delete']) + count($this->processedConfiguration['update']); - return $processed_count / $this->totalToProcess; + public function finishBatch(array &$context) { + $this->eventDispatcher->dispatch(ConfigEvents::IMPORT, new ConfigImporterEvent($this)); + // The import is now complete. + $this->lock->release(static::LOCK_ID); + $this->reset(); + $context['message'] = t('Finalising configuration synchronisation.'); + $context['finished'] = 1; } /** - * Gets the next operation to perform. - * - * We process extensions before we process configuration files. + * Gets the next extension operation to perform. * * @return array|bool - * An array containing the next operation and configuration name to perform - * it on. If there is nothing left to do returns FALSE; + * An array containing the next operation and extension name to perform it + * on. If there is nothing left to do returns FALSE; */ - protected function getNextOperation() { + protected function getNextExtensionOperation() { foreach (array('install', 'uninstall') as $op) { $modules = $this->getUnprocessedExtensions('module'); if (!empty($modules[$op])) { @@ -134,7 +149,20 @@ ); } } - foreach (array('create', 'delete', 'update') as $op) { + return FALSE; + } + + /** + * Gets the next configuration operation to perform. + * + * @return array|bool + * An array containing the next operation and configuration name to perform + * it on. If there is nothing left to do returns FALSE; + */ + protected function getNextConfigurationOperation() { + // The order configuration operations is processed is important. Deletes + // have to come first so that recreates can work. + foreach (array('delete', 'create', 'update') as $op) { $config_names = $this->getUnprocessedConfiguration($op); if (!empty($config_names)) { return array( diff -u b/core/lib/Drupal/Core/Config/ConfigImporter.php b/core/lib/Drupal/Core/Config/ConfigImporter.php --- b/core/lib/Drupal/Core/Config/ConfigImporter.php +++ b/core/lib/Drupal/Core/Config/ConfigImporter.php @@ -120,13 +120,6 @@ protected $themeHandler; /** - * Set to TRUE to recalculate changelist after installing extensions. - * - * @var bool - */ - protected $recalculateChangelist = FALSE; - - /** * Flag set to import system.theme during processing theme enable and disables. * * @var bool @@ -274,6 +267,17 @@ } /** + * Determines if the current import has processed extensions. + * + * @return bool + * TRUE if the ConfigImporter has processed extensions. + */ + protected function hasProcessedExtensions() { + $compare = array_diff($this->processedExtensions, getEmptyExtensionsProcessedList()); + return !empty($compare); + } + + /** * Sets an extension change as processed. * * @param string $type @@ -608,22 +612,23 @@ * Handle changes to installed modules and themes. */ protected function handleExtensions() { + $processed_extension = FALSE; foreach (array('install', 'uninstall') as $op) { $modules = $this->getUnprocessedExtensions('module'); foreach($modules[$op] as $module) { - $this->recalculateChangelist = TRUE; + $processed_extension = TRUE; $this->processExtension('module', $op, $module); } } foreach (array('enable', 'disable') as $op) { $themes = $this->getUnprocessedExtensions('theme'); foreach($themes[$op] as $theme) { - $this->recalculateChangelist = TRUE; + $processed_extension = TRUE; $this->processExtension('theme', $op, $theme); } } - if ($this->recalculateChangelist) { + if ($processed_extension) { // Recalculate differences as default config could have been imported. $this->storageComparer->reset(); $this->processed = $this->storageComparer->getEmptyChangelist(); diff -u b/core/modules/config/lib/Drupal/config/Form/ConfigSync.php b/core/modules/config/lib/Drupal/config/Form/ConfigSync.php --- b/core/modules/config/lib/Drupal/config/Form/ConfigSync.php +++ b/core/modules/config/lib/Drupal/config/Form/ConfigSync.php @@ -256,11 +256,9 @@ drupal_set_message($this->t('Another request may be synchronizing configuration already.')); } else{ - $config_importer->initialize(); + $operations = $config_importer->initialize(); $batch = array( - 'operations' => array( - array(array(get_class($this), 'processBatch'), array($config_importer)), - ), + 'operations' => array(), 'finished' => array(get_class($this), 'finishBatch'), 'title' => t('Synchronizing configuration'), 'init_message' => t('Starting configuration synchronization.'), @@ -268,6 +266,9 @@ 'error_message' => t('Configuration synchronization has encountered an error.'), 'file' => drupal_get_path('module', 'config') . '/config.admin.inc', ); + foreach ($operations as $operation) { + $batch['operations'][] = array(array(get_class($this), 'processBatch'), array($config_importer, $operation)); + } batch_set($batch); } @@ -281,13 +282,13 @@ * @param $context * The batch context. */ - public static function processBatch(BatchConfigImporter $config_importer, &$context) { + public static function processBatch(BatchConfigImporter $config_importer, $operation, &$context) { if (!isset($context['sandbox']['config_importer'])) { $context['sandbox']['config_importer'] = $config_importer; } $config_importer = $context['sandbox']['config_importer']; - $config_importer->processBatch($context); + $config_importer->$operation($context); } /** only in patch2: unchanged: --- a/core/modules/block/block.module +++ b/core/modules/block/block.module @@ -8,6 +8,7 @@ use Drupal\block\BlockInterface; use Drupal\Component\Plugin\Exception\PluginException; use Drupal\language\Entity\Language; +use Drupal\system\Entity\Menu; use Symfony\Cmf\Component\Routing\RouteObjectInterface; /** @@ -422,10 +423,12 @@ function block_user_role_delete($role) { /** * Implements hook_menu_delete(). */ -function block_menu_delete($menu) { - foreach (entity_load_multiple('block') as $block) { - if ($block->get('plugin') == 'system_menu_block:' . $menu->id()) { - $block->delete(); +function block_menu_delete(Menu $menu) { + if (!$menu->isSyncing()) { + foreach (entity_load_multiple('block') as $block) { + if ($block->get('plugin') == 'system_menu_block:' . $menu->id()) { + $block->delete(); + } } } }