You are here

final class ExcludedModulesEventSubscriber in Drupal 10

Same name and namespace in other branches
  1. 8 core/lib/Drupal/Core/EventSubscriber/ExcludedModulesEventSubscriber.php \Drupal\Core\EventSubscriber\ExcludedModulesEventSubscriber
  2. 9 core/lib/Drupal/Core/EventSubscriber/ExcludedModulesEventSubscriber.php \Drupal\Core\EventSubscriber\ExcludedModulesEventSubscriber

The event subscriber preventing excluded modules to be exported.

Hierarchy

Expanded class hierarchy of ExcludedModulesEventSubscriber

1 string reference to 'ExcludedModulesEventSubscriber'
core.services.yml in core/core.services.yml
core/core.services.yml
1 service uses ExcludedModulesEventSubscriber
config_exclude_modules_subscriber in core/core.services.yml
Drupal\Core\EventSubscriber\ExcludedModulesEventSubscriber

File

core/lib/Drupal/Core/EventSubscriber/ExcludedModulesEventSubscriber.php, line 14

Namespace

Drupal\Core\EventSubscriber
View source
final class ExcludedModulesEventSubscriber implements EventSubscriberInterface {

  /**
   * The key in settings and state for listing excluded modules.
   *
   * @var string
   */
  const EXCLUDED_MODULES_KEY = "config_exclude_modules";

  /**
   * @var \Drupal\Core\Config\StorageInterface
   */
  private $activeStorage;

  /**
   * @var \Drupal\Core\Site\Settings
   */
  private $settings;

  /**
   * @var \Drupal\Core\Config\ConfigManagerInterface
   */
  private $manager;

  /**
   * EnvironmentModulesEventSubscriber constructor.
   *
   * @param \Drupal\Core\Config\StorageInterface $active_storage
   *   The active config storage.
   * @param \Drupal\Core\Site\Settings $settings
   *   The Drupal settings.
   * @param \Drupal\Core\Config\ConfigManagerInterface $manager
   *   The config manager.
   */
  public function __construct(StorageInterface $active_storage, Settings $settings, ConfigManagerInterface $manager) {
    $this->activeStorage = $active_storage;
    $this->settings = $settings;
    $this->manager = $manager;
  }

  /**
   * {@inheritdoc}
   */
  public static function getSubscribedEvents() : array {

    // React early on export and late on import.
    return [
      'config.transform.import' => [
        'onConfigTransformImport',
        -500,
      ],
      'config.transform.export' => [
        'onConfigTransformExport',
        500,
      ],
    ];
  }

  /**
   * Transform the storage which is used to import the configuration.
   *
   * Make sure excluded modules are not uninstalled by adding them and their
   * config to the storage when importing configuration.
   *
   * @param \Drupal\Core\Config\StorageTransformEvent $event
   *   The transformation event.
   */
  public function onConfigTransformImport(StorageTransformEvent $event) {
    $storage = $event
      ->getStorage();
    if (!$storage
      ->exists('core.extension')) {

      // If the core.extension config is not present there is nothing to do.
      // This means that probably the storage is empty or non-functional.
      return;
    }
    foreach (array_merge([
      StorageInterface::DEFAULT_COLLECTION,
    ], $this->activeStorage
      ->getAllCollectionNames()) as $collectionName) {
      $collection = $storage
        ->createCollection($collectionName);
      $activeCollection = $this->activeStorage
        ->createCollection($collectionName);
      foreach ($this
        ->getDependentConfigNames() as $configName) {
        if (!$collection
          ->exists($configName) && $activeCollection
          ->exists($configName)) {

          // Make sure the config is not removed if it exists.
          $collection
            ->write($configName, $activeCollection
            ->read($configName));
        }
      }
    }
    $extension = $storage
      ->read('core.extension');
    $existing = $this->activeStorage
      ->read('core.extension');
    $modules = $extension['module'];
    foreach ($this
      ->getExcludedModules() as $module) {
      if (array_key_exists($module, $existing['module'])) {

        // Set the modules weight from the active store.
        $modules[$module] = $existing['module'][$module];
      }
    }

    // Sort the extensions.
    $extension['module'] = module_config_sort($modules);

    // Set the modified extension.
    $storage
      ->write('core.extension', $extension);
  }

  /**
   * Transform the storage which is used to export the configuration.
   *
   * Make sure excluded modules are not exported by removing all the config
   * which depends on them from the storage that is exported.
   *
   * @param \Drupal\Core\Config\StorageTransformEvent $event
   *   The transformation event.
   */
  public function onConfigTransformExport(StorageTransformEvent $event) {
    $storage = $event
      ->getStorage();
    if (!$storage
      ->exists('core.extension')) {

      // If the core.extension config is not present there is nothing to do.
      // This means some other process has rendered it non-functional already.
      return;
    }
    foreach (array_merge([
      StorageInterface::DEFAULT_COLLECTION,
    ], $storage
      ->getAllCollectionNames()) as $collectionName) {
      $collection = $storage
        ->createCollection($collectionName);
      foreach ($this
        ->getDependentConfigNames() as $configName) {
        $collection
          ->delete($configName);
      }
    }
    $extension = $storage
      ->read('core.extension');

    // Remove all the excluded modules from the extensions list.
    $extension['module'] = array_diff_key($extension['module'], array_flip($this
      ->getExcludedModules()));
    $storage
      ->write('core.extension', $extension);
  }

  /**
   * Get the modules set as excluded in the Drupal settings.
   *
   * @return string[]
   *   An array of module names.
   */
  private function getExcludedModules() {
    return $this->settings
      ->get(self::EXCLUDED_MODULES_KEY, []);
  }

  /**
   * Get all the configuration which depends on one of the excluded modules.
   *
   * @return string[]
   *   An array of configuration names.
   */
  private function getDependentConfigNames() {
    $modules = $this
      ->getExcludedModules();
    $dependencyManager = $this->manager
      ->getConfigDependencyManager();
    $config = [];

    // Find all the configuration depending on the excluded modules.
    foreach ($modules as $module) {
      foreach ($dependencyManager
        ->getDependentEntities('module', $module) as $dependent) {
        $config[] = $dependent
          ->getConfigDependencyName();
      }
      $config = array_merge($config, $this->activeStorage
        ->listAll($module . '.'));
    }

    // Find all configuration that depends on the configuration found above.
    foreach ($this->manager
      ->findConfigEntityDependencies('config', array_unique($config)) as $dependent) {
      $config[] = $dependent
        ->getConfigDependencyName();
    }
    return array_unique($config);
  }

}

Members