ConfigInstaller.php 13.5 KB
Newer Older
1 2 3 4 5 6 7 8 9 10
<?php

/**
 * @file
 * Contains Drupal\Core\Config\ConfigInstaller.
 */

namespace Drupal\Core\Config;

use Drupal\Component\Utility\Unicode;
11
use Drupal\Core\Config\Entity\ConfigDependencyManager;
12
use Drupal\Core\Site\Settings;
13 14 15 16 17 18 19
use Symfony\Component\EventDispatcher\EventDispatcherInterface;

class ConfigInstaller implements ConfigInstallerInterface {

  /**
   * The configuration factory.
   *
20
   * @var \Drupal\Core\Config\ConfigFactoryInterface
21 22 23 24
   */
  protected $configFactory;

  /**
25
   * The active configuration storages, keyed by collection.
26
   *
27
   * @var \Drupal\Core\Config\StorageInterface[]
28
   */
29
  protected $activeStorages;
30 31 32 33 34 35 36 37 38

  /**
   * The typed configuration manager.
   *
   * @var \Drupal\Core\Config\TypedConfigManagerInterface
   */
  protected $typedConfig;

  /**
39
   * The configuration manager.
40
   *
41
   * @var \Drupal\Core\Config\ConfigManagerInterface
42
   */
43
  protected $configManager;
44 45 46 47 48 49 50 51

  /**
   * The event dispatcher.
   *
   * @var \Symfony\Component\EventDispatcher\EventDispatcherInterface
   */
  protected $eventDispatcher;

52 53 54 55 56 57 58 59 60 61 62 63 64 65
  /**
   * The configuration storage that provides the default configuration.
   *
   * @var \Drupal\Core\Config\StorageInterface
   */
  protected $sourceStorage;

  /**
   * Is configuration being created as part of a configuration sync.
   *
   * @var bool
   */
  protected $isSyncing = FALSE;

66 67 68
  /**
   * Constructs the configuration installer.
   *
69
   * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
70 71 72 73 74
   *   The configuration factory.
   * @param \Drupal\Core\Config\StorageInterface $active_storage
   *   The active configuration storage.
   * @param \Drupal\Core\Config\TypedConfigManagerInterface $typed_config
   *   The typed configuration manager.
75 76
   * @param \Drupal\Core\Config\ConfigManagerInterface $config_manager
   *   The configuration manager.
77 78 79
   * @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $event_dispatcher
   *   The event dispatcher.
   */
80
  public function __construct(ConfigFactoryInterface $config_factory, StorageInterface $active_storage, TypedConfigManagerInterface $typed_config, ConfigManagerInterface $config_manager, EventDispatcherInterface $event_dispatcher) {
81
    $this->configFactory = $config_factory;
82
    $this->activeStorages[$active_storage->getCollectionName()] = $active_storage;
83
    $this->typedConfig = $typed_config;
84
    $this->configManager = $config_manager;
85 86 87 88 89 90 91
    $this->eventDispatcher = $event_dispatcher;
  }

  /**
   * {@inheritdoc}
   */
  public function installDefaultConfig($type, $name) {
92
    $extension_path = drupal_get_path($type, $name);
93 94 95
    // Refresh the schema cache if the extension provides configuration schema
    // or is a theme.
    if (is_dir($extension_path . '/' . InstallStorage::CONFIG_SCHEMA_DIRECTORY) || $type == 'theme') {
96 97 98
      $this->typedConfig->clearCachedDefinitions();
    }

99 100
    // Gather information about all the supported collections.
    $collection_info = $this->configManager->getConfigCollectionInfo();
101 102 103 104 105 106 107

    $old_state = $this->configFactory->getOverrideState();
    $this->configFactory->setOverrideState(FALSE);

    // Read enabled extensions directly from configuration to avoid circular
    // dependencies with ModuleHandler and ThemeHandler.
    $extension_config = $this->configFactory->get('core.extension');
108 109 110 111 112 113 114 115
    $modules = (array) $extension_config->get('module');
    // Unless we are installing the profile, remove it from the list.
    if ($install_profile = Settings::get('install_profile')) {
      if ($name !== $install_profile) {
        unset($modules[$install_profile]);
      }
    }
    $enabled_extensions = array_keys($modules);
116
    $enabled_extensions += array_keys((array) $extension_config->get('theme'));
117

118 119
    // Core can provide configuration.
    $enabled_extensions[] = 'core';
120

121
    foreach ($collection_info->getCollectionNames(TRUE) as $collection) {
122
      $config_to_install = $this->listDefaultConfigToInstall($type, $name, $collection, $enabled_extensions);
123 124 125 126 127 128 129 130 131 132
      if (!empty($config_to_install)) {
        $this->createConfiguration($collection, $config_to_install);
      }
    }
    $this->configFactory->setOverrideState($old_state);
    // Reset all the static caches and list caches.
    $this->configFactory->reset();
  }

