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); } }