Skip to content

Commit

Permalink
ACMS-000: Add the Multisite global drush command.
Browse files Browse the repository at this point in the history
  • Loading branch information
vishalkhode1 committed Jan 17, 2024
1 parent 18f9c5b commit 3a67134
Show file tree
Hide file tree
Showing 3 changed files with 234 additions and 0 deletions.
36 changes: 36 additions & 0 deletions src/Drush/Commands/HooksDrushCommands.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<?php

declare(strict_types=1);

namespace Acquia\Drupal\RecommendedSettings\Drush\Commands;

use Acquia\Drupal\RecommendedSettings\Helpers\EnvironmentDetector;
use Consolidation\AnnotatedCommand\CommandData;
use Consolidation\AnnotatedCommand\Events\CustomEventAwareInterface;
use Consolidation\AnnotatedCommand\Events\CustomEventAwareTrait;
use Consolidation\AnnotatedCommand\Hooks\HookManager;
use Drush\Attributes as CLI;
use Drush\Commands\DrushCommands;

/**
* This
*/
class HooksDrushCommands extends DrushCommands {

/**
* Generate settings.php for multisite if current env. is not Site factory.
*/
#[CLI\Hook(type: HookManager::ON_EVENT, target: MultisiteDrushCommands::VALIDATE_GENERATE_SETTINGS)]
public function isAcquiaEnvironment(): bool {
return !EnvironmentDetector::isAcsfEnv();
}

/**
* Generate settings.php for multisite if current env. is local or CloudIDE.
*/
#[CLI\Hook(type: HookManager::ON_EVENT, target: MultisiteDrushCommands::VALIDATE_GENERATE_SETTINGS)]
public function isLocalEnvironment(): bool {
return (EnvironmentDetector::isLocalEnv() || EnvironmentDetector::isAhIdeEnv());
}

}
147 changes: 147 additions & 0 deletions src/Drush/Commands/MultisiteDrushCommands.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
<?php

declare(strict_types=1);

namespace Acquia\Drupal\RecommendedSettings\Drush\Commands;

use Acquia\Drupal\RecommendedSettings\Drush\Traits\SiteUriTrait;
use Acquia\Drupal\RecommendedSettings\Helpers\EnvironmentDetector;
use Acquia\Drupal\RecommendedSettings\Settings;
use Consolidation\AnnotatedCommand\CommandData;
use Consolidation\AnnotatedCommand\Events\CustomEventAwareInterface;
use Consolidation\AnnotatedCommand\Events\CustomEventAwareTrait;
use Consolidation\AnnotatedCommand\Hooks\HookManager;
use Drupal\Core\Database\Database;
use Drush\Attributes as CLI;
use Drush\Boot\BootstrapManager;
use Drush\Boot\DrupalBootLevels;
use Drush\Commands\DrushCommands;
use Drush\Drush;
use Psr\Container\ContainerInterface as DrushContainer;
use Symfony\Component\Filesystem\Path;

