Chris@0: configFactory = $config_factory; Chris@0: $this->activeStorages[$active_storage->getCollectionName()] = $active_storage; Chris@0: $this->typedConfig = $typed_config; Chris@0: $this->configManager = $config_manager; Chris@0: $this->eventDispatcher = $event_dispatcher; Chris@0: $this->installProfile = $install_profile; Chris@0: } Chris@0: Chris@0: /** Chris@0: * {@inheritdoc} Chris@0: */ Chris@0: public function installDefaultConfig($type, $name) { Chris@0: $extension_path = $this->drupalGetPath($type, $name); Chris@0: // Refresh the schema cache if the extension provides configuration schema Chris@0: // or is a theme. Chris@0: if (is_dir($extension_path . '/' . InstallStorage::CONFIG_SCHEMA_DIRECTORY) || $type == 'theme') { Chris@0: $this->typedConfig->clearCachedDefinitions(); Chris@0: } Chris@0: Chris@0: $default_install_path = $this->getDefaultConfigDirectory($type, $name); Chris@0: if (is_dir($default_install_path)) { Chris@0: if (!$this->isSyncing()) { Chris@0: $storage = new FileStorage($default_install_path, StorageInterface::DEFAULT_COLLECTION); Chris@0: $prefix = ''; Chris@0: } Chris@0: else { Chris@0: // The configuration importer sets the source storage on the config Chris@0: // installer. The configuration importer handles all of the Chris@0: // configuration entity imports. We only need to ensure that simple Chris@0: // configuration is created when the extension is installed. Chris@0: $storage = $this->getSourceStorage(); Chris@0: $prefix = $name . '.'; Chris@0: } Chris@0: Chris@0: // Gets profile storages to search for overrides if necessary. Chris@0: $profile_storages = $this->getProfileStorages($name); Chris@0: Chris@0: // Gather information about all the supported collections. Chris@0: $collection_info = $this->configManager->getConfigCollectionInfo(); Chris@0: foreach ($collection_info->getCollectionNames() as $collection) { Chris@0: $config_to_create = $this->getConfigToCreate($storage, $collection, $prefix, $profile_storages); Chris@0: // If we're installing a profile ensure configuration that is overriding Chris@0: // is excluded. Chris@0: if ($name == $this->drupalGetProfile()) { Chris@0: $existing_configuration = $this->getActiveStorages($collection)->listAll(); Chris@0: $config_to_create = array_diff_key($config_to_create, array_flip($existing_configuration)); Chris@0: } Chris@0: if (!empty($config_to_create)) { Chris@0: $this->createConfiguration($collection, $config_to_create); Chris@0: } Chris@0: } Chris@0: } Chris@0: Chris@0: // During a drupal installation optional configuration is installed at the Chris@0: // end of the installation process. Chris@0: // @see install_install_profile() Chris@0: if (!$this->isSyncing() && !$this->drupalInstallationAttempted()) { Chris@0: $optional_install_path = $extension_path . '/' . InstallStorage::CONFIG_OPTIONAL_DIRECTORY; Chris@0: if (is_dir($optional_install_path)) { Chris@0: // Install any optional config the module provides. Chris@0: $storage = new FileStorage($optional_install_path, StorageInterface::DEFAULT_COLLECTION); Chris@0: $this->installOptionalConfig($storage, ''); Chris@0: } Chris@0: // Install any optional configuration entities whose dependencies can now Chris@0: // be met. This searches all the installed modules config/optional Chris@0: // directories. Chris@0: $storage = new ExtensionInstallStorage($this->getActiveStorages(StorageInterface::DEFAULT_COLLECTION), InstallStorage::CONFIG_OPTIONAL_DIRECTORY, StorageInterface::DEFAULT_COLLECTION, FALSE, $this->installProfile); Chris@0: $this->installOptionalConfig($storage, [$type => $name]); Chris@0: } Chris@0: Chris@0: // Reset all the static caches and list caches. Chris@0: $this->configFactory->reset(); Chris@0: } Chris@0: Chris@0: /** Chris@0: * {@inheritdoc} Chris@0: */ Chris@0: public function installOptionalConfig(StorageInterface $storage = NULL, $dependency = []) { Chris@0: $profile = $this->drupalGetProfile(); Chris@17: $enabled_extensions = $this->getEnabledExtensions(); Chris@17: $existing_config = $this->getActiveStorages()->listAll(); Chris@17: Chris@17: // Create the storages to read configuration from. Chris@0: if (!$storage) { Chris@0: // Search the install profile's optional configuration too. Chris@0: $storage = new ExtensionInstallStorage($this->getActiveStorages(StorageInterface::DEFAULT_COLLECTION), InstallStorage::CONFIG_OPTIONAL_DIRECTORY, StorageInterface::DEFAULT_COLLECTION, TRUE, $this->installProfile); Chris@0: // The extension install storage ensures that overrides are used. Chris@0: $profile_storage = NULL; Chris@0: } Chris@0: elseif (!empty($profile)) { Chris@0: // Creates a profile storage to search for overrides. Chris@0: $profile_install_path = $this->drupalGetPath('module', $profile) . '/' . InstallStorage::CONFIG_OPTIONAL_DIRECTORY; Chris@0: $profile_storage = new FileStorage($profile_install_path, StorageInterface::DEFAULT_COLLECTION); Chris@0: } Chris@0: else { Chris@0: // Profile has not been set yet. For example during the first steps of the Chris@0: // installer or during unit tests. Chris@0: $profile_storage = NULL; Chris@0: } Chris@0: Chris@17: // Build the list of possible configuration to create. Chris@17: $list = $storage->listAll(); Chris@17: if ($profile_storage && !empty($dependency)) { Chris@17: // Only add the optional profile configuration into the list if we are Chris@17: // have a dependency to check. This ensures that optional profile Chris@17: // configuration is not unexpectedly re-created after being deleted. Chris@17: $list = array_unique(array_merge($list, $profile_storage->listAll())); Chris@17: } Chris@0: Chris@17: // Filter the list of configuration to only include configuration that Chris@17: // should be created. Chris@0: $list = array_filter($list, function ($config_name) use ($existing_config) { Chris@0: // Only list configuration that: Chris@0: // - does not already exist Chris@0: // - is a configuration entity (this also excludes config that has an Chris@0: // implicit dependency on modules that are not yet installed) Chris@0: return !in_array($config_name, $existing_config) && $this->configManager->getEntityTypeIdByName($config_name); Chris@0: }); Chris@0: Chris@0: $all_config = array_merge($existing_config, $list); Chris@0: $all_config = array_combine($all_config, $all_config); Chris@0: $config_to_create = $storage->readMultiple($list); Chris@0: // Check to see if the corresponding override storage has any overrides or Chris@0: // new configuration that can be installed. Chris@0: if ($profile_storage) { Chris@0: $config_to_create = $profile_storage->readMultiple($list) + $config_to_create; Chris@0: } Chris@0: // Sort $config_to_create in the order of the least dependent first. Chris@0: $dependency_manager = new ConfigDependencyManager(); Chris@0: $dependency_manager->setData($config_to_create); Chris@0: $config_to_create = array_merge(array_flip($dependency_manager->sortAll()), $config_to_create); Chris@17: if (!empty($dependency)) { Chris@17: // In order to work out dependencies we need the full config graph. Chris@17: $dependency_manager->setData($this->getActiveStorages()->readMultiple($existing_config) + $config_to_create); Chris@17: $dependencies = $dependency_manager->getDependentEntities(key($dependency), reset($dependency)); Chris@17: } Chris@0: Chris@0: foreach ($config_to_create as $config_name => $data) { Chris@0: // Remove configuration where its dependencies cannot be met. Chris@0: $remove = !$this->validateDependencies($config_name, $data, $enabled_extensions, $all_config); Chris@17: // Remove configuration that is not dependent on $dependency, if it is Chris@17: // defined. Chris@0: if (!$remove && !empty($dependency)) { Chris@17: $remove = !isset($dependencies[$config_name]); Chris@0: } Chris@0: Chris@0: if ($remove) { Chris@0: // Remove from the list of configuration to create. Chris@0: unset($config_to_create[$config_name]); Chris@0: // Remove from the list of all configuration. This ensures that any Chris@0: // configuration that depends on this configuration is also removed. Chris@0: unset($all_config[$config_name]); Chris@0: } Chris@0: } Chris@17: Chris@17: // Create the optional configuration if there is any left after filtering. Chris@0: if (!empty($config_to_create)) { Chris@0: $this->createConfiguration(StorageInterface::DEFAULT_COLLECTION, $config_to_create, TRUE); Chris@0: } Chris@0: } Chris@0: Chris@0: /** Chris@0: * Gets configuration data from the provided storage to create. Chris@0: * Chris@0: * @param StorageInterface $storage Chris@0: * The configuration storage to read configuration from. Chris@0: * @param string $collection Chris@0: * The configuration collection to use. Chris@0: * @param string $prefix Chris@0: * (optional) Limit to configuration starting with the provided string. Chris@0: * @param \Drupal\Core\Config\StorageInterface[] $profile_storages Chris@0: * An array of storage interfaces containing profile configuration to check Chris@0: * for overrides. Chris@0: * Chris@0: * @return array Chris@0: * An array of configuration data read from the source storage keyed by the Chris@0: * configuration object name. Chris@0: */ Chris@0: protected function getConfigToCreate(StorageInterface $storage, $collection, $prefix = '', array $profile_storages = []) { Chris@0: if ($storage->getCollectionName() != $collection) { Chris@0: $storage = $storage->createCollection($collection); Chris@0: } Chris@0: $data = $storage->readMultiple($storage->listAll($prefix)); Chris@0: Chris@0: // Check to see if the corresponding override storage has any overrides. Chris@0: foreach ($profile_storages as $profile_storage) { Chris@0: if ($profile_storage->getCollectionName() != $collection) { Chris@0: $profile_storage = $profile_storage->createCollection($collection); Chris@0: } Chris@0: $data = $profile_storage->readMultiple(array_keys($data)) + $data; Chris@0: } Chris@0: return $data; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Creates configuration in a collection based on the provided list. Chris@0: * Chris@0: * @param string $collection Chris@0: * The configuration collection. Chris@0: * @param array $config_to_create Chris@0: * An array of configuration data to create, keyed by name. Chris@0: */ Chris@0: protected function createConfiguration($collection, array $config_to_create) { Chris@0: // Order the configuration to install in the order of dependencies. Chris@0: if ($collection == StorageInterface::DEFAULT_COLLECTION) { Chris@0: $dependency_manager = new ConfigDependencyManager(); Chris@0: $config_names = $dependency_manager Chris@0: ->setData($config_to_create) Chris@0: ->sortAll(); Chris@0: } Chris@0: else { Chris@0: $config_names = array_keys($config_to_create); Chris@0: } Chris@0: Chris@0: foreach ($config_names as $name) { Chris@0: // Allow config factory overriders to use a custom configuration object if Chris@0: // they are responsible for the collection. Chris@0: $overrider = $this->configManager->getConfigCollectionInfo()->getOverrideService($collection); Chris@0: if ($overrider) { Chris@0: $new_config = $overrider->createConfigObject($name, $collection); Chris@0: } Chris@0: else { Chris@0: $new_config = new Config($name, $this->getActiveStorages($collection), $this->eventDispatcher, $this->typedConfig); Chris@0: } Chris@0: if ($config_to_create[$name] !== FALSE) { Chris@0: $new_config->setData($config_to_create[$name]); Chris@0: // Add a hash to configuration created through the installer so it is Chris@0: // possible to know if the configuration was created by installing an Chris@0: // extension and to track which version of the default config was used. Chris@0: if (!$this->isSyncing() && $collection == StorageInterface::DEFAULT_COLLECTION) { Chris@0: $new_config->set('_core.default_config_hash', Crypt::hashBase64(serialize($config_to_create[$name]))); Chris@0: } Chris@0: } Chris@0: if ($collection == StorageInterface::DEFAULT_COLLECTION && $entity_type = $this->configManager->getEntityTypeIdByName($name)) { Chris@0: // If we are syncing do not create configuration entities. Pluggable Chris@0: // configuration entities can have dependencies on modules that are Chris@0: // not yet enabled. This approach means that any code that expects Chris@0: // default configuration entities to exist will be unstable after the Chris@0: // module has been enabled and before the config entity has been Chris@0: // imported. Chris@0: if ($this->isSyncing()) { Chris@0: continue; Chris@0: } Chris@0: /** @var \Drupal\Core\Config\Entity\ConfigEntityStorageInterface $entity_storage */ Chris@0: $entity_storage = $this->configManager Chris@18: ->getEntityTypeManager() Chris@0: ->getStorage($entity_type); Chris@17: Chris@17: $id = $entity_storage->getIDFromConfigName($name, $entity_storage->getEntityType()->getConfigPrefix()); Chris@0: // It is possible that secondary writes can occur during configuration Chris@0: // creation. Updates of such configuration are allowed. Chris@0: if ($this->getActiveStorages($collection)->exists($name)) { Chris@0: $entity = $entity_storage->load($id); Chris@0: $entity = $entity_storage->updateFromStorageRecord($entity, $new_config->get()); Chris@0: } Chris@0: else { Chris@0: $entity = $entity_storage->createFromStorageRecord($new_config->get()); Chris@0: } Chris@0: if ($entity->isInstallable()) { Chris@0: $entity->trustData()->save(); Chris@17: if ($id !== $entity->id()) { Chris@17: trigger_error(sprintf('The configuration name "%s" does not match the ID "%s"', $name, $entity->id()), E_USER_WARNING); Chris@17: } Chris@0: } Chris@0: } Chris@0: else { Chris@0: $new_config->save(TRUE); Chris@0: } Chris@0: } Chris@0: } Chris@0: Chris@0: /** Chris@0: * {@inheritdoc} Chris@0: */ Chris@0: public function installCollectionDefaultConfig($collection) { Chris@0: $storage = new ExtensionInstallStorage($this->getActiveStorages(StorageInterface::DEFAULT_COLLECTION), InstallStorage::CONFIG_INSTALL_DIRECTORY, $collection, $this->drupalInstallationAttempted(), $this->installProfile); Chris@0: // Only install configuration for enabled extensions. Chris@0: $enabled_extensions = $this->getEnabledExtensions(); Chris@0: $config_to_install = array_filter($storage->listAll(), function ($config_name) use ($enabled_extensions) { Chris@17: $provider = mb_substr($config_name, 0, strpos($config_name, '.')); Chris@0: return in_array($provider, $enabled_extensions); Chris@0: }); Chris@0: if (!empty($config_to_install)) { Chris@0: $this->createConfiguration($collection, $storage->readMultiple($config_to_install)); Chris@0: // Reset all the static caches and list caches. Chris@0: $this->configFactory->reset(); Chris@0: } Chris@0: } Chris@0: Chris@0: /** Chris@0: * {@inheritdoc} Chris@0: */ Chris@0: public function setSourceStorage(StorageInterface $storage) { Chris@0: $this->sourceStorage = $storage; Chris@0: return $this; Chris@0: } Chris@0: Chris@0: /** Chris@18: * {@inheritdoc} Chris@0: */ Chris@0: public function getSourceStorage() { Chris@0: return $this->sourceStorage; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Gets the configuration storage that provides the active configuration. Chris@0: * Chris@0: * @param string $collection Chris@0: * (optional) The configuration collection. Defaults to the default Chris@0: * collection. Chris@0: * Chris@0: * @return \Drupal\Core\Config\StorageInterface Chris@0: * The configuration storage that provides the default configuration. Chris@0: */ Chris@0: protected function getActiveStorages($collection = StorageInterface::DEFAULT_COLLECTION) { Chris@0: if (!isset($this->activeStorages[$collection])) { Chris@0: $this->activeStorages[$collection] = reset($this->activeStorages)->createCollection($collection); Chris@0: } Chris@0: return $this->activeStorages[$collection]; Chris@0: } Chris@0: Chris@0: /** Chris@0: * {@inheritdoc} Chris@0: */ Chris@0: public function setSyncing($status) { Chris@0: if (!$status) { Chris@0: $this->sourceStorage = NULL; Chris@0: } Chris@0: $this->isSyncing = $status; Chris@0: return $this; Chris@0: } Chris@0: Chris@0: /** Chris@0: * {@inheritdoc} Chris@0: */ Chris@0: public function isSyncing() { Chris@0: return $this->isSyncing; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Finds pre-existing configuration objects for the provided extension. Chris@0: * Chris@0: * Extensions can not be installed if configuration objects exist in the Chris@0: * active storage with the same names. This can happen in a number of ways, Chris@0: * commonly: Chris@0: * - if a user has created configuration with the same name as that provided Chris@0: * by the extension. Chris@0: * - if the extension provides default configuration that does not depend on Chris@0: * it and the extension has been uninstalled and is about to the Chris@0: * reinstalled. Chris@0: * Chris@0: * @return array Chris@0: * Array of configuration object names that already exist keyed by Chris@0: * collection. Chris@0: */ Chris@0: protected function findPreExistingConfiguration(StorageInterface $storage) { Chris@0: $existing_configuration = []; Chris@0: // Gather information about all the supported collections. Chris@0: $collection_info = $this->configManager->getConfigCollectionInfo(); Chris@0: Chris@0: foreach ($collection_info->getCollectionNames() as $collection) { Chris@0: $config_to_create = array_keys($this->getConfigToCreate($storage, $collection)); Chris@0: $active_storage = $this->getActiveStorages($collection); Chris@0: foreach ($config_to_create as $config_name) { Chris@0: if ($active_storage->exists($config_name)) { Chris@0: $existing_configuration[$collection][] = $config_name; Chris@0: } Chris@0: } Chris@0: } Chris@0: return $existing_configuration; Chris@0: } Chris@0: Chris@0: /** Chris@0: * {@inheritdoc} Chris@0: */ Chris@0: public function checkConfigurationToInstall($type, $name) { Chris@0: if ($this->isSyncing()) { Chris@0: // Configuration is assumed to already be checked by the config importer Chris@0: // validation events. Chris@0: return; Chris@0: } Chris@0: $config_install_path = $this->getDefaultConfigDirectory($type, $name); Chris@0: if (!is_dir($config_install_path)) { Chris@0: return; Chris@0: } Chris@0: Chris@0: $storage = new FileStorage($config_install_path, StorageInterface::DEFAULT_COLLECTION); Chris@0: Chris@0: $enabled_extensions = $this->getEnabledExtensions(); Chris@0: // Add the extension that will be enabled to the list of enabled extensions. Chris@0: $enabled_extensions[] = $name; Chris@0: // Gets profile storages to search for overrides if necessary. Chris@0: $profile_storages = $this->getProfileStorages($name); Chris@0: Chris@0: // Check the dependencies of configuration provided by the module. Chris@0: list($invalid_default_config, $missing_dependencies) = $this->findDefaultConfigWithUnmetDependencies($storage, $enabled_extensions, $profile_storages); Chris@0: if (!empty($invalid_default_config)) { Chris@0: throw UnmetDependenciesException::create($name, array_unique($missing_dependencies, SORT_REGULAR)); Chris@0: } Chris@0: Chris@0: // Install profiles can not have config clashes. Configuration that Chris@0: // has the same name as a module's configuration will be used instead. Chris@0: if ($name != $this->drupalGetProfile()) { Chris@0: // Throw an exception if the module being installed contains configuration Chris@0: // that already exists. Additionally, can not continue installing more Chris@0: // modules because those may depend on the current module being installed. Chris@0: $existing_configuration = $this->findPreExistingConfiguration($storage); Chris@0: if (!empty($existing_configuration)) { Chris@0: throw PreExistingConfigException::create($name, $existing_configuration); Chris@0: } Chris@0: } Chris@0: } Chris@0: Chris@0: /** Chris@0: * Finds default configuration with unmet dependencies. Chris@0: * Chris@0: * @param \Drupal\Core\Config\StorageInterface $storage Chris@0: * The storage containing the default configuration. Chris@0: * @param array $enabled_extensions Chris@0: * A list of all the currently enabled modules and themes. Chris@0: * @param \Drupal\Core\Config\StorageInterface[] $profile_storages Chris@0: * An array of storage interfaces containing profile configuration to check Chris@0: * for overrides. Chris@0: * Chris@0: * @return array Chris@0: * An array containing: Chris@0: * - A list of configuration that has unmet dependencies. Chris@0: * - An array that will be filled with the missing dependency names, keyed Chris@0: * by the dependents' names. Chris@0: */ Chris@0: protected function findDefaultConfigWithUnmetDependencies(StorageInterface $storage, array $enabled_extensions, array $profile_storages = []) { Chris@0: $missing_dependencies = []; Chris@0: $config_to_create = $this->getConfigToCreate($storage, StorageInterface::DEFAULT_COLLECTION, '', $profile_storages); Chris@0: $all_config = array_merge($this->configFactory->listAll(), array_keys($config_to_create)); Chris@0: foreach ($config_to_create as $config_name => $config) { Chris@0: if ($missing = $this->getMissingDependencies($config_name, $config, $enabled_extensions, $all_config)) { Chris@0: $missing_dependencies[$config_name] = $missing; Chris@0: } Chris@0: } Chris@0: return [ Chris@0: array_intersect_key($config_to_create, $missing_dependencies), Chris@0: $missing_dependencies, Chris@0: ]; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Validates an array of config data that contains dependency information. Chris@0: * Chris@0: * @param string $config_name Chris@0: * The name of the configuration object that is being validated. Chris@0: * @param array $data Chris@0: * Configuration data. Chris@0: * @param array $enabled_extensions Chris@0: * A list of all the currently enabled modules and themes. Chris@0: * @param array $all_config Chris@0: * A list of all the active configuration names. Chris@0: * Chris@0: * @return bool Chris@0: * TRUE if all dependencies are present, FALSE otherwise. Chris@0: */ Chris@0: protected function validateDependencies($config_name, array $data, array $enabled_extensions, array $all_config) { Chris@0: if (!isset($data['dependencies'])) { Chris@0: // Simple config or a config entity without dependencies. Chris@0: list($provider) = explode('.', $config_name, 2); Chris@0: return in_array($provider, $enabled_extensions, TRUE); Chris@0: } Chris@0: Chris@0: $missing = $this->getMissingDependencies($config_name, $data, $enabled_extensions, $all_config); Chris@0: return empty($missing); Chris@0: } Chris@0: Chris@0: /** Chris@0: * Returns an array of missing dependencies for a config object. Chris@0: * Chris@0: * @param string $config_name Chris@0: * The name of the configuration object that is being validated. Chris@0: * @param array $data Chris@0: * Configuration data. Chris@0: * @param array $enabled_extensions Chris@0: * A list of all the currently enabled modules and themes. Chris@0: * @param array $all_config Chris@0: * A list of all the active configuration names. Chris@0: * Chris@0: * @return array Chris@0: * A list of missing config dependencies. Chris@0: */ Chris@0: protected function getMissingDependencies($config_name, array $data, array $enabled_extensions, array $all_config) { Chris@0: $missing = []; Chris@0: if (isset($data['dependencies'])) { Chris@0: list($provider) = explode('.', $config_name, 2); Chris@0: $all_dependencies = $data['dependencies']; Chris@0: Chris@0: // Ensure enforced dependencies are included. Chris@0: if (isset($all_dependencies['enforced'])) { Chris@0: $all_dependencies = array_merge($all_dependencies, $data['dependencies']['enforced']); Chris@0: unset($all_dependencies['enforced']); Chris@0: } Chris@0: // Ensure the configuration entity type provider is in the list of Chris@0: // dependencies. Chris@0: if (!isset($all_dependencies['module']) || !in_array($provider, $all_dependencies['module'])) { Chris@0: $all_dependencies['module'][] = $provider; Chris@0: } Chris@0: Chris@0: foreach ($all_dependencies as $type => $dependencies) { Chris@0: $list_to_check = []; Chris@0: switch ($type) { Chris@0: case 'module': Chris@0: case 'theme': Chris@0: $list_to_check = $enabled_extensions; Chris@0: break; Chris@0: case 'config': Chris@0: $list_to_check = $all_config; Chris@0: break; Chris@0: } Chris@0: if (!empty($list_to_check)) { Chris@0: $missing = array_merge($missing, array_diff($dependencies, $list_to_check)); Chris@0: } Chris@0: } Chris@0: } Chris@0: Chris@0: return $missing; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Gets the list of enabled extensions including both modules and themes. Chris@0: * Chris@0: * @return array Chris@0: * A list of enabled extensions which includes both modules and themes. Chris@0: */ Chris@0: protected function getEnabledExtensions() { Chris@0: // Read enabled extensions directly from configuration to avoid circular Chris@0: // dependencies on ModuleHandler and ThemeHandler. Chris@0: $extension_config = $this->configFactory->get('core.extension'); Chris@0: $enabled_extensions = (array) $extension_config->get('module'); Chris@0: $enabled_extensions += (array) $extension_config->get('theme'); Chris@0: // Core can provide configuration. Chris@0: $enabled_extensions['core'] = 'core'; Chris@0: return array_keys($enabled_extensions); Chris@0: } Chris@0: Chris@0: /** Chris@0: * Gets the profile storage to use to check for profile overrides. Chris@0: * Chris@0: * The install profile can override module configuration during a module Chris@0: * install. Both the install and optional directories are checked for matching Chris@0: * configuration. This allows profiles to override default configuration for Chris@0: * modules they do not depend on. Chris@0: * Chris@0: * @param string $installing_name Chris@0: * (optional) The name of the extension currently being installed. Chris@0: * Chris@0: * @return \Drupal\Core\Config\StorageInterface[]|null Chris@0: * Storages to access configuration from the installation profile. If we're Chris@0: * installing the profile itself, then it will return an empty array as the Chris@0: * profile storage should not be used. Chris@0: */ Chris@0: protected function getProfileStorages($installing_name = '') { Chris@0: $profile = $this->drupalGetProfile(); Chris@0: $profile_storages = []; Chris@0: if ($profile && $profile != $installing_name) { Chris@0: $profile_path = $this->drupalGetPath('module', $profile); Chris@0: foreach ([InstallStorage::CONFIG_INSTALL_DIRECTORY, InstallStorage::CONFIG_OPTIONAL_DIRECTORY] as $directory) { Chris@0: if (is_dir($profile_path . '/' . $directory)) { Chris@0: $profile_storages[] = new FileStorage($profile_path . '/' . $directory, StorageInterface::DEFAULT_COLLECTION); Chris@0: } Chris@0: } Chris@0: } Chris@0: return $profile_storages; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Gets an extension's default configuration directory. Chris@0: * Chris@0: * @param string $type Chris@0: * Type of extension to install. Chris@0: * @param string $name Chris@0: * Name of extension to install. Chris@0: * Chris@0: * @return string Chris@0: * The extension's default configuration directory. Chris@0: */ Chris@0: protected function getDefaultConfigDirectory($type, $name) { Chris@0: return $this->drupalGetPath($type, $name) . '/' . InstallStorage::CONFIG_INSTALL_DIRECTORY; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Wrapper for drupal_get_path(). Chris@0: * Chris@0: * @param $type Chris@0: * The type of the item; one of 'core', 'profile', 'module', 'theme', or Chris@0: * 'theme_engine'. Chris@0: * @param $name Chris@0: * The name of the item for which the path is requested. Ignored for Chris@0: * $type 'core'. Chris@0: * Chris@0: * @return string Chris@0: * The path to the requested item or an empty string if the item is not Chris@0: * found. Chris@0: */ Chris@0: protected function drupalGetPath($type, $name) { Chris@0: return drupal_get_path($type, $name); Chris@0: } Chris@0: Chris@0: /** Chris@0: * Gets the install profile from settings. Chris@0: * Chris@0: * @return string|null Chris@0: * The name of the installation profile or NULL if no installation profile Chris@0: * is currently active. This is the case for example during the first steps Chris@0: * of the installer or during unit tests. Chris@0: */ Chris@0: protected function drupalGetProfile() { Chris@0: return $this->installProfile; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Wrapper for drupal_installation_attempted(). Chris@0: * Chris@0: * @return bool Chris@0: * TRUE if a Drupal installation is currently being attempted. Chris@0: */ Chris@0: protected function drupalInstallationAttempted() { Chris@0: return drupal_installation_attempted(); Chris@0: } Chris@0: Chris@0: }