  /**
133 134 135 136 137
   * Lists default configuration for an extension that is available to install.
   *
   * This looks in the extension's config/install directory and all of the
   * currently enabled extensions config/install directories for configuration
   * that begins with the extension's name.
138 139 140 141 142
   *
   * @param string $type
   *   The extension type; e.g., 'module' or 'theme'.
   * @param string $name
   *   The name of the module or theme to install default configuration for.
143 144
   * @param string $collection
   *  The configuration collection to install.
145 146 147 148 149 150
   * @param array $enabled_extensions
   *   A list of all the currently enabled modules and themes.
   *
   * @return array
   *   The list of configuration objects to create.
   */
151
  protected function listDefaultConfigToInstall($type, $name, $collection, array $enabled_extensions) {
152 153 154 155
    // Get all default configuration owned by this extension.
    $source_storage = $this->getSourceStorage($collection);
    $config_to_install = $source_storage->listAll($name . '.');

156 157 158
    // If not installing the core base system default configuration, work out if
    // this extension provides default configuration for any other enabled
    // extensions.
159
    $extension_path = drupal_get_path($type, $name);
160
    if ($type !== 'core' && is_dir($extension_path . '/' . InstallStorage::CONFIG_INSTALL_DIRECTORY)) {
161
      $default_storage = new FileStorage($extension_path . '/' . InstallStorage::CONFIG_INSTALL_DIRECTORY, $collection);
162 163 164 165 166 167
      $extension_provided_config = array_filter($default_storage->listAll(), function ($config_name) use ($config_to_install, $enabled_extensions) {
        // Ensure that we have not already discovered the config to install.
        if (in_array($config_name, $config_to_install)) {
          return FALSE;
        }
        // Ensure the configuration is provided by an enabled module.
168 169 170 171
        $provider = Unicode::substr($config_name, 0, strpos($config_name, '.'));
        return in_array($provider, $enabled_extensions);
      });

172
      $config_to_install = array_merge($config_to_install, $extension_provided_config);
173 174
    }

175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190
    return $config_to_install;
  }

  /**
   * Creates configuration in a collection based on the provided list.
   *
   * @param string $collection
   *   The configuration collection.
   * @param array $config_to_install
   *   A list of configuration object names to create.
   */
  protected function createConfiguration($collection, array $config_to_install) {
    // Order the configuration to install in the order of dependencies.
    $data = $this->getSourceStorage($collection)->readMultiple($config_to_install);
    $config_entity_support = $this->configManager->supportsConfigurationEntities($collection);
    if ($config_entity_support) {
191
      $dependency_manager = new ConfigDependencyManager();
192
      $config_to_install = $dependency_manager
193 194
        ->setData($data)
        ->sortAll();
195
    }
196

197
    // Remove configuration that already exists in the active storage.
198
    $config_to_install = array_diff($config_to_install, $this->getActiveStorages($collection)->listAll());
199

200
    foreach ($config_to_install as $name) {
201 202 203 204 205 206 207
      // Allow config factory overriders to use a custom configuration object if
      // they are responsible for the collection.
      $overrider = $this->configManager->getConfigCollectionInfo()->getOverrideService($collection);
      if ($overrider) {
        $new_config = $overrider->createConfigObject($name, $collection);
      }
      else {
208
        $new_config = new Config($name, $this->getActiveStorages($collection), $this->eventDispatcher, $this->typedConfig);
209
      }
210 211 212 213
      if ($data[$name] !== FALSE) {
        $new_config->setData($data[$name]);
      }
      if ($config_entity_support && $entity_type = $this->configManager->getEntityTypeIdByName($name)) {
214

215 216 217 218 219 220 221 222
        // If we are syncing do not create configuration entities. Pluggable
        // configuration entities can have dependencies on modules that are
        // not yet enabled. This approach means that any code that expects
        // default configuration entities to exist will be unstable after the
        // module has been enabled and before the config entity has been
        // imported.
        if ($this->isSyncing) {
          continue;
223
        }
224 225 226 227 228
        $entity_storage = $this->configManager
          ->getEntityManager()
          ->getStorage($entity_type);
        // It is possible that secondary writes can occur during configuration
        // creation. Updates of such configuration are allowed.
229
        if ($this->getActiveStorages($collection)->exists($name)) {
230 231
          $id = $entity_storage->getIDFromConfigName($name, $entity_storage->getEntityType()->getConfigPrefix());
          $entity = $entity_storage->load($id);
232
          $entity = $entity_storage->updateFromStorageRecord($entity, $new_config->get());
233 234
        }
        else {
235
          $entity = $entity_storage->createFromStorageRecord($new_config->get());
236
        }
237
        $entity->save();
238
      }
239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260
      else {
        $new_config->save();
      }
    }
  }