/**
* A Drush command to generate settings.php for Multisite.
*/
class MultisiteDrushCommands extends DrushCommands implements CustomEventAwareInterface {

use CustomEventAwareTrait;
use SiteUriTrait;

const VALIDATE_GENERATE_SETTINGS = 'validate-generate-settings';
const POST_GENERATE_SETTINGS = 'post-generate-settings';

/**
* Construct an object of Multisite commands.
*/
public function __construct(private BootstrapManager $bootstrapManager) {
parent::__construct();
}

/**
* {@inheritDoc}
*/
public static function createEarly(DrushContainer $drush_container): self {
return new static(
$drush_container->get('bootstrap.manager')
);
}

/**
* Execute code before pre-validate site:install.
*/
#[CLI\Hook(type: HookManager::PRE_ARGUMENT_VALIDATOR, target: 'site-install')]
public function preValidateSiteInstall(CommandData $commandData): void {
if ($this->validateGenerateSettings()) {
$uri = $commandData->input()->getOption('uri') ?? 'default';
$sitesSubdir = $this->getSitesSubdirFromUri(DRUPAL_ROOT, $uri);
$commandData->input()->setOption('sites-subdir', $sitesSubdir);
$options = $commandData->options();
$this->bootstrapManager->setUri('http://' . $sitesSubdir);

// Try to get any already configured database information.
$this->bootstrapManager->bootstrapMax(DrupalBootLevels::CONFIGURATION, $commandData->annotationData());

// By default, bootstrap manager boot site from default/setting.php
// hence remove the database connection if site is other than default.
if (($sitesSubdir && "sites/$sitesSubdir" !== $this->bootstrapManager->bootstrap()->confpath())) {
Database::removeConnection('default');
$db = [
'database' => 'drupal',
'username' => 'drupal',
'password' => 'drupal',
'host' => 'localhost',
'port' => '3306',
];
$dbSpec = [
"drupal" => ["db" => $db]
];
if (!($options['db-url'])) {
if (EnvironmentDetector::isLocalEnv()) {
$db = $this->askDbCredentials($sitesSubdir, $db);
$dbSpec["drupal"]["db"] = $db;
}
$commandData->input()->setOption("db-url",
"mysql://" . $db['username'] . ":" . $db['password'] . "@" . $db['host'] . ":" . $db['port'] . "/" . $db['database']
);
}
$settings = new Settings(DRUPAL_ROOT, $sitesSubdir);
try {
$settings->generate($dbSpec);
$this->postGenerateSettings();
}
catch (SettingsException $e) {
$this->io()->warning($e->getMessage());
}
}
}
}

/**
* Function to check if multisite should be setup or not.
*/
protected function validateGenerateSettings(): bool {
$handlers = $this->getCustomEventHandlers(self::VALIDATE_GENERATE_SETTINGS);
$status = TRUE;
foreach ($handlers as $handler) {
$status = $handler();
if (!$status) {
return FALSE;
}
}
return $status;
}

/**
* Function to run if post generation of settings.php.
*/
protected function postGenerateSettings(): void {
$handlers = $this->getCustomEventHandlers(self::POST_GENERATE_SETTINGS);
foreach ($handlers as $handler) {
$handler();
}
}

/**
* Get local database specs.
*
* @param string $site_name
* The site name.
*
* @return array
* The database specs.
*/
private function askDbCredentials(string $site_name, array $defaultCredentials): array {
$shouldAsk = $this->io()->confirm(dt("Would you like to configure the local database credentials?"));
$credentials = $defaultCredentials;
if ($shouldAsk) {
$credentials['database'] = $this->io()->ask("Local database name", $site_name);
$credentials['username'] = $this->io()->ask("Local database user", $credentials['username']);
$credentials['password'] = $this->io()->ask("Local database password", $credentials['password']);
$credentials['host'] = $this->io()->ask("Local database host", $credentials['host']);
$credentials['port'] = $this->io()->ask("Local database port", $credentials['port']);
}
return $credentials;
}

}
51 changes: 51 additions & 0 deletions src/Drush/Traits/SiteUriTrait.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
<?php

declare(strict_types=1);

namespace Acquia\Drupal\RecommendedSettings\Drush\Traits;

use Symfony\Component\Filesystem\Path;

/**
* Trait to deletermine Site Uri.
*/
trait SiteUriTrait {

/**
* Determine an appropriate site subdir name to use for the provided uri.
*
* This code copied from SiteInstallCommands.php file.
*
* @return array|false|mixed|string|string[]
* Returns the site uri.
*/
private function getSitesSubdirFromUri($root, $uri): mixed {
$dir = strtolower($uri);
// Always accept simple uris (e.g. 'dev', 'stage', etc.)
if (preg_match('#^[a-z0-9_-]*$#', $dir)) {
return $dir;
}
// Strip off the protocol from the provided uri -- however,
// now we will require that the sites subdir already exist.
$dir = preg_replace('#[^/]*/*#', '', $dir);
if ($dir && file_exists(Path::join($root, $dir))) {
return $dir;
}
// Find the dir from sites.php file.
$sites_file = $root . '/sites/sites.php';
if (file_exists($sites_file)) {
$sites = [];
include $sites_file;
if (!empty($sites) && array_key_exists($uri, $sites)) {
return $sites[$uri];
}
}
// Fall back to default directory if it exists.
if (file_exists(Path::join($root, 'sites', 'default'))) {
return 'default';
}

return FALSE;
}

}

0 comments on commit 3a67134

Please sign in to comment.