class FeaturesGenerationArchive in Features 8.4
Same name and namespace in other branches
- 8.3 src/Plugin/FeaturesGeneration/FeaturesGenerationArchive.php \Drupal\features\Plugin\FeaturesGeneration\FeaturesGenerationArchive
Class for generating a compressed archive of packages.
Plugin annotation
@Plugin(
id = \Drupal\features\Plugin\FeaturesGeneration\FeaturesGenerationArchive::METHOD_ID,
weight = -2,
name = @Translation("Download Archive"),
description = @Translation("Generate packages and optional profile as a compressed archive for download."),
)
Hierarchy
- class \Drupal\features\FeaturesGenerationMethodBase implements FeaturesGenerationMethodInterface uses StringTranslationTrait
- class \Drupal\features\Plugin\FeaturesGeneration\FeaturesGenerationArchive implements ContainerFactoryPluginInterface
Expanded class hierarchy of FeaturesGenerationArchive
File
- src/
Plugin/ FeaturesGeneration/ FeaturesGenerationArchive.php, line 25
Namespace
Drupal\features\Plugin\FeaturesGenerationView source
class FeaturesGenerationArchive extends FeaturesGenerationMethodBase implements ContainerFactoryPluginInterface {
/**
* The app root.
*
* @var string
*/
protected $root;
/**
* The CSRF token generator.
*
* @var \Drupal\Core\Access\CsrfTokenGenerator
*/
protected $csrfToken;
/**
* The file system service.
*
* @var \Drupal\Core\File\FileSystem
*/
protected $fileSystem;
/**
* Creates a new FeaturesGenerationArchive instance.
*
* @param string $root
* The app root.
* @param \Drupal\Core\Access\CsrfTokenGenerator $csrf_token
* The CSRF token generator.
* @param \Drupal\Core\File\FileSystem $file_system
* The file system service.
*/
public function __construct($root, CsrfTokenGenerator $csrf_token, FileSystem $file_system) {
$this->root = $root;
$this->csrfToken = $csrf_token;
$this->fileSystem = $file_system;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
return new static($container
->get('app.root'), $container
->get('csrf_token'), $container
->get('file_system'));
}
/**
* The package generation method id.
*/
const METHOD_ID = 'archive';
/**
* The filename being written.
*
* @var string
*/
protected $archiveName;
/**
* Reads and merges in existing files for a given package or profile.
*/
protected function preparePackage(Package $package, array $existing_packages, FeaturesBundleInterface $bundle = NULL) {
if (isset($existing_packages[$package
->getMachineName()])) {
$existing_directory = $existing_packages[$package
->getMachineName()];
}
else {
$existing_directory = $package
->getDirectory();
}
$existing_directory = $this->root . '/' . $existing_directory;
if (is_dir($existing_directory)) {
// Scan for all files.
$files = $this->fileSystem
->scanDirectory($existing_directory, '/.*/');
// Skip any existing .features.yml as it will be replaced.
$exclude_files = [
$package
->getMachineName() . '.features',
];
foreach ($files as $file) {
// Skip files in the any existing configuration directory, as these
// will be replaced.
foreach (array_keys($this->featuresManager
->getExtensionStorages()
->getExtensionStorages()) as $directory) {
if (strpos($file->uri, $directory) !== FALSE) {
continue 2;
}
}
// Merge in the info file.
if ($file->name == $package
->getMachineName() . '.info') {
$files = $package
->getFiles();
$files['info']['string'] = $this
->mergeInfoFile($package
->getFiles()['info']['string'], $file->uri);
$package
->setFiles($files);
}
elseif (!in_array($file->name, $exclude_files)) {
// Determine if the file is within a subdirectory of the
// extension's directory.
$file_directory = dirname($file->uri);
if ($file_directory !== $existing_directory) {
$subdirectory = substr($file_directory, strlen($existing_directory) + 1);
}
else {
$subdirectory = NULL;
}
$package
->appendFile([
'filename' => $file->filename,
'subdirectory' => $subdirectory,
'string' => file_get_contents($file->uri),
]);
}
}
}
}
/**
* {@inheritdoc}
*/
public function generate(array $packages = [], FeaturesBundleInterface $bundle = NULL) {
// If no packages were specified, get all packages.
if (empty($packages)) {
$packages = $this->featuresManager
->getPackages();
}
// Determine the best name for the tar archive.
// Single package export, so name by package name.
if (count($packages) == 1) {
$filename = current($packages)
->getMachineName();
}
elseif (isset($bundle) && $bundle
->isProfile()) {
$filename = $bundle
->getProfileName();
}
elseif (isset($bundle) && !$bundle
->isDefault()) {
$filename = $bundle
->getMachineName();
}
else {
$filename = 'generated_features';
}
$return = [];
$this->archiveName = $filename . '.tar.gz';
$archive_name = $this->fileSystem
->getTempDirectory() . '/' . $this->archiveName;
if (file_exists($archive_name)) {
$this->fileSystem
->delete($archive_name);
}
$archiver = new ArchiveTar($archive_name);
// Add package files.
foreach ($packages as $package) {
if (count($packages) == 1) {
// Single module export, so don't generate entire modules dir structure.
$package
->setDirectory($package
->getMachineName());
}
$this
->generatePackage($return, $package, $archiver);
}
return $return;
}
/**
* Writes a package or profile's files to an archive.
*
* @param array &$return
* The return value, passed by reference.
* @param \Drupal\features\Package $package
* The package or profile.
* @param \Drupal\Core\Archiver\ArchiveTar $archiver
* The archiver.
*/
protected function generatePackage(array &$return, Package $package, ArchiveTar $archiver) {
$success = TRUE;
foreach ($package
->getFiles() as $file) {
try {
$this
->generateFile($package
->getDirectory(), $file, $archiver);
} catch (\Exception $exception) {
$this
->failure($return, $package, $exception);
$success = FALSE;
break;
}
}
if ($success) {
$this
->success($return, $package);
}
}
/**
* Registers a successful package or profile archive operation.
*
* @param array &$return
* The return value, passed by reference.
* @param \Drupal\features\Package $package
* The package or profile.
*/
protected function success(array &$return, Package $package) {
$type = $package
->getType() == 'module' ? $this
->t('Package') : $this
->t('Profile');
$return[] = [
'success' => TRUE,
// Archive writing doesn't merit a message, and if done through the UI
// would appear on the subsequent page load.
'display' => FALSE,
'message' => '@type @package written to archive.',
'variables' => [
'@type' => $type,
'@package' => $package
->getName(),
],
];
}
/**
* Registers a failed package or profile archive operation.
*
* @param array &$return
* The return value, passed by reference.
* @param \Drupal\features\Package $package
* The package or profile.
* @param \Exception $exception
* The exception object.
* @param string $message
* Error message when there isn't an Exception object.
*/
protected function failure(array &$return, Package $package, \Exception $exception = NULL, $message = '') {
$type = $package
->getType() == 'module' ? $this
->t('Package') : $this
->t('Profile');
$return[] = [
'success' => FALSE,
// Archive writing doesn't merit a message, and if done through the UI
// would appear on the subsequent page load.
'display' => FALSE,
'message' => '@type @package not written to archive. Error: @error.',
'variables' => [
'@type' => $type,
'@package' => $package
->getName(),
'@error' => isset($exception) ? $exception
->getMessage() : $message,
],
];
}
/**
* 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 \Drupal\Core\Archiver\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'],
]));
}
}
/**
* {@inheritdoc}
*/
public function exportFormSubmit(array &$form, FormStateInterface $form_state) {
// Redirect to the archive file download.
$form_state
->setRedirect('features.export_download', [
'uri' => $this->archiveName,
'token' => $this->csrfToken
->get($this->archiveName),
]);
}
}
Members
Name | Modifiers | Type | Description | Overrides |
---|---|---|---|---|
FeaturesGenerationArchive:: |
protected | property | The filename being written. | |
FeaturesGenerationArchive:: |
protected | property | The CSRF token generator. | |
FeaturesGenerationArchive:: |
protected | property | The file system service. | |
FeaturesGenerationArchive:: |
protected | property | The app root. | |
FeaturesGenerationArchive:: |
public static | function |
Creates an instance of the plugin. Overrides ContainerFactoryPluginInterface:: |
|
FeaturesGenerationArchive:: |
public | function |
Responds to the submission of
\Drupal\features_ui\Form\FeaturesExportForm. Overrides FeaturesGenerationMethodBase:: |
|
FeaturesGenerationArchive:: |
protected | function | Registers a failed package or profile archive operation. | |
FeaturesGenerationArchive:: |
public | function |
Performs package generation. Overrides FeaturesGenerationMethodInterface:: |
|
FeaturesGenerationArchive:: |
protected | function | Writes a file to the file system, creating its directory as needed. | |
FeaturesGenerationArchive:: |
protected | function | Writes a package or profile's files to an archive. | |
FeaturesGenerationArchive:: |
constant | The package generation method id. | ||
FeaturesGenerationArchive:: |
protected | function |
Reads and merges in existing files for a given package or profile. Overrides FeaturesGenerationMethodBase:: |
|
FeaturesGenerationArchive:: |
protected | function | Registers a successful package or profile archive operation. | |
FeaturesGenerationArchive:: |
public | function | Creates a new FeaturesGenerationArchive instance. | |
FeaturesGenerationMethodBase:: |
protected | property | The features assigner. | |
FeaturesGenerationMethodBase:: |
protected | property | The features manager. | |
FeaturesGenerationMethodBase:: |
protected | function | Merges an info file into a package's info file. | |
FeaturesGenerationMethodBase:: |
public | function |
Prepares packages for generation. Overrides FeaturesGenerationMethodInterface:: |
|
FeaturesGenerationMethodBase:: |
public | function |
Injects the features assigner. Overrides FeaturesGenerationMethodInterface:: |
|
FeaturesGenerationMethodBase:: |
public | function |
Injects the features manager. Overrides FeaturesGenerationMethodInterface:: |
|
StringTranslationTrait:: |
protected | property | The string translation service. | 1 |
StringTranslationTrait:: |
protected | function | Formats a string containing a count of items. | |
StringTranslationTrait:: |
protected | function | Returns the number of plurals supported by a given language. | |
StringTranslationTrait:: |
protected | function | Gets the string translation service. | |
StringTranslationTrait:: |
public | function | Sets the string translation service to use. | 2 |
StringTranslationTrait:: |
protected | function | Translates a string to the current language or to a given language. |