/** * {@inheritdoc} */ public function submitForm(array &$form, FormStateInterface $form_state) { global $config_directories, $install_state; $sync_directory = $form_state->getValue('sync_directory'); if ($sync_directory != config_get_config_directory(CONFIG_SYNC_DIRECTORY)) { $settings['config_directories'][CONFIG_SYNC_DIRECTORY] = (object) array('value' => $sync_directory, 'required' => TRUE); drupal_rewrite_settings($settings); $config_directories[CONFIG_SYNC_DIRECTORY] = $sync_directory; } if ($path = $form_state->getValue('import_tarball')) { // Ensure that we have an empty directory if we're going. $sync = new FileStorage($sync_directory); $sync->deleteAll(); try { $archiver = new ArchiveTar($path, 'gz'); $files = array(); foreach ($archiver->listContent() as $file) { $files[] = $file['filename']; } $archiver->extractList($files, config_get_config_directory(CONFIG_SYNC_DIRECTORY)); drupal_set_message($this->t('Your configuration files were successfully uploaded, ready for import.')); } catch (\Exception $e) { drupal_set_message($this->t('Could not extract the contents of the tar file. The error message is <em>@message</em>', array('@message' => $e->getMessage())), 'error'); } drupal_unlink($path); } // Change the langcode to the site default langcode provided by the // configuration. $config_storage = new FileStorage(config_get_config_directory(CONFIG_SYNC_DIRECTORY)); $install_state['parameters']['langcode'] = $config_storage->read('system.site')['langcode']; }
/** * {@inheritdoc} */ protected function execute(InputInterface $input, OutputInterface $output) { $messageHelper = $this->getHelperSet()->get('message'); $directory = $input->getArgument('directory'); if (!$directory) { $config = $this->getConfigFactory()->get('system.file'); $directory = $config->get('path.temporary') ?: file_directory_temp(); $directory .= '/' . CONFIG_STAGING_DIRECTORY; } if (!is_dir($directory)) { mkdir($directory, 0777, true); } $config_export_file = $directory . '/config.tar.gz'; file_unmanaged_delete($config_export_file); try { $archiver = new ArchiveTar($config_export_file, 'gz'); $this->configManager = $this->getConfigManager(); // Get raw configuration data without overrides. foreach ($this->configManager->getConfigFactory()->listAll() as $name) { $archiver->addString("{$name}.yml", Yaml::encode($this->configManager->getConfigFactory()->get($name)->getRawData())); } $this->targetStorage = $this->getConfigStorage(); // Get all override data from the remaining collections. foreach ($this->targetStorage->getAllCollectionNames() as $collection) { $collection_storage = $this->targetStorage->createCollection($collection); foreach ($collection_storage->listAll() as $name) { $archiver->addString(str_replace('.', '/', $collection) . "/{$name}.yml", Yaml::encode($collection_storage->read($name))); } } } catch (\Exception $e) { $output->writeln('[+] <error>' . $e->getMessage() . '</error>'); return; } $messageHelper->addSuccessMessage(sprintf($this->trans('commands.config.export.messages.directory'), $config_export_file)); }
/** * {@inheritdoc} */ public function listContents() { $files = array(); foreach ($this->tar->listContent() as $file_data) { $files[] = $file_data['filename']; } return $files; }
/** * Writes a file to the file system, creating its directory as needed. * * @param string $directory * The extension's directory. * @param array $file * Array with the following keys: * - 'filename': the name of the file. * - 'subdirectory': any subdirectory of the file within the extension * directory. * - 'string': the contents of the file. * @param ArchiveTar $archiver * The archiver. * * @throws Exception */ protected function generateFile($directory, array $file, ArchiveTar $archiver) { $filename = $directory; if (!empty($file['subdirectory'])) { $filename .= '/' . $file['subdirectory']; } $filename .= '/' . $file['filename']; // Set the mode to 0644 rather than the default of 0600. if ($archiver->addString($filename, $file['string'], FALSE, ['mode' => 0644]) === FALSE) { throw new \Exception($this->t('Failed to archive file @filename.', ['@filename' => $file['filename']])); } }
/** * Extracts the contents of the archive file into the config directory. * * @param DrupalStyle $io * IO object to print messages. * @param string $archiveFile * The archive file to extract * @param string $configDir * The directory to extract the files into. * * @return \Drupal\Core\Archiver\ArchiveTar * The initialised object. * * @throws \Exception * If something went wrong during extraction. */ private function extractArchive(DrupalStyle $io, $archiveFile, $configDir) { $archiveTar = new ArchiveTar($archiveFile, 'gz'); $io->simple($this->trans('commands.config.import.messages.config_files_imported')); foreach ($archiveTar->listContent() as $file) { $io->info('[-] ' . $file['filename']); } try { $archiveTar->extract($configDir . '/'); } catch (\Exception $e) { $io->error($e->getMessage()); return; } }
/** * {@inheritdoc} */ protected function execute(InputInterface $input, OutputInterface $output) { $io = new DrupalStyle($input, $output); $directory = $input->getOption('directory'); $tar = $input->getOption('tar'); $removeUuid = $input->getOption('remove-uuid'); if (!$directory) { $directory = config_get_config_directory(CONFIG_SYNC_DIRECTORY); } if ($tar) { if (!is_dir($directory)) { mkdir($directory, 0777, true); } $dateTime = new \DateTime(); $archiveFile = sprintf('%s/config-%s.tar.gz', $directory, $dateTime->format('Y-m-d-H-i-s')); $archiveTar = new ArchiveTar($archiveFile, 'gz'); } try { // Get raw configuration data without overrides. foreach ($this->configManager->getConfigFactory()->listAll() as $name) { $configData = $this->configManager->getConfigFactory()->get($name)->getRawData(); $configName = sprintf('%s.yml', $name); // The _core is site-specific, so don't export it. unset($configData['_core']); if ($removeUuid) { unset($configData['uuid']); } $ymlData = Yaml::encode($configData); if ($tar) { $archiveTar->addString($configName, $ymlData); continue; } $configFileName = sprintf('%s/%s', $directory, $configName); $fileSystem = new Filesystem(); try { $fileSystem->mkdir($directory); } catch (IOExceptionInterface $e) { $io->error(sprintf($this->trans('commands.config.export.messages.error'), $e->getPath())); } file_put_contents($configFileName, $ymlData); } } catch (\Exception $e) { $io->error($e->getMessage()); } $io->info(sprintf($this->trans('commands.config.export.messages.directory'), $directory)); }
/** * Downloads a tarball of the site configuration. */ public function downloadExport() { file_unmanaged_delete(file_directory_temp() . '/config.tar.gz'); $archiver = new ArchiveTar(file_directory_temp() . '/config.tar.gz', 'gz'); // Get raw configuration data without overrides. foreach ($this->configManager->getConfigFactory()->listAll() as $name) { $archiver->addString("{$name}.yml", Yaml::encode($this->configManager->getConfigFactory()->get($name)->getRawData())); } // Get all override data from the remaining collections. foreach ($this->targetStorage->getAllCollectionNames() as $collection) { $collection_storage = $this->targetStorage->createCollection($collection); foreach ($collection_storage->listAll() as $name) { $archiver->addString(str_replace('.', '/', $collection) . "/{$name}.yml", Yaml::encode($collection_storage->read($name))); } } $request = new Request(array('file' => 'config.tar.gz')); return $this->fileDownloadController->download($request, 'temporary'); }
/** * {@inheritdoc} */ public function submitForm(array &$form, FormStateInterface $form_state) { if ($path = $form_state->getValue('import_tarball')) { $this->configStorage->deleteAll(); try { $archiver = new ArchiveTar($path, 'gz'); $files = array(); foreach ($archiver->listContent() as $file) { $files[] = $file['filename']; } $archiver->extractList($files, config_get_config_directory(CONFIG_STAGING_DIRECTORY)); drupal_set_message($this->t('Your configuration files were successfully uploaded, ready for import.')); $form_state->setRedirect('config.sync'); } catch (\Exception $e) { drupal_set_message($this->t('Could not extract the contents of the tar file. The error message is <em>@message</em>', array('@message' => $e->getMessage())), 'error'); } drupal_unlink($path); } }
/** * {@inheritdoc} */ protected function setUpSyncForm() { // Create a new sync directory. drupal_mkdir($this->sync_dir); // Extract the tarball into the sync directory. $archiver = new ArchiveTar($this->tarball, 'gz'); $files = array(); foreach ($archiver->listContent() as $file) { $files[] = $file['filename']; } $archiver->extractList($files, $this->sync_dir); // Change the user.settings::register so that we can test that // standard_install() does not override it. $sync = new FileStorage($this->sync_dir); $user_settings = $sync->read('user.settings'); $user_settings['register'] = USER_REGISTER_ADMINISTRATORS_ONLY; $sync->write('user.settings', $user_settings); $this->drupalPostForm(NULL, array('sync_directory' => drupal_realpath($this->sync_dir)), 'Save and continue'); }
/** * {@inheritdoc} */ protected function execute(InputInterface $input, OutputInterface $output) { $config_file = $input->getArgument('config-file'); $copy_only = $input->getOption('copy-only'); try { $files = array(); $archiver = new ArchiveTar($config_file, 'gz'); $output->writeln($this->trans('commands.config.import.messages.config_files_imported')); foreach ($archiver->listContent() as $file) { $pathinfo = pathinfo($file['filename']); $files[$pathinfo['filename']] = $file['filename']; $output->writeln('[-] <info>' . $file['filename'] . '</info>'); } $config_staging_dir = config_get_config_directory(CONFIG_SYNC_DIRECTORY); try { $archiver->extract($config_staging_dir . '/'); } catch (\Exception $e) { $output->writeln('[+] <error>' . $e->getMessage() . '</error>'); return; } if ($copy_only) { $output->writeln(sprintf($this->trans('commands.config.import.messages.copied'), CONFIG_SYNC_DIRECTORY)); } else { foreach ($files as $cofig_name => $filename) { $config = $this->getConfigFactory()->getEditable($cofig_name); $parser = new Parser(); $config_value = $parser->parse(file_get_contents($config_staging_dir . '/' . $filename)); $config->setData($config_value); try { $config->save(); } catch (\Exception $e) { $output->writeln('[+] <error>' . $e->getMessage() . '</error>'); return; } } $output->writeln(sprintf($this->trans('commands.config.import.messages.imported'), CONFIG_SYNC_DIRECTORY)); } } catch (\Exception $e) { $output->writeln('[+] <error>' . $e->getMessage() . '</error>'); return; } }
/** * {@inheritdoc} */ protected function execute(InputInterface $input, OutputInterface $output) { $io = new DrupalStyle($input, $output); $configFile = $input->getOption('file'); $removeFiles = $input->getOption('remove-files'); $configSyncDir = config_get_config_directory(CONFIG_SYNC_DIRECTORY); if ($configFile) { $archiveTar = new ArchiveTar($configFile, 'gz'); $io->simple($this->trans('commands.config.import.messages.config_files_imported')); foreach ($archiveTar->listContent() as $file) { $io->info('[-] ' . $file['filename']); } try { $archiveTar->extract($configSyncDir . '/'); } catch (\Exception $e) { $io->error($e->getMessage()); return; } } $finder = new Finder(); $finder->in($configSyncDir); $finder->name("*.yml"); foreach ($finder as $configFile) { $configName = $configFile->getBasename('.yml'); $configFilePath = sprintf('%s/%s', $configSyncDir, $configFile->getBasename()); $config = $this->getConfigFactory()->getEditable($configName); $parser = new Parser(); $configData = $parser->parse(file_get_contents($configFilePath)); $config->setData($configData); if ($removeFiles) { file_unmanaged_delete($configFilePath); } try { $config->save(); } catch (\Exception $e) { $io->error($e->getMessage()); return; } } $io->success(sprintf($this->trans('commands.config.import.messages.imported'), CONFIG_SYNC_DIRECTORY)); }
/** * {@inheritdoc} */ protected function execute(InputInterface $input, OutputInterface $output) { $messageHelper = $this->getMessageHelper(); $directory = $input->getOption('directory'); $tar = $input->getOption('tar'); $archiveTar = new ArchiveTar(); if (!$directory) { $directory = config_get_config_directory(CONFIG_SYNC_DIRECTORY); } if ($tar) { if (!is_dir($directory)) { mkdir($directory, 0777, true); } $dateTime = new \DateTime(); $archiveFile = sprintf('%s/config-%s.tar.gz', $directory, $dateTime->format('Y-m-d-H-i-s')); $archiveTar = new ArchiveTar($archiveFile, 'gz'); } try { $configManager = $this->getConfigManager(); // Get raw configuration data without overrides. foreach ($configManager->getConfigFactory()->listAll() as $name) { $configData = $configManager->getConfigFactory()->get($name)->getRawData(); $configName = sprintf('%s.yml', $name); $ymlData = Yaml::encode($configData); if ($tar) { $archiveTar->addString($configName, $ymlData); continue; } $configFileName = sprintf('%s/%s', $directory, $configName); file_put_contents($configFileName, $ymlData); } } catch (\Exception $e) { $output->writeln('[+] <error>' . $e->getMessage() . '</error>'); return; } $messageHelper->addSuccessMessage(sprintf($this->trans('commands.config.export.messages.directory'))); $messageHelper->addSuccessMessage($directory); }
/** * @param $migration */ protected function archiveSite(&$migration) { $exclude = $this->exclude($migration); $files = $this->filesToBackup(DRUPAL_ROOT, $exclude); if (!empty($files) && isset($migration['file'])) { $this->validateArchiveFiles($migration, $files); if ($migration['error'] != FALSE) { return; } $dest_file = $migration['file'] . '.tar'; if (!empty($migration['compression_ext'])) { $dest_file .= '.' . $migration['compression_ext']; } $gz = new Archiver\ArchiveTar($dest_file, $migration['compression_ext'] ? $migration['compression_ext'] : NULL); if (!empty($migration['db_file'])) { // Add db file. $ret = $gz->addModify(array($migration['db_file']), '', $migration['dir'] . DIRECTORY_SEPARATOR); } // Remove Drupal root from the file paths, OS dependent. if (defined('OS_WINDOWS')) { $remove_dir = DRUPAL_ROOT . '\\'; } else { $remove_dir = DRUPAL_ROOT . '/'; } $ret = $gz->addModify($files, '', $remove_dir); $migration['tar_file'] = $dest_file; } else { $migration['error'] = TRUE; } }
/** * Tests an export and import of collections. */ public function testExportImportCollections() { /** @var \Drupal\Core\Config\StorageInterface $active_storage */ $active_storage = \Drupal::service('config.storage'); $test1_storage = $active_storage->createCollection('collection.test1'); $test1_storage->write('config_test.create', array('foo' => 'bar')); $test1_storage->write('config_test.update', array('foo' => 'bar')); $test2_storage = $active_storage->createCollection('collection.test2'); $test2_storage->write('config_test.another_create', array('foo' => 'bar')); $test2_storage->write('config_test.another_update', array('foo' => 'bar')); // Export the configuration. $this->drupalPostForm('admin/config/development/configuration/full/export', array(), 'Export'); $this->tarball = $this->drupalGetContent(); $filename = file_directory_temp() . '/' . $this->randomName(); file_put_contents($filename, $this->tarball); // Set up the active storage collections to test import. $test1_storage->delete('config_test.create'); $test1_storage->write('config_test.update', array('foo' => 'baz')); $test1_storage->write('config_test.delete', array('foo' => 'bar')); $test2_storage->delete('config_test.another_create'); $test2_storage->write('config_test.another_update', array('foo' => 'baz')); $test2_storage->write('config_test.another_delete', array('foo' => 'bar')); // Create a snapshot. $snapshot_storage = \Drupal::service('config.storage.snapshot'); \Drupal::service('config.manager')->createSnapshot($active_storage, $snapshot_storage); // Ensure that the snapshot has the expected collection data before import. $test1_snapshot = $snapshot_storage->createCollection('collection.test1'); $data = $test1_snapshot->read('config_test.delete'); $this->assertEqual($data, array('foo' => 'bar'), 'The config_test.delete in collection.test1 exists in the snapshot storage.'); $data = $test1_snapshot->read('config_test.update'); $this->assertEqual($data, array('foo' => 'baz'), 'The config_test.update in collection.test1 exists in the snapshot storage.'); $this->assertFalse($test1_snapshot->read('config_test.create'), 'The config_test.create in collection.test1 does not exist in the snapshot storage.'); $test2_snapshot = $snapshot_storage->createCollection('collection.test2'); $data = $test2_snapshot->read('config_test.another_delete'); $this->assertEqual($data, array('foo' => 'bar'), 'The config_test.another_delete in collection.test2 exists in the snapshot storage.'); $data = $test2_snapshot->read('config_test.another_update'); $this->assertEqual($data, array('foo' => 'baz'), 'The config_test.another_update in collection.test2 exists in the snapshot storage.'); $this->assertFalse($test2_snapshot->read('config_test.another_create'), 'The config_test.another_create in collection.test2 does not exist in the snapshot storage.'); // Create the tar contains the expected contect for the collections. $tar = new ArchiveTar($filename, 'gz'); $content_list = $tar->listContent(); // Convert the list of files into something easy to search. $files = array(); foreach ($content_list as $file) { $files[] = $file['filename']; } $this->assertTrue(in_array('collection/test1/config_test.create.yml', $files), 'Config export contains collection/test1/config_test.create.yml.'); $this->assertTrue(in_array('collection/test2/config_test.another_create.yml', $files), 'Config export contains collection/test2/config_test.another_create.yml.'); $this->assertTrue(in_array('collection/test1/config_test.update.yml', $files), 'Config export contains collection/test1/config_test.update.yml.'); $this->assertTrue(in_array('collection/test2/config_test.another_update.yml', $files), 'Config export contains collection/test2/config_test.another_update.yml.'); $this->assertFalse(in_array('collection/test1/config_test.delete.yml', $files), 'Config export does not contain collection/test1/config_test.delete.yml.'); $this->assertFalse(in_array('collection/test2/config_test.another_delete.yml', $files), 'Config export does not contain collection/test2/config_test.another_delete.yml.'); $this->drupalPostForm('admin/config/development/configuration/full/import', array('files[import_tarball]' => $filename), 'Upload'); // Verify that there are configuration differences to import. $this->drupalGet('admin/config/development/configuration'); $this->assertNoText(t('There are no configuration changes.')); $this->assertText(t('!collection configuration collection', array('!collection' => 'collection.test1'))); $this->assertText(t('!collection configuration collection', array('!collection' => 'collection.test2'))); $this->assertText('config_test.create'); $this->assertLinkByHref('admin/config/development/configuration/sync/diff_collection/collection.test1/config_test.create'); $this->assertText('config_test.update'); $this->assertLinkByHref('admin/config/development/configuration/sync/diff_collection/collection.test1/config_test.update'); $this->assertText('config_test.delete'); $this->assertLinkByHref('admin/config/development/configuration/sync/diff_collection/collection.test1/config_test.delete'); $this->assertText('config_test.another_create'); $this->assertLinkByHref('admin/config/development/configuration/sync/diff_collection/collection.test2/config_test.another_create'); $this->assertText('config_test.another_update'); $this->assertLinkByHref('admin/config/development/configuration/sync/diff_collection/collection.test2/config_test.another_update'); $this->assertText('config_test.another_delete'); $this->assertLinkByHref('admin/config/development/configuration/sync/diff_collection/collection.test2/config_test.another_delete'); $this->drupalPostForm(NULL, array(), 'Import all'); $this->assertText(t('There are no configuration changes.')); // Test data in collections. $data = $test1_storage->read('config_test.create'); $this->assertEqual($data, array('foo' => 'bar'), 'The config_test.create in collection.test1 has been created.'); $data = $test1_storage->read('config_test.update'); $this->assertEqual($data, array('foo' => 'bar'), 'The config_test.update in collection.test1 has been updated.'); $this->assertFalse($test1_storage->read('config_test.delete'), 'The config_test.delete in collection.test1 has been deleted.'); $data = $test2_storage->read('config_test.another_create'); $this->assertEqual($data, array('foo' => 'bar'), 'The config_test.another_create in collection.test2 has been created.'); $data = $test2_storage->read('config_test.another_update'); $this->assertEqual($data, array('foo' => 'bar'), 'The config_test.another_update in collection.test2 has been updated.'); $this->assertFalse($test2_storage->read('config_test.another_delete'), 'The config_test.another_delete in collection.test2 has been deleted.'); // Ensure that the snapshot has been updated with the collection data. $snapshot_storage = \Drupal::service('config.storage.snapshot'); $test1_snapshot = $snapshot_storage->createCollection('collection.test1'); $data = $test1_snapshot->read('config_test.create'); $this->assertEqual($data, array('foo' => 'bar'), 'The config_test.create in collection.test1 has been created in the snapshot storage.'); $data = $test1_snapshot->read('config_test.update'); $this->assertEqual($data, array('foo' => 'bar'), 'The config_test.update in collection.test1 has been updated in the snapshot storage.'); $this->assertFalse($test1_snapshot->read('config_test.delete'), 'The config_test.delete in collection.test1 does not exist in the snapshot storage.'); $test2_snapshot = $snapshot_storage->createCollection('collection.test2'); $data = $test2_snapshot->read('config_test.another_create'); $this->assertEqual($data, array('foo' => 'bar'), 'The config_test.another_create in collection.test2 has been created in the snapshot storage.'); $data = $test2_snapshot->read('config_test.another_update'); $this->assertEqual($data, array('foo' => 'bar'), 'The config_test.another_update in collection.test2 has been updated in the snapshot storage.'); $this->assertFalse($test2_snapshot->read('config_test.another_delete'), 'The config_test.another_delete in collection.test2 does not exist in the snapshot storage.'); }
/** * Tests creating a feature via UI and download it. */ public function testCreateFeaturesUI() { $feature_name = 'test_feature2'; $admin_user = $this->createUser(['administer site configuration', 'export configuration', 'administer modules']); $this->drupalLogin($admin_user); $this->drupalPlaceBlock('local_actions_block'); $this->drupalGet('admin/config/development/features'); $this->clickLink('Create new feature'); $this->assertResponse(200); $edit = ['name' => 'Test feature', 'machine_name' => $feature_name, 'description' => 'Test description: <strong>giraffe</strong>', 'version' => '8.x-1.0', 'system_simple[sources][selected][system.theme]' => TRUE, 'system_simple[sources][selected][user.settings]' => TRUE]; $this->drupalPostForm(NULL, $edit, $this->t('Download Archive')); $this->assertResponse(200); $archive = $this->getRawContent(); $filename = tempnam($this->tempFilesDirectory, 'feature'); file_put_contents($filename, $archive); $archive = new ArchiveTar($filename); $files = $archive->listContent(); $this->assertEqual(4, count($files)); $this->assertEqual($feature_name . '/' . $feature_name . '.info.yml', $files[0]['filename']); $this->assertEqual($feature_name . '/' . $feature_name . '.features.yml', $files[1]['filename']); $this->assertEqual($feature_name . '/config/install/system.theme.yml', $files[2]['filename']); $this->assertEqual($feature_name . '/config/install/user.settings.yml', $files[3]['filename']); // Ensure that the archive contains the expected values. $info_filename = tempnam($this->tempFilesDirectory, 'feature'); file_put_contents($info_filename, $archive->extractInString($feature_name . '/' . $feature_name . '.info.yml')); $features_info_filename = tempnam($this->tempFilesDirectory, 'feature'); file_put_contents($features_info_filename, $archive->extractInString($feature_name . '/' . $feature_name . '.features.yml')); /** @var \Drupal\Core\Extension\InfoParser $info_parser */ $info_parser = \Drupal::service('info_parser'); $parsed_info = $info_parser->parse($info_filename); $this->assertEqual('Test feature', $parsed_info['name']); $parsed_features_info = Yaml::decode(file_get_contents($features_info_filename)); $this->assertEqual(['required' => ['system.theme', 'user.settings']], $parsed_features_info); $archive->extract(\Drupal::service('kernel')->getSitePath() . '/modules'); $module_path = \Drupal::service('kernel')->getSitePath() . '/modules/' . $feature_name; // Ensure that the features listing renders the right content. $this->drupalGet('admin/config/development/features'); $tr = $this->xpath('//table[contains(@class, "features-listing")]/tbody/tr[td[3] = "' . $feature_name . '"]')[0]; $this->assertLink('Test feature'); $this->assertEqual($feature_name, (string) $tr->children()[2]); $description_column = (string) $tr->children()[3]->asXml(); $this->assertTrue(strpos($description_column, 'system.theme') !== FALSE); $this->assertTrue(strpos($description_column, 'user.settings') !== FALSE); $this->assertRaw('Test description: <strong>giraffe</strong>'); $this->assertEqual('Uninstalled', (string) $tr->children()[5]); $this->assertEqual('', (string) $tr->children()[6]); // Remove one and add new configuration. $this->clickLink('Test feature'); $edit = ['system_simple[included][system.theme]' => FALSE, 'user_role[sources][selected][authenticated]' => TRUE]; $this->drupalPostForm(NULL, $edit, $this->t('Write')); $info_filename = $module_path . '/' . $feature_name . '.info.yml'; $parsed_info = $info_parser->parse($info_filename); $this->assertEqual('Test feature', $parsed_info['name']); $features_info_filename = $module_path . '/' . $feature_name . '.features.yml'; $parsed_features_info = Yaml::decode(file_get_contents($features_info_filename)); $this->assertEqual(['required' => ['user.settings', 'user.role.authenticated']], $parsed_features_info); $this->drupalGet('admin/modules'); $edit = ['modules[Other][' . $feature_name . '][enable]' => TRUE]; $this->drupalPostForm(NULL, $edit, $this->t('Install')); // Check that the feature is listed as installed. $this->drupalGet('admin/config/development/features'); $tr = $this->xpath('//table[contains(@class, "features-listing")]/tbody/tr[td[3] = "' . $feature_name . '"]')[0]; $this->assertEqual('Installed', (string) $tr->children()[5]); // Check that a config change results in a feature marked as changed. \Drupal::configFactory()->getEditable('user.settings')->set('anonymous', 'Anonymous giraffe')->save(); $this->drupalGet('admin/config/development/features'); $tr = $this->xpath('//table[contains(@class, "features-listing")]/tbody/tr[td[3] = "' . $feature_name . '"]')[0]; $this->assertTrue(strpos($tr->children()[6]->asXml(), 'Changed') !== FALSE); // Uninstall module. $this->drupalPostForm('admin/modules/uninstall', ['uninstall[' . $feature_name . ']' => TRUE], $this->t('Uninstall')); $this->drupalPostForm(NULL, [], $this->t('Uninstall')); $this->drupalGet('admin/config/development/features'); $tr = $this->xpath('//table[contains(@class, "features-listing")]/tbody/tr[td[3] = "' . $feature_name . '"]')[0]; $this->assertTrue(strpos($tr->children()[6]->asXml(), 'Changed') !== FALSE); $this->clickLink($this->t('Changed')); $this->assertRaw('<td class="diff-context diff-deletedline">anonymous : Anonymous <span class="diffchange">giraffe</span></td>'); $this->assertRaw('<td class="diff-context diff-addedline">anonymous : Anonymous</td>'); $this->drupalGet('admin/modules'); $edit = ['modules[Other][' . $feature_name . '][enable]' => TRUE]; $this->drupalPostForm(NULL, $edit, $this->t('Install')); $this->drupalGet('admin/config/development/features'); $tr = $this->xpath('//table[contains(@class, "features-listing")]/tbody/tr[td[3] = "' . $feature_name . '"]')[0]; $this->assertEqual('Installed', (string) $tr->children()[5]); // Ensure that the changed config got overridden. $this->assertEqual('Anonymous', \Drupal::config('user.settings')->get('anonymous')); // Change the value, export and ensure that its not shown as changed. \Drupal::configFactory()->getEditable('user.settings')->set('anonymous', 'Anonymous giraffe')->save(); // Ensure that exporting this change will result in an unchanged feature. $this->drupalGet('admin/config/development/features'); $tr = $this->xpath('//table[contains(@class, "features-listing")]/tbody/tr[td[3] = "' . $feature_name . '"]')[0]; $this->assertTrue(strpos($tr->children()[6]->asXml(), 'Changed') !== FALSE); $this->clickLink('Test feature'); $this->drupalPostForm(NULL, [], $this->t('Write')); $this->drupalGet('admin/config/development/features'); $tr = $this->xpath('//table[contains(@class, "features-listing")]/tbody/tr[td[3] = "' . $feature_name . '"]')[0]; $this->assertEqual('Installed', (string) $tr->children()[5]); }
protected function execute(InputInterface $input, OutputInterface $output) { $client = $this->getHttpClient(); $module = $input->getArgument('module'); $version = $input->getArgument('version'); if ($version) { $release_selected = '8.x-' . $version; } else { // Getting Module page header and parse to get module Node $output->writeln('[+] <info>' . sprintf($this->trans('commands.module.download.messages.getting-releases'), implode(',', array($module))) . '</info>'); $response = $client->head('https://www.drupal.org/project/' . $module); $header_link = explode(';', $response->getHeader('link')); $project_node = str_replace('<', '', str_replace('>', '', $header_link[0])); $project_release_d8 = $project_node . '/release?api_version%5B%5D=7234'; // Parse release module page to get Drupal 8 releases try { $response = $client->get($project_release_d8); $html = $response->getBody()->__tostring(); } catch (\Exception $e) { $output->writeln('[+] <error>' . $e->getMessage() . '</error>'); return; } $crawler = new Crawler($html); $releases = []; foreach ($crawler->filter('span.file a') as $element) { if (strpos($element->nodeValue, '.tar.gz') > 0) { $release_name = str_replace('.tar.gz', '', str_replace($module . '-', '', $element->nodeValue)); $releases[$release_name] = $element->nodeValue; } } if (empty($releases)) { $output->writeln('[+] <error>' . sprintf($this->trans('commands.module.download.messages.no-releases'), implode(',', array($module))) . '</error>'); return; } // List module releases to enable user to select his favorite release $questionHelper = $this->getQuestionHelper(); $question = new ChoiceQuestion('Please select your favorite release', array_keys($releases), 0); $release_selected = $questionHelper->ask($input, $output, $question); // Start the process to download the zip file of release and copy in contrib folter $output->writeln('[+] <info>' . sprintf($this->trans('commands.module.download.messages.downloading'), $module, $release_selected) . '</info>'); } $release_file_path = 'http://ftp.drupal.org/files/projects/' . $module . '-' . $release_selected . '.tar.gz'; // Destination file to download the release $destination = tempnam(sys_get_temp_dir(), 'console.') . 'tar.gz'; try { $client->get($release_file_path, ['save_to' => $destination]); // Determine destination folder for contrib modules $drupalAutoLoad = $this->getHelperSet()->get('drupal-autoload'); $module_contrib_path = $drupalAutoLoad->getDrupalRoot() . '/modules/contrib'; // Create directory if does not exist if (file_exists(dirname($module_contrib_path))) { mkdir($module_contrib_path, 0777, true); } // Preper release to unzip and untar $archiver = new ArchiveTar($destination, 'gz'); $archiver->extract($module_contrib_path . '/'); fclose($destination . '.tar.gz'); $output->writeln('[+] <info>' . sprintf($this->trans('commands.module.download.messages.downloaded'), $module, $release_selected, $module_contrib_path) . '</info>'); } catch (\Exception $e) { $output->writeln('[+] <error>' . $e->getMessage() . '</error>'); return; } return true; }
/** * Exports all stored profiles (cap limit at 1000 items). * * @param \Drupal\webprofiler\Profiler\Profiler $profiler * @param string $directory * @param \Symfony\Component\Console\Output\OutputInterface $output * * @return string */ private function exportAll(Profiler $profiler, $directory, $output) { $filename = $directory . DIRECTORY_SEPARATOR . 'profiles_' . time() . '.tar.gz'; $archiver = new ArchiveTar($filename, 'gz'); $profiles = $profiler->find(NULL, NULL, 1000, NULL, '', ''); $progress = new ProgressBar($output, count($profiles) + 2); $progress->setFormat(' %current%/%max% [%bar%] %percent:3s%% %message%'); $files = []; $progress->start(); $progress->setMessage($this->trans('commands.webprofiler.export.progress.exporting')); foreach ($profiles as $profile) { $data = $profiler->export($profiler->loadProfile($profile['token'])); $profileFilename = $directory . "/{$profile['token']}.txt"; file_put_contents($profileFilename, $data); $files[] = $profileFilename; $progress->advance(); } $progress->setMessage($this->trans('commands.webprofiler.export.progress.archive')); $archiver->createModify($files, '', $directory); $progress->advance(); $progress->setMessage($this->trans('commands.webprofiler.export.progress.delete_tmp')); foreach ($files as $file) { unlink($file); } $progress->advance(); $progress->setMessage($this->trans('commands.webprofiler.export.progress.done')); $progress->finish(); $output->writeln(''); $output->writeln(sprintf($this->trans('commands.webprofiler.export.messages.exported_count'), count($profiles))); return $filename; }
/** * Writes a file to the file system, creating its directory as needed. * * @param string $directory * The extension's directory. * @param array $file * Array with the following keys: * - 'filename': the name of the file. * - 'subdirectory': any subdirectory of the file within the extension * directory. * - 'string': the contents of the file. * @param ArchiveTar $archiver * The archiver. * * @throws Exception */ protected function generateFile($directory, array $file, ArchiveTar $archiver) { $filename = $directory; if (!empty($file['subdirectory'])) { $filename .= '/' . $file['subdirectory']; } $filename .= '/' . $file['filename']; if ($archiver->addString($filename, $file['string']) === FALSE) { throw new \Exception($this->t('Failed to archive file @filename.', ['@filename' => $file['filename']])); } }
/** * @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'); }