  /**
   * {@inheritdoc}
   */
  public function installCollectionDefaultConfig($collection) {
    $config_to_install = $this->getSourceStorage($collection)->listAll();
    $extension_config = $this->configFactory->get('core.extension');
    $enabled_extensions = array_keys((array) $extension_config->get('module'));
    $enabled_extensions += array_keys((array) $extension_config->get('theme'));
    $config_to_install = array_filter($config_to_install, function ($config_name) use ($enabled_extensions) {
      $provider = Unicode::substr($config_name, 0, strpos($config_name, '.'));
      return in_array($provider, $enabled_extensions);
    });
    if (!empty($config_to_install)) {
      $old_state = $this->configFactory->getOverrideState();
      $this->configFactory->setOverrideState(FALSE);
      $this->createConfiguration($collection, $config_to_install);
261
      $this->configFactory->setOverrideState($old_state);
262 263
      // Reset all the static caches and list caches.
      $this->configFactory->reset();
264 265 266
    }
  }

267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285
  /**
   * {@inheritdoc}
   */
  public function setSourceStorage(StorageInterface $storage) {
    $this->sourceStorage = $storage;
    return $this;
  }

  /**
   * {@inheritdoc}
   */
  public function resetSourceStorage() {
    $this->sourceStorage = null;
    return $this;
  }

  /**
   * Gets the configuration storage that provides the default configuration.
   *
286 287 288 289
   * @param string $collection
   *   (optional) The configuration collection. Defaults to the default
   *   collection.
   *
290 291 292
   * @return \Drupal\Core\Config\StorageInterface
   *   The configuration storage that provides the default configuration.
   */
293
  public function getSourceStorage($collection = StorageInterface::DEFAULT_COLLECTION) {
294 295
    if (!isset($this->sourceStorage)) {
      // Default to using the ExtensionInstallStorage which searches extension's
296 297
      // config directories for default configuration. Only include the profile
      // configuration during Drupal installation.
298
      $this->sourceStorage = new ExtensionInstallStorage($this->getActiveStorages(StorageInterface::DEFAULT_COLLECTION), InstallStorage::CONFIG_INSTALL_DIRECTORY, $collection, drupal_installation_attempted());
299 300 301
    }
    if ($this->sourceStorage->getCollectionName() != $collection) {
      $this->sourceStorage = $this->sourceStorage->createCollection($collection);
302 303 304 305
    }
    return $this->sourceStorage;
  }

306 307 308 309 310 311 312 313 314 315
  /**
   * Gets the configuration storage that provides the active configuration.
   *
   * @param string $collection
   *   (optional) The configuration collection. Defaults to the default
   *   collection.
   *
   * @return \Drupal\Core\Config\StorageInterface
   *   The configuration storage that provides the default configuration.
   */
316 317 318
  protected function getActiveStorages($collection = StorageInterface::DEFAULT_COLLECTION) {
    if (!isset($this->activeStorages[$collection])) {
      $this->activeStorages[$collection] = reset($this->activeStorages)->createCollection($collection);
319
    }
320
    return $this->activeStorages[$collection];
321 322
  }

323 324 325 326 327 328 329 330 331 332 333 334 335 336
  /**
   * {@inheritdoc}
   */
  public function setSyncing($status) {
    $this->isSyncing = $status;
    return $this;
  }

  /**
   * {@inheritdoc}
   */
  public function isSyncing() {
    return $this->isSyncing;
  }
337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363

  /**
   * {@inheritdoc}
   */
  public function findPreExistingConfiguration($type, $name) {
    $existing_configuration = array();
    // Gather information about all the supported collections.
    $collection_info = $this->configManager->getConfigCollectionInfo();

    // Read enabled extensions directly from configuration to avoid circular
    // dependencies on ModuleHandler and ThemeHandler.
    $extension_config = $this->configFactory->get('core.extension');
    $enabled_extensions = array_keys((array) $extension_config->get('module'));
    $enabled_extensions += array_keys((array) $extension_config->get('theme'));
    // Add the extension that will be enabled to the list of enabled extensions.
    $enabled_extensions[] = $name;
    foreach ($collection_info->getCollectionNames(TRUE) as $collection) {
      $config_to_install = $this->listDefaultConfigToInstall($type, $name, $collection, $enabled_extensions);
      $active_storage = $this->getActiveStorages($collection);
      foreach ($config_to_install as $config_name) {
        if ($active_storage->exists($config_name)) {
          $existing_configuration[$collection][] = $config_name;
        }
      }
    }
    return $existing_configuration;
  }
364
}