/**
   * {@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();
 }
Exemple #10
0
 /**
  * @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());
 }