chrisbryan17's picture
Upload folder using huggingface_hub
d2897cd verified
<?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);
}
}