Initial commit: CloudOps infrastructure platform
This commit is contained in:
@@ -0,0 +1,501 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mautic\InstallBundle\Install;
|
||||
|
||||
use Doctrine\Common\DataFixtures\Executor\ORMExecutor;
|
||||
use Doctrine\Common\DataFixtures\Purger\ORMPurger;
|
||||
use Doctrine\ORM\EntityManager;
|
||||
use Mautic\CoreBundle\Configurator\Configurator;
|
||||
use Mautic\CoreBundle\Configurator\Step\StepInterface;
|
||||
use Mautic\CoreBundle\Doctrine\Loader\FixturesLoaderInterface;
|
||||
use Mautic\CoreBundle\Helper\CacheHelper;
|
||||
use Mautic\CoreBundle\Helper\EncryptionHelper;
|
||||
use Mautic\CoreBundle\Helper\InputHelper;
|
||||
use Mautic\CoreBundle\Helper\PathsHelper;
|
||||
use Mautic\CoreBundle\Loader\ParameterLoader;
|
||||
use Mautic\CoreBundle\Release\ThisRelease;
|
||||
use Mautic\InstallBundle\Configurator\Step\DoctrineStep;
|
||||
use Mautic\InstallBundle\Exception\AlreadyInstalledException;
|
||||
use Mautic\InstallBundle\Exception\DatabaseVersionTooOldException;
|
||||
use Mautic\InstallBundle\Helper\SchemaHelper;
|
||||
use Mautic\UserBundle\Entity\Role;
|
||||
use Mautic\UserBundle\Entity\User;
|
||||
use Symfony\Bundle\FrameworkBundle\Console\Application;
|
||||
use Symfony\Component\Console\Input\ArgvInput;
|
||||
use Symfony\Component\Console\Output\BufferedOutput;
|
||||
use Symfony\Component\HttpKernel\KernelInterface;
|
||||
use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasher;
|
||||
use Symfony\Component\Validator\Constraints as Assert;
|
||||
use Symfony\Component\Validator\Validator\ValidatorInterface;
|
||||
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||
|
||||
class InstallService
|
||||
{
|
||||
public const CHECK_STEP = 0;
|
||||
|
||||
public const DOCTRINE_STEP = 1;
|
||||
|
||||
public const USER_STEP = 2;
|
||||
|
||||
public const FINAL_STEP = 3;
|
||||
|
||||
public function __construct(
|
||||
private Configurator $configurator,
|
||||
private CacheHelper $cacheHelper,
|
||||
protected PathsHelper $pathsHelper,
|
||||
private EntityManager $entityManager,
|
||||
private TranslatorInterface $translator,
|
||||
private KernelInterface $kernel,
|
||||
private ValidatorInterface $validator,
|
||||
private UserPasswordHasher $hasher,
|
||||
private FixturesLoaderInterface $fixturesLoader,
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Get step object for given index or appropriate step index.
|
||||
*
|
||||
* @param int $index The step number to retrieve
|
||||
*
|
||||
* @return StepInterface the valid step given installation status
|
||||
*
|
||||
* @throws \InvalidArgumentException|AlreadyInstalledException
|
||||
*/
|
||||
public function getStep(int $index = 0): StepInterface
|
||||
{
|
||||
// We're going to assume a bit here; if the config file exists already and DB info is provided, assume the app
|
||||
// is installed and redirect
|
||||
if ($this->checkIfInstalled()) {
|
||||
throw new AlreadyInstalledException();
|
||||
}
|
||||
|
||||
$params = $this->configurator->getParameters();
|
||||
|
||||
// Check to ensure the installer is in the right place
|
||||
if ((empty($params)
|
||||
|| !isset($params['db_driver'])
|
||||
|| empty($params['db_driver'])) && $index > 1) {
|
||||
return $this->configurator->getStep(self::DOCTRINE_STEP)[0];
|
||||
}
|
||||
|
||||
return $this->configurator->getStep($index)[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get local config file location.
|
||||
*/
|
||||
private function localConfig(): string
|
||||
{
|
||||
return ParameterLoader::getLocalConfigFile($this->pathsHelper->getSystemPath('root').'/app');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get local config parameters.
|
||||
*/
|
||||
public function localConfigParameters(): array
|
||||
{
|
||||
$localConfigFile = $this->localConfig();
|
||||
|
||||
if (file_exists($localConfigFile)) {
|
||||
/** @var array $parameters */
|
||||
$parameters = [];
|
||||
|
||||
// Load local config to override parameters
|
||||
include $localConfigFile;
|
||||
$localParameters = $parameters;
|
||||
} else {
|
||||
$localParameters = [];
|
||||
}
|
||||
|
||||
return $localParameters;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the application has been installed and redirects if so.
|
||||
*/
|
||||
public function checkIfInstalled(): bool
|
||||
{
|
||||
// If the config file doesn't even exist, no point in checking further
|
||||
$localConfigFile = $this->localConfig();
|
||||
if (!file_exists($localConfigFile)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$params = $this->configurator->getParameters();
|
||||
|
||||
// if db_driver and site_url are present then it is assumed all the steps of the installation have been
|
||||
// performed; manually deleting these values or deleting the config file will be required to re-enter
|
||||
// installation.
|
||||
if (empty($params['db_driver']) || empty($params['site_url'])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Translation messages array.
|
||||
*/
|
||||
private function translateMessages(array $messages): array
|
||||
{
|
||||
if (empty($messages)) {
|
||||
return $messages;
|
||||
}
|
||||
|
||||
foreach ($messages as $key => $value) {
|
||||
$messages[$key] = $this->translator->trans($value);
|
||||
}
|
||||
|
||||
return $messages;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks for step's requirements.
|
||||
*/
|
||||
public function checkRequirements(StepInterface $step): array
|
||||
{
|
||||
$messages = $step->checkRequirements();
|
||||
|
||||
return $this->translateMessages($messages);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks for step's optional settings.
|
||||
*/
|
||||
public function checkOptionalSettings(StepInterface $step): array
|
||||
{
|
||||
$messages = $step->checkOptionalSettings();
|
||||
|
||||
return $this->translateMessages($messages);
|
||||
}
|
||||
|
||||
public function saveConfiguration($params, ?StepInterface $step = null, $clearCache = false): array
|
||||
{
|
||||
if ($step instanceof StepInterface) {
|
||||
$params = $step->update($step);
|
||||
}
|
||||
|
||||
$this->configurator->mergeParameters($params);
|
||||
|
||||
$messages = [];
|
||||
try {
|
||||
$this->configurator->write();
|
||||
} catch (\RuntimeException) {
|
||||
$messages = [
|
||||
'error' => $this->translator->trans(
|
||||
'mautic.installer.error.writing.configuration',
|
||||
[],
|
||||
'flashes'
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
if ($clearCache) {
|
||||
$this->cacheHelper->refreshConfig();
|
||||
}
|
||||
|
||||
return $messages;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array Validation errors
|
||||
*/
|
||||
public function validateDatabaseParams(array $dbParams): array
|
||||
{
|
||||
$required = [
|
||||
'driver',
|
||||
'host',
|
||||
'name',
|
||||
'user',
|
||||
];
|
||||
|
||||
$messages = [];
|
||||
foreach ($required as $r) {
|
||||
if (!isset($dbParams[$r]) || empty($dbParams[$r])) {
|
||||
$messages[$r] = $this->translator->trans(
|
||||
'mautic.core.value.required',
|
||||
[],
|
||||
'validators'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (!isset($dbParams['port']) || (int) $dbParams['port'] <= 0) {
|
||||
$messages['port'] = $this->translator->trans(
|
||||
'mautic.install.database.port.invalid',
|
||||
[],
|
||||
'validators'
|
||||
);
|
||||
}
|
||||
|
||||
if (!empty($dbParams['driver']) && !in_array($dbParams['driver'], DoctrineStep::getDriverKeys())) {
|
||||
$messages['driver'] = $this->translator->trans(
|
||||
'mautic.install.database.driver.invalid',
|
||||
['%drivers%' => implode(', ', DoctrineStep::getDriverKeys())],
|
||||
'validators'
|
||||
);
|
||||
}
|
||||
|
||||
return $messages;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the database.
|
||||
*/
|
||||
public function createDatabaseStep(StepInterface $step, array $dbParams): array
|
||||
{
|
||||
$messages = $this->validateDatabaseParams($dbParams);
|
||||
|
||||
if (!empty($messages)) {
|
||||
return $messages;
|
||||
}
|
||||
|
||||
// Check if connection works and/or create database if applicable
|
||||
$schemaHelper = new SchemaHelper($dbParams);
|
||||
|
||||
try {
|
||||
$schemaHelper->testConnection();
|
||||
$schemaHelper->validateDatabaseVersion();
|
||||
|
||||
if ($schemaHelper->createDatabase()) {
|
||||
$messages = $this->saveConfiguration($dbParams, $step, true);
|
||||
if (empty($messages)) {
|
||||
return $messages;
|
||||
}
|
||||
}
|
||||
|
||||
$messages['error'] = $this->translator->trans(
|
||||
'mautic.installer.error.creating.database',
|
||||
['%name%' => $dbParams['name']],
|
||||
'flashes'
|
||||
);
|
||||
} catch (DatabaseVersionTooOldException $e) {
|
||||
$metadata = ThisRelease::getMetadata();
|
||||
|
||||
$messages['error'] = $this->translator->trans(
|
||||
'mautic.installer.error.database.version',
|
||||
[
|
||||
'%currentversion%' => $e->getCurrentVersion(),
|
||||
'%mysqlminversion%' => $metadata->getMinSupportedMySqlVersion(),
|
||||
'%mariadbminversion%' => $metadata->getMinSupportedMariaDbVersion(),
|
||||
],
|
||||
'flashes'
|
||||
);
|
||||
} catch (\Exception $exception) {
|
||||
$messages['error'] = $this->translator->trans(
|
||||
'mautic.installer.error.connecting.database',
|
||||
['%exception%' => $exception->getMessage()],
|
||||
'flashes'
|
||||
);
|
||||
}
|
||||
|
||||
return $messages;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the database schema.
|
||||
*/
|
||||
public function createSchemaStep(array $dbParams): array
|
||||
{
|
||||
$schemaHelper = new SchemaHelper($dbParams);
|
||||
$schemaHelper->setEntityManager($this->entityManager);
|
||||
|
||||
$messages = [];
|
||||
try {
|
||||
if (!$schemaHelper->installSchema()) {
|
||||
$messages['error'] = $this->translator->trans(
|
||||
'mautic.installer.error.no.metadata',
|
||||
[],
|
||||
'flashes'
|
||||
);
|
||||
}
|
||||
} catch (\Exception $exception) {
|
||||
$messages['error'] = $this->translator->trans(
|
||||
'mautic.installer.error.installing.data',
|
||||
['%exception%' => $exception->getMessage()],
|
||||
'flashes'
|
||||
);
|
||||
}
|
||||
|
||||
return $messages;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the database fixtures in the database.
|
||||
*/
|
||||
public function createFixturesStep(): array
|
||||
{
|
||||
$messages = [];
|
||||
|
||||
try {
|
||||
$this->installDatabaseFixtures();
|
||||
} catch (\Exception $exception) {
|
||||
$messages['error'] = $this->translator->trans(
|
||||
'mautic.installer.error.adding.fixtures',
|
||||
['%exception%' => $exception->getMessage()],
|
||||
'flashes'
|
||||
);
|
||||
}
|
||||
|
||||
return $messages;
|
||||
}
|
||||
|
||||
/**
|
||||
* Installs data fixtures for the application.
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function installDatabaseFixtures(): void
|
||||
{
|
||||
$fixtures = $this->fixturesLoader->getFixtures(['group_install']);
|
||||
|
||||
if (!$fixtures) {
|
||||
throw new \InvalidArgumentException('Could not find any fixtures to load with the "group_install" group.');
|
||||
}
|
||||
|
||||
$purger = new ORMPurger($this->entityManager);
|
||||
$purger->setPurgeMode(ORMPurger::PURGE_MODE_DELETE);
|
||||
$executor = new ORMExecutor($this->entityManager, $purger);
|
||||
/*
|
||||
* FIXME entity manager does not load configuration if local.php just created by CLI install
|
||||
* [error] An error occurred while attempting to add default data
|
||||
* An exception occured in driver:
|
||||
* SQLSTATE[HY000] [1045] Access refused for user: ''@'@localhost' (mot de passe: NON)
|
||||
*/
|
||||
$executor->execute($fixtures, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the administrator user.
|
||||
*/
|
||||
public function createAdminUserStep(array $data): array
|
||||
{
|
||||
$entityManager = $this->entityManager;
|
||||
|
||||
// ensure the username and email are unique
|
||||
try {
|
||||
/** @var User $existingUser */
|
||||
$existingUser = $entityManager->getRepository(User::class)->find(1);
|
||||
} catch (\Exception) {
|
||||
$existingUser = null;
|
||||
}
|
||||
|
||||
if (null !== $existingUser) {
|
||||
$user = $existingUser;
|
||||
} else {
|
||||
$user = new User();
|
||||
}
|
||||
|
||||
$required = [
|
||||
'firstname',
|
||||
'lastname',
|
||||
'username',
|
||||
'email',
|
||||
'password',
|
||||
];
|
||||
|
||||
$messages = [];
|
||||
foreach ($required as $r) {
|
||||
if (!isset($data[$r])) {
|
||||
$messages[$r] = $this->translator->trans(
|
||||
'mautic.core.value.required',
|
||||
[],
|
||||
'validators'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($messages)) {
|
||||
return $messages;
|
||||
}
|
||||
|
||||
$validations = [];
|
||||
|
||||
$emailConstraint = new Assert\Email();
|
||||
$emailConstraint->message = $this->translator->trans('mautic.core.email.required', [], 'validators');
|
||||
|
||||
$passwordConstraint = new Assert\Length(['min' => 6]);
|
||||
$passwordConstraint->minMessage = $this->translator->trans('mautic.install.password.minlength', [], 'validators');
|
||||
|
||||
$validations[] = $this->validator->validate($data['email'], $emailConstraint);
|
||||
$validations[] = $this->validator->validate($data['password'], $passwordConstraint);
|
||||
|
||||
$messages = [];
|
||||
foreach ($validations as $errors) {
|
||||
foreach ($errors as $error) {
|
||||
$messages[] = $error->getMessage();
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($messages)) {
|
||||
return $messages;
|
||||
}
|
||||
|
||||
$hasher = $this->hasher;
|
||||
|
||||
$user->setFirstName(InputHelper::clean($data['firstname']));
|
||||
$user->setLastName(InputHelper::clean($data['lastname']));
|
||||
$user->setUsername(InputHelper::clean($data['username']));
|
||||
$user->setEmail(InputHelper::email($data['email']));
|
||||
$user->setPassword($hasher->hashPassword($user, $data['password']));
|
||||
|
||||
$adminRole = null;
|
||||
try {
|
||||
$adminRole = $entityManager->getReference(Role::class, 1);
|
||||
} catch (\Exception $exception) {
|
||||
$messages['error'] = $this->translator->trans(
|
||||
'mautic.installer.error.getting.role',
|
||||
['%exception%' => $exception->getMessage()],
|
||||
'flashes'
|
||||
);
|
||||
}
|
||||
|
||||
if (!empty($adminRole)) {
|
||||
$user->setRole($adminRole);
|
||||
|
||||
try {
|
||||
$entityManager->persist($user);
|
||||
$entityManager->flush();
|
||||
} catch (\Exception $exception) {
|
||||
$messages['error'] = $this->translator->trans(
|
||||
'mautic.installer.error.creating.user',
|
||||
['%exception%' => $exception->getMessage()],
|
||||
'flashes'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return $messages;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the final configuration.
|
||||
*/
|
||||
public function createFinalConfigStep(string $siteUrl): array
|
||||
{
|
||||
// Merge final things into the config, wipe the container, and we're done!
|
||||
$finalConfigVars = [
|
||||
'secret_key' => EncryptionHelper::generateKey(),
|
||||
'site_url' => $siteUrl,
|
||||
];
|
||||
|
||||
return $this->saveConfiguration($finalConfigVars, null, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Final migration step for install.
|
||||
*/
|
||||
public function finalMigrationStep(): void
|
||||
{
|
||||
// Add database migrations up to this point since this is a fresh install (must be done at this point
|
||||
// after the cache has been rebuilt
|
||||
$input = new ArgvInput(['console', 'doctrine:migrations:version', '--add', '--all', '--no-interaction']);
|
||||
$output = new BufferedOutput();
|
||||
|
||||
$application = new Application($this->kernel);
|
||||
$application->setAutoExit(false);
|
||||
$application->run($input, $output);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user