Issue #2090115 by alexpott, xjm: Don't install a module when its default configuration has unmet dependencies
							parent
							
								
									d6ea0767a3
								
							
						
					
					
						commit
						40cc6312d9
					
				| 
						 | 
				
			
			@ -741,6 +741,8 @@ function install_tasks($install_state) {
 | 
			
		|||
    ),
 | 
			
		||||
    'install_profile_themes' => array(
 | 
			
		||||
    ),
 | 
			
		||||
    'install_install_profile' => array(
 | 
			
		||||
    ),
 | 
			
		||||
    'install_import_translations' => array(
 | 
			
		||||
      'display_name' => t('Set up translations'),
 | 
			
		||||
      'display' => $needs_translations,
 | 
			
		||||
| 
						 | 
				
			
			@ -1022,10 +1024,6 @@ function install_base_system(&$install_state) {
 | 
			
		|||
  // State can be set to the database now that system.module is installed.
 | 
			
		||||
  $modules = $install_state['profile_info']['dependencies'];
 | 
			
		||||
 | 
			
		||||
  // The installation profile is also a module, which needs to be installed
 | 
			
		||||
  // after all the dependencies have been installed.
 | 
			
		||||
  $modules[] = drupal_get_profile();
 | 
			
		||||
 | 
			
		||||
  \Drupal::state()->set('install_profile_modules', array_diff($modules, array('system')));
 | 
			
		||||
  $install_state['base_system_verified'] = TRUE;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1571,10 +1569,7 @@ function install_profile_modules(&$install_state) {
 | 
			
		|||
  // the modules.
 | 
			
		||||
  $required = array();
 | 
			
		||||
  $non_required = array();
 | 
			
		||||
  // Although the profile module is marked as required, it needs to go after
 | 
			
		||||
  // every dependency, including non-required ones. So clear its required
 | 
			
		||||
  // flag for now to allow it to install late.
 | 
			
		||||
  $files[$install_state['parameters']['profile']]->info['required'] = FALSE;
 | 
			
		||||
 | 
			
		||||
  // Add modules that other modules depend on.
 | 
			
		||||
  foreach ($modules as $module) {
 | 
			
		||||
    if ($files[$module]->requires) {
 | 
			
		||||
| 
						 | 
				
			
			@ -1633,6 +1628,24 @@ function install_profile_themes(&$install_state) {
 | 
			
		|||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Installs the install profile.
 | 
			
		||||
 *
 | 
			
		||||
 * @param $install_state
 | 
			
		||||
 *   An array of information about the current installation state.
 | 
			
		||||
 */
 | 
			
		||||
function install_install_profile(&$install_state) {
 | 
			
		||||
  \Drupal::service('module_installer')->install(array(drupal_get_profile()), FALSE);
 | 
			
		||||
  // Install all available optional config. During installation the module order
 | 
			
		||||
  // is determined by dependencies. If there are no dependencies between modules
 | 
			
		||||
  // then the order in which they are installed is dependent on random factors
 | 
			
		||||
  // like PHP version. Optional configuration therefore might or might not be
 | 
			
		||||
  // created depending on this order. Ensuring that we have installed all of the
 | 
			
		||||
  // optional configuration whose dependencies can be met at this point removes
 | 
			
		||||
  // any disparities that this creates.
 | 
			
		||||
  \Drupal::service('config.installer')->installOptionalConfig();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Prepares the system for import and downloads additional translations.
 | 
			
		||||
 *
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -786,8 +786,7 @@ class ConfigImporter {
 | 
			
		|||
 | 
			
		||||
    $this->setProcessedExtension($type, $op, $name);
 | 
			
		||||
    \Drupal::service('config.installer')
 | 
			
		||||
      ->setSyncing(FALSE)
 | 
			
		||||
      ->resetSourceStorage();
 | 
			
		||||
      ->setSyncing(FALSE);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -9,6 +9,7 @@ namespace Drupal\Core\Config;
 | 
			
		|||
 | 
			
		||||
use Drupal\Component\Utility\Unicode;
 | 
			
		||||
use Drupal\Core\Config\Entity\ConfigDependencyManager;
 | 
			
		||||
use Drupal\Core\Entity\EntityTypeInterface;
 | 
			
		||||
use Drupal\Core\Site\Settings;
 | 
			
		||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -89,36 +90,66 @@ class ConfigInstaller implements ConfigInstallerInterface {
 | 
			
		|||
   * {@inheritdoc}
 | 
			
		||||
   */
 | 
			
		||||
  public function installDefaultConfig($type, $name) {
 | 
			
		||||
    $extension_path = drupal_get_path($type, $name);
 | 
			
		||||
    $extension_path = $this->drupalGetPath($type, $name);
 | 
			
		||||
    // 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') {
 | 
			
		||||
      $this->typedConfig->clearCachedDefinitions();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Gather information about all the supported collections.
 | 
			
		||||
    $collection_info = $this->configManager->getConfigCollectionInfo();
 | 
			
		||||
    $default_install_path = $this->getDefaultConfigDirectory($type, $name);
 | 
			
		||||
    if (is_dir($default_install_path)) {
 | 
			
		||||
      if (!$this->isSyncing()) {
 | 
			
		||||
        $storage = new FileStorage($default_install_path, StorageInterface::DEFAULT_COLLECTION);
 | 
			
		||||
        $prefix = '';
 | 
			
		||||
      }
 | 
			
		||||
      else {
 | 
			
		||||
        // The configuration importer sets the source storage on the config
 | 
			
		||||
        // installer. The configuration importer handles all of the
 | 
			
		||||
        // configuration entity imports. We only need to ensure that simple
 | 
			
		||||
        // configuration is created when the extension is installed.
 | 
			
		||||
        $storage = $this->getSourceStorage();
 | 
			
		||||
        $prefix = $name . '.';
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
    // Read enabled extensions directly from configuration to avoid circular
 | 
			
		||||
    // dependencies with ModuleHandler and ThemeHandler.
 | 
			
		||||
    $extension_config = $this->configFactory->get('core.extension');
 | 
			
		||||
    $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]);
 | 
			
		||||
      // Gets a profile storage to search for overrides if necessary.
 | 
			
		||||
      $profile_storage = $this->getProfileStorage($name);
 | 
			
		||||
 | 
			
		||||
      // Gather information about all the supported collections.
 | 
			
		||||
      $collection_info = $this->configManager->getConfigCollectionInfo();
 | 
			
		||||
      foreach ($collection_info->getCollectionNames() as $collection) {
 | 
			
		||||
        $config_to_create = $this->getConfigToCreate($storage, $collection, $prefix, $profile_storage);
 | 
			
		||||
        // If we're installing a profile ensure configuration that is overriding
 | 
			
		||||
        // is excluded.
 | 
			
		||||
        if ($name == $this->drupalGetProfile()) {
 | 
			
		||||
          $existing_configuration = $this->getActiveStorages($collection)->listAll();
 | 
			
		||||
          $config_to_create = array_diff_key($config_to_create, array_flip($existing_configuration));
 | 
			
		||||
        }
 | 
			
		||||
        if (!empty($config_to_create)) {
 | 
			
		||||
          $this->createConfiguration($collection, $config_to_create);
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    $enabled_extensions = array_keys($modules);
 | 
			
		||||
    $enabled_extensions += array_keys((array) $extension_config->get('theme'));
 | 
			
		||||
 | 
			
		||||
    // Core can provide configuration.
 | 
			
		||||
    $enabled_extensions[] = 'core';
 | 
			
		||||
 | 
			
		||||
    foreach ($collection_info->getCollectionNames(TRUE) as $collection) {
 | 
			
		||||
      $config_to_install = $this->listDefaultConfigToInstall($type, $name, $collection, $enabled_extensions);
 | 
			
		||||
      if (!empty($config_to_install)) {
 | 
			
		||||
        $this->createConfiguration($collection, $config_to_install);
 | 
			
		||||
    // During a drupal installation optional configuration is installed at the
 | 
			
		||||
    // end of the installation process.
 | 
			
		||||
    // @see install_install_profile()
 | 
			
		||||
    if (!$this->isSyncing() && !$this->drupalInstallationAttempted()) {
 | 
			
		||||
      $optional_install_path = $extension_path . '/' . InstallStorage::CONFIG_OPTIONAL_DIRECTORY;
 | 
			
		||||
      if (is_dir($optional_install_path)) {
 | 
			
		||||
        // Install any optional config the module provides.
 | 
			
		||||
        $storage = new FileStorage($optional_install_path, StorageInterface::DEFAULT_COLLECTION);
 | 
			
		||||
        $this->installOptionalConfig($storage, '');
 | 
			
		||||
      }
 | 
			
		||||
      // Install any optional configuration entities whose type this extension
 | 
			
		||||
      // provides. This searches all the installed modules config/optional
 | 
			
		||||
      // directories.
 | 
			
		||||
      $provides_config_entity_type = array_reduce($this->configManager->getEntityManager()->getDefinitions(), function ($return, EntityTypeInterface $entity_type) use ($name) {
 | 
			
		||||
        return $return ?: $entity_type->getProvider() && $entity_type->getConfigPrefix();
 | 
			
		||||
      }, FALSE);
 | 
			
		||||
      if ($provides_config_entity_type) {
 | 
			
		||||
        $storage = new ExtensionInstallStorage($this->getActiveStorages(StorageInterface::DEFAULT_COLLECTION), InstallStorage::CONFIG_OPTIONAL_DIRECTORY, StorageInterface::DEFAULT_COLLECTION, FALSE);
 | 
			
		||||
        $this->installOptionalConfig($storage, $name . '.');
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -127,49 +158,76 @@ class ConfigInstaller implements ConfigInstallerInterface {
 | 
			
		|||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * 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.
 | 
			
		||||
   *
 | 
			
		||||
   * @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.
 | 
			
		||||
   * @param string $collection
 | 
			
		||||
   *  The configuration collection to install.
 | 
			
		||||
   * @param array $enabled_extensions
 | 
			
		||||
   *   A list of all the currently enabled modules and themes.
 | 
			
		||||
   *
 | 
			
		||||
   * @return array
 | 
			
		||||
   *   The list of configuration objects to create.
 | 
			
		||||
   * {@inheritdoc}
 | 
			
		||||
   */
 | 
			
		||||
  protected function listDefaultConfigToInstall($type, $name, $collection, array $enabled_extensions) {
 | 
			
		||||
    // Get all default configuration owned by this extension.
 | 
			
		||||
    $source_storage = $this->getSourceStorage($collection);
 | 
			
		||||
    $config_to_install = $source_storage->listAll($name . '.');
 | 
			
		||||
 | 
			
		||||
    // If not installing the core base system default configuration, work out if
 | 
			
		||||
    // this extension provides default configuration for any other enabled
 | 
			
		||||
    // extensions.
 | 
			
		||||
    $extension_path = drupal_get_path($type, $name);
 | 
			
		||||
    if ($type !== 'core' && is_dir($extension_path . '/' . InstallStorage::CONFIG_INSTALL_DIRECTORY)) {
 | 
			
		||||
      $default_storage = new FileStorage($extension_path . '/' . InstallStorage::CONFIG_INSTALL_DIRECTORY, $collection);
 | 
			
		||||
      $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.
 | 
			
		||||
        $provider = Unicode::substr($config_name, 0, strpos($config_name, '.'));
 | 
			
		||||
        return in_array($provider, $enabled_extensions);
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
      $config_to_install = array_merge($config_to_install, $extension_provided_config);
 | 
			
		||||
  public function installOptionalConfig(StorageInterface $storage = NULL, $prefix = '') {
 | 
			
		||||
    if (!$storage) {
 | 
			
		||||
      // Search the install profile's optional configuration too.
 | 
			
		||||
      $storage = new ExtensionInstallStorage($this->getActiveStorages(StorageInterface::DEFAULT_COLLECTION), InstallStorage::CONFIG_OPTIONAL_DIRECTORY, StorageInterface::DEFAULT_COLLECTION, TRUE);
 | 
			
		||||
      // The extension install storage ensures that overrides are used.
 | 
			
		||||
      $profile_storage = NULL;
 | 
			
		||||
    }
 | 
			
		||||
    else {
 | 
			
		||||
      // Creates a profile storage to search for overrides.
 | 
			
		||||
      $profile_install_path = $this->drupalGetPath('module', $this->drupalGetProfile()) . '/' . InstallStorage::CONFIG_OPTIONAL_DIRECTORY;
 | 
			
		||||
      $profile_storage = new FileStorage($profile_install_path, StorageInterface::DEFAULT_COLLECTION);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return $config_to_install;
 | 
			
		||||
    $collection_info = $this->configManager->getConfigCollectionInfo();
 | 
			
		||||
    $enabled_extensions = $this->getEnabledExtensions();
 | 
			
		||||
 | 
			
		||||
    foreach ($collection_info->getCollectionNames() as $collection) {
 | 
			
		||||
      if (!$this->configManager->supportsConfigurationEntities($collection)) {
 | 
			
		||||
        continue;
 | 
			
		||||
      }
 | 
			
		||||
      $existing_config = $this->getActiveStorages($collection)->listAll($prefix);
 | 
			
		||||
      $config_to_create = $this->getConfigToCreate($storage, $collection, $prefix, $profile_storage);
 | 
			
		||||
      $all_config = array_merge($existing_config, array_keys($config_to_create));
 | 
			
		||||
      foreach ($config_to_create as $config_name => $data) {
 | 
			
		||||
        // Exclude configuration that:
 | 
			
		||||
        // - already exists
 | 
			
		||||
        // - is a not configuration entity
 | 
			
		||||
        // - or its dependencies cannot be met.
 | 
			
		||||
        if (in_array($config_name, $existing_config) ||
 | 
			
		||||
          !$this->configManager->getEntityTypeIdByName($config_name) ||
 | 
			
		||||
          !$this->validateDependencies($config_name, $data, $enabled_extensions, $all_config)) {
 | 
			
		||||
          unset($config_to_create[$config_name]);
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
      if (!empty($config_to_create)) {
 | 
			
		||||
        $this->createConfiguration($collection, $config_to_create, TRUE);
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Gets configuration data from the provided storage to create.
 | 
			
		||||
   *
 | 
			
		||||
   * @param StorageInterface $storage
 | 
			
		||||
   *   The configuration storage to read configuration from.
 | 
			
		||||
   * @param string $collection
 | 
			
		||||
   *  The configuration collection to use.
 | 
			
		||||
   * @param string $prefix
 | 
			
		||||
   *   (optional) Limit to configuration starting with the provided string.
 | 
			
		||||
   *
 | 
			
		||||
   * @return array
 | 
			
		||||
   *   An array of configuration data read from the source storage keyed by the
 | 
			
		||||
   *   configuration object name.
 | 
			
		||||
   */
 | 
			
		||||
  protected function getConfigToCreate(StorageInterface $storage, $collection, $prefix = '', StorageInterface $profile_storage = NULL) {
 | 
			
		||||
    if ($storage->getCollectionName() != $collection) {
 | 
			
		||||
      $storage = $storage->createCollection($collection);
 | 
			
		||||
    }
 | 
			
		||||
    $data = $storage->readMultiple($storage->listAll($prefix));
 | 
			
		||||
 | 
			
		||||
    // Check to see if the corresponding override storage has any overrides.
 | 
			
		||||
    if ($profile_storage) {
 | 
			
		||||
      if ($profile_storage->getCollectionName() != $collection) {
 | 
			
		||||
        $profile_storage = $profile_storage->createCollection($collection);
 | 
			
		||||
      }
 | 
			
		||||
      $data = $profile_storage->readMultiple(array_keys($data)) + $data;
 | 
			
		||||
    }
 | 
			
		||||
    return $data;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
| 
						 | 
				
			
			@ -177,24 +235,23 @@ class ConfigInstaller implements ConfigInstallerInterface {
 | 
			
		|||
   *
 | 
			
		||||
   * @param string $collection
 | 
			
		||||
   *   The configuration collection.
 | 
			
		||||
   * @param array $config_to_install
 | 
			
		||||
   *   A list of configuration object names to create.
 | 
			
		||||
   * @param array $config_to_create
 | 
			
		||||
   *   An array of configuration data to create, keyed by name.
 | 
			
		||||
   */
 | 
			
		||||
  protected function createConfiguration($collection, array $config_to_install) {
 | 
			
		||||
  protected function createConfiguration($collection, array $config_to_create) {
 | 
			
		||||
    // 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) {
 | 
			
		||||
      $dependency_manager = new ConfigDependencyManager();
 | 
			
		||||
      $config_to_install = $dependency_manager
 | 
			
		||||
        ->setData($data)
 | 
			
		||||
      $config_names = $dependency_manager
 | 
			
		||||
        ->setData($config_to_create)
 | 
			
		||||
        ->sortAll();
 | 
			
		||||
    }
 | 
			
		||||
    else {
 | 
			
		||||
      $config_names = array_keys($config_to_create);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Remove configuration that already exists in the active storage.
 | 
			
		||||
    $config_to_install = array_diff($config_to_install, $this->getActiveStorages($collection)->listAll());
 | 
			
		||||
 | 
			
		||||
    foreach ($config_to_install as $name) {
 | 
			
		||||
    foreach ($config_names as $name) {
 | 
			
		||||
      // Allow config factory overriders to use a custom configuration object if
 | 
			
		||||
      // they are responsible for the collection.
 | 
			
		||||
      $overrider = $this->configManager->getConfigCollectionInfo()->getOverrideService($collection);
 | 
			
		||||
| 
						 | 
				
			
			@ -204,18 +261,17 @@ class ConfigInstaller implements ConfigInstallerInterface {
 | 
			
		|||
      else {
 | 
			
		||||
        $new_config = new Config($name, $this->getActiveStorages($collection), $this->eventDispatcher, $this->typedConfig);
 | 
			
		||||
      }
 | 
			
		||||
      if ($data[$name] !== FALSE) {
 | 
			
		||||
        $new_config->setData($data[$name]);
 | 
			
		||||
      if ($config_to_create[$name] !== FALSE) {
 | 
			
		||||
        $new_config->setData($config_to_create[$name]);
 | 
			
		||||
      }
 | 
			
		||||
      if ($config_entity_support && $entity_type = $this->configManager->getEntityTypeIdByName($name)) {
 | 
			
		||||
 | 
			
		||||
        // 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) {
 | 
			
		||||
        if ($this->isSyncing()) {
 | 
			
		||||
          continue;
 | 
			
		||||
        }
 | 
			
		||||
        /** @var \Drupal\Core\Config\Entity\ConfigEntityStorageInterface $entity_storage */
 | 
			
		||||
| 
						 | 
				
			
			@ -246,16 +302,15 @@ class ConfigInstaller implements ConfigInstallerInterface {
 | 
			
		|||
   * {@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) {
 | 
			
		||||
    $storage = new ExtensionInstallStorage($this->getActiveStorages(StorageInterface::DEFAULT_COLLECTION), InstallStorage::CONFIG_INSTALL_DIRECTORY, $collection, $this->drupalInstallationAttempted());
 | 
			
		||||
    // Only install configuration for enabled extensions.
 | 
			
		||||
    $enabled_extensions = $this->getEnabledExtensions();
 | 
			
		||||
    $config_to_install = array_filter($storage->listAll(), 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)) {
 | 
			
		||||
      $this->createConfiguration($collection, $config_to_install);
 | 
			
		||||
      $this->createConfiguration($collection, $storage->readMultiple($config_to_install));
 | 
			
		||||
      // Reset all the static caches and list caches.
 | 
			
		||||
      $this->configFactory->reset();
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			@ -269,34 +324,14 @@ class ConfigInstaller implements ConfigInstallerInterface {
 | 
			
		|||
    return $this;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * {@inheritdoc}
 | 
			
		||||
   */
 | 
			
		||||
  public function resetSourceStorage() {
 | 
			
		||||
    $this->sourceStorage = null;
 | 
			
		||||
    return $this;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Gets the configuration storage that provides the default configuration.
 | 
			
		||||
   *
 | 
			
		||||
   * @param string $collection
 | 
			
		||||
   *   (optional) The configuration collection. Defaults to the default
 | 
			
		||||
   *   collection.
 | 
			
		||||
   *
 | 
			
		||||
   * @return \Drupal\Core\Config\StorageInterface
 | 
			
		||||
   * @return \Drupal\Core\Config\StorageInterface|null
 | 
			
		||||
   *   The configuration storage that provides the default configuration.
 | 
			
		||||
   *   Returns null if the source storage has not been set.
 | 
			
		||||
   */
 | 
			
		||||
  public function getSourceStorage($collection = StorageInterface::DEFAULT_COLLECTION) {
 | 
			
		||||
    if (!isset($this->sourceStorage)) {
 | 
			
		||||
      // Default to using the ExtensionInstallStorage which searches extension's
 | 
			
		||||
      // config directories for default configuration. Only include the profile
 | 
			
		||||
      // configuration during Drupal installation.
 | 
			
		||||
      $this->sourceStorage = new ExtensionInstallStorage($this->getActiveStorages(StorageInterface::DEFAULT_COLLECTION), InstallStorage::CONFIG_INSTALL_DIRECTORY, $collection, drupal_installation_attempted());
 | 
			
		||||
    }
 | 
			
		||||
    if ($this->sourceStorage->getCollectionName() != $collection) {
 | 
			
		||||
      $this->sourceStorage = $this->sourceStorage->createCollection($collection);
 | 
			
		||||
    }
 | 
			
		||||
  public function getSourceStorage() {
 | 
			
		||||
    return $this->sourceStorage;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -321,6 +356,9 @@ class ConfigInstaller implements ConfigInstallerInterface {
 | 
			
		|||
   * {@inheritdoc}
 | 
			
		||||
   */
 | 
			
		||||
  public function setSyncing($status) {
 | 
			
		||||
    if (!$status) {
 | 
			
		||||
      $this->sourceStorage = NULL;
 | 
			
		||||
    }
 | 
			
		||||
    $this->isSyncing = $status;
 | 
			
		||||
    return $this;
 | 
			
		||||
  }
 | 
			
		||||
| 
						 | 
				
			
			@ -333,24 +371,30 @@ class ConfigInstaller implements ConfigInstallerInterface {
 | 
			
		|||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * {@inheritdoc}
 | 
			
		||||
   * Finds pre-existing configuration objects for the provided extension.
 | 
			
		||||
   *
 | 
			
		||||
   * Extensions can not be installed if configuration objects exist in the
 | 
			
		||||
   * active storage with the same names. This can happen in a number of ways,
 | 
			
		||||
   * commonly:
 | 
			
		||||
   * - if a user has created configuration with the same name as that provided
 | 
			
		||||
   *   by the extension.
 | 
			
		||||
   * - if the extension provides default configuration that does not depend on
 | 
			
		||||
   *   it and the extension has been uninstalled and is about to the
 | 
			
		||||
   *   reinstalled.
 | 
			
		||||
   *
 | 
			
		||||
   * @return array
 | 
			
		||||
   *   Array of configuration object names that already exist keyed by
 | 
			
		||||
   *   collection.
 | 
			
		||||
   */
 | 
			
		||||
  public function findPreExistingConfiguration($type, $name) {
 | 
			
		||||
  protected function findPreExistingConfiguration(StorageInterface $storage) {
 | 
			
		||||
    $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);
 | 
			
		||||
    foreach ($collection_info->getCollectionNames() as $collection) {
 | 
			
		||||
      $config_to_create = array_keys($this->getConfigToCreate($storage, $collection));
 | 
			
		||||
      $active_storage = $this->getActiveStorages($collection);
 | 
			
		||||
      foreach ($config_to_install as $config_name) {
 | 
			
		||||
      foreach ($config_to_create as $config_name) {
 | 
			
		||||
        if ($active_storage->exists($config_name)) {
 | 
			
		||||
          $existing_configuration[$collection][] = $config_name;
 | 
			
		||||
        }
 | 
			
		||||
| 
						 | 
				
			
			@ -358,4 +402,225 @@ class ConfigInstaller implements ConfigInstallerInterface {
 | 
			
		|||
    }
 | 
			
		||||
    return $existing_configuration;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * {@inheritdoc}
 | 
			
		||||
   */
 | 
			
		||||
  public function checkConfigurationToInstall($type, $name) {
 | 
			
		||||
    if ($this->isSyncing()) {
 | 
			
		||||
      // Configuration is assumed to already be checked by the config importer
 | 
			
		||||
      // validation events.
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
    $config_install_path = $this->getDefaultConfigDirectory($type, $name);
 | 
			
		||||
    if (!is_dir($config_install_path)) {
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    $storage = new FileStorage($config_install_path, StorageInterface::DEFAULT_COLLECTION);
 | 
			
		||||
 | 
			
		||||
    $enabled_extensions = $this->getEnabledExtensions();
 | 
			
		||||
    // Add the extension that will be enabled to the list of enabled extensions.
 | 
			
		||||
    $enabled_extensions[] = $name;
 | 
			
		||||
    // Gets a profile storage to search for overrides if necessary.
 | 
			
		||||
    $profile_storage = $this->getProfileStorage($name);
 | 
			
		||||
 | 
			
		||||
    // Check the dependencies of configuration provided by the module.
 | 
			
		||||
    $invalid_default_config = $this->findDefaultConfigWithUnmetDependencies($storage, $enabled_extensions, $profile_storage);
 | 
			
		||||
    if (!empty($invalid_default_config)) {
 | 
			
		||||
      throw UnmetDependenciesException::create($name, $invalid_default_config);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Install profiles can not have config clashes. Configuration that
 | 
			
		||||
    // has the same name as a module's configuration will be used instead.
 | 
			
		||||
    if ($name != $this->drupalGetProfile()) {
 | 
			
		||||
      // Throw an exception if the module being installed contains configuration
 | 
			
		||||
      // that already exists. Additionally, can not continue installing more
 | 
			
		||||
      // modules because those may depend on the current module being installed.
 | 
			
		||||
      $existing_configuration = $this->findPreExistingConfiguration($storage);
 | 
			
		||||
      if (!empty($existing_configuration)) {
 | 
			
		||||
        throw PreExistingConfigException::create($name, $existing_configuration);
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Finds default configuration with unmet dependencies.
 | 
			
		||||
   *
 | 
			
		||||
   * @param array $enabled_extensions
 | 
			
		||||
   *   A list of all the currently enabled modules and themes.
 | 
			
		||||
   *
 | 
			
		||||
   * @return array
 | 
			
		||||
   *   List of configuration that has unmet dependencies
 | 
			
		||||
   */
 | 
			
		||||
  protected function findDefaultConfigWithUnmetDependencies(StorageInterface $storage, array $enabled_extensions, StorageInterface $profile_storage = NULL) {
 | 
			
		||||
    $config_to_create = $this->getConfigToCreate($storage, StorageInterface::DEFAULT_COLLECTION, '', $profile_storage);
 | 
			
		||||
    $all_config = array_merge($this->configFactory->listAll(), array_keys($config_to_create));
 | 
			
		||||
    return array_filter(array_keys($config_to_create), function($config_name) use ($enabled_extensions, $all_config, $config_to_create) {
 | 
			
		||||
      return !$this->validateDependencies($config_name, $config_to_create[$config_name], $enabled_extensions, $all_config);
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Validates an array of config data that contains dependency information.
 | 
			
		||||
   *
 | 
			
		||||
   * @param string $config_name
 | 
			
		||||
   *   The name of the configuration object that is being validated.
 | 
			
		||||
   * @param array $data
 | 
			
		||||
   *   Configuration data.
 | 
			
		||||
   * @param array $enabled_extensions
 | 
			
		||||
   *   A list of all the currently enabled modules and themes.
 | 
			
		||||
   * @param array $all_config
 | 
			
		||||
   *   A list of all the active configuration names.
 | 
			
		||||
   *
 | 
			
		||||
   * @return bool
 | 
			
		||||
   *   TRUE if the dependencies are met, FALSE if not.
 | 
			
		||||
   */
 | 
			
		||||
  protected function validateDependencies($config_name, array $data, array $enabled_extensions, array $all_config) {
 | 
			
		||||
    // All the migrate tests will fail if we check since they install the
 | 
			
		||||
    // migrate_drupal module but only set up the dependencies for the single
 | 
			
		||||
    // migration they are testing.
 | 
			
		||||
    if (strpos($config_name, 'migrate.migration.') === 0) {
 | 
			
		||||
      return TRUE;
 | 
			
		||||
    }
 | 
			
		||||
    if (isset($data['dependencies'])) {
 | 
			
		||||
      $all_dependencies = $data['dependencies'];
 | 
			
		||||
 | 
			
		||||
      // Ensure enforced dependencies are included.
 | 
			
		||||
      if (isset($all_dependencies['enforced'])) {
 | 
			
		||||
        $all_dependencies = array_merge($all_dependencies, $data['dependencies']['enforced']);
 | 
			
		||||
        unset($all_dependencies['enforced']);
 | 
			
		||||
      }
 | 
			
		||||
      // Ensure the configuration entity type provider is in the list of
 | 
			
		||||
      // dependencies.
 | 
			
		||||
      list($provider) = explode('.', $config_name, 2);
 | 
			
		||||
      if (!isset($all_dependencies['module'])) {
 | 
			
		||||
        $all_dependencies['module'][] = $provider;
 | 
			
		||||
      }
 | 
			
		||||
      elseif (!in_array($provider, $all_dependencies['module'])) {
 | 
			
		||||
        $all_dependencies['module'][] = $provider;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      foreach ($all_dependencies as $type => $dependencies) {
 | 
			
		||||
        $list_to_check = [];
 | 
			
		||||
        switch ($type) {
 | 
			
		||||
          case 'module':
 | 
			
		||||
          case 'theme':
 | 
			
		||||
            $list_to_check = $enabled_extensions;
 | 
			
		||||
            break;
 | 
			
		||||
          case 'config':
 | 
			
		||||
            $list_to_check = $all_config;
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
        if (!empty($list_to_check)) {
 | 
			
		||||
          $missing = array_diff($dependencies, $list_to_check);
 | 
			
		||||
          if (!empty($missing)) {
 | 
			
		||||
            return FALSE;
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    return TRUE;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Gets the list of enabled extensions including both modules and themes.
 | 
			
		||||
   *
 | 
			
		||||
   * @return array
 | 
			
		||||
   *   A list of enabled extensions which includes both modules and themes.
 | 
			
		||||
   */
 | 
			
		||||
  protected function getEnabledExtensions() {
 | 
			
		||||
    // Read enabled extensions directly from configuration to avoid circular
 | 
			
		||||
    // dependencies on ModuleHandler and ThemeHandler.
 | 
			
		||||
    $extension_config = $this->configFactory->get('core.extension');
 | 
			
		||||
    $enabled_extensions = (array) $extension_config->get('module');
 | 
			
		||||
    $enabled_extensions += (array) $extension_config->get('theme');
 | 
			
		||||
    // Core can provide configuration.
 | 
			
		||||
    $enabled_extensions['core'] = 'core';
 | 
			
		||||
    return array_keys($enabled_extensions);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Gets the profile storage to use to check for profile overrides.
 | 
			
		||||
   *
 | 
			
		||||
   * @param string $installing_name
 | 
			
		||||
   *   (optional) The name of the extension currently being installed.
 | 
			
		||||
   *
 | 
			
		||||
   * @return \Drupal\Core\Config\StorageInterface|null
 | 
			
		||||
   *   A storage to access configuration from the installation profile. If a
 | 
			
		||||
   *   Drupal installation is not in progress or we're installing the profile
 | 
			
		||||
   *   itself, then it will return NULL as the profile storage should not be
 | 
			
		||||
   *   used.
 | 
			
		||||
   */
 | 
			
		||||
  protected function getProfileStorage($installing_name = '') {
 | 
			
		||||
    $profile = $this->drupalGetProfile();
 | 
			
		||||
    if ($this->drupalInstallationAttempted() && $profile != $installing_name) {
 | 
			
		||||
      // Profiles should not contain optional configuration so always use the
 | 
			
		||||
      // install directory.
 | 
			
		||||
      $profile_install_path = $this->getDefaultConfigDirectory('module', $profile);
 | 
			
		||||
      $profile_storage = new FileStorage($profile_install_path, StorageInterface::DEFAULT_COLLECTION);
 | 
			
		||||
    }
 | 
			
		||||
    else {
 | 
			
		||||
      $profile_storage = NULL;
 | 
			
		||||
    }
 | 
			
		||||
    return $profile_storage;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Gets an extension's default configuration directory.
 | 
			
		||||
   *
 | 
			
		||||
   * @param string $type
 | 
			
		||||
   *   Type of extension to install.
 | 
			
		||||
   * @param string $name
 | 
			
		||||
   *   Name of extension to install.
 | 
			
		||||
   *
 | 
			
		||||
   * @return string
 | 
			
		||||
   *   The extension's default configuration directory.
 | 
			
		||||
   */
 | 
			
		||||
  protected function getDefaultConfigDirectory($type, $name) {
 | 
			
		||||
    return $this->drupalGetPath($type, $name) . '/' . InstallStorage::CONFIG_INSTALL_DIRECTORY;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Wrapper for drupal_get_path().
 | 
			
		||||
   *
 | 
			
		||||
   * @param $type
 | 
			
		||||
   *   The type of the item; one of 'core', 'profile', 'module', 'theme', or
 | 
			
		||||
   *   'theme_engine'.
 | 
			
		||||
   * @param $name
 | 
			
		||||
   *   The name of the item for which the path is requested. Ignored for
 | 
			
		||||
   *   $type 'core'.
 | 
			
		||||
   *
 | 
			
		||||
   * @return string
 | 
			
		||||
   *   The path to the requested item or an empty string if the item is not
 | 
			
		||||
   *   found.
 | 
			
		||||
   */
 | 
			
		||||
  protected function drupalGetPath($type, $name) {
 | 
			
		||||
    return drupal_get_path($type, $name);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Gets the install profile from settings.
 | 
			
		||||
   *
 | 
			
		||||
   * @return string|null $profile
 | 
			
		||||
   *   The name of the installation profile or NULL if no installation profile
 | 
			
		||||
   *   is currently active. This is the case for example during the first steps
 | 
			
		||||
   *   of the installer or during unit tests.
 | 
			
		||||
   */
 | 
			
		||||
  protected function drupalGetProfile() {
 | 
			
		||||
    // Settings is safe to use because settings.php is written before any module
 | 
			
		||||
    // is installed.
 | 
			
		||||
    return Settings::get('install_profile');
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Wrapper for drupal_installation_attempted().
 | 
			
		||||
   *
 | 
			
		||||
   * @return bool
 | 
			
		||||
   *   TRUE if a Drupal installation is currently being attempted.
 | 
			
		||||
   */
 | 
			
		||||
  protected function drupalInstallationAttempted() {
 | 
			
		||||
    return drupal_installation_attempted();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -37,6 +37,24 @@ interface ConfigInstallerInterface {
 | 
			
		|||
   */
 | 
			
		||||
  public function installDefaultConfig($type, $name);
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Installs optional configuration.
 | 
			
		||||
   *
 | 
			
		||||
   * Optional configuration is only installed if:
 | 
			
		||||
   * - the configuration does not exist already.
 | 
			
		||||
   * - it's a configuration entity.
 | 
			
		||||
   * - its dependencies can be met.
 | 
			
		||||
   *
 | 
			
		||||
   * @param \Drupal\Core\Config\StorageInterface
 | 
			
		||||
   *   (optional) The configuration storage to search for optional
 | 
			
		||||
   *   configuration. If not provided, all enabled extension's optional
 | 
			
		||||
   *   configuration directories will be searched.
 | 
			
		||||
   * @param string $prefix
 | 
			
		||||
   *   (optional) If set, limits the installed configuration to only
 | 
			
		||||
   *   configuration beginning with the provided value.
 | 
			
		||||
   */
 | 
			
		||||
  public function installOptionalConfig(StorageInterface $storage = NULL, $prefix = '');
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Installs all default configuration in the specified collection.
 | 
			
		||||
   *
 | 
			
		||||
| 
						 | 
				
			
			@ -59,13 +77,6 @@ interface ConfigInstallerInterface {
 | 
			
		|||
   */
 | 
			
		||||
  public function setSourceStorage(StorageInterface $storage);
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Resets the configuration storage that provides the default configuration.
 | 
			
		||||
   *
 | 
			
		||||
   * @return $this
 | 
			
		||||
   */
 | 
			
		||||
  public function resetSourceStorage();
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Sets the status of the isSyncing flag.
 | 
			
		||||
   *
 | 
			
		||||
| 
						 | 
				
			
			@ -85,25 +96,16 @@ interface ConfigInstallerInterface {
 | 
			
		|||
  public function isSyncing();
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Finds pre-existing configuration objects for the provided extension.
 | 
			
		||||
   *
 | 
			
		||||
   * Extensions can not be installed if configuration objects exist in the
 | 
			
		||||
   * active storage with the same names. This can happen in a number of ways,
 | 
			
		||||
   * commonly:
 | 
			
		||||
   * - if a user has created configuration with the same name as that provided
 | 
			
		||||
   *   by the extension.
 | 
			
		||||
   * - if the extension provides default configuration that does not depend on
 | 
			
		||||
   *   it and the extension has been uninstalled and is about to the
 | 
			
		||||
   *   reinstalled.
 | 
			
		||||
   * Checks the configuration that will be installed for an extension.
 | 
			
		||||
   *
 | 
			
		||||
   * @param string $type
 | 
			
		||||
   *   Type of extension to install.
 | 
			
		||||
   * @param string $name
 | 
			
		||||
   *   Name of extension to install.
 | 
			
		||||
   *
 | 
			
		||||
   * @return array
 | 
			
		||||
   *   Array of configuration objects that already exist keyed by collection.
 | 
			
		||||
   * @throws \Drupal\Core\Config\UnmetDependenciesException
 | 
			
		||||
   * @throws \Drupal\Core\Config\PreExistingConfigException
 | 
			
		||||
   */
 | 
			
		||||
  public function findPreExistingConfiguration($type, $name);
 | 
			
		||||
  public function checkConfigurationToInstall($type, $name);
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -45,7 +45,7 @@ class ExtensionInstallStorage extends InstallStorage {
 | 
			
		|||
   *   default collection.
 | 
			
		||||
   * @param bool $include_profile
 | 
			
		||||
   *   (optional) Whether to include the install profile in extensions to
 | 
			
		||||
   *   search.
 | 
			
		||||
   *   search and to get overrides from.
 | 
			
		||||
   */
 | 
			
		||||
  public function __construct(StorageInterface $config_storage, $directory = self::CONFIG_INSTALL_DIRECTORY, $collection = StorageInterface::DEFAULT_COLLECTION, $include_profile = TRUE) {
 | 
			
		||||
    $this->configStorage = $config_storage;
 | 
			
		||||
| 
						 | 
				
			
			@ -82,27 +82,24 @@ class ExtensionInstallStorage extends InstallStorage {
 | 
			
		|||
      $this->folders = array();
 | 
			
		||||
      $this->folders += $this->getComponentNames('core', array('core'));
 | 
			
		||||
 | 
			
		||||
      $install_profile = Settings::get('install_profile');
 | 
			
		||||
      $extensions = $this->configStorage->read('core.extension');
 | 
			
		||||
      if (!empty($extensions['module'])) {
 | 
			
		||||
        $modules = $extensions['module'];
 | 
			
		||||
        if (!$this->includeProfile) {
 | 
			
		||||
          if ($install_profile = Settings::get('install_profile')) {
 | 
			
		||||
            unset($modules[$install_profile]);
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
        // Remove the install profile as this is handled later.
 | 
			
		||||
        unset($modules[$install_profile]);
 | 
			
		||||
        $this->folders += $this->getComponentNames('module', array_keys($modules));
 | 
			
		||||
      }
 | 
			
		||||
      if (!empty($extensions['theme'])) {
 | 
			
		||||
        $this->folders += $this->getComponentNames('theme', array_keys($extensions['theme']));
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      // The install profile can override module default configuration. We do
 | 
			
		||||
      // this by replacing the config file path from the module/theme with the
 | 
			
		||||
      // install profile version if there are any duplicates.
 | 
			
		||||
      $profile_folders = $this->getComponentNames('profile', array(drupal_get_profile()));
 | 
			
		||||
      $folders_to_replace = array_intersect_key($profile_folders, $this->folders);
 | 
			
		||||
      if (!empty($folders_to_replace)) {
 | 
			
		||||
        $this->folders = array_merge($this->folders, $folders_to_replace);
 | 
			
		||||
      if ($this->includeProfile) {
 | 
			
		||||
        // The install profile can override module default configuration. We do
 | 
			
		||||
        // this by replacing the config file path from the module/theme with the
 | 
			
		||||
        // install profile version if there are any duplicates.
 | 
			
		||||
        $profile_folders = $this->getComponentNames('profile', array(drupal_get_profile()));
 | 
			
		||||
        $this->folders = $profile_folders + $this->folders;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    return $this->folders;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -27,6 +27,11 @@ class InstallStorage extends FileStorage {
 | 
			
		|||
   */
 | 
			
		||||
  const CONFIG_INSTALL_DIRECTORY = 'config/install';
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Extension sub-directory containing optional configuration for installation.
 | 
			
		||||
   */
 | 
			
		||||
  const CONFIG_OPTIONAL_DIRECTORY = 'config/optional';
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Extension sub-directory containing configuration schema.
 | 
			
		||||
   */
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,95 @@
 | 
			
		|||
<?php
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @file
 | 
			
		||||
 * Contains \Drupal\Core\Config\UnmetDependenciesException.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
namespace Drupal\Core\Config;
 | 
			
		||||
 | 
			
		||||
use Drupal\Component\Utility\String;
 | 
			
		||||
use Drupal\Core\StringTranslation\TranslationInterface;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * An exception thrown if configuration has unmet dependencies.
 | 
			
		||||
 */
 | 
			
		||||
class UnmetDependenciesException extends ConfigException {
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * A list of configuration objects that have unmet dependencies.
 | 
			
		||||
   *
 | 
			
		||||
   * @var array
 | 
			
		||||
   */
 | 
			
		||||
  protected $configObjects = [];
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * The name of the extension that is being installed.
 | 
			
		||||
   *
 | 
			
		||||
   * @var string
 | 
			
		||||
   */
 | 
			
		||||
  protected $extension;
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Gets the list of configuration objects that have unmet dependencies.
 | 
			
		||||
   *
 | 
			
		||||
   * @return array
 | 
			
		||||
   *   A list of configuration objects that have unmet dependencies.
 | 
			
		||||
   */
 | 
			
		||||
  public function getConfigObjects() {
 | 
			
		||||
    return $this->configObjects;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Gets the name of the extension that is being installed.
 | 
			
		||||
   *
 | 
			
		||||
   * @return string
 | 
			
		||||
   *   The name of the extension that is being installed.
 | 
			
		||||
   */
 | 
			
		||||
  public function getExtension() {
 | 
			
		||||
    return $this->extension;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Gets a translated message from the exception.
 | 
			
		||||
   *
 | 
			
		||||
   * @param \Drupal\Core\StringTranslation\TranslationInterface $string_translation
 | 
			
		||||
   *   The string translation service.
 | 
			
		||||
   *
 | 
			
		||||
   * @return string
 | 
			
		||||
   */
 | 
			
		||||
  public function getTranslatedMessage(TranslationInterface $string_translation, $extension) {
 | 
			
		||||
    return $string_translation->formatPlural(
 | 
			
		||||
      count($this->getConfigObjects()),
 | 
			
		||||
      'Unable to install @extension, %config_names has unmet dependencies.',
 | 
			
		||||
      'Unable to install @extension, %config_names have unmet dependencies.',
 | 
			
		||||
      [
 | 
			
		||||
        '%config_names' => implode(', ', $this->getConfigObjects()),
 | 
			
		||||
        '@extension' => $extension,
 | 
			
		||||
      ]
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Creates an exception for an extension and a list of configuration objects.
 | 
			
		||||
   *
 | 
			
		||||
   * @param $extension
 | 
			
		||||
   *   The name of the extension that is being installed.
 | 
			
		||||
   * @param array $config_objects
 | 
			
		||||
   *   A list of configuration object names that have unmet dependencies
 | 
			
		||||
   *
 | 
			
		||||
   * @return \Drupal\Core\Config\PreExistingConfigException
 | 
			
		||||
   */
 | 
			
		||||
  public static function create($extension, array $config_objects) {
 | 
			
		||||
    $message = String::format('Configuration objects (@config_names) provided by @extension have unmet dependencies',
 | 
			
		||||
      array(
 | 
			
		||||
        '@config_names' => implode(', ', $config_objects),
 | 
			
		||||
        '@extension' => $extension
 | 
			
		||||
      )
 | 
			
		||||
    );
 | 
			
		||||
    $e = new static($message);
 | 
			
		||||
    $e->configObjects = $config_objects;
 | 
			
		||||
    $e->extension = $extension;
 | 
			
		||||
    return $e;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -151,17 +151,9 @@ class ModuleInstaller implements ModuleInstallerInterface {
 | 
			
		|||
          )));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Install profiles can not have config clashes. Configuration that
 | 
			
		||||
        // has the same name as a module's configuration will be used instead.
 | 
			
		||||
        if ($module != drupal_get_profile()) {
 | 
			
		||||
          // Validate default configuration of this module. Bail if unable to
 | 
			
		||||
          // install. Should not continue installing more modules because those
 | 
			
		||||
          // may depend on this one.
 | 
			
		||||
          $existing_configuration = $config_installer->findPreExistingConfiguration('module', $module);
 | 
			
		||||
          if (!empty($existing_configuration)) {
 | 
			
		||||
            throw PreExistingConfigException::create($module, $existing_configuration);
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
        // Check the validity of the default configuration. This will throw
 | 
			
		||||
        // exceptions if the configuration is not valid.
 | 
			
		||||
        $config_installer->checkConfigurationToInstall('module', $module);
 | 
			
		||||
 | 
			
		||||
        $extension_config
 | 
			
		||||
          ->set("module.$module", 0)
 | 
			
		||||
| 
						 | 
				
			
			@ -249,12 +241,6 @@ class ModuleInstaller implements ModuleInstallerInterface {
 | 
			
		|||
            ->setSyncing(TRUE)
 | 
			
		||||
            ->setSourceStorage($source_storage);
 | 
			
		||||
        }
 | 
			
		||||
        else {
 | 
			
		||||
          // If we're not in a config synchronization reset the source storage
 | 
			
		||||
          // so that the extension install storage will pick up the new
 | 
			
		||||
          // configuration.
 | 
			
		||||
          $config_installer->resetSourceStorage();
 | 
			
		||||
        }
 | 
			
		||||
        \Drupal::service('config.installer')->installDefaultConfig('module', $module);
 | 
			
		||||
 | 
			
		||||
        // If the module has no current updates, but has some that were
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -258,10 +258,7 @@ class ThemeHandler implements ThemeHandlerInterface {
 | 
			
		|||
 | 
			
		||||
      // Validate default configuration of the theme. If there is existing
 | 
			
		||||
      // configuration then stop installing.
 | 
			
		||||
      $existing_configuration = $this->configInstaller->findPreExistingConfiguration('theme', $key);
 | 
			
		||||
      if (!empty($existing_configuration)) {
 | 
			
		||||
        throw PreExistingConfigException::create($key, $existing_configuration);
 | 
			
		||||
      }
 | 
			
		||||
      $this->configInstaller->checkConfigurationToInstall('theme', $key);
 | 
			
		||||
 | 
			
		||||
      // The value is not used; the weight is ignored for themes currently.
 | 
			
		||||
      $extension_config
 | 
			
		||||
| 
						 | 
				
			
			@ -288,16 +285,6 @@ class ThemeHandler implements ThemeHandlerInterface {
 | 
			
		|||
      // Only install default configuration if this theme has not been installed
 | 
			
		||||
      // already.
 | 
			
		||||
      if (!isset($installed_themes[$key])) {
 | 
			
		||||
        // The default config installation storage only knows about the
 | 
			
		||||
        // currently installed list of themes, so it has to be reset in order to
 | 
			
		||||
        // pick up the default config of the newly installed theme. However, do
 | 
			
		||||
        // not reset the source storage when synchronizing configuration, since
 | 
			
		||||
        // that would needlessly trigger a reload of the whole configuration to
 | 
			
		||||
        // be imported.
 | 
			
		||||
        if (!$this->configInstaller->isSyncing()) {
 | 
			
		||||
          $this->configInstaller->resetSourceStorage();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Install default configuration of the theme.
 | 
			
		||||
        $this->configInstaller->installDefaultConfig('theme', $key);
 | 
			
		||||
      }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -149,6 +149,7 @@ class BlockStorageUnitTest extends KernelTestBase {
 | 
			
		|||
   * Tests the installation of default blocks.
 | 
			
		||||
   */
 | 
			
		||||
  public function testDefaultBlocks() {
 | 
			
		||||
    \Drupal::service('theme_handler')->install(['classy']);
 | 
			
		||||
    $entities = $this->controller->loadMultiple();
 | 
			
		||||
    $this->assertTrue(empty($entities), 'There are no blocks initially.');
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -4,3 +4,5 @@ description: 'Provides test blocks.'
 | 
			
		|||
package: Testing
 | 
			
		||||
version: VERSION
 | 
			
		||||
core: 8.x
 | 
			
		||||
dependencies:
 | 
			
		||||
  - block
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -19,7 +19,7 @@ class BlockContentPageViewTest extends BlockContentTestBase {
 | 
			
		|||
   *
 | 
			
		||||
   * @var array
 | 
			
		||||
   */
 | 
			
		||||
  public static $modules = array('block', 'block_content', 'block_content_test');
 | 
			
		||||
  public static $modules = array('block_content_test');
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Checks block edit and fallback functionality.
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -4,3 +4,5 @@ description: "Support module for custom block related testing."
 | 
			
		|||
package: Testing
 | 
			
		||||
version: VERSION
 | 
			
		||||
core: 8.x
 | 
			
		||||
dependencies:
 | 
			
		||||
  - block_content
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -4,7 +4,7 @@ dependencies:
 | 
			
		|||
  module:
 | 
			
		||||
    - block_content
 | 
			
		||||
  theme:
 | 
			
		||||
    - stark
 | 
			
		||||
    - classy
 | 
			
		||||
id: foobargorilla
 | 
			
		||||
theme: classy
 | 
			
		||||
region: content
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -5,4 +5,8 @@ cache: true
 | 
			
		|||
targetEntityType: node
 | 
			
		||||
dependencies:
 | 
			
		||||
  module:
 | 
			
		||||
    - book
 | 
			
		||||
    - node
 | 
			
		||||
  enforced:
 | 
			
		||||
    module:
 | 
			
		||||
      - book
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -42,7 +42,7 @@ class CommentDefaultFormatterCacheTagsTest extends EntityUnitTestBase {
 | 
			
		|||
 | 
			
		||||
    // Install tables and config needed to render comments.
 | 
			
		||||
    $this->installSchema('comment', array('comment_entity_statistics'));
 | 
			
		||||
    $this->installConfig(array('system', 'filter'));
 | 
			
		||||
    $this->installConfig(array('system', 'filter', 'comment'));
 | 
			
		||||
 | 
			
		||||
    // Comment rendering generates links, so build the router.
 | 
			
		||||
    $this->installSchema('system', array('router'));
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -79,7 +79,7 @@ class CommentFieldAccessTest extends EntityUnitTestBase {
 | 
			
		|||
   */
 | 
			
		||||
  protected function setUp() {
 | 
			
		||||
    parent::setUp();
 | 
			
		||||
    $this->installConfig(array('user'));
 | 
			
		||||
    $this->installConfig(array('user', 'comment'));
 | 
			
		||||
    $this->installSchema('comment', array('comment_entity_statistics'));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -25,6 +25,16 @@ class ConfigEntityListTest extends WebTestBase {
 | 
			
		|||
   */
 | 
			
		||||
  public static $modules = array('config_test');
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * {@inheritdoc}
 | 
			
		||||
   */
 | 
			
		||||
  protected function setUp() {
 | 
			
		||||
    parent::setUp();
 | 
			
		||||
    // Delete the override config_test entity since it is not required by this
 | 
			
		||||
    // test.
 | 
			
		||||
    \Drupal::entityManager()->getStorage('config_test')->load('override')->delete();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Tests entity list builder methods.
 | 
			
		||||
   */
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -38,7 +38,7 @@ class ConfigImportRecreateTest extends KernelTestBase {
 | 
			
		|||
    parent::setUp();
 | 
			
		||||
 | 
			
		||||
    $this->installEntitySchema('node');
 | 
			
		||||
    $this->installConfig(array('field'));
 | 
			
		||||
    $this->installConfig(array('field', 'node'));
 | 
			
		||||
 | 
			
		||||
    $this->copyConfig($this->container->get('config.storage'), $this->container->get('config.storage.staging'));
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -94,8 +94,8 @@ class ConfigImportRecreateTest extends KernelTestBase {
 | 
			
		|||
    // will be recreated.
 | 
			
		||||
    $creates = $this->configImporter->getUnprocessedConfiguration('create');
 | 
			
		||||
    $deletes = $this->configImporter->getUnprocessedConfiguration('delete');
 | 
			
		||||
    $this->assertEqual(4, count($creates), 'There are 4 configuration items to create.');
 | 
			
		||||
    $this->assertEqual(4, count($deletes), 'There are 4 configuration items to delete.');
 | 
			
		||||
    $this->assertEqual(5, count($creates), 'There are 5 configuration items to create.');
 | 
			
		||||
    $this->assertEqual(5, count($deletes), 'There are 5 configuration items to delete.');
 | 
			
		||||
    $this->assertEqual(0, count($this->configImporter->getUnprocessedConfiguration('update')), 'There are no configuration items to update.');
 | 
			
		||||
    $this->assertIdentical($creates, array_reverse($deletes), 'Deletes and creates contain the same configuration names in opposite orders due to dependencies.');
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -32,7 +32,7 @@ class ConfigImporterTest extends KernelTestBase {
 | 
			
		|||
   *
 | 
			
		||||
   * @var array
 | 
			
		||||
   */
 | 
			
		||||
  public static $modules = array('config_test', 'system', 'config_import_test', 'config_test_language');
 | 
			
		||||
  public static $modules = array('config_test', 'system', 'config_import_test');
 | 
			
		||||
 | 
			
		||||
  protected function setUp() {
 | 
			
		||||
    parent::setUp();
 | 
			
		||||
| 
						 | 
				
			
			@ -541,24 +541,4 @@ class ConfigImporterTest extends KernelTestBase {
 | 
			
		|||
    $this->assertTrue($this->container->get('config.storage')->exists($config_name));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Tests imported configuration entities with and without language information.
 | 
			
		||||
   */
 | 
			
		||||
  function testLanguage() {
 | 
			
		||||
    // Test imported configuration with implicit language code.
 | 
			
		||||
    $data = $this->container->get('config.storage.installer')->read('config_test.dynamic.dotted.english');
 | 
			
		||||
    $this->assertTrue(!isset($data['langcode']));
 | 
			
		||||
    $this->assertEqual(
 | 
			
		||||
      $this->config('config_test.dynamic.dotted.english')->get('langcode'),
 | 
			
		||||
      'en'
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    // Test imported configuration with explicit language code.
 | 
			
		||||
    $data = $this->container->get('config.storage.installer')->read('config_test.dynamic.dotted.french');
 | 
			
		||||
    $this->assertEqual($data['langcode'], 'fr');
 | 
			
		||||
    $this->assertEqual(
 | 
			
		||||
      $this->config('config_test.dynamic.dotted.french')->get('langcode'),
 | 
			
		||||
      'fr'
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,120 @@
 | 
			
		|||
<?php
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @file
 | 
			
		||||
 * Contains \Drupal\config\Tests\ConfigInstallProfileOverrideTest.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
namespace Drupal\config\Tests;
 | 
			
		||||
 | 
			
		||||
use Drupal\Core\Config\InstallStorage;
 | 
			
		||||
use Drupal\simpletest\WebTestBase;
 | 
			
		||||
use Drupal\Core\Config\FileStorage;
 | 
			
		||||
use Drupal\system\Entity\Action;
 | 
			
		||||
use Drupal\tour\Entity\Tour;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Tests installation and removal of configuration objects in install, disable
 | 
			
		||||
 * and uninstall functionality.
 | 
			
		||||
 *
 | 
			
		||||
 * @group config
 | 
			
		||||
 */
 | 
			
		||||
class ConfigInstallProfileOverrideTest extends WebTestBase {
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * The profile to install as a basis for testing.
 | 
			
		||||
   *
 | 
			
		||||
   * @var string
 | 
			
		||||
   */
 | 
			
		||||
  protected $profile = 'testing_config_overrides';
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Tests install profile config changes.
 | 
			
		||||
   */
 | 
			
		||||
  function testInstallProfileConfigOverwrite() {
 | 
			
		||||
    $config_name = 'system.cron';
 | 
			
		||||
    // The expected configuration from the system module.
 | 
			
		||||
    $expected_original_data = array(
 | 
			
		||||
      'threshold' => array(
 | 
			
		||||
        'autorun' => 0,
 | 
			
		||||
        'requirements_warning' => 172800,
 | 
			
		||||
        'requirements_error' => 1209600,
 | 
			
		||||
      ),
 | 
			
		||||
    );
 | 
			
		||||
    // The expected active configuration altered by the install profile.
 | 
			
		||||
    $expected_profile_data = array(
 | 
			
		||||
      'threshold' => array(
 | 
			
		||||
        'autorun' => 0,
 | 
			
		||||
        'requirements_warning' => 259200,
 | 
			
		||||
        'requirements_error' => 1209600,
 | 
			
		||||
      ),
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    // Verify that the original data matches. We have to read the module config
 | 
			
		||||
    // file directly, because the install profile default system.cron.yml
 | 
			
		||||
    // configuration file was used to create the active configuration.
 | 
			
		||||
    $config_dir = drupal_get_path('module', 'system') . '/'. InstallStorage::CONFIG_INSTALL_DIRECTORY;
 | 
			
		||||
    $this->assertTrue(is_dir($config_dir));
 | 
			
		||||
    $source_storage = new FileStorage($config_dir);
 | 
			
		||||
    $data = $source_storage->read($config_name);
 | 
			
		||||
    $this->assertIdentical($data, $expected_original_data);
 | 
			
		||||
 | 
			
		||||
    // Verify that active configuration matches the expected data, which was
 | 
			
		||||
    // created from the testing install profile's system.cron.yml file.
 | 
			
		||||
    $config = $this->config($config_name);
 | 
			
		||||
    $this->assertIdentical($config->get(), $expected_profile_data);
 | 
			
		||||
 | 
			
		||||
    // Ensure that the configuration entity has the expected dependencies and
 | 
			
		||||
    // overrides.
 | 
			
		||||
    $action = Action::load('user_block_user_action');
 | 
			
		||||
    $this->assertEqual($action->label(), 'Overridden block the selected user(s)');
 | 
			
		||||
    $action = Action::load('user_cancel_user_action');
 | 
			
		||||
    $this->assertEqual($action->label(), 'Cancel the selected user account(s)', 'Default configuration that is not overridden is not affected.');
 | 
			
		||||
 | 
			
		||||
    // Ensure that optional configuration can be overridden.
 | 
			
		||||
    $tour = Tour::load('language');
 | 
			
		||||
    $this->assertEqual(count($tour->getTips()), 1, 'Optional configuration can be overridden. The language tour only has one tip');
 | 
			
		||||
    $tour = Tour::load('language-add');
 | 
			
		||||
    $this->assertEqual(count($tour->getTips()), 3, 'Optional configuration that is not overridden is not affected.');
 | 
			
		||||
 | 
			
		||||
    // Ensure that optional configuration from a profile is created if
 | 
			
		||||
    // dependencies are met.
 | 
			
		||||
    $this->assertEqual(Tour::load('testing_config_overrides')->label(), 'Config override test');
 | 
			
		||||
 | 
			
		||||
    // Ensure that optional configuration from a profile is not created if
 | 
			
		||||
    // dependencies are not met. Cannot use the entity system since the entity
 | 
			
		||||
    // type does not exist.
 | 
			
		||||
    $optional_dir = drupal_get_path('module', 'testing_config_overrides') . '/' . InstallStorage::CONFIG_OPTIONAL_DIRECTORY;
 | 
			
		||||
    $optional_storage = new FileStorage($optional_dir);
 | 
			
		||||
    foreach (['config_test.dynamic.dotted.default', 'config_test.dynamic.override','config_test.dynamic.override_unmet'] as $id) {
 | 
			
		||||
      $this->assertTrue(\Drupal::config($id)->isNew(), "The config_test entity $id contained in the profile's optional directory does not exist.");
 | 
			
		||||
      // Make that we don't get false positives from the assertion above.
 | 
			
		||||
      $this->assertTrue($optional_storage->exists($id), "The config_test entity $id does exist in the profile's optional directory.");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Install the config_test module and ensure that the override from the
 | 
			
		||||
    // install profile is not used. Optional configuration can not override
 | 
			
		||||
    // configuration in a modules config/install directory.
 | 
			
		||||
    $this->container->get('module_installer')->install(['config_test']);
 | 
			
		||||
    $this->rebuildContainer();
 | 
			
		||||
    $config_test_storage = \Drupal::entityManager()->getStorage('config_test');
 | 
			
		||||
    $this->assertEqual($config_test_storage->load('dotted.default')->label(), 'Default', 'The config_test entity is not overridden by the profile optional configuration.');
 | 
			
		||||
    // Test that override of optional configuration does work.
 | 
			
		||||
    $this->assertEqual($config_test_storage->load('override')->label(), 'Override', 'The optional config_test entity is overridden by the profile optional configuration.');
 | 
			
		||||
    // Test that override of optional configuration which introduces an unmet
 | 
			
		||||
    // dependency does not get created.
 | 
			
		||||
    $this->assertNull($config_test_storage->load('override_unmet'), 'The optional config_test entity with unmet dependencies is not created.');
 | 
			
		||||
 | 
			
		||||
    $this->container->get('module_installer')->install(['dblog']);
 | 
			
		||||
    $this->rebuildContainer();
 | 
			
		||||
    // Just installing db_log does not create the optional configuration.
 | 
			
		||||
    $this->assertNull($config_test_storage->load('override_unmet'), 'The optional config_test entity with unmet dependencies is not created.');
 | 
			
		||||
    // Install all available optional configuration.
 | 
			
		||||
    $this->container->get('config.installer')->installOptionalConfig();
 | 
			
		||||
    $this->assertEqual($config_test_storage->load('override_unmet')->label(), 'Override', 'The optional config_test entity is overridden by the profile optional configuration.');
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,100 @@
 | 
			
		|||
<?php
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @file
 | 
			
		||||
 * Contains \Drupal\config\Tests\ConfigInstallProfileUnmetDependenciesTest.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
namespace Drupal\config\Tests;
 | 
			
		||||
 | 
			
		||||
use Drupal\Component\Serialization\Yaml;
 | 
			
		||||
use Drupal\Core\Config\InstallStorage;
 | 
			
		||||
use Drupal\simpletest\InstallerTestBase;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Tests install profile config overrides can not add unmet dependencies.
 | 
			
		||||
 *
 | 
			
		||||
 * @group Config
 | 
			
		||||
 */
 | 
			
		||||
class ConfigInstallProfileUnmetDependenciesTest extends InstallerTestBase {
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * The installation profile to install.
 | 
			
		||||
   *
 | 
			
		||||
   * @var string
 | 
			
		||||
   */
 | 
			
		||||
  protected $profile = 'testing_config_overrides';
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Set to TRUE if the expected exception is thrown.
 | 
			
		||||
   *
 | 
			
		||||
   * @var bool
 | 
			
		||||
   */
 | 
			
		||||
  protected $expectedException = FALSE;
 | 
			
		||||
 | 
			
		||||
  protected function setUp() {
 | 
			
		||||
    // Copy the testing_config_overrides install profile so we can change the
 | 
			
		||||
    // configuration to include a dependency that can not be met. File API
 | 
			
		||||
    // functions are not available yet.
 | 
			
		||||
    $dest = $this->siteDirectory . '/profiles/testing_config_overrides';
 | 
			
		||||
    mkdir($dest, 0777, TRUE);
 | 
			
		||||
    $source = DRUPAL_ROOT . '/core/profiles/testing_config_overrides';
 | 
			
		||||
    $iterator = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($source, \RecursiveDirectoryIterator::SKIP_DOTS), \RecursiveIteratorIterator::SELF_FIRST);
 | 
			
		||||
    foreach ($iterator as $item) {
 | 
			
		||||
      if ($item->isDir()) {
 | 
			
		||||
        mkdir($dest . DIRECTORY_SEPARATOR . $iterator->getSubPathName());
 | 
			
		||||
      }
 | 
			
		||||
      else {
 | 
			
		||||
        copy($item, $dest . DIRECTORY_SEPARATOR . $iterator->getSubPathName());
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Add a dependency that can not be met because User is installed before
 | 
			
		||||
    // Action.
 | 
			
		||||
    $config_file = $dest . DIRECTORY_SEPARATOR . InstallStorage::CONFIG_INSTALL_DIRECTORY . DIRECTORY_SEPARATOR . 'system.action.user_block_user_action.yml';
 | 
			
		||||
    $action = Yaml::decode(file_get_contents($config_file));
 | 
			
		||||
    $action['dependencies']['module'][] = 'action';
 | 
			
		||||
    file_put_contents($config_file, Yaml::encode($action));
 | 
			
		||||
 | 
			
		||||
    parent::setUp();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * {@inheritdoc}
 | 
			
		||||
   *
 | 
			
		||||
   * Override the error method so we can test for the expected exception.
 | 
			
		||||
   */
 | 
			
		||||
  protected function error($message = '', $group = 'Other', array $caller = NULL) {
 | 
			
		||||
    if ($group == 'User notice') {
 | 
			
		||||
      // Since 'User notice' is set by trigger_error() which is used for debug
 | 
			
		||||
      // set the message to a status of 'debug'.
 | 
			
		||||
      return $this->assert('debug', $message, 'Debug', $caller);
 | 
			
		||||
    }
 | 
			
		||||
    if ($group == 'Drupal\Core\Config\UnmetDependenciesException') {
 | 
			
		||||
      $this->expectedException = TRUE;
 | 
			
		||||
      return FALSE;
 | 
			
		||||
    }
 | 
			
		||||
    return $this->assert('exception', $message, $group, $caller);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * {@inheritdoc}
 | 
			
		||||
   */
 | 
			
		||||
  protected function setUpSite() {
 | 
			
		||||
    // This step is not reached due to the exception.
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Confirms that the installation succeeded.
 | 
			
		||||
   */
 | 
			
		||||
  public function testInstalled() {
 | 
			
		||||
    if ($this->expectedException) {
 | 
			
		||||
      $this->pass('Expected Drupal\Core\Config\UnmetDependenciesException exception thrown');
 | 
			
		||||
    }
 | 
			
		||||
    else {
 | 
			
		||||
      $this->fail('Expected Drupal\Core\Config\UnmetDependenciesException exception thrown');
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -9,6 +9,7 @@ namespace Drupal\config\Tests;
 | 
			
		|||
 | 
			
		||||
use Drupal\Core\Config\PreExistingConfigException;
 | 
			
		||||
use Drupal\Core\Config\StorageInterface;
 | 
			
		||||
use Drupal\Core\Config\UnmetDependenciesException;
 | 
			
		||||
use Drupal\simpletest\KernelTestBase;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
| 
						 | 
				
			
			@ -19,11 +20,17 @@ use Drupal\simpletest\KernelTestBase;
 | 
			
		|||
 */
 | 
			
		||||
class ConfigInstallTest extends KernelTestBase {
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * {@inheritdoc}
 | 
			
		||||
   */
 | 
			
		||||
  public static $modules = ['system'];
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * {@inheritdoc}
 | 
			
		||||
   */
 | 
			
		||||
  protected function setUp() {
 | 
			
		||||
    parent::setUp();
 | 
			
		||||
    $this->installSchema('system', ['router']);
 | 
			
		||||
 | 
			
		||||
    // Ensure the global variable being asserted by this test does not exist;
 | 
			
		||||
    // a previous test executed in this request/process might have set it.
 | 
			
		||||
| 
						 | 
				
			
			@ -48,11 +55,7 @@ class ConfigInstallTest extends KernelTestBase {
 | 
			
		|||
    $this->assertFalse(\Drupal::service('config.typed')->hasConfigSchema('config_schema_test.schema_in_install'), 'Configuration schema for config_schema_test.schema_in_install does not exist.');
 | 
			
		||||
 | 
			
		||||
    // Install the test module.
 | 
			
		||||
    $this->enableModules(array('config_test', 'config_schema_test'));
 | 
			
		||||
    $this->installConfig(array('config_test', 'config_schema_test'));
 | 
			
		||||
 | 
			
		||||
    // After module installation the new schema should exist.
 | 
			
		||||
    $this->assertTrue(\Drupal::service('config.typed')->hasConfigSchema('config_schema_test.schema_in_install'), 'Configuration schema for config_schema_test.schema_in_install exists.');
 | 
			
		||||
    $this->installModules(array('config_test'));
 | 
			
		||||
 | 
			
		||||
    // Verify that default module config exists.
 | 
			
		||||
    \Drupal::configFactory()->reset($default_config);
 | 
			
		||||
| 
						 | 
				
			
			@ -71,6 +74,13 @@ class ConfigInstallTest extends KernelTestBase {
 | 
			
		|||
    $this->assertFalse(isset($GLOBALS['hook_config_test']['predelete']));
 | 
			
		||||
    $this->assertFalse(isset($GLOBALS['hook_config_test']['delete']));
 | 
			
		||||
 | 
			
		||||
    // Install the schema test module.
 | 
			
		||||
    $this->enableModules(array('config_schema_test'));
 | 
			
		||||
    $this->installConfig(array('config_schema_test'));
 | 
			
		||||
 | 
			
		||||
    // After module installation the new schema should exist.
 | 
			
		||||
    $this->assertTrue(\Drupal::service('config.typed')->hasConfigSchema('config_schema_test.schema_in_install'), 'Configuration schema for config_schema_test.schema_in_install exists.');
 | 
			
		||||
 | 
			
		||||
    // Ensure that data type casting is applied during config installation.
 | 
			
		||||
    $config = $this->config('config_schema_test.schema_in_install');
 | 
			
		||||
    $this->assertIdentical($config->get('integer'), 1);
 | 
			
		||||
| 
						 | 
				
			
			@ -170,8 +180,7 @@ class ConfigInstallTest extends KernelTestBase {
 | 
			
		|||
    );
 | 
			
		||||
    \Drupal::state()->set('config_collection_install_test.collection_names', $collections);
 | 
			
		||||
    // Install the test module.
 | 
			
		||||
    $this->enableModules(array('config_test', 'config_collection_install_test'));
 | 
			
		||||
    $this->installConfig(array('config_test'));
 | 
			
		||||
    $this->installModules(array('config_test', 'config_collection_install_test'));
 | 
			
		||||
    /** @var \Drupal\Core\Config\StorageInterface $active_storage */
 | 
			
		||||
    $active_storage = \Drupal::service('config.storage');
 | 
			
		||||
    $this->assertEqual($collections, $active_storage->getAllCollectionNames());
 | 
			
		||||
| 
						 | 
				
			
			@ -189,4 +198,56 @@ class ConfigInstallTest extends KernelTestBase {
 | 
			
		|||
    $this->assertIdentical(array('label' => 'entity'), $data);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Tests the configuration with unmet dependencies is not installed.
 | 
			
		||||
   */
 | 
			
		||||
  public function testDependencyChecking() {
 | 
			
		||||
    $this->installModules(['config_test']);
 | 
			
		||||
    try {
 | 
			
		||||
      $this->installModules(['config_install_dependency_test']);
 | 
			
		||||
      $this->fail('Expected UnmetDependenciesException not thrown.');
 | 
			
		||||
    }
 | 
			
		||||
    catch (UnmetDependenciesException $e) {
 | 
			
		||||
      $this->assertEqual($e->getExtension(), 'config_install_dependency_test');
 | 
			
		||||
      $this->assertEqual($e->getConfigObjects(), ['config_test.dynamic.other_module_test_with_dependency']);
 | 
			
		||||
      $this->assertEqual($e->getMessage(), 'Configuration objects (config_test.dynamic.other_module_test_with_dependency) provided by config_install_dependency_test have unmet dependencies');
 | 
			
		||||
    }
 | 
			
		||||
    $this->installModules(['config_other_module_config_test']);
 | 
			
		||||
    $this->installModules(['config_install_dependency_test']);
 | 
			
		||||
    $this->assertTrue(entity_load('config_test', 'other_module_test_with_dependency'), 'The config_test.dynamic.other_module_test_with_dependency configuration has been created during install.');
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Tests imported configuration entities with and without language information.
 | 
			
		||||
   */
 | 
			
		||||
  function testLanguage() {
 | 
			
		||||
    $this->installModules(['config_test_language']);
 | 
			
		||||
    // Test imported configuration with implicit language code.
 | 
			
		||||
    $data = $this->container->get('config.storage.installer')->read('config_test.dynamic.dotted.english');
 | 
			
		||||
    $this->assertTrue(!isset($data['langcode']));
 | 
			
		||||
    $this->assertEqual(
 | 
			
		||||
      $this->config('config_test.dynamic.dotted.english')->get('langcode'),
 | 
			
		||||
      'en'
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    // Test imported configuration with explicit language code.
 | 
			
		||||
    $data = $this->container->get('config.storage.installer')->read('config_test.dynamic.dotted.french');
 | 
			
		||||
    $this->assertEqual($data['langcode'], 'fr');
 | 
			
		||||
    $this->assertEqual(
 | 
			
		||||
      $this->config('config_test.dynamic.dotted.french')->get('langcode'),
 | 
			
		||||
      'fr'
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Installs a module.
 | 
			
		||||
   *
 | 
			
		||||
   * @param array $modules
 | 
			
		||||
   *   The module names.
 | 
			
		||||
   */
 | 
			
		||||
  protected function installModules(array $modules) {
 | 
			
		||||
    $this->container->get('module_installer')->install($modules);
 | 
			
		||||
    $this->container = \Drupal::getContainer();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -7,12 +7,10 @@
 | 
			
		|||
 | 
			
		||||
namespace Drupal\config\Tests;
 | 
			
		||||
 | 
			
		||||
use Drupal\Core\Config\InstallStorage;
 | 
			
		||||
use Drupal\Core\Config\PreExistingConfigException;
 | 
			
		||||
use Drupal\Core\Config\StorageInterface;
 | 
			
		||||
use Drupal\language\Entity\ConfigurableLanguage;
 | 
			
		||||
use Drupal\simpletest\WebTestBase;
 | 
			
		||||
use Drupal\Core\Config\FileStorage;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Tests installation and removal of configuration objects in install, disable
 | 
			
		||||
| 
						 | 
				
			
			@ -119,43 +117,6 @@ class ConfigInstallWebTest extends WebTestBase {
 | 
			
		|||
    $this->assertIdentical($config_entity->get('label'), 'Default integration config label');
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Tests install profile config changes.
 | 
			
		||||
   */
 | 
			
		||||
  function testInstallProfileConfigOverwrite() {
 | 
			
		||||
    $config_name = 'system.cron';
 | 
			
		||||
    // The expected configuration from the system module.
 | 
			
		||||
    $expected_original_data = array(
 | 
			
		||||
      'threshold' => array(
 | 
			
		||||
        'autorun' => 0,
 | 
			
		||||
        'requirements_warning' => 172800,
 | 
			
		||||
        'requirements_error' => 1209600,
 | 
			
		||||
      ),
 | 
			
		||||
    );
 | 
			
		||||
    // The expected active configuration altered by the install profile.
 | 
			
		||||
    $expected_profile_data = array(
 | 
			
		||||
      'threshold' => array(
 | 
			
		||||
        'autorun' => 0,
 | 
			
		||||
        'requirements_warning' => 259200,
 | 
			
		||||
        'requirements_error' => 1209600,
 | 
			
		||||
      ),
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    // Verify that the original data matches. We have to read the module config
 | 
			
		||||
    // file directly, because the install profile default system.cron.yml
 | 
			
		||||
    // configuration file was used to create the active configuration.
 | 
			
		||||
    $config_dir = drupal_get_path('module', 'system') . '/'. InstallStorage::CONFIG_INSTALL_DIRECTORY;
 | 
			
		||||
    $this->assertTrue(is_dir($config_dir));
 | 
			
		||||
    $source_storage = new FileStorage($config_dir);
 | 
			
		||||
    $data = $source_storage->read($config_name);
 | 
			
		||||
    $this->assertIdentical($data, $expected_original_data);
 | 
			
		||||
 | 
			
		||||
    // Verify that active configuration matches the expected data, which was
 | 
			
		||||
    // created from the testing install profile's system.cron.yml file.
 | 
			
		||||
    $config = $this->config($config_name);
 | 
			
		||||
    $this->assertIdentical($config->get(), $expected_profile_data);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Tests pre-existing configuration detection.
 | 
			
		||||
   */
 | 
			
		||||
| 
						 | 
				
			
			@ -214,4 +175,21 @@ class ConfigInstallWebTest extends WebTestBase {
 | 
			
		|||
      $this->assertEqual($e->getMessage(), 'Configuration objects (config_test.dynamic.dotted.default, language/fr/config_test.dynamic.dotted.default) provided by config_clash_test_theme already exist in active configuration');
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Tests unmet dependencies detection.
 | 
			
		||||
   */
 | 
			
		||||
  public function testUnmetDependenciesInstall() {
 | 
			
		||||
    $this->drupalLogin($this->adminUser);
 | 
			
		||||
    // We need to install separately since config_install_dependency_test does
 | 
			
		||||
    // not depend on config_test and order is important.
 | 
			
		||||
    $this->drupalPostForm('admin/modules', array('modules[Testing][config_test][enable]' => TRUE), t('Save configuration'));
 | 
			
		||||
    $this->drupalPostForm('admin/modules', array('modules[Testing][config_install_dependency_test][enable]' => TRUE), t('Save configuration'));
 | 
			
		||||
    $this->assertRaw('Unable to install Config install dependency test, <em class="placeholder">config_test.dynamic.other_module_test_with_dependency</em> has unmet dependencies.');
 | 
			
		||||
 | 
			
		||||
    $this->drupalPostForm('admin/modules', array('modules[Testing][config_other_module_config_test][enable]' => TRUE), t('Save configuration'));
 | 
			
		||||
    $this->drupalPostForm('admin/modules', array('modules[Testing][config_install_dependency_test][enable]' => TRUE), t('Save configuration'));
 | 
			
		||||
    $this->rebuildContainer();
 | 
			
		||||
    $this->assertTrue(entity_load('config_test', 'other_module_test_with_dependency'), 'The config_test.dynamic.other_module_test_with_dependency configuration has been created during install.');
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -7,8 +7,6 @@
 | 
			
		|||
 | 
			
		||||
namespace Drupal\config\Tests;
 | 
			
		||||
 | 
			
		||||
use Drupal\Core\Config\PreExistingConfigException;
 | 
			
		||||
use Drupal\Core\Config\StorageInterface;
 | 
			
		||||
use Drupal\simpletest\WebTestBase;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
| 
						 | 
				
			
			@ -59,18 +57,10 @@ class ConfigOtherModuleTest extends WebTestBase {
 | 
			
		|||
    // Default configuration provided by config_test should still exist.
 | 
			
		||||
    $this->assertTrue(entity_load('config_test', 'dotted.default', TRUE), 'The configuration is not deleted.');
 | 
			
		||||
 | 
			
		||||
    // Re-enable module to test that pre-existing default configuration throws
 | 
			
		||||
    // an error.
 | 
			
		||||
    $msg = "The expected PreExistingConfigException is thrown by reinstalling config_other_module_config_test.";
 | 
			
		||||
    try {
 | 
			
		||||
      $this->installModule('config_other_module_config_test');
 | 
			
		||||
      $this->fail($msg);
 | 
			
		||||
    }
 | 
			
		||||
    catch (PreExistingConfigException $e) {
 | 
			
		||||
      $this->pass($msg);
 | 
			
		||||
      $this->assertEqual($e->getExtension(), 'config_other_module_config_test');
 | 
			
		||||
      $this->assertEqual($e->getConfigObjects(), [StorageInterface::DEFAULT_COLLECTION => ['config_test.dynamic.other_module_test']]);
 | 
			
		||||
    }
 | 
			
		||||
    // Re-enable module to test that pre-existing optional configuration does
 | 
			
		||||
    // not throw an error.
 | 
			
		||||
    $this->installModule('config_other_module_config_test');
 | 
			
		||||
    $this->assertTrue(\Drupal::moduleHandler()->moduleExists('config_other_module_config_test'), 'The config_other_module_config_test module is installed.');
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,11 @@
 | 
			
		|||
id: other_module_test_with_dependency
 | 
			
		||||
label: 'Other module test with dependency'
 | 
			
		||||
weight: 0
 | 
			
		||||
style: ''
 | 
			
		||||
status: true
 | 
			
		||||
langcode: en
 | 
			
		||||
protected_property: Default
 | 
			
		||||
dependencies:
 | 
			
		||||
  enforced:
 | 
			
		||||
    module:
 | 
			
		||||
      - config_other_module_config_test
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,5 @@
 | 
			
		|||
name: 'Config install dependency test'
 | 
			
		||||
type: module
 | 
			
		||||
package: Testing
 | 
			
		||||
version: VERSION
 | 
			
		||||
core: 8.x
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,6 @@
 | 
			
		|||
# See \Drupal\config\Tests\ConfigInstallProfileOverrideTest
 | 
			
		||||
id: override
 | 
			
		||||
label: Default
 | 
			
		||||
weight: 0
 | 
			
		||||
protected_property: Default
 | 
			
		||||
status: 1
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,10 @@
 | 
			
		|||
# See \Drupal\config\Tests\ConfigInstallProfileOverrideTest
 | 
			
		||||
# This configuration entity has dependencies which would be met.
 | 
			
		||||
id: override_unmet
 | 
			
		||||
label: Default
 | 
			
		||||
weight: 0
 | 
			
		||||
protected_property: Default
 | 
			
		||||
status: 1
 | 
			
		||||
dependencies:
 | 
			
		||||
  module:
 | 
			
		||||
    - tour
 | 
			
		||||
| 
						 | 
				
			
			@ -3,3 +3,5 @@ type: module
 | 
			
		|||
package: Testing
 | 
			
		||||
version: VERSION
 | 
			
		||||
core: 8.x
 | 
			
		||||
dependencies:
 | 
			
		||||
  - config_test
 | 
			
		||||
| 
						 | 
				
			
			@ -32,7 +32,7 @@ class MessageEntityTest extends EntityUnitTestBase {
 | 
			
		|||
 | 
			
		||||
  protected function setUp() {
 | 
			
		||||
    parent::setUp();
 | 
			
		||||
    $this->installConfig(array('contact'));
 | 
			
		||||
    $this->installConfig(array('contact', 'contact_test'));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -28,6 +28,7 @@ class EditorFileUsageTest extends EntityUnitTestBase {
 | 
			
		|||
    $this->installEntitySchema('file');
 | 
			
		||||
    $this->installSchema('node', array('node_access'));
 | 
			
		||||
    $this->installSchema('file', array('file_usage'));
 | 
			
		||||
    $this->installConfig(['node']);
 | 
			
		||||
 | 
			
		||||
    // Add text formats.
 | 
			
		||||
    $filtered_html_format = entity_create('filter_format', array(
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -6,3 +6,6 @@ package: Testing
 | 
			
		|||
version: VERSION
 | 
			
		||||
dependencies:
 | 
			
		||||
  - entity_reference
 | 
			
		||||
  - node
 | 
			
		||||
  - user
 | 
			
		||||
  - views
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -31,6 +31,7 @@ class FieldImportChangeTest extends FieldUnitTestBase {
 | 
			
		|||
   * Tests importing an updated field.
 | 
			
		||||
   */
 | 
			
		||||
  function testImportChange() {
 | 
			
		||||
    $this->installConfig(['field_test_config']);
 | 
			
		||||
    $field_storage_id = 'field_test_import';
 | 
			
		||||
    $field_id = "entity_test.entity_test.$field_storage_id";
 | 
			
		||||
    $field_config_name = "field.field.$field_id";
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -33,6 +33,7 @@ class FieldImportDeleteTest extends FieldUnitTestBase {
 | 
			
		|||
   * Tests deleting field storages and fields as part of config import.
 | 
			
		||||
   */
 | 
			
		||||
  public function testImportDelete() {
 | 
			
		||||
    $this->installConfig(['field_test_config']);
 | 
			
		||||
    // At this point there are 5 field configuration objects in the active
 | 
			
		||||
    // storage.
 | 
			
		||||
    // - field.storage.entity_test.field_test_import
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -28,7 +28,7 @@ class EntityDisplayTest extends KernelTestBase {
 | 
			
		|||
    parent::setUp();
 | 
			
		||||
    $this->installSchema('system', array('router'));
 | 
			
		||||
    $this->installEntitySchema('node');
 | 
			
		||||
    $this->installConfig(array('field'));
 | 
			
		||||
    $this->installConfig(array('field', 'node'));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -29,7 +29,7 @@ class FilterAPITest extends EntityUnitTestBase {
 | 
			
		|||
    parent::setUp();
 | 
			
		||||
 | 
			
		||||
    $this->installSchema('system', array('router'));
 | 
			
		||||
    $this->installConfig(array('system', 'filter'));
 | 
			
		||||
    $this->installConfig(array('system', 'filter', 'filter_test'));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -4,3 +4,5 @@ description: 'Tests filter hooks and functions.'
 | 
			
		|||
package: Testing
 | 
			
		||||
version: VERSION
 | 
			
		||||
core: 8.x
 | 
			
		||||
dependencies:
 | 
			
		||||
  - filter
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -35,6 +35,7 @@ class EntityTest extends NormalizerTestBase {
 | 
			
		|||
    $this->installSchema('system', array('sequences'));
 | 
			
		||||
    $this->installSchema('comment', array('comment_entity_statistics'));
 | 
			
		||||
    $this->installEntitySchema('taxonomy_term');
 | 
			
		||||
    $this->installConfig(['node', 'comment']);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -427,3 +427,5 @@ display:
 | 
			
		|||
    id: block_1
 | 
			
		||||
    display_title: Block
 | 
			
		||||
    position: 1
 | 
			
		||||
    display_options:
 | 
			
		||||
      display_extenders: {  }
 | 
			
		||||
| 
						 | 
				
			
			@ -37,7 +37,7 @@ class NodeBodyFieldStorageTest extends KernelTestBase {
 | 
			
		|||
    $this->installSchema('user', 'users_data');
 | 
			
		||||
    $this->installEntitySchema('user');
 | 
			
		||||
    $this->installEntitySchema('node');
 | 
			
		||||
    $this->installConfig(array('field'));
 | 
			
		||||
    $this->installConfig(array('field', 'node'));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -30,7 +30,7 @@ class NodeTokenReplaceTest extends TokenReplaceUnitTestBase {
 | 
			
		|||
   */
 | 
			
		||||
  protected function setUp() {
 | 
			
		||||
    parent::setUp();
 | 
			
		||||
    $this->installConfig(array('filter'));
 | 
			
		||||
    $this->installConfig(array('filter', 'node'));
 | 
			
		||||
 | 
			
		||||
    $node_type = entity_create('node_type', array('type' => 'article', 'name' => 'Article'));
 | 
			
		||||
    $node_type->save();
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -211,6 +211,8 @@ EOS;
 | 
			
		|||
   * Tests expected behavior of installConfig().
 | 
			
		||||
   */
 | 
			
		||||
  function testInstallConfig() {
 | 
			
		||||
    // The user module has configuration that depends on system.
 | 
			
		||||
    $this->enableModules(array('system'));
 | 
			
		||||
    $module = 'user';
 | 
			
		||||
 | 
			
		||||
    // Verify that default config can only be installed for enabled modules.
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -9,6 +9,7 @@ namespace Drupal\system\Controller;
 | 
			
		|||
 | 
			
		||||
use Drupal\Core\Config\ConfigFactoryInterface;
 | 
			
		||||
use Drupal\Core\Config\PreExistingConfigException;
 | 
			
		||||
use Drupal\Core\Config\UnmetDependenciesException;
 | 
			
		||||
use Drupal\Core\Controller\ControllerBase;
 | 
			
		||||
use Drupal\Core\Extension\ThemeHandlerInterface;
 | 
			
		||||
use Drupal\Core\Routing\RouteBuilderInterface;
 | 
			
		||||
| 
						 | 
				
			
			@ -144,6 +145,9 @@ class ThemeController extends ControllerBase {
 | 
			
		|||
          'error'
 | 
			
		||||
        );
 | 
			
		||||
      }
 | 
			
		||||
      catch (UnmetDependenciesException $e) {
 | 
			
		||||
        drupal_set_message($e->getTranslatedMessage($this->getStringTranslation(), $theme), 'error');
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      return $this->redirect('system.themes_page');
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -8,6 +8,7 @@
 | 
			
		|||
namespace Drupal\system\Form;
 | 
			
		||||
 | 
			
		||||
use Drupal\Core\Config\PreExistingConfigException;
 | 
			
		||||
use Drupal\Core\Config\UnmetDependenciesException;
 | 
			
		||||
use Drupal\Core\Extension\ModuleHandlerInterface;
 | 
			
		||||
use Drupal\Core\Extension\ModuleInstallerInterface;
 | 
			
		||||
use Drupal\Core\Form\ConfirmFormBase;
 | 
			
		||||
| 
						 | 
				
			
			@ -176,6 +177,13 @@ class ModulesListConfirmForm extends ConfirmFormBase {
 | 
			
		|||
        );
 | 
			
		||||
        return;
 | 
			
		||||
      }
 | 
			
		||||
      catch (UnmetDependenciesException $e) {
 | 
			
		||||
        drupal_set_message(
 | 
			
		||||
          $e->getTranslatedMessage($this->getStringTranslation(), $this->modules['install'][$e->getExtension()]),
 | 
			
		||||
          'error'
 | 
			
		||||
        );
 | 
			
		||||
        return;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Gets module list after install process, flushes caches and displays a
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -10,6 +10,7 @@ namespace Drupal\system\Form;
 | 
			
		|||
use Drupal\Component\Utility\String;
 | 
			
		||||
use Drupal\Component\Utility\Unicode;
 | 
			
		||||
use Drupal\Core\Config\PreExistingConfigException;
 | 
			
		||||
use Drupal\Core\Config\UnmetDependenciesException;
 | 
			
		||||
use Drupal\Core\Controller\TitleResolverInterface;
 | 
			
		||||
use Drupal\Core\Access\AccessManagerInterface;
 | 
			
		||||
use Drupal\Core\Entity\EntityManagerInterface;
 | 
			
		||||
| 
						 | 
				
			
			@ -536,6 +537,13 @@ class ModulesListForm extends FormBase {
 | 
			
		|||
        );
 | 
			
		||||
        return;
 | 
			
		||||
      }
 | 
			
		||||
      catch (UnmetDependenciesException $e) {
 | 
			
		||||
        drupal_set_message(
 | 
			
		||||
          $e->getTranslatedMessage($this->getStringTranslation(), $modules['install'][$e->getExtension()]),
 | 
			
		||||
          'error'
 | 
			
		||||
        );
 | 
			
		||||
        return;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Gets module list after install process, flushes caches and displays a
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -52,6 +52,7 @@ class EntityCrudHookTest extends EntityUnitTestBase {
 | 
			
		|||
    $this->installSchema('file', array('file_usage'));
 | 
			
		||||
    $this->installSchema('node', array('node_access'));
 | 
			
		||||
    $this->installSchema('comment', array('comment_entity_statistics'));
 | 
			
		||||
    $this->installConfig(['node', 'comment']);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -31,6 +31,15 @@ class InstallerExistingSettingsTest extends InstallerTestBase {
 | 
			
		|||
      'required' => TRUE,
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    // Actually the install profile should be skipped to because it is written
 | 
			
		||||
    // to settings.php.
 | 
			
		||||
    // @todo https://www.drupal.org/node/2451369 Fix install_profile so that it
 | 
			
		||||
    //   is written to an existing settings.php if possible or if set used.
 | 
			
		||||
    $this->settings['settings']['install_profile'] = (object) array(
 | 
			
		||||
      'value' => 'testing',
 | 
			
		||||
      'required' => TRUE,
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    // Pre-configure database credentials.
 | 
			
		||||
    $connection_info = Database::getConnectionInfo();
 | 
			
		||||
    unset($connection_info['default']['pdo']);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -7,3 +7,7 @@ status: true
 | 
			
		|||
langcode: en
 | 
			
		||||
locked: false
 | 
			
		||||
pattern: 'U'
 | 
			
		||||
dependencies:
 | 
			
		||||
  enforced:
 | 
			
		||||
    theme:
 | 
			
		||||
      - test_basetheme
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -6,3 +6,5 @@ version: VERSION
 | 
			
		|||
core: 8.x
 | 
			
		||||
required: true
 | 
			
		||||
configure: user.admin_index
 | 
			
		||||
dependencies:
 | 
			
		||||
  - system
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -142,8 +142,8 @@ class ModuleTest extends ViewUnitTestBase {
 | 
			
		|||
   * Tests the load wrapper/helper functions.
 | 
			
		||||
   */
 | 
			
		||||
  public function testLoadFunctions() {
 | 
			
		||||
    $this->enableModules(['text', 'node']);
 | 
			
		||||
    $this->installConfig(['node']);
 | 
			
		||||
    $this->enableModules(array('field', 'text', 'node'));
 | 
			
		||||
    $this->installConfig(array('node'));
 | 
			
		||||
    $storage = $this->container->get('entity.manager')->getStorage('view');
 | 
			
		||||
 | 
			
		||||
    // Test views_view_is_enabled/disabled.
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -84,7 +84,7 @@ class ViewExecutableTest extends ViewUnitTestBase {
 | 
			
		|||
    $this->installEntitySchema('node');
 | 
			
		||||
    $this->installEntitySchema('comment');
 | 
			
		||||
    $this->installSchema('comment', array('comment_entity_statistics'));
 | 
			
		||||
    $this->installConfig(array('field'));
 | 
			
		||||
    $this->installConfig(array('system', 'field', 'node', 'comment'));
 | 
			
		||||
 | 
			
		||||
    entity_create('node_type', array(
 | 
			
		||||
      'type' => 'page',
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -7,3 +7,5 @@ dependencies:
 | 
			
		|||
  - node
 | 
			
		||||
  - block
 | 
			
		||||
  - dblog
 | 
			
		||||
themes:
 | 
			
		||||
  - stark
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,9 @@
 | 
			
		|||
id: user_block_user_action
 | 
			
		||||
label: 'Overridden block the selected user(s)'
 | 
			
		||||
status: true
 | 
			
		||||
langcode: en
 | 
			
		||||
type: user
 | 
			
		||||
plugin: user_block_user_action
 | 
			
		||||
dependencies:
 | 
			
		||||
  module:
 | 
			
		||||
    - user
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,13 @@
 | 
			
		|||
id: language
 | 
			
		||||
module: language
 | 
			
		||||
label: Language
 | 
			
		||||
langcode: en
 | 
			
		||||
routes:
 | 
			
		||||
  - route_name: entity.configurable_language.collection
 | 
			
		||||
tips:
 | 
			
		||||
  language-overview:
 | 
			
		||||
    id: language-overview
 | 
			
		||||
    plugin: text
 | 
			
		||||
    label: Languages
 | 
			
		||||
    body: '<p>The "Languages" page allows you to add, edit, delete, and reorder languages for the site.</p>'
 | 
			
		||||
    weight: 1
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,6 @@
 | 
			
		|||
id: dotted.default
 | 
			
		||||
label: 'Default install profile override'
 | 
			
		||||
weight: 0
 | 
			
		||||
protected_property: Default
 | 
			
		||||
# Intentionally commented out to verify default status behavior.
 | 
			
		||||
# status: 1
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,8 @@
 | 
			
		|||
id: override
 | 
			
		||||
label: Override
 | 
			
		||||
weight: 0
 | 
			
		||||
protected_property: Default
 | 
			
		||||
status: 1
 | 
			
		||||
dependencies:
 | 
			
		||||
  module:
 | 
			
		||||
    - tour
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,8 @@
 | 
			
		|||
id: override_unmet
 | 
			
		||||
label: Override
 | 
			
		||||
weight: 0
 | 
			
		||||
protected_property: Default
 | 
			
		||||
status: 1
 | 
			
		||||
dependencies:
 | 
			
		||||
  module:
 | 
			
		||||
    - dblog
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,13 @@
 | 
			
		|||
id: testing_config_overrides
 | 
			
		||||
module: testing_config_overrides
 | 
			
		||||
label: Config override test
 | 
			
		||||
langcode: en
 | 
			
		||||
routes:
 | 
			
		||||
  - route_name: entity.configurable_language.collection
 | 
			
		||||
tips:
 | 
			
		||||
  language-overview:
 | 
			
		||||
    id: language-overview
 | 
			
		||||
    plugin: text
 | 
			
		||||
    label: Languages
 | 
			
		||||
    body: '<p>The "Languages" page allows you to add, edit, delete, and reorder languages for the site.</p>'
 | 
			
		||||
    weight: 1
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,10 @@
 | 
			
		|||
name: Testing config overrides
 | 
			
		||||
type: profile
 | 
			
		||||
description: 'Minimal profile for running tests with config overrides in a profile.'
 | 
			
		||||
version: VERSION
 | 
			
		||||
core: 8.x
 | 
			
		||||
hidden: true
 | 
			
		||||
dependencies:
 | 
			
		||||
  - action
 | 
			
		||||
  - language
 | 
			
		||||
  - tour
 | 
			
		||||
		Loading…
	
		Reference in New Issue