/** * {@inheritdoc} */ public function prepare(array &$packages = array(), FeaturesBundleInterface $bundle = NULL) { // If no packages were specified, get all packages. if (empty($packages)) { $packages = $this->featuresManager->getPackages(); } // If any packages exist, read in their files. $existing_packages = $this->featuresManager->listPackageDirectories(array_keys($packages), $bundle); foreach ($packages as &$package) { list($full_name, $path) = $this->featuresManager->getExportInfo($package, $bundle); $package['directory'] = $path . '/' . $full_name; $this->preparePackage($package, $existing_packages, $bundle); } // Clean up the $package pass by reference. unset($package); if (isset($bundle) && $bundle->isProfile()) { $profile_name = $bundle->getProfileName(); $profile_package = $this->featuresManager->getPackage($profile_name); if (isset($profile_package)) { $package['directory'] = 'profiles/' . $profile_name; $this->preparePackage($profile_package, $existing_packages, $bundle); } } }
/** * {@inheritdoc} */ public function prepare(array &$packages = array(), FeaturesBundleInterface $bundle = NULL) { // If no packages were specified, get all packages. if (empty($packages)) { $packages = $this->featuresManager->getPackages(); } // If any packages exist, read in their files. $existing_packages = $this->featuresManager->listPackageDirectories(array_keys($packages), $bundle); foreach ($packages as &$package) { list($full_name, $path) = $this->featuresManager->getExportInfo($package, $bundle); if (empty($package->getDirectory())) { $package->setDirectory($path); } // If this is the profile, its directory is already assigned. if (!isset($bundle) || !$bundle->isProfilePackage($package->getMachineName())) { $current_path = $package->getDirectory(); if (strpos($current_path, $full_name) < strlen($current_path) - strlen($full_name)) { // Only append package name if it isn't already there. $package->setDirectory($package->getDirectory() . '/' . $full_name); } } $this->preparePackage($package, $existing_packages, $bundle); } // Clean up the $package pass by reference. unset($package); }
/** * {@inheritdoc} */ protected function findPreExistingConfiguration(StorageInterface $storage) { // Override // Drupal\Core\Config\ConfigInstaller::findPreExistingConfiguration(). // Allow config that already exists coming from Features. $features_config = array_keys($this->featuresManager->listExistingConfig()); // Map array so we can use isset instead of in_array for faster access. $features_config = array_combine($features_config, $features_config); $existing_configuration = array(); // Gather information about all the supported collections. $collection_info = $this->configManager->getConfigCollectionInfo(); foreach ($collection_info->getCollectionNames() as $collection) { $config_to_create = array_keys($this->getConfigToCreate($storage, $collection)); $active_storage = $this->getActiveStorages($collection); foreach ($config_to_create as $config_name) { if ($active_storage->exists($config_name)) { // Test if config is part of a Feature package. if (!isset($features_config[$config_name])) { $existing_configuration[$collection][] = $config_name; } } } } return $existing_configuration; }
/** * Adds configuration types checkboxes. */ protected function setConfigTypeSelect(&$form, $defaults, $type, $bundles_only = FALSE) { $options = $this->featuresManager->listConfigTypes($bundles_only); if (!isset($form['types'])) { $form['types'] = array('#type' => 'container', '#tree' => TRUE); } $form['types']['config'] = array('#type' => 'checkboxes', '#title' => $this->t('Configuration types'), '#description' => $this->t('Select types of configuration that should be considered @type types.', array('@type' => $type)), '#options' => $options, '#default_value' => $defaults); }
/** * Adds configuration types checkboxes. */ protected function setTypeSelect(&$form, $defaults, $type) { $options = $this->featuresManager->listConfigTypes(); $form['types'] = array( '#type' => 'checkboxes', '#title' => $this->t('Types'), '#description' => $this->t('Select types of configuration that should be considered !type types.', array('!type' => $type)), '#options' => $options, '#default_value' => $defaults, ); }
public function testExportWrite() { $package = $this->featuresManager->getPackage(self::PACKAGE_NAME); // Find out where package will be exported list($full_name, $path) = $this->featuresManager->getExportInfo($package, $this->assigner->getBundle()); $path = $path . '/' . $full_name; if (file_exists($path)) { file_unmanaged_delete_recursive($path); } $this->assertFalse(file_exists($path), 'Package directory already exists.'); $this->generator->generatePackages('write', [self::PACKAGE_NAME], $this->assigner->getBundle()); $this->assertTrue(file_exists($path), 'Package directory was not generated.'); $this->assertTrue(file_exists($path . '/' . self::PACKAGE_NAME . '.info.yml'), 'Package info.yml not generated.'); $this->assertTrue(file_exists($path . '/config/install'), 'Package config/install not generated.'); $this->assertTrue(file_exists($path . '/config/install/system.site.yml'), 'Config.yml not exported.'); }
/** * {@inheritdoc} */ public function createBundlesFromPackages() { $existing_bundles = $this->getBundleList(); $new_bundles = []; // Only parse from installed features. $modules = $this->featuresManager->getFeaturesModules(NULL, TRUE); foreach ($modules as $module) { $info = $this->featuresManager->getExtensionInfo($module); // @todo This entire function could be simplified a lot using packages. $features_info = $this->featuresManager->getFeaturesInfo($module); // Create a new bundle if: // - the feature specifies a bundle and // - that bundle doesn't yet exist locally. // Allow profiles to override previous values. if (!empty($features_info['bundle']) && !isset($existing_bundles[$features_info['bundle']]) && (!in_array($features_info['bundle'], $new_bundles) || $info['type'] == 'profile')) { if ($info['type'] == 'profile') { $new_bundle = ['name' => $info['name'], 'description' => $info['description'], 'is_profile' => TRUE, 'profile_name' => $module->getName()]; } else { $new_bundle = ['name' => isset($info['package']) ? $info['package'] : ucwords(str_replace('_', ' ', $features_info['bundle'])), 'description' => NULL, 'is_profile' => FALSE, 'profile_name' => NULL]; } $new_bundle['machine_name'] = $features_info['bundle']; $new_bundles[$new_bundle['machine_name']] = $new_bundle; } } foreach ($new_bundles as $new_bundle) { $new_bundle = $this->createBundleFromDefault($new_bundle['machine_name'], $new_bundle['name'], $new_bundle['description'], $new_bundle['is_profile']); drupal_set_message($this->t('Features bundle @name automatically created.', ['@name' => $new_bundle->getName()])); } }
/** * Returns the configuration dependent on given items. * * @param array $item_names * An array of item names. * * @return array * An array of config items. */ protected function getConfigDependents(array $item_names = NULL) { $result = []; $config_collection = $this->featuresManager->getConfigCollection(); $packages = $this->featuresManager->getPackages(); $settings = $this->featuresManager->getSettings(); $allow_conflicts = $settings->get('conflicts'); if (empty($item_names)) { $item_names = array_keys($config_collection); } foreach ($item_names as $item_name) { if ($config_collection[$item_name]->getPackage()) { foreach ($config_collection[$item_name]->getDependents() as $dependent_item_name) { if (isset($config_collection[$dependent_item_name])) { $allow = TRUE; if (!$allow_conflicts && $config_collection[$dependent_item_name]->getPackage()) { if ($packages[$config_collection[$dependent_item_name]->getPackage()]) { $allow = $packages[$config_collection[$dependent_item_name]->getPackage()]->getStatus() == FeaturesManagerInterface::STATUS_NO_EXPORT || $config_collection[$item_name]->getPackage() == $config_collection[$dependent_item_name]->getPackage(); } } if ($allow) { $result[] = $dependent_item_name; } } } } } return $result; }
/** * {@inheritdoc} */ public function purgeConfiguration() { // Ensure that we are getting the defined package assignment information. // An invocation of \Drupal\Core\Extension\ModuleHandler::install() or // \Drupal\Core\Extension\ModuleHandler::uninstall() could invalidate the // cached information. $this->assignerManager->clearCachedDefinitions(); $this->featuresManager->reset(); }
/** * @covers ::getExportInfo */ public function testGetExportInfoWithBundle() { $config_factory = $this->getConfigFactoryStub(['features.settings' => ['export' => ['folder' => 'custom']]]); $this->featuresManager = new FeaturesManager($this->root, $this->entityManager, $config_factory, $this->configStorage, $this->configManager, $this->moduleHandler); $package = new Package('test_feature'); $bundle = new FeaturesBundle(['machine_name' => 'test_bundle'], 'features_bundle'); $result = $this->featuresManager->getExportInfo($package, $bundle); $this->assertEquals(['test_bundle_test_feature', 'modules/custom'], $result); }
/** * @covers \Drupal\features\Plugin\FeaturesGeneration\FeaturesGenerationWrite */ public function testExportWrite() { // Set a fake drupal root, so the testbot can also write into it. vfsStream::setup('drupal'); \Drupal::getContainer()->set('app.root', 'vfs://drupal'); $package = $this->featuresManager->getPackage(self::PACKAGE_NAME); // Find out where package will be exported list($full_name, $path) = $this->featuresManager->getExportInfo($package, $this->assigner->getBundle()); $path = 'vfs://drupal/' . $path . '/' . $full_name; if (file_exists($path)) { file_unmanaged_delete_recursive($path); } $this->assertFalse(file_exists($path), 'Package directory already exists.'); $this->generator->generatePackages('write', $this->assigner->getBundle(), [self::PACKAGE_NAME]); $this->assertTrue(file_exists($path), 'Package directory was not generated.'); $this->assertTrue(file_exists($path . '/' . self::PACKAGE_NAME . '.info.yml'), 'Package info.yml not generated.'); $this->assertTrue(file_exists($path . '/config/install'), 'Package config/install not generated.'); $this->assertTrue(file_exists($path . '/config/install/system.site.yml'), 'Config.yml not exported.'); }
/** * {@inheritdoc} */ public function prepare(array &$packages = array(), FeaturesBundleInterface $bundle = NULL) { // If no packages were specified, get all packages. if (empty($packages)) { $packages = $this->featuresManager->getPackages(); } // If any packages exist, read in their files. $existing_packages = $this->featuresManager->listPackageDirectories(array_keys($packages), $bundle); foreach ($packages as &$package) { list($full_name, $path) = $this->featuresManager->getExportInfo($package, $bundle); $package['directory'] = $path; // If this is the profile, its directory is already assigned. if (!isset($bundle) || !$bundle->isProfilePackage($package['machine_name'])) { $package['directory'] .= '/' . $full_name; } $this->preparePackage($package, $existing_packages, $bundle); } // Clean up the $package pass by reference. unset($package); }
/** * @covers ::reset */ public function testReset() { $packages = ['package' => ['machine_name' => 'package', 'config' => ['example.config', 'example.config3'], 'dependencies' => [], 'bundle' => 'giraffe'], 'package2' => ['machine_name' => 'package2', 'config' => ['example.config2'], 'dependencies' => [], 'bundle' => 'giraffe']]; $this->featuresManager->setPackages($packages); $config_item = new ConfigurationItem('example', [], ['package' => 'package']); $config_item2 = new ConfigurationItem('example2', [], ['package' => 'package2']); $this->featuresManager->setConfigCollection([$config_item, $config_item2]); $this->featuresManager->reset(); $this->assertEmpty($this->featuresManager->getPackages()); $config_collection = $this->featuresManager->getConfigCollection(); $this->assertEquals('', $config_collection[0]->getPackage()); $this->assertEquals('', $config_collection[1]->getPackage()); }
/** * Assigns a given subdirectory to configuration of specified types. * * @param string $subdirectory * The subdirectory that designated configuration should be exported to. */ protected function assignSubdirectoryByConfigTypes($subdirectory) { $current_bundle = $this->assigner->getBundle(); $settings = $current_bundle->getAssignmentSettings($this->getPluginId()); $types = $settings['types']['config']; $config_collection = $this->featuresManager->getConfigCollection(); foreach ($config_collection as &$item) { if (in_array($item->getType(), $types)) { $item->setSubdirectory($subdirectory); } } // Clean up the $item pass by reference. unset($item); $this->featuresManager->setConfigCollection($config_collection); }
/** * Generates a file representation of configuration packages and, optionally, * an install profile. * * @param string $method_id * The ID of the generation method to use. * @param string[] $package_names * Names of packages to be generated. If none are specified, all * available packages will be added. * @param \Drupal\features\FeaturesBundleInterface $bundle * The optional bundle used for the generation. Used to generate profiles. * * @return array * Array of results for profile and/or packages, each result including the * following keys: * - 'success': boolean TRUE or FALSE for successful writing. * - 'display': boolean TRUE if the message should be displayed to the * user, otherwise FALSE. * - 'message': a message about the result of the operation. * - 'variables': an array of substitutions to be used in the message. */ protected function generate($method_id, array $package_names = array(), FeaturesBundleInterface $bundle = NULL) { // Prepare the files. $this->featuresManager->prepareFiles(); $packages = $this->featuresManager->getPackages(); // Filter out the packages that weren't requested. if (!empty($package_names)) { $packages = array_intersect_key($packages, array_fill_keys($package_names, NULL)); } $return = $this->applyGenerationMethod($method_id, $packages, $bundle); foreach ($return as $message) { if ($message['display']) { $type = $message['success'] ? 'status' : 'error'; drupal_set_message($this->t($message['message'], $message['variables']), $type); } $type = $message['success'] ? 'notice' : 'error'; \Drupal::logger('features')->{$type}($message['message'], $message['variables']); } return $return; }
/** * Returns the configuration dependent on given items. * * @param array $item_names * An array of item names. * @param string $package_name * Short machine name of feature to process. * * @return array * An array of config items. */ protected function getConfigDependents(array $item_names, $package_name) { $result = []; $config_collection = $this->featuresManager->getConfigCollection(); $packages = $this->featuresManager->getPackages(); $settings = $this->featuresManager->getSettings(); $allow_conflicts = $settings->get('conflicts'); if (empty($item_names)) { $item_names = array_keys($config_collection); } // Add any existing auto-detected items already in the package config $this->package = $packages[$package_name]; $package_config = isset($this->package) ? $this->package->getConfig() : array(); $package_config = !empty($package_config) ? array_unique(array_merge($package_config, $item_names)) : $item_names; foreach ($package_config as $config_name) { if (!$config_collection[$config_name]->getPackageExcluded()) { $result[] = $config_name; } } // Now add dependents of the items selected foreach ($item_names as $item_name) { if ($config_collection[$item_name]->getPackage()) { foreach ($config_collection[$item_name]->getDependents() as $dependent_item_name) { if (isset($config_collection[$dependent_item_name])) { $allow = TRUE; if (!$allow_conflicts && $config_collection[$dependent_item_name]->getPackage()) { if ($packages[$config_collection[$dependent_item_name]->getPackage()]) { $allow = $packages[$config_collection[$dependent_item_name]->getPackage()]->getStatus() == FeaturesManagerInterface::STATUS_NO_EXPORT || $config_collection[$item_name]->getPackage() == $config_collection[$dependent_item_name]->getPackage(); } } if ($allow) { $result[] = $dependent_item_name; } } } } } return $result; }
/** * @covers \Drupal\features\Plugin\FeaturesGeneration\FeaturesGenerationWrite */ public function testExportWrite() { // Set a fake drupal root, so the testbot can also write into it. vfsStream::setup('drupal'); \Drupal::getContainer()->set('app.root', 'vfs://drupal'); $this->featuresManager->setRoot('vfs://drupal'); $package = $this->featuresManager->getPackage(self::PACKAGE_NAME); // Find out where package will be exported list($full_name, $path) = $this->featuresManager->getExportInfo($package, $this->assigner->getBundle()); $path = 'vfs://drupal/' . $path . '/' . $full_name; if (file_exists($path)) { file_unmanaged_delete_recursive($path); } $this->assertFalse(file_exists($path), 'Package directory already exists.'); $this->generator->generatePackages('write', $this->assigner->getBundle(), [self::PACKAGE_NAME]); $info_file_uri = $path . '/' . self::PACKAGE_NAME . '.info.yml'; $this->assertTrue(file_exists($path), 'Package directory was not generated.'); $this->assertTrue(file_exists($info_file_uri), 'Package info.yml not generated.'); $this->assertTrue(file_exists($path . '/config/install'), 'Package config/install not generated.'); $this->assertTrue(file_exists($path . '/config/install/system.site.yml'), 'Config.yml not exported.'); $expected_info = ["name" => "My test package", "type" => "module", "core" => "8.x"]; $info = Yaml::decode(file_get_contents($info_file_uri)); $this->assertEquals($expected_info, $info, 'Incorrect info file generated'); // Now, add stuff to the feature and re-export to ensure it is preserved // Add a dependency to the package itself to see that it gets exported. $package->setDependencies(['user']); $this->featuresManager->setPackage($package); // Add dependency and custom key to the info file to simulate manual edit. $info['dependencies'] = ['node']; $info['mykey'] = "test value"; $info_contents = Yaml::encode($info); file_put_contents($info_file_uri, $info_contents); // Add an extra file that should be retained. $css_file = $path . '/' . self::PACKAGE_NAME . '.css'; $file_contents = "This is a dummy file"; file_put_contents($css_file, $file_contents); // Add a config file that should be removed since it's not part of the // feature. $config_file = $path . '/config/install/node.type.mytype.yml'; file_put_contents($config_file, $file_contents); $this->generator->generatePackages('write', $this->assigner->getBundle(), [self::PACKAGE_NAME]); $this->assertTrue(file_exists($info_file_uri), 'Package info.yml not generated.'); $expected_info = ["name" => "My test package", "type" => "module", "core" => "8.x", "dependencies" => ["node", "user"], "mykey" => "test value"]; $info = Yaml::decode(file_get_contents($info_file_uri)); $this->assertEquals($expected_info, $info, 'Incorrect info file generated'); $this->assertTrue(file_exists($css_file), 'Extra file was not retained.'); $this->assertFalse(file_exists($config_file), 'Config directory was not cleaned.'); $this->assertEquals($file_contents, file_get_contents($css_file), 'Extra file contents not retained'); // Next, test that generating an Archive picks up the extra files. $filename = file_directory_temp() . '/' . self::PACKAGE_NAME . '.tar.gz'; if (file_exists($filename)) { unlink($filename); } $this->assertFalse(file_exists($filename), 'Archive file already exists.'); $this->generator->generatePackages('archive', $this->assigner->getBundle(), [self::PACKAGE_NAME]); $this->assertTrue(file_exists($filename), 'Archive file was not generated.'); $archive = new ArchiveTar($filename); $files = $archive->listContent(); $this->assertEquals(4, count($files)); $this->assertEquals(self::PACKAGE_NAME . '/' . self::PACKAGE_NAME . '.info.yml', $files[0]['filename']); $this->assertEquals(self::PACKAGE_NAME . '/' . self::PACKAGE_NAME . '.features.yml', $files[1]['filename']); $this->assertEquals(self::PACKAGE_NAME . '/config/install/system.site.yml', $files[2]['filename']); $this->assertEquals(self::PACKAGE_NAME . '/' . self::PACKAGE_NAME . '.css', $files[3]['filename']); $expected_info = ["name" => "My test package", "type" => "module", "core" => "8.x", "dependencies" => ["node", "user"], "mykey" => "test value"]; $info = Yaml::decode($archive->extractInString(self::PACKAGE_NAME . '/' . self::PACKAGE_NAME . '.info.yml')); $this->assertEquals($expected_info, $info, 'Incorrect info file generated'); $this->assertEquals($file_contents, $archive->extractInString(self::PACKAGE_NAME . '/' . self::PACKAGE_NAME . '.css'), 'Extra file contents not retained'); }
/** * @covers ::setConfigCollection * @covers ::getConfigCollection */ public function testConfigCollection() { $config = ['config' => 'collection']; $this->featuresManager->setConfigCollection($config); $this->assertArrayEquals($config, $this->featuresManager->getConfigCollection()); }