Skip to content

Commit

Permalink
Issue #239: Prevent double download of project (#269)
Browse files Browse the repository at this point in the history
  • Loading branch information
yorkshire-pudding authored May 3, 2023
1 parent 24886ce commit a4a4e6c
Show file tree
Hide file tree
Showing 2 changed files with 193 additions and 14 deletions.
197 changes: 183 additions & 14 deletions commands/download.bee.inc
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,16 @@ function download_bee_command() {
'hide-progress' => array(
'description' => bt('Hide the download progress bar.'),
),
'allow-multisite-copy' => array(
'description' => bt('Override the check that would prevent the project being downloaded to a multisite site if the project exists in the shared project directory.'),
),
),
'aliases' => array('dl', 'pm-download'),
'examples' => array(
'bee download webform' => bt('Download the Webform module.'),
'bee download simplify thesis bamboo' => bt('Download the Simplify module, Thesis theme, and Bamboo layout.'),
'bee download devel --hide-progress' => bt("Download the Devel module, and don't show the download progress bar."),
'bee --site=site_name download simplify --allow-multisite-copy' => bt('Download an additional copy of the Simplify module into the site_name multisite module folder.'),
),
),
'download-core' => array(
Expand Down Expand Up @@ -58,6 +62,7 @@ function download_bee_callback($arguments, $options) {
global $_bee_backdrop_root, $_bee_backdrop_site;

$progress = (!empty($options['hide-progress'])) ? FALSE : TRUE;
$allow_multisite_copy = (!empty($options['allow-multisite-copy'])) ? TRUE : FALSE;

// Get list of all core projects so we don't try to download core dependencies
// when we download a named project.
Expand Down Expand Up @@ -111,15 +116,55 @@ function download_bee_callback($arguments, $options) {
// name.
$dependency = explode(" ", $dependency, 2);
$dependency = $dependency[0];
// Only add contrib dependencies to the list of projects to download.
if (!in_array($dependency, $core_projects)) {
$arguments['projects'][] = $dependency;
bee_message(bt("The '!dependency' module will also be downloaded, as it is required by the '!project' module.", array(
'!dependency' => $dependency,
'!project' => $project,
)), 'status');
// Check if the dependency is a core project.
$dependency_is_core = in_array($dependency, $core_projects);
// Check if dependency exists in the site file system. Only
// check contrib dependencies.
if (!$dependency_is_core) {
// Get information about the dependency project.
$dependency_info = download_bee_git_info($dependency);
// Check whether the dependency exists within the site file system.
$dependency_existing_location = download_bee_check_project_exists($dependency, $dependency_info['type']);
$dependency_allow_multisite_copy = download_bee_check_multisite_copy($allow_multisite_copy, $dependency_existing_location);
if ($dependency_existing_location == FALSE || $dependency_allow_multisite_copy) {
// If project does not exist in the site file system, or it is
// but a multisite copy is allowed, then check whether it is
// already in the download list.
if (in_array($dependency, $arguments['projects'])) {
// If project is already in the list to download, do not add to
// the list to download again.
bee_message(bt("The '!dependency' !dependency_type is also required by the '!project' !project_type.", array(
'!dependency' => $dependency,
'!dependency_type' => $dependency_info['type'],
'!project' => $project,
'!project_type' => $info['type'],
)), 'status');
}
else {
// If project is not already in download list, then add to list
// and prepare message.
$arguments['projects'][] = $dependency;
bee_message(bt("The '!dependency' !dependency_type will also be downloaded, as it is required by the '!project' !project_type.", array(
'!dependency' => $dependency,
'!dependency_type' => $dependency_info['type'],
'!project' => $project,
'!project_type' => $info['type'],
)), 'status');
}
}
else {
// If project does exist in the site file system, give a
// meaningful message so user is aware of the dependency and that
// it is already met.
bee_message(bt("The '!dependency' !dependency_type is required by the '!project' !project_type but already exists at '!dependency_location'.", array(
'!dependency' => $dependency,
'!dependency_type' => $dependency_info['type'],
'!project' => $project,
'!project_type' => $info['type'],
'!dependency_location' => $dependency_existing_location,
)), 'status');
}
}

}
}

Expand All @@ -132,15 +177,15 @@ function download_bee_callback($arguments, $options) {
}
else {
// Add an 's' to the end of the type name.
$info['type'] .= 's';
$type_folder = $info['type'] . 's';
}

// Get the directory to download the project into.
if (!empty($_bee_backdrop_site)) {
$destination = "$_bee_backdrop_root/sites/$_bee_backdrop_site/" . $info['type'];
$destination = "$_bee_backdrop_root/sites/$_bee_backdrop_site/" . $type_folder;
}
elseif (!empty($_bee_backdrop_root)) {
$destination = "$_bee_backdrop_root/" . $info['type'];
$destination = "$_bee_backdrop_root/" . $type_folder;
}
else {
bee_message(bt("The download destination could not be determined. Re-run the command from within a Backdrop installation, or set the global '--root'/'--site' options."), 'error');
Expand All @@ -150,10 +195,13 @@ function download_bee_callback($arguments, $options) {
$destination .= '/contrib';
}
$destination .= "/$project";
if (file_exists($destination)) {
bee_message(bt("'!project' already exists in '!destination'.", array(
// Check if the project exists within the site file system.
$project_existing_location = download_bee_check_project_exists($project, $info['type']);
$project_allow_multisite_copy = download_bee_check_multisite_copy($allow_multisite_copy, $project_existing_location);
if ($project_existing_location != FALSE && !$project_allow_multisite_copy) {
bee_message(bt("'!project' already exists in '!existing_location'.", array(
'!project' => $project,
'!destination' => $destination,
'!existing_location' => $project_existing_location,
)), 'error');
return;
}
Expand Down Expand Up @@ -366,3 +414,124 @@ function download_bee_download_project($project, array $info, $destination, $pro
)), 'status') : '';
return TRUE;
}

/**
* Get list of all projects in filesystem.
*
* @param string $type
* The type of project to list:
* - module
* - theme
* - layout
* This will find projects in both core and contrib.
*
* @return array
* An associative array of all projects with their locations, together with
* the location type and project type.
*/
function download_bee_find_all_projects($type) {
global $_bee_backdrop_root, $_bee_backdrop_site;
// Create an empty array for files.
$files = array();
// Set the pattern to look for .info files.
$pattern = '#\.info$#';
// Define the locations to search.
$locations = array('core','contrib');
(empty($_bee_backdrop_site)) ?: $locations[] = 'site_contrib';
// Loop through each location.
foreach ($locations as $location) {
// Set the path depending on location.
switch ($location) {
case 'core':
$path = "${_bee_backdrop_root}/core/${type}s";
break;
case 'contrib':
$path = "${_bee_backdrop_root}/${type}s";
break;
case 'site_contrib':
$path = "${_bee_backdrop_root}/sites/${_bee_backdrop_site}/${type}s";
break;
default:
$path = $location;
break;
}
// Scan the location recursively for projects.
$options = array(
'key' => 'name',
);
$files = bee_file_scan_directory($path, $pattern, $options);
// Modify each record to meet our needs.
$projects[] = array();
foreach ($files as &$file) {
// Add the revised project record to a common list of projects.
$key = $file->name;
$projects[$key] = array(
'name' => $file->name,
'uri' => $file->uri,
'location_category' => $location,
'type' => $type,
);
}
}
return $projects;
}

/**
* Check if a project exists in the site filesystem.
*
* @param string $project
* The name of the project.
* @param string $type
* The type of the project.
*
* @return string|false
* The project location, or FALSE if not found.
*/
function download_bee_check_project_exists($project, $type) {
// Get the list of all projects for the type.
$projects = download_bee_find_all_projects($type);
if (array_key_exists($project, $projects)) {
// If project exists, return the path where it is located.
return dirname($projects[$project]['uri']);
}
else {
// If project does not exist, return FALSE.
return FALSE;
}
}

/**
* Check if download for multisite can be downloaded if it exists in the
* root directory for the same project type.
*
* @param boolean $allow_multisite_copy
* Whether or not to allow multisite copy.
* @param string $existing_location_path
* The path of the existing project.
*
* @return boolean
* TRUE if the download for multisite can be downloaded. FALSE if not.
*/
function download_bee_check_multisite_copy($allow_multisite_copy, $existing_location_path) {
global $_bee_backdrop_site;
// Check if site is defined for this download.
if (!empty($_bee_backdrop_site)) {
// If site defined then check if existing location is in site folder.
$existing_in_site_folder = stripos($existing_location_path, $_bee_backdrop_site) > 0;
if (!$existing_in_site_folder && $allow_multisite_copy) {
// If existing is in root directory and the option to allow multisite
// copy is included, then we can download to site directory.
return TRUE;
}
else {
// If either the existing copy of the project is in the site directory OR
// the option to allow multisite copy is NOT included, then we should not
// allow a download to the site directory.
return FALSE;
}
}
else {
// If no site defined then this check should always return FALSE.
return FALSE;
}
}
10 changes: 10 additions & 0 deletions tests/multisite/MultisiteDownloadCommandsTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,16 @@ public function test_download_command_works() {
$this->assertStringContainsString("'simplify' was downloaded into '/app/multisite/modules/simplify'.", (string) $output_root);
$this->assertTrue(file_exists('/app/multisite/modules/simplify/simplify.info'));

// Root directory, site specified, 'allow-multisite-copy' option NOT
// included.
$output_root = shell_exec('bee --site=multi_one download --hide-progress simplify');
$this->assertStringContainsString("'simplify' already exists in '/app/multisite/modules/simplify'.", (string) $output_root);

// Root directory, site specified, 'allow-multisite-copy' option included.
$output_root = shell_exec('bee --site=multi_one download --hide-progress --allow-multisite-copy simplify');
$this->assertStringContainsString("'simplify' was downloaded into '/app/multisite/sites/multi_one/modules/simplify'.", (string) $output_root);
$this->assertTrue(file_exists('/app/multisite/sites/multi_one/modules/simplify/simplify.info'));

// Root directory, site specified.
$output_root_site = shell_exec('bee download --site=multi_one --hide-progress lumi');
$this->assertStringContainsString("'lumi' was downloaded into '/app/multisite/sites/multi_one/themes/lumi'.", (string) $output_root_site);
Expand Down

0 comments on commit a4a4e6c

Please sign in to comment.