Initial commit: CloudOps infrastructure platform
This commit is contained in:
@@ -0,0 +1,26 @@
|
||||
var MauticLang = {};
|
||||
var MauticInstaller = {
|
||||
showWaitMessage: function(event) {
|
||||
event.preventDefault();
|
||||
|
||||
if (mQuery('#waitMessage').length) {
|
||||
mQuery('#stepNavigation').addClass('hide');
|
||||
mQuery('#waitMessage').removeClass('hide');
|
||||
}
|
||||
|
||||
mQuery('.btn-next').prop('disabled', true);
|
||||
mQuery('.btn-next').html('<i class=\"ri-loader-3-line ri-spin ri-fw\"></i>Please wait...');
|
||||
|
||||
setTimeout(function () {
|
||||
mQuery('form').submit();
|
||||
}, 10);
|
||||
},
|
||||
|
||||
toggleBackupPrefix: function() {
|
||||
if (mQuery('#install_doctrine_step_backup_tables_0').prop('checked')) {
|
||||
mQuery('#backupPrefix').addClass('hide');
|
||||
} else {
|
||||
mQuery('#backupPrefix').removeClass('hide');
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,458 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mautic\InstallBundle\Command;
|
||||
|
||||
use Doctrine\Persistence\ManagerRegistry;
|
||||
use Mautic\InstallBundle\Configurator\Step\CheckStep;
|
||||
use Mautic\InstallBundle\Configurator\Step\DoctrineStep;
|
||||
use Mautic\InstallBundle\Install\InstallService;
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Helper\QuestionHelper;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Question\ConfirmationQuestion;
|
||||
|
||||
/**
|
||||
* CLI Command to install Mautic.
|
||||
*/
|
||||
#[AsCommand(
|
||||
name: InstallCommand::COMMAND,
|
||||
description: 'Installs Mautic'
|
||||
)]
|
||||
class InstallCommand extends Command
|
||||
{
|
||||
public const COMMAND = 'mautic:install';
|
||||
|
||||
public function __construct(
|
||||
private InstallService $installer,
|
||||
private ManagerRegistry $doctrineRegistry,
|
||||
) {
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* Note: in every option (addOption()), please leave the default value empty to prevent problems with values from local.php being overwritten.
|
||||
*/
|
||||
protected function configure()
|
||||
{
|
||||
$this
|
||||
->setName(self::COMMAND)
|
||||
->setHelp('This command allows you to trigger the install process. It will try to get configuration values both from the local config file and command line options/arguments, where the latter takes precedence.')
|
||||
->addArgument(
|
||||
'site_url',
|
||||
InputArgument::REQUIRED,
|
||||
'Site URL.',
|
||||
null
|
||||
)
|
||||
->addArgument(
|
||||
'step',
|
||||
InputArgument::OPTIONAL,
|
||||
'Install process start index. 0 for requirements check, 1 for database, 2 for admin, 3 for configuration, 4 for final step. Each successful step will trigger the next until completion.',
|
||||
'0'
|
||||
)
|
||||
->addOption(
|
||||
'--force',
|
||||
'-f',
|
||||
InputOption::VALUE_NONE,
|
||||
'Do not ask confirmation if recommendations triggered.',
|
||||
null
|
||||
)
|
||||
->addOption(
|
||||
'--db_driver',
|
||||
null,
|
||||
InputOption::VALUE_REQUIRED,
|
||||
'Database driver.',
|
||||
null
|
||||
)
|
||||
->addOption(
|
||||
'--db_host',
|
||||
null,
|
||||
InputOption::VALUE_REQUIRED,
|
||||
'Database host.',
|
||||
null
|
||||
)
|
||||
->addOption(
|
||||
'--db_port',
|
||||
null,
|
||||
InputOption::VALUE_REQUIRED,
|
||||
'Database port.',
|
||||
null
|
||||
)
|
||||
->addOption(
|
||||
'--db_name',
|
||||
null,
|
||||
InputOption::VALUE_REQUIRED,
|
||||
'Database name.',
|
||||
null
|
||||
)
|
||||
->addOption(
|
||||
'--db_user',
|
||||
null,
|
||||
InputOption::VALUE_REQUIRED,
|
||||
'Database user.',
|
||||
null
|
||||
)
|
||||
->addOption(
|
||||
'--db_password',
|
||||
null,
|
||||
InputOption::VALUE_REQUIRED,
|
||||
'Database password.',
|
||||
null
|
||||
)
|
||||
->addOption(
|
||||
'--db_table_prefix',
|
||||
null,
|
||||
InputOption::VALUE_REQUIRED,
|
||||
'Database tables prefix.',
|
||||
null
|
||||
)
|
||||
->addOption(
|
||||
'--db_backup_tables',
|
||||
null,
|
||||
InputOption::VALUE_REQUIRED,
|
||||
'Backup database tables if they exist; otherwise drop them. (true|false)',
|
||||
null
|
||||
)
|
||||
->addOption(
|
||||
'--db_backup_prefix',
|
||||
null,
|
||||
InputOption::VALUE_REQUIRED,
|
||||
'Database backup tables prefix.',
|
||||
null
|
||||
)
|
||||
->addOption(
|
||||
'--admin_firstname',
|
||||
null,
|
||||
InputOption::VALUE_REQUIRED,
|
||||
'Admin first name.',
|
||||
null
|
||||
)
|
||||
->addOption(
|
||||
'--admin_lastname',
|
||||
null,
|
||||
InputOption::VALUE_REQUIRED,
|
||||
'Admin last name.',
|
||||
null
|
||||
)
|
||||
->addOption(
|
||||
'--admin_username',
|
||||
null,
|
||||
InputOption::VALUE_REQUIRED,
|
||||
'Admin username.',
|
||||
null
|
||||
)
|
||||
->addOption(
|
||||
'--admin_email',
|
||||
null,
|
||||
InputOption::VALUE_REQUIRED,
|
||||
'Admin email.',
|
||||
null
|
||||
)
|
||||
->addOption(
|
||||
'--admin_password',
|
||||
null,
|
||||
InputOption::VALUE_REQUIRED,
|
||||
'Admin user.',
|
||||
null
|
||||
);
|
||||
|
||||
parent::configure();
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
// Check Mautic is not already installed
|
||||
if ($this->installer->checkIfInstalled()) {
|
||||
$output->writeln('Mautic already installed');
|
||||
|
||||
return Command::SUCCESS;
|
||||
}
|
||||
|
||||
$output->writeln([
|
||||
'Mautic Install',
|
||||
'==============',
|
||||
'',
|
||||
]);
|
||||
|
||||
if (!defined('IS_PHPUNIT')) {
|
||||
// Prevents querying of database tables that do not exist during the installation process
|
||||
define('MAUTIC_INSTALLER', 1);
|
||||
}
|
||||
|
||||
// Build objects to pass to the install service from local.php or command line options
|
||||
$output->writeln('Parsing options and arguments...');
|
||||
$options = $input->getOptions();
|
||||
|
||||
// Convert boolean options to actual booleans.
|
||||
$options['db_backup_tables'] = (bool) filter_var($options['db_backup_tables'], FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE);
|
||||
|
||||
/**
|
||||
* We need to have some default database parameters, as it could be the case that the
|
||||
* user didn't set them both in local.php and the command line options.
|
||||
*/
|
||||
$dbParams = [
|
||||
'driver' => 'pdo_mysql',
|
||||
'host' => null,
|
||||
'port' => null,
|
||||
'name' => null,
|
||||
'user' => null,
|
||||
'password' => null,
|
||||
'table_prefix' => null,
|
||||
'backup_tables' => true,
|
||||
'backup_prefix' => 'bak_',
|
||||
];
|
||||
$adminParam = [
|
||||
'firstname' => 'Admin',
|
||||
'lastname' => 'Mautic',
|
||||
'username' => 'admin',
|
||||
];
|
||||
$allParams = $this->installer->localConfigParameters();
|
||||
|
||||
// Initialize DB and admin params from local.php
|
||||
foreach ((array) $allParams as $opt => $value) {
|
||||
if (str_starts_with($opt, 'db_')) {
|
||||
$dbParams[substr($opt, 3)] = $value;
|
||||
} elseif (str_starts_with($opt, 'admin_')) {
|
||||
$adminParam[substr($opt, 6)] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize DB and admin params from cli options
|
||||
foreach ($options as $opt => $value) {
|
||||
if (isset($value)) {
|
||||
if (str_starts_with($opt, 'db_')) {
|
||||
$dbParams[substr($opt, 3)] = $value;
|
||||
$allParams[$opt] = $value;
|
||||
} elseif (str_starts_with($opt, 'admin_')) {
|
||||
$adminParam[substr($opt, 6)] = $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($allParams['site_url'])) {
|
||||
$siteUrl = $allParams['site_url'];
|
||||
} else {
|
||||
$siteUrl = $input->getArgument('site_url');
|
||||
$allParams['site_url'] = $siteUrl;
|
||||
}
|
||||
|
||||
$step = (float) $input->getArgument('step');
|
||||
|
||||
switch ($step) {
|
||||
default:
|
||||
case InstallService::CHECK_STEP:
|
||||
$output->writeln($step.' - Checking installation requirements...');
|
||||
$messages = $this->stepAction($this->installer, ['site_url' => $siteUrl], $step);
|
||||
if (!empty($messages)) {
|
||||
if (isset($messages['requirements']) && !empty($messages['requirements'])) {
|
||||
// Stop install if requirements not met
|
||||
$output->writeln('Missing requirements:');
|
||||
$this->handleInstallerErrors($output, $messages['requirements']);
|
||||
$output->writeln('Install canceled');
|
||||
|
||||
return (int) -$step;
|
||||
} elseif (isset($messages['optional']) && !empty($messages['optional'])) {
|
||||
$output->writeln('Missing optional settings:');
|
||||
$this->handleInstallerErrors($output, $messages['optional']);
|
||||
|
||||
if (empty($options['force'])) {
|
||||
// Ask user to confirm install when optional settings missing
|
||||
|
||||
/** @var QuestionHelper $helper */
|
||||
$helper = $this->getHelper('question');
|
||||
$question = new ConfirmationQuestion('Continue with install anyway? [yes/no]', false);
|
||||
|
||||
if (!$helper->ask($input, $output, $question)) {
|
||||
return (int) -$step;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
$output->writeln('Ready to Install!');
|
||||
// Keep on with next step
|
||||
$step = InstallService::DOCTRINE_STEP;
|
||||
|
||||
// no break
|
||||
case InstallService::DOCTRINE_STEP:
|
||||
$output->writeln($step.' - Creating database...');
|
||||
|
||||
/**
|
||||
* This is needed for installations with database prefixes to work correctly.
|
||||
*/
|
||||
$connectionWrapper = $this->doctrineRegistry->getConnection();
|
||||
$connectionWrapper->initConnection($dbParams);
|
||||
|
||||
$messages = $this->stepAction($this->installer, $dbParams, $step);
|
||||
if (!empty($messages)) {
|
||||
$output->writeln('Errors in database configuration/installation:');
|
||||
$this->handleInstallerErrors($output, $messages);
|
||||
|
||||
$output->writeln('Install canceled');
|
||||
|
||||
return (int) -$step;
|
||||
}
|
||||
|
||||
$step = InstallService::DOCTRINE_STEP + .1;
|
||||
$output->writeln($step.' - Creating schema...');
|
||||
$messages = $this->stepAction($this->installer, $dbParams, $step);
|
||||
if (!empty($messages)) {
|
||||
$output->writeln('Errors in schema configuration/installation:');
|
||||
$this->handleInstallerErrors($output, $messages);
|
||||
|
||||
$output->writeln('Install canceled');
|
||||
|
||||
return -InstallService::DOCTRINE_STEP;
|
||||
}
|
||||
|
||||
$step = InstallService::DOCTRINE_STEP + .2;
|
||||
$output->writeln($step.' - Loading fixtures...');
|
||||
$messages = $this->stepAction($this->installer, $dbParams, $step);
|
||||
if (!empty($messages)) {
|
||||
$output->writeln('Errors in fixtures configuration/installation:');
|
||||
$this->handleInstallerErrors($output, $messages);
|
||||
|
||||
$output->writeln('Install canceled');
|
||||
|
||||
return -InstallService::DOCTRINE_STEP;
|
||||
}
|
||||
|
||||
// Keep on with next step
|
||||
$step = InstallService::USER_STEP;
|
||||
|
||||
// no break
|
||||
case InstallService::USER_STEP:
|
||||
$output->writeln($step.' - Creating admin user...');
|
||||
$messages = $this->stepAction($this->installer, $adminParam, $step);
|
||||
if (!empty($messages)) {
|
||||
$output->writeln('Errors in admin user configuration/installation:');
|
||||
$this->handleInstallerErrors($output, $messages);
|
||||
|
||||
$output->writeln('Install canceled');
|
||||
|
||||
return (int) -$step;
|
||||
}
|
||||
// Keep on with next step
|
||||
$step = InstallService::FINAL_STEP;
|
||||
|
||||
// no break
|
||||
case InstallService::FINAL_STEP:
|
||||
$output->writeln($step.' - Final steps...');
|
||||
$messages = $this->stepAction($this->installer, $allParams, $step);
|
||||
if (!empty($messages)) {
|
||||
$output->writeln('Errors in final step:');
|
||||
$this->handleInstallerErrors($output, $messages);
|
||||
|
||||
$output->writeln('Install canceled');
|
||||
|
||||
return (int) -$step;
|
||||
}
|
||||
}
|
||||
|
||||
$output->writeln([
|
||||
'',
|
||||
'================',
|
||||
'Install complete',
|
||||
'================',
|
||||
]);
|
||||
|
||||
return Command::SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Controller action for install steps.
|
||||
*
|
||||
* @param InstallService $installer The install process
|
||||
* @param array $params The install parameters
|
||||
* @param float $index The step number to process
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
protected function stepAction(InstallService $installer, array $params, float $index = 0): array
|
||||
{
|
||||
if ($index - floor($index) > 0) {
|
||||
$subIndex = (int) (round($index - floor($index), 1) * 10);
|
||||
$index = floor($index);
|
||||
}
|
||||
$index = (int) $index;
|
||||
|
||||
$messages = [];
|
||||
|
||||
switch ($index) {
|
||||
case InstallService::CHECK_STEP:
|
||||
// Check installation requirements
|
||||
$step = $installer->getStep($index);
|
||||
if ($step instanceof CheckStep) {
|
||||
// Set all step fields based on parameters
|
||||
$step->site_url = $params['site_url'];
|
||||
}
|
||||
|
||||
$messages['requirements'] = $installer->checkRequirements($step);
|
||||
$messages['optional'] = $installer->checkOptionalSettings($step);
|
||||
break;
|
||||
|
||||
case InstallService::DOCTRINE_STEP:
|
||||
$step = $installer->getStep($index);
|
||||
if ($step instanceof DoctrineStep) {
|
||||
// Set all step fields based on parameters
|
||||
foreach ($step as $key => $value) {
|
||||
if (isset($params[$key])) {
|
||||
$step->$key = $params[$key];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!isset($subIndex)) {
|
||||
// Install database
|
||||
$messages = $installer->createDatabaseStep($step, $params);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
switch ($subIndex) {
|
||||
case 1:
|
||||
// Install schema
|
||||
$messages = $installer->createSchemaStep($params);
|
||||
break;
|
||||
|
||||
case 2:
|
||||
// Install fixtures
|
||||
$messages = $installer->createFixturesStep();
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case InstallService::USER_STEP:
|
||||
// Create admin user
|
||||
$messages = $installer->createAdminUserStep($params);
|
||||
break;
|
||||
|
||||
case InstallService::FINAL_STEP:
|
||||
// Save final configuration
|
||||
$siteUrl = $params['site_url'];
|
||||
$messages = $installer->createFinalConfigStep($siteUrl);
|
||||
if (empty($messages)) {
|
||||
$installer->finalMigrationStep();
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return $messages;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle install command errors.
|
||||
*
|
||||
* @param array<string,string> $messages
|
||||
*/
|
||||
private function handleInstallerErrors(OutputInterface $output, array $messages): void
|
||||
{
|
||||
foreach ($messages as $type => $message) {
|
||||
$output->writeln(" - [$type] $message");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'routes' => [
|
||||
'public' => [
|
||||
'mautic_installer_home' => [
|
||||
'path' => '/installer',
|
||||
'controller' => 'Mautic\InstallBundle\Controller\InstallController::stepAction',
|
||||
],
|
||||
'mautic_installer_remove_slash' => [
|
||||
'path' => '/installer/',
|
||||
'controller' => 'Mautic\CoreBundle\Controller\CommonController::removeTrailingSlashAction',
|
||||
],
|
||||
'mautic_installer_step' => [
|
||||
'path' => '/installer/step/{index}',
|
||||
'controller' => 'Mautic\InstallBundle\Controller\InstallController::stepAction',
|
||||
],
|
||||
'mautic_installer_final' => [
|
||||
'path' => '/installer/final',
|
||||
'controller' => 'Mautic\InstallBundle\Controller\InstallController::finalAction',
|
||||
],
|
||||
'mautic_installer_catchcall' => [
|
||||
'path' => '/installer/{noerror}',
|
||||
'controller' => 'Mautic\InstallBundle\Controller\InstallController::stepAction',
|
||||
'requirements' => [
|
||||
'noerror' => '^(?).+',
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
|
||||
'services' => [
|
||||
'fixtures' => [
|
||||
'mautic.install.fixture.lead_field' => [
|
||||
'class' => Mautic\InstallBundle\InstallFixtures\ORM\LeadFieldData::class,
|
||||
'tag' => Doctrine\Bundle\FixturesBundle\DependencyInjection\CompilerPass\FixturesCompilerPass::FIXTURE_TAG,
|
||||
'arguments' => ['translator'],
|
||||
],
|
||||
'mautic.install.fixture.role' => [
|
||||
'class' => Mautic\InstallBundle\InstallFixtures\ORM\RoleData::class,
|
||||
'tag' => Doctrine\Bundle\FixturesBundle\DependencyInjection\CompilerPass\FixturesCompilerPass::FIXTURE_TAG,
|
||||
'arguments' => ['translator'],
|
||||
],
|
||||
'mautic.install.fixture.report_data' => [
|
||||
'class' => Mautic\InstallBundle\InstallFixtures\ORM\LoadReportData::class,
|
||||
'tag' => Doctrine\Bundle\FixturesBundle\DependencyInjection\CompilerPass\FixturesCompilerPass::FIXTURE_TAG,
|
||||
'arguments' => [],
|
||||
],
|
||||
],
|
||||
'other' => [
|
||||
'mautic.install.configurator.step.check' => [
|
||||
'class' => Mautic\InstallBundle\Configurator\Step\CheckStep::class,
|
||||
'arguments' => [
|
||||
'mautic.configurator',
|
||||
'%kernel.project_dir%',
|
||||
'request_stack',
|
||||
'mautic.cipher.openssl',
|
||||
],
|
||||
'tag' => 'mautic.configurator.step',
|
||||
'tagArguments' => [
|
||||
'priority' => 0,
|
||||
],
|
||||
],
|
||||
'mautic.install.configurator.step.doctrine' => [
|
||||
'class' => Mautic\InstallBundle\Configurator\Step\DoctrineStep::class,
|
||||
'arguments' => [
|
||||
'mautic.configurator',
|
||||
],
|
||||
'tag' => 'mautic.configurator.step',
|
||||
'tagArguments' => [
|
||||
'priority' => 1,
|
||||
],
|
||||
],
|
||||
'mautic.install.configurator.step.user' => [
|
||||
'class' => Mautic\InstallBundle\Configurator\Step\UserStep::class,
|
||||
'tag' => 'mautic.configurator.step',
|
||||
'tagArguments' => [
|
||||
'priority' => 2,
|
||||
],
|
||||
],
|
||||
'mautic.install.service' => [
|
||||
'class' => Mautic\InstallBundle\Install\InstallService::class,
|
||||
'arguments' => [
|
||||
'mautic.configurator',
|
||||
'mautic.helper.cache',
|
||||
'mautic.helper.paths',
|
||||
'doctrine.orm.entity_manager',
|
||||
'translator',
|
||||
'kernel',
|
||||
'validator',
|
||||
'security.password_hasher',
|
||||
'mautic.doctrine.loader.mautic_fixtures_loader',
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
];
|
||||
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Mautic\CoreBundle\DependencyInjection\MauticCoreExtension;
|
||||
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
|
||||
|
||||
return function (ContainerConfigurator $configurator): void {
|
||||
$services = $configurator->services()
|
||||
->defaults()
|
||||
->autowire()
|
||||
->autoconfigure()
|
||||
->public();
|
||||
|
||||
$excludes = [
|
||||
'Helper/SchemaHelper.php',
|
||||
];
|
||||
|
||||
$services->load('Mautic\\InstallBundle\\', '../')
|
||||
->exclude('../{'.implode(',', array_merge(MauticCoreExtension::DEFAULT_EXCLUDES, $excludes)).'}');
|
||||
};
|
||||
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\InstallBundle\Configurator\Form;
|
||||
|
||||
use Mautic\CoreBundle\Form\Type\FormButtonsType;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\HiddenType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
|
||||
/**
|
||||
* @extends AbstractType<mixed>
|
||||
*/
|
||||
class CheckStepType extends AbstractType
|
||||
{
|
||||
public function buildForm(FormBuilderInterface $builder, array $options): void
|
||||
{
|
||||
$builder->add(
|
||||
'buttons',
|
||||
FormButtonsType::class,
|
||||
[
|
||||
'pre_extra_buttons' => [
|
||||
[
|
||||
'name' => 'next',
|
||||
'label' => 'mautic.install.next.step',
|
||||
'type' => 'submit',
|
||||
'attr' => [
|
||||
'class' => 'btn btn-success pull-right btn-next',
|
||||
'icon' => 'ri-arrow-right-circle-line',
|
||||
'onclick' => 'MauticInstaller.showWaitMessage(event);',
|
||||
],
|
||||
],
|
||||
],
|
||||
'apply_text' => '',
|
||||
'save_text' => '',
|
||||
'cancel_text' => '',
|
||||
]
|
||||
);
|
||||
|
||||
$builder->add('site_url', HiddenType::class);
|
||||
$builder->add('cache_path', HiddenType::class);
|
||||
$builder->add('log_path', HiddenType::class);
|
||||
|
||||
if (!empty($options['action'])) {
|
||||
$builder->setAction($options['action']);
|
||||
}
|
||||
}
|
||||
|
||||
public function getBlockPrefix(): string
|
||||
{
|
||||
return 'install_check_step';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,177 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\InstallBundle\Configurator\Form;
|
||||
|
||||
use Mautic\CoreBundle\Form\Type\FormButtonsType;
|
||||
use Mautic\CoreBundle\Form\Type\YesNoButtonGroupType;
|
||||
use Mautic\InstallBundle\Configurator\Step\DoctrineStep;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\PasswordType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\TextType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\Validator\Constraints\Choice;
|
||||
|
||||
/**
|
||||
* Doctrine Form Type.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* @note This class is based on Sensio\Bundle\DistributionBundle\Configurator\Form\DoctrineStepType
|
||||
*
|
||||
* @extends AbstractType<mixed>
|
||||
*/
|
||||
class DoctrineStepType extends AbstractType
|
||||
{
|
||||
public function buildForm(FormBuilderInterface $builder, array $options): void
|
||||
{
|
||||
$builder->add(
|
||||
'driver',
|
||||
ChoiceType::class,
|
||||
[
|
||||
'choices' => array_flip(DoctrineStep::getDrivers()),
|
||||
'expanded' => false,
|
||||
'multiple' => false,
|
||||
'label' => 'mautic.install.form.database.driver',
|
||||
'label_attr' => ['class' => 'control-label'],
|
||||
'placeholder' => false,
|
||||
'required' => true,
|
||||
'attr' => [
|
||||
'class' => 'form-control',
|
||||
],
|
||||
'constraints' => [
|
||||
new Choice(
|
||||
[
|
||||
'callback' => '\Mautic\InstallBundle\Configurator\Step\DoctrineStep::getDriverKeys',
|
||||
]
|
||||
),
|
||||
],
|
||||
]
|
||||
);
|
||||
|
||||
$builder->add(
|
||||
'host',
|
||||
TextType::class,
|
||||
[
|
||||
'label' => 'mautic.install.form.database.host',
|
||||
'label_attr' => ['class' => 'control-label'],
|
||||
'attr' => ['class' => 'form-control'],
|
||||
'required' => true,
|
||||
]
|
||||
);
|
||||
|
||||
$builder->add(
|
||||
'port',
|
||||
TextType::class,
|
||||
[
|
||||
'label' => 'mautic.install.form.database.port',
|
||||
'label_attr' => ['class' => 'control-label'],
|
||||
'attr' => ['class' => 'form-control'],
|
||||
'required' => false,
|
||||
]
|
||||
);
|
||||
|
||||
$builder->add(
|
||||
'name',
|
||||
TextType::class,
|
||||
[
|
||||
'label' => 'mautic.install.form.database.name',
|
||||
'label_attr' => ['class' => 'control-label'],
|
||||
'attr' => ['class' => 'form-control'],
|
||||
'required' => true,
|
||||
]
|
||||
);
|
||||
|
||||
$builder->add(
|
||||
'table_prefix',
|
||||
TextType::class,
|
||||
[
|
||||
'label' => 'mautic.install.form.database.table.prefix',
|
||||
'label_attr' => ['class' => 'control-label'],
|
||||
'attr' => ['class' => 'form-control'],
|
||||
'required' => false,
|
||||
]
|
||||
);
|
||||
|
||||
$builder->add(
|
||||
'user',
|
||||
TextType::class,
|
||||
[
|
||||
'label' => 'mautic.install.form.database.user',
|
||||
'label_attr' => ['class' => 'control-label'],
|
||||
'attr' => ['class' => 'form-control'],
|
||||
'required' => true,
|
||||
]
|
||||
);
|
||||
|
||||
$builder->add(
|
||||
'password',
|
||||
PasswordType::class,
|
||||
[
|
||||
'label' => 'mautic.install.form.database.password',
|
||||
'label_attr' => ['class' => 'control-label'],
|
||||
'attr' => [
|
||||
'class' => 'form-control',
|
||||
'preaddon' => 'ri-lock-fill',
|
||||
],
|
||||
'required' => false,
|
||||
]
|
||||
);
|
||||
|
||||
$builder->add(
|
||||
'backup_tables',
|
||||
YesNoButtonGroupType::class,
|
||||
[
|
||||
'label' => 'mautic.install.form.existing_tables',
|
||||
'attr' => [
|
||||
'tooltip' => 'mautic.install.form.existing_tables_descr',
|
||||
'onchange' => 'MauticInstaller.toggleBackupPrefix();',
|
||||
],
|
||||
]
|
||||
);
|
||||
|
||||
$builder->add(
|
||||
'backup_prefix',
|
||||
TextType::class,
|
||||
[
|
||||
'label' => 'mautic.install.form.backup_prefix',
|
||||
'label_attr' => ['class' => 'control-label'],
|
||||
'attr' => [
|
||||
'class' => 'form-control',
|
||||
],
|
||||
'required' => false,
|
||||
]
|
||||
);
|
||||
|
||||
$builder->add(
|
||||
'buttons',
|
||||
FormButtonsType::class,
|
||||
[
|
||||
'pre_extra_buttons' => [
|
||||
[
|
||||
'name' => 'next',
|
||||
'label' => 'mautic.install.next.step',
|
||||
'type' => 'submit',
|
||||
'attr' => [
|
||||
'class' => 'btn btn-success pull-right btn-next',
|
||||
'icon' => 'ri-arrow-right-circle-line',
|
||||
'onclick' => 'MauticInstaller.showWaitMessage(event);',
|
||||
],
|
||||
],
|
||||
],
|
||||
'apply_text' => '',
|
||||
'save_text' => '',
|
||||
'cancel_text' => '',
|
||||
]
|
||||
);
|
||||
|
||||
if (!empty($options['action'])) {
|
||||
$builder->setAction($options['action']);
|
||||
}
|
||||
}
|
||||
|
||||
public function getBlockPrefix(): string
|
||||
{
|
||||
return 'install_doctrine_step';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,177 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\InstallBundle\Configurator\Form;
|
||||
|
||||
use Mautic\CoreBundle\Form\Type\FormButtonsType;
|
||||
use Mautic\UserBundle\Form\Validator\Constraints\NotWeak;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\EmailType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\PasswordType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\TextType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\HttpFoundation\RequestStack;
|
||||
use Symfony\Component\Validator\Constraints as Assert;
|
||||
|
||||
/**
|
||||
* @extends AbstractType<mixed>
|
||||
*/
|
||||
class UserStepType extends AbstractType
|
||||
{
|
||||
public function __construct(
|
||||
private RequestStack $requestStack,
|
||||
) {
|
||||
}
|
||||
|
||||
public function buildForm(FormBuilderInterface $builder, array $options): void
|
||||
{
|
||||
$storedData = $this->requestStack->getSession()->get('mautic.installer.user', new \stdClass());
|
||||
|
||||
$builder->add(
|
||||
'firstname',
|
||||
TextType::class,
|
||||
[
|
||||
'label' => 'mautic.core.firstname',
|
||||
'label_attr' => ['class' => 'control-label'],
|
||||
'attr' => ['class' => 'form-control'],
|
||||
'required' => true,
|
||||
'data' => (!empty($storedData->firstname)) ? $storedData->firstname : '',
|
||||
'constraints' => [
|
||||
new Assert\NotBlank(
|
||||
[
|
||||
'message' => 'mautic.core.value.required',
|
||||
]
|
||||
),
|
||||
],
|
||||
]
|
||||
);
|
||||
|
||||
$builder->add(
|
||||
'lastname',
|
||||
TextType::class,
|
||||
[
|
||||
'label' => 'mautic.core.lastname',
|
||||
'label_attr' => ['class' => 'control-label'],
|
||||
'attr' => ['class' => 'form-control'],
|
||||
'required' => true,
|
||||
'data' => (!empty($storedData->lastname)) ? $storedData->lastname : '',
|
||||
'constraints' => [
|
||||
new Assert\NotBlank(
|
||||
[
|
||||
'message' => 'mautic.core.value.required',
|
||||
]
|
||||
),
|
||||
],
|
||||
]
|
||||
);
|
||||
|
||||
$builder->add(
|
||||
'email',
|
||||
EmailType::class,
|
||||
[
|
||||
'label' => 'mautic.install.form.user.email',
|
||||
'label_attr' => ['class' => 'control-label'],
|
||||
'attr' => [
|
||||
'class' => 'form-control',
|
||||
'preaddon' => 'ri-mail-line',
|
||||
],
|
||||
'required' => true,
|
||||
'data' => (!empty($storedData->email)) ? $storedData->email : '',
|
||||
'constraints' => [
|
||||
new Assert\NotBlank(
|
||||
[
|
||||
'message' => 'mautic.core.value.required',
|
||||
]
|
||||
),
|
||||
new Assert\Email(
|
||||
[
|
||||
'message' => 'mautic.core.email.required',
|
||||
]
|
||||
),
|
||||
],
|
||||
]
|
||||
);
|
||||
|
||||
$builder->add(
|
||||
'username',
|
||||
TextType::class,
|
||||
[
|
||||
'label' => 'mautic.install.form.user.username',
|
||||
'label_attr' => ['class' => 'control-label'],
|
||||
'attr' => [
|
||||
'class' => 'form-control',
|
||||
],
|
||||
'required' => true,
|
||||
'data' => (!empty($storedData->username)) ? $storedData->username : '',
|
||||
'constraints' => [
|
||||
new Assert\NotBlank(
|
||||
[
|
||||
'message' => 'mautic.core.value.required',
|
||||
]
|
||||
),
|
||||
],
|
||||
]
|
||||
);
|
||||
|
||||
$builder->add(
|
||||
'password',
|
||||
PasswordType::class,
|
||||
[
|
||||
'label' => 'mautic.install.form.user.password',
|
||||
'label_attr' => ['class' => 'control-label'],
|
||||
'attr' => [
|
||||
'class' => 'form-control',
|
||||
'tooltip' => 'mautic.user.user.form.help.passwordrequirements',
|
||||
'preaddon' => 'ri-lock-fill',
|
||||
],
|
||||
'required' => true,
|
||||
'constraints' => [
|
||||
new Assert\NotBlank(
|
||||
[
|
||||
'message' => 'mautic.core.value.required',
|
||||
]
|
||||
),
|
||||
new Assert\Length(
|
||||
[
|
||||
'min' => 6,
|
||||
'minMessage' => 'mautic.install.password.minlength',
|
||||
]
|
||||
),
|
||||
new NotWeak([
|
||||
'message' => 'mautic.user.user.password.weak',
|
||||
]),
|
||||
],
|
||||
]
|
||||
);
|
||||
|
||||
$builder->add(
|
||||
'buttons',
|
||||
FormButtonsType::class,
|
||||
[
|
||||
'pre_extra_buttons' => [
|
||||
[
|
||||
'name' => 'next',
|
||||
'label' => 'mautic.install.next.step',
|
||||
'type' => 'submit',
|
||||
'attr' => [
|
||||
'class' => 'btn btn-success pull-right btn-next',
|
||||
'icon' => 'ri-arrow-right-circle-line',
|
||||
'onclick' => 'MauticInstaller.showWaitMessage(event);',
|
||||
],
|
||||
],
|
||||
],
|
||||
'apply_text' => '',
|
||||
'save_text' => '',
|
||||
'cancel_text' => '',
|
||||
]
|
||||
);
|
||||
|
||||
if (!empty($options['action'])) {
|
||||
$builder->setAction($options['action']);
|
||||
}
|
||||
}
|
||||
|
||||
public function getBlockPrefix(): string
|
||||
{
|
||||
return 'install_user_step';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,249 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\InstallBundle\Configurator\Step;
|
||||
|
||||
use Mautic\CoreBundle\Configurator\Configurator;
|
||||
use Mautic\CoreBundle\Configurator\Step\StepInterface;
|
||||
use Mautic\CoreBundle\Helper\FileHelper;
|
||||
use Mautic\CoreBundle\Security\Cryptography\Cipher\Symmetric\OpenSSLCipher;
|
||||
use Mautic\InstallBundle\Configurator\Form\CheckStepType;
|
||||
use Symfony\Component\HttpFoundation\RequestStack;
|
||||
|
||||
class CheckStep implements StepInterface
|
||||
{
|
||||
/**
|
||||
* Flag if the configuration file is writable.
|
||||
*/
|
||||
private bool $configIsWritable;
|
||||
|
||||
/**
|
||||
* Absolute path to cache directory.
|
||||
* Required in step.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $cache_path = '%kernel.project_dir%/var/cache';
|
||||
|
||||
/**
|
||||
* Absolute path to log directory.
|
||||
* Required in step.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $log_path = '%kernel.project_dir%/var/logs';
|
||||
|
||||
/**
|
||||
* Set the domain URL for use in getting the absolute URL for cli/cronjob generated URLs.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $site_url = '';
|
||||
|
||||
/**
|
||||
* Recommended minimum memory limit for Mautic.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public const RECOMMENDED_MEMORY_LIMIT = '512M';
|
||||
|
||||
/**
|
||||
* @param Configurator $configurator Configurator service
|
||||
* @param string $projectDir Kernel root path
|
||||
* @param RequestStack $requestStack Request stack
|
||||
*/
|
||||
public function __construct(
|
||||
Configurator $configurator,
|
||||
private string $projectDir,
|
||||
RequestStack $requestStack,
|
||||
private OpenSSLCipher $openSSLCipher,
|
||||
) {
|
||||
$request = $requestStack->getCurrentRequest();
|
||||
|
||||
$this->configIsWritable = $configurator->isFileWritable();
|
||||
if (!empty($request)) {
|
||||
$this->site_url = $request->getSchemeAndHttpHost().$request->getBasePath();
|
||||
}
|
||||
}
|
||||
|
||||
public function getFormType(): string
|
||||
{
|
||||
return CheckStepType::class;
|
||||
}
|
||||
|
||||
public function checkRequirements(): array
|
||||
{
|
||||
$messages = [];
|
||||
|
||||
if (!is_dir($this->projectDir.'/vendor/composer')) {
|
||||
$messages[] = 'mautic.install.composer.dependencies';
|
||||
}
|
||||
|
||||
if (!$this->configIsWritable) {
|
||||
$messages[] = 'mautic.install.config.unwritable';
|
||||
}
|
||||
|
||||
if (!is_writable(str_replace('%kernel.project_dir%', $this->projectDir, $this->cache_path))) {
|
||||
$messages[] = 'mautic.install.cache.unwritable';
|
||||
}
|
||||
|
||||
if (!is_writable(str_replace('%kernel.project_dir%', $this->projectDir, $this->log_path))) {
|
||||
$messages[] = 'mautic.install.logs.unwritable';
|
||||
}
|
||||
|
||||
$timezones = [];
|
||||
|
||||
foreach (\DateTimeZone::listAbbreviations() as $abbreviations) {
|
||||
foreach ($abbreviations as $abbreviation) {
|
||||
$timezones[$abbreviation['timezone_id']] = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!isset($timezones[date_default_timezone_get()])) {
|
||||
$messages[] = 'mautic.install.timezone.not.supported';
|
||||
}
|
||||
|
||||
if (!function_exists('json_encode')) {
|
||||
$messages[] = 'mautic.install.function.jsonencode';
|
||||
}
|
||||
|
||||
if (!function_exists('session_start')) {
|
||||
$messages[] = 'mautic.install.function.sessionstart';
|
||||
}
|
||||
|
||||
if (!function_exists('ctype_alpha')) {
|
||||
$messages[] = 'mautic.install.function.ctypealpha';
|
||||
}
|
||||
|
||||
if (!function_exists('token_get_all')) {
|
||||
$messages[] = 'mautic.install.function.tokengetall';
|
||||
}
|
||||
|
||||
if (!function_exists('simplexml_import_dom')) {
|
||||
$messages[] = 'mautic.install.function.simplexml';
|
||||
}
|
||||
|
||||
if (false === $this->openSSLCipher->isSupported()) {
|
||||
$messages[] = 'mautic.install.extension.openssl';
|
||||
}
|
||||
|
||||
if (!function_exists('curl_init')) {
|
||||
$messages[] = 'mautic.install.extension.curl';
|
||||
}
|
||||
|
||||
if (!function_exists('finfo_open')) {
|
||||
$messages[] = 'mautic.install.extension.fileinfo';
|
||||
}
|
||||
|
||||
if (!function_exists('mb_strtolower')) {
|
||||
$messages[] = 'mautic.install.extension.mbstring';
|
||||
}
|
||||
|
||||
if (extension_loaded('xdebug')) {
|
||||
if (ini_get('xdebug.show_exception_trace')) {
|
||||
$messages[] = 'mautic.install.xdebug.exception.trace';
|
||||
}
|
||||
|
||||
if (ini_get('xdebug.scream')) {
|
||||
$messages[] = 'mautic.install.xdebug.scream';
|
||||
}
|
||||
}
|
||||
|
||||
return $messages;
|
||||
}
|
||||
|
||||
public function checkOptionalSettings(): array
|
||||
{
|
||||
$messages = [];
|
||||
|
||||
if (extension_loaded('xdebug')) {
|
||||
$cfgValue = ini_get('xdebug.max_nesting_level');
|
||||
|
||||
if ($cfgValue <= 100) {
|
||||
$messages[] = 'mautic.install.xdebug.nesting';
|
||||
}
|
||||
}
|
||||
|
||||
if (!extension_loaded('zip')) {
|
||||
$messages[] = 'mautic.install.extension.zip';
|
||||
}
|
||||
|
||||
// We set a default timezone in the app bootstrap, but advise the user if their PHP config is missing it
|
||||
if (!ini_get('date.timezone')) {
|
||||
$messages[] = 'mautic.install.date.timezone.not.set';
|
||||
}
|
||||
|
||||
if (!class_exists('\\DomDocument')) {
|
||||
$messages[] = 'mautic.install.module.phpxml';
|
||||
}
|
||||
|
||||
if (!function_exists('iconv')) {
|
||||
$messages[] = 'mautic.install.function.iconv';
|
||||
}
|
||||
|
||||
if (!extension_loaded('xml')) {
|
||||
$messages[] = 'mautic.install.function.xml';
|
||||
}
|
||||
|
||||
if (!function_exists('imap_open')) {
|
||||
$messages[] = 'mautic.install.extension.imap';
|
||||
}
|
||||
|
||||
if (!$this->site_url || !str_starts_with($this->site_url, 'https')) {
|
||||
$messages[] = 'mautic.install.ssl.certificate';
|
||||
}
|
||||
|
||||
if (!defined('PHP_WINDOWS_VERSION_BUILD')) {
|
||||
if (!function_exists('posix_isatty')) {
|
||||
$messages[] = 'mautic.install.function.posix.enable';
|
||||
}
|
||||
}
|
||||
|
||||
$memoryLimit = FileHelper::convertPHPSizeToBytes(ini_get('memory_limit'));
|
||||
$suggestedLimit = FileHelper::convertPHPSizeToBytes(self::RECOMMENDED_MEMORY_LIMIT);
|
||||
if ($memoryLimit > -1 && $memoryLimit < $suggestedLimit) {
|
||||
$messages[] = 'mautic.install.memory.limit';
|
||||
}
|
||||
|
||||
if (!class_exists('\\Locale')) {
|
||||
$messages[] = 'mautic.install.module.intl';
|
||||
}
|
||||
|
||||
if (class_exists('\\Collator')) {
|
||||
try {
|
||||
if (is_null(new \Collator('fr_FR'))) {
|
||||
$messages[] = 'mautic.install.intl.config';
|
||||
}
|
||||
} catch (\Exception) {
|
||||
$messages[] = 'mautic.install.intl.config';
|
||||
}
|
||||
}
|
||||
|
||||
if (-1 !== (int) ini_get('zend.assertions')) {
|
||||
$messages[] = 'mautic.install.zend_assertions';
|
||||
}
|
||||
|
||||
return $messages;
|
||||
}
|
||||
|
||||
public function getTemplate(): string
|
||||
{
|
||||
return '@MauticInstall/Install/check.html.twig';
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed[]
|
||||
*/
|
||||
public function update(StepInterface $data): array
|
||||
{
|
||||
$parameters = [];
|
||||
|
||||
foreach ($data as $key => $value) {
|
||||
// Exclude keys from the config
|
||||
if (!in_array($key, ['configIsWritable', 'projectDir'])) {
|
||||
$parameters[$key] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
return $parameters;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,169 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\InstallBundle\Configurator\Step;
|
||||
|
||||
use Mautic\CoreBundle\Configurator\Configurator;
|
||||
use Mautic\CoreBundle\Configurator\Step\StepInterface;
|
||||
use Mautic\InstallBundle\Configurator\Form\DoctrineStepType;
|
||||
|
||||
/**
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class DoctrineStep implements StepInterface
|
||||
{
|
||||
/**
|
||||
* Database driver.
|
||||
*/
|
||||
public $driver = 'pdo_mysql';
|
||||
|
||||
/**
|
||||
* Database host.
|
||||
*/
|
||||
public $host = 'localhost';
|
||||
|
||||
/**
|
||||
* Database host. Read Only Replica.
|
||||
*/
|
||||
public ?string $host_ro = null;
|
||||
|
||||
/**
|
||||
* Database table prefix.
|
||||
* Required in step.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $table_prefix;
|
||||
|
||||
/**
|
||||
* Database connection port.
|
||||
*/
|
||||
public $port = 3306;
|
||||
|
||||
/**
|
||||
* Database name.
|
||||
*/
|
||||
public $name;
|
||||
|
||||
/**
|
||||
* Database user.
|
||||
*/
|
||||
public $user;
|
||||
|
||||
/**
|
||||
* Database user's password.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $password;
|
||||
|
||||
/**
|
||||
* Backup tables if they exist; otherwise drop them.
|
||||
* Required in step.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
public $backup_tables = true;
|
||||
|
||||
/**
|
||||
* Prefix for backup tables.
|
||||
* Required in step.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $backup_prefix = 'bak_';
|
||||
|
||||
public ?string $server_version;
|
||||
|
||||
public function __construct(Configurator $configurator)
|
||||
{
|
||||
$parameters = $configurator->getParameters();
|
||||
|
||||
foreach ($parameters as $key => $value) {
|
||||
if (str_starts_with($key, 'db_')) {
|
||||
$parameters[substr($key, 3)] = $value;
|
||||
$key = substr($key, 3);
|
||||
$this->$key = $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function getFormType(): string
|
||||
{
|
||||
return DoctrineStepType::class;
|
||||
}
|
||||
|
||||
public function checkRequirements(): array
|
||||
{
|
||||
$messages = [];
|
||||
|
||||
if (!class_exists('\PDO')) {
|
||||
$messages[] = 'mautic.install.pdo.mandatory';
|
||||
} else {
|
||||
if (!in_array('mysql', \PDO::getAvailableDrivers(), true)) {
|
||||
$messages[] = 'mautic.install.pdo.drivers';
|
||||
}
|
||||
}
|
||||
|
||||
return $messages;
|
||||
}
|
||||
|
||||
public function checkOptionalSettings(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed[]
|
||||
*/
|
||||
public function update(StepInterface $data): array
|
||||
{
|
||||
$parameters = [];
|
||||
|
||||
foreach ($data as $key => $value) {
|
||||
$parameters['db_'.$key] = $value;
|
||||
}
|
||||
|
||||
return $parameters;
|
||||
}
|
||||
|
||||
public function getTemplate(): string
|
||||
{
|
||||
return '@MauticInstall/Install/doctrine.html.twig';
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the key values of the available driver array.
|
||||
* Required in step.
|
||||
*
|
||||
* @see \Mautic\InstallBundle\Configurator\Form\DoctrineStepType::buildForm()
|
||||
*/
|
||||
public static function getDriverKeys(): array
|
||||
{
|
||||
return array_keys(static::getDrivers());
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches the available database drivers for the environment.
|
||||
*/
|
||||
public static function getDrivers(): array
|
||||
{
|
||||
$mauticSupported = [
|
||||
'pdo_mysql' => 'MySQL PDO (Recommended)',
|
||||
];
|
||||
|
||||
$supported = [];
|
||||
|
||||
// Add PDO drivers if supported
|
||||
if (class_exists('\PDO')) {
|
||||
$pdoDrivers = \PDO::getAvailableDrivers();
|
||||
|
||||
foreach ($pdoDrivers as $driver) {
|
||||
if (array_key_exists('pdo_'.$driver, $mauticSupported)) {
|
||||
$supported['pdo_'.$driver] = $mauticSupported['pdo_'.$driver];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $supported;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\InstallBundle\Configurator\Step;
|
||||
|
||||
use Mautic\CoreBundle\Configurator\Step\StepInterface;
|
||||
use Mautic\InstallBundle\Configurator\Form\UserStepType;
|
||||
|
||||
class UserStep implements StepInterface
|
||||
{
|
||||
/**
|
||||
* User's first name.
|
||||
*/
|
||||
public $firstname;
|
||||
|
||||
/**
|
||||
* User's last name.
|
||||
*/
|
||||
public $lastname;
|
||||
|
||||
/**
|
||||
* User's e-mail address.
|
||||
*/
|
||||
public $email;
|
||||
|
||||
/**
|
||||
* User's username.
|
||||
*/
|
||||
public $username;
|
||||
|
||||
/**
|
||||
* User's password.
|
||||
*/
|
||||
public $password;
|
||||
|
||||
public function getFormType(): string
|
||||
{
|
||||
return UserStepType::class;
|
||||
}
|
||||
|
||||
public function checkRequirements(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
public function checkOptionalSettings(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
public function getTemplate(): string
|
||||
{
|
||||
return '@MauticInstall/Install/user.html.twig';
|
||||
}
|
||||
|
||||
public function update(StepInterface $data): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,291 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\InstallBundle\Controller;
|
||||
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Doctrine\Persistence\ManagerRegistry;
|
||||
use Mautic\CoreBundle\Configurator\Configurator;
|
||||
use Mautic\CoreBundle\Configurator\Step\StepInterface;
|
||||
use Mautic\CoreBundle\Controller\CommonController;
|
||||
use Mautic\CoreBundle\Factory\ModelFactory;
|
||||
use Mautic\CoreBundle\Helper\CoreParametersHelper;
|
||||
use Mautic\CoreBundle\Helper\PathsHelper;
|
||||
use Mautic\CoreBundle\Helper\UserHelper;
|
||||
use Mautic\CoreBundle\Loader\ParameterLoader;
|
||||
use Mautic\CoreBundle\Security\Permissions\CorePermissions;
|
||||
use Mautic\CoreBundle\Service\FlashBag;
|
||||
use Mautic\CoreBundle\Translation\Translator;
|
||||
use Mautic\InstallBundle\Install\InstallService;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||
use Symfony\Component\Form\FormError;
|
||||
use Symfony\Component\Form\FormInterface;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\RequestStack;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
class InstallController extends CommonController
|
||||
{
|
||||
public function __construct(
|
||||
private Configurator $configurator,
|
||||
private InstallService $installer,
|
||||
ManagerRegistry $doctrine,
|
||||
ModelFactory $modelFactory,
|
||||
UserHelper $userHelper,
|
||||
CoreParametersHelper $coreParametersHelper,
|
||||
EventDispatcherInterface $dispatcher,
|
||||
Translator $translator,
|
||||
FlashBag $flashBag,
|
||||
RequestStack $requestStack,
|
||||
CorePermissions $security,
|
||||
) {
|
||||
parent::__construct($doctrine, $modelFactory, $userHelper, $coreParametersHelper, $dispatcher, $translator, $flashBag, $requestStack, $security);
|
||||
}
|
||||
|
||||
/**
|
||||
* Controller action for install steps.
|
||||
*
|
||||
* @param int $index The step number to process
|
||||
*
|
||||
* @throws \Doctrine\DBAL\Exception
|
||||
*/
|
||||
public function stepAction(Request $request, EntityManagerInterface $entityManager, PathsHelper $pathsHelper, float $index = 0): \Symfony\Component\HttpFoundation\RedirectResponse|Response
|
||||
{
|
||||
// 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->installer->checkIfInstalled()) {
|
||||
return $this->redirectToRoute('mautic_dashboard_index');
|
||||
}
|
||||
|
||||
if ($index - floor($index) > 0) {
|
||||
$subIndex = (int) (round($index - floor($index), 1) * 10);
|
||||
$index = floor($index);
|
||||
}
|
||||
$index = (int) $index;
|
||||
|
||||
$params = $this->configurator->getParameters();
|
||||
|
||||
$session = $request->getSession();
|
||||
$completedSteps = $session->get('mautic.installer.completedsteps', []);
|
||||
|
||||
// Check to ensure the installer is in the right place
|
||||
if ((empty($params) || empty($params['db_driver'])) && $index > 1) {
|
||||
$session->set('mautic.installer.completedsteps', [0]);
|
||||
|
||||
return $this->redirectToRoute('mautic_installer_step', ['index' => 1]);
|
||||
}
|
||||
|
||||
$step = $this->configurator->getStep($index)[0];
|
||||
\assert($step instanceof StepInterface);
|
||||
$action = $this->generateUrl('mautic_installer_step', ['index' => $index]);
|
||||
|
||||
$form = $this->createForm($step->getFormType(), $step, ['action' => $action]);
|
||||
$tmpl = $request->isXmlHttpRequest() ? $request->get('tmpl', 'index') : 'index';
|
||||
|
||||
// Note if this step is complete
|
||||
$complete = false;
|
||||
|
||||
if ('POST' === $request->getMethod()) {
|
||||
$form->handleRequest($request);
|
||||
if ($form->isValid()) {
|
||||
// Post-step processing
|
||||
$formData = (array) $form->getData();
|
||||
|
||||
switch ($index) {
|
||||
case InstallService::CHECK_STEP:
|
||||
$complete = true;
|
||||
|
||||
break;
|
||||
case InstallService::DOCTRINE_STEP:
|
||||
// password field does not retain configured defaults
|
||||
if (empty($formData['password']) && !empty($params['db_password'])) {
|
||||
$formData['password'] = $params['db_password'];
|
||||
}
|
||||
$messages = $this->installer->createDatabaseStep($step, $formData);
|
||||
if (!empty($messages)) {
|
||||
$this->handleInstallerErrors($form, $messages);
|
||||
break;
|
||||
}
|
||||
|
||||
/**
|
||||
* We need to clear the ORM metadata cache before creating the schema. If the user provided a database
|
||||
* table prefix in the UI installer, cached table names don't have the prefix yet (e.g. oauth2_clients).
|
||||
* After clearing the metadata cache, Doctrine automatically recreates it with the correct prefixes (e.g.
|
||||
* mau_oauth2_clients), if applicable.
|
||||
*/
|
||||
$entityManager->getConfiguration()->getMetadataCache()->clear();
|
||||
|
||||
// Refresh to install schema with new connection information in the container
|
||||
return $this->redirectToRoute('mautic_installer_step', ['index' => 1.1]);
|
||||
case InstallService::USER_STEP:
|
||||
$messages = $this->installer->createAdminUserStep($formData);
|
||||
|
||||
if (!empty($messages)) {
|
||||
$this->handleInstallerErrors($form, $messages);
|
||||
break;
|
||||
}
|
||||
|
||||
// Store the data to repopulate the form
|
||||
unset($formData['password']);
|
||||
$session->set('mautic.installer.user', $formData);
|
||||
|
||||
$complete = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} elseif (!empty($subIndex)) {
|
||||
switch ($index) {
|
||||
case InstallService::DOCTRINE_STEP:
|
||||
$dbParams = (array) $step;
|
||||
|
||||
switch ($subIndex) {
|
||||
case 1:
|
||||
$messages = $this->installer->createSchemaStep($dbParams);
|
||||
if (!empty($messages)) {
|
||||
$this->handleInstallerErrors($form, $messages);
|
||||
|
||||
return $this->redirectToRoute('mautic_installer_step', ['index' => 1]);
|
||||
}
|
||||
|
||||
return $this->redirectToRoute('mautic_installer_step', ['index' => 1.2]);
|
||||
case 2:
|
||||
$messages = $this->installer->createFixturesStep();
|
||||
if (!empty($messages)) {
|
||||
$this->handleInstallerErrors($form, $messages);
|
||||
|
||||
return $this->redirectToRoute('mautic_installer_step', ['index' => 1]);
|
||||
}
|
||||
|
||||
$complete = true;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ($complete) {
|
||||
$completedSteps[] = $index;
|
||||
$session->set('mautic.installer.completedsteps', $completedSteps);
|
||||
++$index;
|
||||
|
||||
if ($index < $this->configurator->getStepCount()) {
|
||||
// On to the next step
|
||||
|
||||
return $this->redirectToRoute('mautic_installer_step', ['index' => (int) $index]);
|
||||
} else {
|
||||
$siteUrl = $request->getSchemeAndHttpHost().$request->getBaseUrl();
|
||||
$messages = $this->installer->createFinalConfigStep($siteUrl);
|
||||
|
||||
if (!empty($messages)) {
|
||||
$this->handleInstallerErrors($form, $messages);
|
||||
}
|
||||
|
||||
return $this->postActionRedirect(
|
||||
[
|
||||
'viewParameters' => [
|
||||
'welcome_url' => $this->generateUrl('mautic_dashboard_index'),
|
||||
'parameters' => $this->configurator->render(),
|
||||
'version' => MAUTIC_VERSION,
|
||||
'tmpl' => $tmpl,
|
||||
],
|
||||
'returnUrl' => $this->generateUrl('mautic_installer_final'),
|
||||
'contentTemplate' => '@MauticInstall/Install/final.html.twig',
|
||||
'forwardController' => false,
|
||||
]
|
||||
);
|
||||
}
|
||||
} else {
|
||||
// Redirect back to last step if the user advanced ahead via the URL
|
||||
$last = (int) end($completedSteps) + 1;
|
||||
if ($index && $index > $last) {
|
||||
return $this->redirectToRoute('mautic_installer_step', ['index' => $last]);
|
||||
}
|
||||
}
|
||||
|
||||
return $this->delegateView(
|
||||
[
|
||||
'viewParameters' => [
|
||||
'form' => $form->createView(),
|
||||
'index' => $index,
|
||||
'count' => $this->configurator->getStepCount(),
|
||||
'version' => MAUTIC_VERSION,
|
||||
'tmpl' => $tmpl,
|
||||
'majors' => $this->configurator->getRequirements(),
|
||||
'minors' => $this->configurator->getOptionalSettings(),
|
||||
'appRoot' => $this->coreParametersHelper->get('mautic.application_dir').'/app',
|
||||
'cacheDir' => $this->coreParametersHelper->get('kernel.cache_dir'),
|
||||
'logDir' => $this->coreParametersHelper->get('kernel.logs_dir'),
|
||||
'configFile' => ParameterLoader::getLocalConfigFile($pathsHelper->getSystemPath('root').'/app'),
|
||||
'completedSteps' => $completedSteps,
|
||||
],
|
||||
'contentTemplate' => $step->getTemplate(),
|
||||
'passthroughVars' => [
|
||||
'route' => $this->generateUrl('mautic_installer_step', ['index' => $index]),
|
||||
],
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Controller action for the final step.
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function finalAction(Request $request, PathsHelper $pathsHelper): \Symfony\Component\HttpFoundation\RedirectResponse|Response
|
||||
{
|
||||
$session = $request->getSession();
|
||||
|
||||
// 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->installer->checkIfInstalled()) {
|
||||
if (!$session->has('mautic.installer.completedsteps')) {
|
||||
// Arrived here by directly browsing to URL so redirect to the dashboard
|
||||
|
||||
return $this->redirectToRoute('mautic_dashboard_index');
|
||||
}
|
||||
} else {
|
||||
// Shouldn't have made it to this step without having a successful install
|
||||
return $this->redirectToRoute('mautic_installer_home');
|
||||
}
|
||||
|
||||
// Remove installer session variables
|
||||
$session->remove('mautic.installer.completedsteps');
|
||||
$session->remove('mautic.installer.user');
|
||||
|
||||
$this->installer->finalMigrationStep();
|
||||
|
||||
$welcomeUrl = $this->generateUrl('mautic_dashboard_index');
|
||||
|
||||
$tmpl = $request->isXmlHttpRequest() ? $request->get('tmpl', 'index') : 'index';
|
||||
|
||||
return $this->delegateView(
|
||||
[
|
||||
'viewParameters' => [
|
||||
'welcome_url' => $welcomeUrl,
|
||||
'parameters' => $this->configurator->render(),
|
||||
'config_path' => ParameterLoader::getLocalConfigFile($pathsHelper->getSystemPath('root').'/app'),
|
||||
'is_writable' => $this->configurator->isFileWritable(),
|
||||
'version' => MAUTIC_VERSION,
|
||||
'tmpl' => $tmpl,
|
||||
],
|
||||
'contentTemplate' => '@MauticInstall/Install/final.html.twig',
|
||||
'passthroughVars' => [
|
||||
'activeLink' => '#mautic_installer_index',
|
||||
'mauticContent' => 'installer',
|
||||
'route' => $this->generateUrl('mautic_installer_final'),
|
||||
],
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle installer errors.
|
||||
*/
|
||||
private function handleInstallerErrors(FormInterface $form, array $messages): void
|
||||
{
|
||||
foreach ($messages as $type => $message) {
|
||||
match ($type) {
|
||||
'warning', 'error', 'notice' => $this->addFlashMessage($message, [], $type),
|
||||
default => $form[$type]->addError(new FormError($message)),
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mautic\InstallBundle\DependencyInjection\Compiler;
|
||||
|
||||
use Mautic\InstallBundle\Command\InstallCommand;
|
||||
use Symfony\Component\Console\Input\ArgvInput;
|
||||
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
|
||||
class InstallCommandPass implements CompilerPassInterface
|
||||
{
|
||||
public function process(ContainerBuilder $container): void
|
||||
{
|
||||
$args = $_SERVER['argv'] ?? [];
|
||||
|
||||
if (!in_array(InstallCommand::COMMAND, $args, true)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$input = new ArgvInput($args);
|
||||
$tablePrefix = $input->hasOption('db_table_prefix')
|
||||
? $input->getOption('db_table_prefix')
|
||||
: MAUTIC_TABLE_PREFIX;
|
||||
|
||||
if (!$tablePrefix) {
|
||||
return;
|
||||
}
|
||||
|
||||
$container->setParameter('mautic.db_table_prefix', $tablePrefix);
|
||||
$container->getDefinition('mautic.tblprefix_subscriber')->setArgument('$tablePrefix', $tablePrefix);
|
||||
$container->getDefinition('mautic.schema.helper.column')->setArgument('$prefix', $tablePrefix);
|
||||
$container->getDefinition('mautic.schema.helper.index')->setArgument('$prefix', $tablePrefix);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mautic\InstallBundle\DependencyInjection;
|
||||
|
||||
use Symfony\Component\Config\FileLocator;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use Symfony\Component\DependencyInjection\Extension\Extension;
|
||||
use Symfony\Component\DependencyInjection\Loader\PhpFileLoader;
|
||||
|
||||
class MauticInstallExtension extends Extension
|
||||
{
|
||||
/**
|
||||
* @param mixed[] $configs
|
||||
*/
|
||||
public function load(array $configs, ContainerBuilder $container): void
|
||||
{
|
||||
$loader = new PhpFileLoader($container, new FileLocator(__DIR__.'/../Config'));
|
||||
$loader->load('services.php');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\InstallBundle\EventListener;
|
||||
|
||||
use Doctrine\Bundle\DoctrineBundle\Attribute\AsDoctrineListener;
|
||||
use Doctrine\ORM\Tools\Event\GenerateSchemaEventArgs;
|
||||
use Doctrine\ORM\Tools\ToolEvents;
|
||||
use Mautic\LeadBundle\Field\SchemaDefinition;
|
||||
use Mautic\LeadBundle\Model\FieldModel;
|
||||
|
||||
#[AsDoctrineListener(ToolEvents::postGenerateSchema)]
|
||||
class DoctrineEventSubscriber
|
||||
{
|
||||
public function postGenerateSchema(GenerateSchemaEventArgs $args): void
|
||||
{
|
||||
$fieldGroups = [
|
||||
'leads' => FieldModel::$coreFields,
|
||||
'companies' => FieldModel::$coreCompanyFields,
|
||||
];
|
||||
|
||||
foreach ($fieldGroups as $tableName => $fields) {
|
||||
$fullTableName = MAUTIC_TABLE_PREFIX.$tableName;
|
||||
if (!$args->getSchema()->hasTable($fullTableName)) {
|
||||
// Ignore during plugin installations as not all tables are present in the schema.
|
||||
continue;
|
||||
}
|
||||
$table = $args->getSchema()->getTable($fullTableName);
|
||||
|
||||
foreach ($fields as $alias => $field) {
|
||||
if (!$table->hasColumn($alias)) {
|
||||
$type = $field['type'] ?? 'text';
|
||||
$definition = SchemaDefinition::getSchemaDefinition($alias, $type, !empty($field['unique']));
|
||||
$table->addColumn($definition['name'], $definition['type'], $definition['options']);
|
||||
|
||||
if ('textarea' !== $type) {
|
||||
$table->addIndex([$definition['name']], $definition['name'].'_search');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ('leads' === $tableName) {
|
||||
// Add an attribution index
|
||||
$table->addIndex(['attribution', 'attribution_date'], 'contact_attribution');
|
||||
// Add date added and country index
|
||||
$table->addIndex(['date_added', 'country'], 'date_added_country_index');
|
||||
} else {
|
||||
$table->addIndex(['companyname', 'companyemail'], 'company_filter');
|
||||
$table->addIndex(['companyname', 'companycity', 'companycountry', 'companystate'], 'company_match');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mautic\InstallBundle\Exception;
|
||||
|
||||
class AlreadyInstalledException extends \Exception
|
||||
{
|
||||
protected $message = 'Mautic is already installed.';
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mautic\InstallBundle\Exception;
|
||||
|
||||
class DatabaseVersionTooOldException extends \Exception
|
||||
{
|
||||
public function __construct(
|
||||
private string $currentVersion,
|
||||
) {
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current database version as reported by the database itself.
|
||||
*/
|
||||
public function getCurrentVersion(): string
|
||||
{
|
||||
return $this->currentVersion;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,340 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\InstallBundle\Helper;
|
||||
|
||||
use Doctrine\DBAL\Connection;
|
||||
use Doctrine\DBAL\DriverManager;
|
||||
use Doctrine\DBAL\Platforms\AbstractPlatform;
|
||||
use Doctrine\DBAL\Platforms\SqlitePlatform;
|
||||
use Doctrine\DBAL\Schema\AbstractSchemaManager;
|
||||
use Doctrine\DBAL\Schema\ForeignKeyConstraint;
|
||||
use Doctrine\DBAL\Schema\Index;
|
||||
use Doctrine\ORM\EntityManager;
|
||||
use Doctrine\ORM\ORMException;
|
||||
use Doctrine\ORM\Tools\SchemaTool;
|
||||
use Mautic\CoreBundle\Release\ThisRelease;
|
||||
use Mautic\InstallBundle\Exception\DatabaseVersionTooOldException;
|
||||
|
||||
class SchemaHelper
|
||||
{
|
||||
protected Connection $db;
|
||||
|
||||
/**
|
||||
* @var EntityManager
|
||||
*/
|
||||
protected $em;
|
||||
|
||||
/**
|
||||
* @var AbstractPlatform
|
||||
*/
|
||||
protected $platform;
|
||||
|
||||
protected array $dbParams;
|
||||
|
||||
/**
|
||||
* @var AbstractSchemaManager<AbstractPlatform>|null
|
||||
*/
|
||||
private ?AbstractSchemaManager $schemaManager = null;
|
||||
|
||||
/**
|
||||
* @throws \Doctrine\DBAL\Exception
|
||||
*/
|
||||
public function __construct(array $dbParams)
|
||||
{
|
||||
// suppress display of errors as we know its going to happen while testing the connection
|
||||
ini_set('display_errors', '0');
|
||||
|
||||
// Support for env variables
|
||||
foreach ($dbParams as &$v) {
|
||||
if (!empty($v) && is_string($v) && preg_match('/getenv\((.*?)\)/', $v, $match)) {
|
||||
$v = (string) getenv($match[1]);
|
||||
}
|
||||
}
|
||||
|
||||
$dbParams['charset'] = 'utf8mb4';
|
||||
if (isset($dbParams['name'])) {
|
||||
$dbParams['dbname'] = $dbParams['name'];
|
||||
unset($dbParams['name']);
|
||||
}
|
||||
|
||||
$this->db = DriverManager::getConnection($dbParams);
|
||||
|
||||
$this->dbParams = $dbParams;
|
||||
}
|
||||
|
||||
public function setEntityManager(EntityManager $em): void
|
||||
{
|
||||
$this->em = $em;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test db connection.
|
||||
*/
|
||||
public function testConnection(): void
|
||||
{
|
||||
if (isset($this->dbParams['dbname'])) {
|
||||
// Test connection credentials
|
||||
$dbParams = $this->dbParams;
|
||||
unset($dbParams['dbname']);
|
||||
$db = DriverManager::getConnection($dbParams);
|
||||
|
||||
$db->connect();
|
||||
$db->close();
|
||||
} else {
|
||||
$this->db->connect();
|
||||
$this->db->close();
|
||||
}
|
||||
}
|
||||
|
||||
public function createDatabase(): bool
|
||||
{
|
||||
try {
|
||||
$this->db->connect();
|
||||
} catch (\Exception) {
|
||||
// it failed to connect so remove the dbname and try to create it
|
||||
$dbName = $this->dbParams['dbname'];
|
||||
$this->dbParams['dbname'] = null;
|
||||
|
||||
try {
|
||||
// database does not exist so try to create it
|
||||
$this->getSchemaManager()->createDatabase($dbName);
|
||||
|
||||
// close the connection and reconnect with the new database name
|
||||
$this->db->close();
|
||||
|
||||
$this->dbParams['dbname'] = $dbName;
|
||||
$this->db = DriverManager::getConnection($this->dbParams);
|
||||
$this->db->close();
|
||||
} catch (\Exception) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates SQL for installation.
|
||||
*
|
||||
* @throws \Doctrine\DBAL\Exception
|
||||
* @throws ORMException
|
||||
*/
|
||||
public function installSchema(): bool
|
||||
{
|
||||
$sm = $this->getSchemaManager();
|
||||
|
||||
try {
|
||||
// check to see if the table already exist
|
||||
$tables = $sm->listTableNames();
|
||||
} catch (\Exception $e) {
|
||||
$this->db->close();
|
||||
|
||||
throw $e;
|
||||
}
|
||||
|
||||
$this->platform = $this->db->getDatabasePlatform();
|
||||
$backupPrefix = (!empty($this->dbParams['backup_prefix'])) ? $this->dbParams['backup_prefix'] : 'bak_';
|
||||
|
||||
$metadatas = $this->em->getMetadataFactory()->getAllMetadata();
|
||||
if (empty($metadatas)) {
|
||||
$this->db->close();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
$schemaTool = new SchemaTool($this->em);
|
||||
$installSchema = $schemaTool->getSchemaFromMetadata($metadatas);
|
||||
$mauticTables = [];
|
||||
|
||||
foreach ($installSchema->getTables() as $m) {
|
||||
$tableName = $m->getName();
|
||||
$mauticTables[$tableName] = $this->generateBackupName($this->dbParams['table_prefix'], $backupPrefix, $tableName);
|
||||
}
|
||||
|
||||
$isSqlite = $this->em->getConnection()->getDatabasePlatform() instanceof SqlitePlatform;
|
||||
$sql = $isSqlite ? [] : ['SET foreign_key_checks = 0;'];
|
||||
if ($this->dbParams['backup_tables']) {
|
||||
$sql = array_merge($sql, $this->backupExistingSchema($tables, $mauticTables, $backupPrefix));
|
||||
} else {
|
||||
$sql = array_merge($sql, $this->dropExistingSchema($tables, $mauticTables));
|
||||
}
|
||||
|
||||
$sql = array_merge($sql, $installSchema->toSql($this->platform));
|
||||
|
||||
// Execute drop queries
|
||||
foreach ($sql as $q) {
|
||||
try {
|
||||
$this->db->executeStatement($q);
|
||||
} catch (\Exception $exception) {
|
||||
$this->db->close();
|
||||
|
||||
throw $exception;
|
||||
}
|
||||
}
|
||||
|
||||
$this->db->close();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function validateDatabaseVersion(): void
|
||||
{
|
||||
// Version strings are in the format 10.3.30-MariaDB-1:10.3.30+maria~focal-log
|
||||
$version = $this->db->executeQuery('SELECT VERSION()')->fetchOne();
|
||||
|
||||
// Platform class names are in the format Doctrine\DBAL\Platforms\MariaDb1027Platform
|
||||
$platform = strtolower($this->db->getDatabasePlatform()::class);
|
||||
$metadata = ThisRelease::getMetadata();
|
||||
|
||||
/**
|
||||
* The second case is for MariaDB < 10.2, where Doctrine reports it as MySQLPlatform. Here we can use a little
|
||||
* help from the version string, which contains "MariaDB" in that case: 10.1.48-MariaDB-1~bionic.
|
||||
*/
|
||||
if (str_contains($platform, 'mariadb') || str_contains(strtolower($version), 'mariadb')) {
|
||||
$minSupported = $metadata->getMinSupportedMariaDbVersion();
|
||||
} elseif (str_contains($platform, 'mysql')) {
|
||||
$minSupported = $metadata->getMinSupportedMySqlVersion();
|
||||
} else {
|
||||
throw new \Exception('Invalid database platform '.$platform.'. Mautic only supports MySQL and MariaDB!');
|
||||
}
|
||||
|
||||
if (true !== version_compare($version, $minSupported, 'gt')) {
|
||||
throw new DatabaseVersionTooOldException($version);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws \Doctrine\DBAL\Exception
|
||||
*/
|
||||
protected function backupExistingSchema($tables, $mauticTables, $backupPrefix): array
|
||||
{
|
||||
$sql = [];
|
||||
$sm = $this->getSchemaManager();
|
||||
|
||||
// backup existing tables
|
||||
$backupRestraints = $backupSequences = $backupIndexes = $backupTables = $dropSequences = $dropTables = [];
|
||||
|
||||
// cycle through the first time to drop all the foreign keys
|
||||
foreach ($tables as $t) {
|
||||
if (!isset($mauticTables[$t]) && !in_array($t, $mauticTables)) {
|
||||
// Not an applicable table
|
||||
continue;
|
||||
}
|
||||
|
||||
$restraints = $sm->listTableForeignKeys($t);
|
||||
|
||||
if (isset($mauticTables[$t])) {
|
||||
// to be backed up
|
||||
$backupRestraints[$mauticTables[$t]] = $restraints;
|
||||
$backupTables[$t] = $mauticTables[$t];
|
||||
$backupIndexes[$t] = $sm->listTableIndexes($t);
|
||||
} else {
|
||||
// existing backup to be dropped
|
||||
$dropTables[] = $t;
|
||||
}
|
||||
|
||||
foreach ($restraints as $restraint) {
|
||||
$sql[] = $this->platform->getDropForeignKeySQL($restraint, $t);
|
||||
}
|
||||
}
|
||||
|
||||
// now drop all the backup tables
|
||||
foreach ($dropTables as $t) {
|
||||
$sql[] = $this->platform->getDropTableSQL($t);
|
||||
}
|
||||
|
||||
// now backup tables
|
||||
foreach ($backupTables as $t => $backup) {
|
||||
// drop old indexes
|
||||
/** @var Index $oldIndex */
|
||||
foreach ($backupIndexes[$t] as $indexName => $oldIndex) {
|
||||
if ('primary' == $indexName) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$oldName = $oldIndex->getName();
|
||||
$newName = $this->generateBackupName($this->dbParams['table_prefix'], $backupPrefix, $oldName);
|
||||
|
||||
$newIndex = new Index(
|
||||
$newName,
|
||||
$oldIndex->getColumns(),
|
||||
$oldIndex->isUnique(),
|
||||
$oldIndex->isPrimary(),
|
||||
$oldIndex->getFlags(),
|
||||
$oldIndex->getOptions()
|
||||
);
|
||||
|
||||
$newIndexes[] = $newIndex;
|
||||
$sql[] = $this->platform->getDropIndexSQL($oldIndex, $t);
|
||||
}
|
||||
|
||||
// rename table
|
||||
$queries = $this->platform->getRenameTableSQL($t, $backup);
|
||||
$sql = array_merge($sql, $queries);
|
||||
|
||||
// create new index
|
||||
if (!empty($newIndexes)) {
|
||||
foreach ($newIndexes as $newIndex) {
|
||||
$sql[] = $this->platform->getCreateIndexSQL($newIndex, $backup);
|
||||
}
|
||||
unset($newIndexes);
|
||||
}
|
||||
}
|
||||
|
||||
// apply foreign keys to backup tables
|
||||
foreach ($backupRestraints as $table => $oldRestraints) {
|
||||
foreach ($oldRestraints as $or) {
|
||||
$foreignTable = $or->getForeignTableName();
|
||||
$foreignTableName = $this->generateBackupName($this->dbParams['table_prefix'], $backupPrefix, $foreignTable);
|
||||
$r = new ForeignKeyConstraint(
|
||||
$or->getLocalColumns(),
|
||||
$foreignTableName,
|
||||
$or->getForeignColumns(),
|
||||
$backupPrefix.$or->getName(),
|
||||
$or->getOptions()
|
||||
);
|
||||
$sql[] = $this->platform->getCreateForeignKeySQL($r, $table);
|
||||
}
|
||||
}
|
||||
|
||||
return $sql;
|
||||
}
|
||||
|
||||
protected function dropExistingSchema($tables, $mauticTables): array
|
||||
{
|
||||
$sql = [];
|
||||
|
||||
// drop tables
|
||||
foreach ($tables as $t) {
|
||||
if (isset($mauticTables[$t])) {
|
||||
$sql[] = $this->platform->getDropTableSQL($t);
|
||||
}
|
||||
}
|
||||
|
||||
return $sql;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed|string
|
||||
*/
|
||||
protected function generateBackupName($prefix, $backupPrefix, $name)
|
||||
{
|
||||
if (empty($prefix) || !str_contains($name, $prefix)) {
|
||||
return $backupPrefix.$name;
|
||||
} else {
|
||||
return str_replace($prefix, $backupPrefix, $name);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return AbstractSchemaManager<AbstractPlatform>
|
||||
*/
|
||||
private function getSchemaManager(): AbstractSchemaManager
|
||||
{
|
||||
if (null !== $this->schemaManager) {
|
||||
return $this->schemaManager;
|
||||
}
|
||||
|
||||
return $this->schemaManager = $this->db->createSchemaManager();
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\InstallBundle\InstallFixtures\ORM;
|
||||
|
||||
use Doctrine\Bundle\FixturesBundle\FixtureGroupInterface;
|
||||
use Doctrine\Common\DataFixtures\AbstractFixture;
|
||||
use Doctrine\Common\DataFixtures\OrderedFixtureInterface;
|
||||
use Doctrine\Persistence\ObjectManager;
|
||||
use Mautic\LeadBundle\Entity\LeadField;
|
||||
use Mautic\LeadBundle\Model\FieldModel;
|
||||
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||
|
||||
class LeadFieldData extends AbstractFixture implements OrderedFixtureInterface, FixtureGroupInterface
|
||||
{
|
||||
public function __construct(
|
||||
private TranslatorInterface $translator,
|
||||
) {
|
||||
}
|
||||
|
||||
public static function getGroups(): array
|
||||
{
|
||||
return ['group_install', 'group_mautic_install_data'];
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws \Doctrine\DBAL\Schema\SchemaException
|
||||
*/
|
||||
public function load(ObjectManager $manager): void
|
||||
{
|
||||
$fieldGroups['lead'] = FieldModel::$coreFields;
|
||||
$fieldGroups['company'] = FieldModel::$coreCompanyFields;
|
||||
|
||||
foreach ($fieldGroups as $fields) {
|
||||
$order = 1;
|
||||
foreach ($fields as $alias => $field) {
|
||||
$type = $field['type'] ?? 'text';
|
||||
|
||||
$entity = new LeadField();
|
||||
$entity->setLabel($this->translator->trans('mautic.lead.field.'.$alias, [], 'fixtures'));
|
||||
$entity->setGroup($field['group'] ?? 'core');
|
||||
$entity->setOrder($order);
|
||||
$entity->setAlias($alias);
|
||||
$entity->setIsRequired($field['required'] ?? false);
|
||||
$entity->setType($type);
|
||||
$entity->setObject($field['object']);
|
||||
$entity->setIsUniqueIdentifer(!empty($field['unique']));
|
||||
$entity->setProperties($field['properties'] ?? []);
|
||||
$entity->setIsFixed(!empty($field['fixed']));
|
||||
$entity->setIsListable(!empty($field['listable']));
|
||||
$entity->setIsShortVisible(!empty($field['short']));
|
||||
|
||||
if (isset($field['default'])) {
|
||||
$entity->setDefaultValue($field['default']);
|
||||
}
|
||||
|
||||
$manager->persist($entity);
|
||||
$manager->flush();
|
||||
|
||||
if (!$this->hasReference('leadfield-'.$alias)) {
|
||||
$this->addReference('leadfield-'.$alias, $entity);
|
||||
}
|
||||
++$order;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function getOrder()
|
||||
{
|
||||
return 4;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\InstallBundle\InstallFixtures\ORM;
|
||||
|
||||
use Doctrine\Bundle\FixturesBundle\FixtureGroupInterface;
|
||||
use Doctrine\Common\DataFixtures\AbstractFixture;
|
||||
use Doctrine\Common\DataFixtures\OrderedFixtureInterface;
|
||||
use Doctrine\Persistence\ObjectManager;
|
||||
use Mautic\CoreBundle\Helper\CsvHelper;
|
||||
use Mautic\CoreBundle\Helper\Serializer;
|
||||
use Mautic\ReportBundle\Entity\Report;
|
||||
|
||||
class LoadReportData extends AbstractFixture implements OrderedFixtureInterface, FixtureGroupInterface
|
||||
{
|
||||
public static function getGroups(): array
|
||||
{
|
||||
return ['group_install', 'group_mautic_install_data'];
|
||||
}
|
||||
|
||||
public function load(ObjectManager $manager): void
|
||||
{
|
||||
$reports = CsvHelper::csv_to_array(__DIR__.'/fakereportdata.csv');
|
||||
foreach ($reports as $count => $rows) {
|
||||
$report = new Report();
|
||||
$key = $count + 1;
|
||||
foreach ($rows as $col => $val) {
|
||||
if ('NULL' != $val) {
|
||||
$setter = 'set'.ucfirst($col);
|
||||
if (in_array($col, ['columns', 'filters', 'graphs', 'tableOrder'])) {
|
||||
$val = Serializer::decode(stripslashes($val));
|
||||
}
|
||||
$report->$setter($val);
|
||||
}
|
||||
}
|
||||
|
||||
$manager->persist($report);
|
||||
|
||||
$this->setReference('report-'.$key, $report);
|
||||
}
|
||||
$manager->flush();
|
||||
}
|
||||
|
||||
public function getOrder()
|
||||
{
|
||||
return 5;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\InstallBundle\InstallFixtures\ORM;
|
||||
|
||||
use Doctrine\Bundle\FixturesBundle\FixtureGroupInterface;
|
||||
use Doctrine\Common\DataFixtures\AbstractFixture;
|
||||
use Doctrine\Common\DataFixtures\OrderedFixtureInterface;
|
||||
use Doctrine\Persistence\ObjectManager;
|
||||
use Mautic\UserBundle\Entity\Role;
|
||||
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||
|
||||
class RoleData extends AbstractFixture implements OrderedFixtureInterface, FixtureGroupInterface
|
||||
{
|
||||
public function __construct(
|
||||
private TranslatorInterface $translator,
|
||||
) {
|
||||
}
|
||||
|
||||
public static function getGroups(): array
|
||||
{
|
||||
return ['group_install', 'group_mautic_install_data'];
|
||||
}
|
||||
|
||||
public function load(ObjectManager $manager): void
|
||||
{
|
||||
if ($this->hasReference('admin-role')) {
|
||||
return;
|
||||
}
|
||||
|
||||
$role = new Role();
|
||||
$role->setName($this->translator->trans('mautic.user.role.admin.name', [], 'fixtures'));
|
||||
$role->setDescription($this->translator->trans('mautic.user.role.admin.description', [], 'fixtures'));
|
||||
$role->setIsAdmin(1);
|
||||
$manager->persist($role);
|
||||
$manager->flush();
|
||||
|
||||
$this->addReference('admin-role', $role);
|
||||
}
|
||||
|
||||
public function getOrder()
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
"name","description","system","source","columns","filters","tableOrder","graphs"
|
||||
"Visits published Pages",NULL,1,"page.hits","a:7:{i:0;s:11:\"ph.date_hit\";i:1;s:6:\"ph.url\";i:2;s:12:\"ph.url_title\";i:3;s:10:\"ph.referer\";i:4;s:12:\"i.ip_address\";i:5;s:7:\"ph.city\";i:6;s:10:\"ph.country\";}","a:2:{i:0;a:3:{s:6:\"column\";s:7:\"ph.code\";s:9:\"condition\";s:2:\"eq\";s:5:\"value\";s:3:\"200\";}i:1;a:3:{s:6:\"column\";s:14:\"p.is_published\";s:9:\"condition\";s:2:\"eq\";s:5:\"value\";s:1:\"1\";}}","a:1:{i:0;a:2:{s:6:\"column\";s:11:\"ph.date_hit\";s:9:\"direction\";s:3:\"ASC\";}}","a:8:{i:0;s:35:\"mautic.page.graph.line.time.on.site\";i:1;s:27:\"mautic.page.graph.line.hits\";i:2;s:38:\"mautic.page.graph.pie.new.vs.returning\";i:3;s:31:\"mautic.page.graph.pie.languages\";i:4;s:34:\"mautic.page.graph.pie.time.on.site\";i:5;s:27:\"mautic.page.table.referrers\";i:6;s:30:\"mautic.page.table.most.visited\";i:7;s:37:\"mautic.page.table.most.visited.unique\";}"
|
||||
"Downloads of all Assets",NULL,1,"asset.downloads","a:7:{i:0;s:16:\"ad.date_download\";i:1;s:7:\"a.title\";i:2;s:12:\"i.ip_address\";i:3;s:11:\"l.firstname\";i:4;s:10:\"l.lastname\";i:5;s:7:\"l.email\";i:6;s:4:\"a.id\";}","a:1:{i:0;a:3:{s:6:\"column\";s:14:\"a.is_published\";s:9:\"condition\";s:2:\"eq\";s:5:\"value\";s:1:\"1\";}}","a:1:{i:0;a:2:{s:6:\"column\";s:16:\"ad.date_download\";s:9:\"direction\";s:3:\"ASC\";}}","a:4:{i:0;s:33:\"mautic.asset.graph.line.downloads\";i:1;s:31:\"mautic.asset.graph.pie.statuses\";i:2;s:34:\"mautic.asset.table.most.downloaded\";i:3;s:32:\"mautic.asset.table.top.referrers\";}"
|
||||
"Submissions of published Forms",NULL,1,"form.submissions","a:0:{}","a:1:{i:1;a:3:{s:6:\"column\";s:14:\"f.is_published\";s:9:\"condition\";s:2:\"eq\";s:5:\"value\";s:1:\"1\";}}","a:0:{}","a:3:{i:0;s:34:\"mautic.form.graph.line.submissions\";i:1;s:32:\"mautic.form.table.most.submitted\";i:2;s:31:\"mautic.form.table.top.referrers\";}"
|
||||
"All Emails",NULL,1,"email.stats","a:5:{i:0;s:12:\"es.date_sent\";i:1;s:12:\"es.date_read\";i:2;s:9:\"e.subject\";i:3;s:16:\"es.email_address\";i:4;s:4:\"e.id\";}","a:1:{i:0;a:3:{s:6:\"column\";s:14:\"e.is_published\";s:9:\"condition\";s:2:\"eq\";s:5:\"value\";s:1:\"1\";}}","a:1:{i:0;a:2:{s:6:\"column\";s:12:\"es.date_sent\";s:9:\"direction\";s:3:\"ASC\";}}","a:6:{i:0;s:29:\"mautic.email.graph.line.stats\";i:1;s:42:\"mautic.email.graph.pie.ignored.read.failed\";i:2;s:35:\"mautic.email.table.most.emails.read\";i:3;s:35:\"mautic.email.table.most.emails.sent\";i:4;s:43:\"mautic.email.table.most.emails.read.percent\";i:5;s:37:\"mautic.email.table.most.emails.failed\";}"
|
||||
"Leads and Points",NULL,1,"lead.pointlog","a:7:{i:0;s:13:\"lp.date_added\";i:1;s:7:\"lp.type\";i:2;s:13:\"lp.event_name\";i:3;s:11:\"l.firstname\";i:4;s:10:\"l.lastname\";i:5;s:7:\"l.email\";i:6;s:8:\"lp.delta\";}","a:0:{}","a:1:{i:0;a:2:{s:6:\"column\";s:13:\"lp.date_added\";s:9:\"direction\";s:3:\"ASC\";}}","a:6:{i:0;s:29:\"mautic.lead.graph.line.points\";i:1;s:29:\"mautic.lead.table.most.points\";i:2;s:29:\"mautic.lead.table.top.actions\";i:3;s:28:\"mautic.lead.table.top.cities\";i:4;s:31:\"mautic.lead.table.top.countries\";i:5;s:28:\"mautic.lead.table.top.events\";}"
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 61.
|
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mautic\InstallBundle;
|
||||
|
||||
use Mautic\InstallBundle\DependencyInjection\Compiler\InstallCommandPass;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use Symfony\Component\HttpKernel\Bundle\Bundle;
|
||||
|
||||
class MauticInstallBundle extends Bundle
|
||||
{
|
||||
public function build(ContainerBuilder $container): void
|
||||
{
|
||||
parent::build($container);
|
||||
|
||||
$container->addCompilerPass(new InstallCommandPass(), \Symfony\Component\DependencyInjection\Compiler\PassConfig::TYPE_BEFORE_OPTIMIZATION, 0);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
{% set headerTitle = 'Install' %}
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
{% do addAssetScript('app/bundles/InstallBundle/Assets/install/install.js') %}
|
||||
{{- include('@MauticCore/Default/head.html.twig') -}}
|
||||
<body>
|
||||
<!-- start: app-wrapper -->
|
||||
<section id="app-wrapper">
|
||||
<div class="container ml-a mr-a">
|
||||
<div class="row mt-20">
|
||||
<div class="text-center">
|
||||
<img src="{{ getOverridableUrl('images/mautic_logo_lb200.png') }}" height="50px" alt="Mautic" />
|
||||
</div>
|
||||
|
||||
<div class="mt-20 col-lg-6 col-lg-offset-3">
|
||||
<div id="app-content" class="panel panel-default">
|
||||
{{- include('@MauticCore/Notification/flashes.html.twig') -}}
|
||||
{{ block('content') }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<!--/ end: app-content -->
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,104 @@
|
||||
{#
|
||||
@copyright 2014 Mautic Contributors. All rights reserved
|
||||
@author Mautic
|
||||
@link http://mautic.org
|
||||
@license GNU/GPLv3 http://www.gnu.org/licenses/gpl-3.0.html
|
||||
#}
|
||||
|
||||
{#use Mautic\InstallBundle\Configurator\Step\CheckStep;#}
|
||||
|
||||
{% if 'index' == tmpl %}
|
||||
{% extends '@MauticInstall/Install/content.html.twig' %}
|
||||
{% endif %}
|
||||
|
||||
{% block content %}
|
||||
<div class="panel-heading">
|
||||
<h2 class="panel-title">
|
||||
{{ 'mautic.install.heading.check.environment'|trans }}
|
||||
</h2>
|
||||
</div>
|
||||
{% autoescape false %}
|
||||
<div class="panel-body">
|
||||
{% if majors|length %}
|
||||
<div class="panel-group" id="minors">
|
||||
<div class="panel panel-danger">
|
||||
<div class="panel-heading">
|
||||
<h3 class="panel-title" style="font-size: 1em;">
|
||||
{{ 'mautic.install.heading.major.problems'|trans }}
|
||||
</h3>
|
||||
</div>
|
||||
<div class="panel-body alert-warning">
|
||||
{{ 'mautic.install.sentence.major.problems'|trans ({'%majors%' : majors|length}) }}
|
||||
</div>
|
||||
<ul class="list-group">
|
||||
{% for message in majors %}
|
||||
{% if message == 'mautic.install.cache.unwritable' %}
|
||||
<li class="list-group-item">{{ 'mautic.install.directory.unwritable'|trans ({'%path%' : cacheDir}) }}</li>
|
||||
{% elseif message == 'mautic.install.config.unwritable' %}
|
||||
<li class="list-group-item">{{ message|trans ({'%path%' : configFile}) }}</li>
|
||||
{% elseif message == 'mautic.install.logs.unwritable' %}
|
||||
<li class="list-group-item">{{ 'mautic.install.directory.unwritable'|trans ({'%path%' : logDir}) }}</li>
|
||||
{% elseif message == 'mautic.install.apc.version' %}
|
||||
{% set minAPCverison = (constant('PHP_VERSION') >= '5.4.0') ? '3.1.13' : '3.0.17' %}
|
||||
<li class="list-group-item">{{ message|trans ({'%minapc%' : minAPCverison, '%currentapc%' : 'apc'|phpversion}) }}</li>
|
||||
{% else %}
|
||||
<li class="list-group-item">{{ message|trans }}</li>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if not (majors|length) %}
|
||||
<div class="alert alert-success">
|
||||
<h4><i class="ri-check-line"></i> {{ 'mautic.install.heading.ready'|trans }}</h4>
|
||||
<p>{{ 'mautic.install.sentence.ready'|trans }}</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if minors|length %}
|
||||
<div class="panel-group" id="minors">
|
||||
<div class="panel panel-warning">
|
||||
<div class="panel-heading">
|
||||
<h3 class="panel-title" style="font-size: 1em;">
|
||||
<a data-toggle="collapse" style="display: block; " href="#minorDetails"><i class="ri-arrow-down-s-line"></i> {{ 'mautic.install.heading.minor.problems'|trans }}</a>
|
||||
</h3>
|
||||
</div>
|
||||
<div id="minorDetails" class="panel-collapse collapse">
|
||||
<div class="panel-body alert-warning">
|
||||
<p>{{ 'mautic.install.sentence.minor.problems'|trans }}</p>
|
||||
</div>
|
||||
<ul class="list-group">
|
||||
{% for message in minors %}
|
||||
{% if message == 'mautic.install.pcre.version' %}
|
||||
<li class="list-group-item">{{ message|trans ({'%pcreversion%' : constant('PCRE_VERSION')}) }}</li>
|
||||
{% elseif message == 'mautic.install.php.version.not.supported' %}
|
||||
<li class="list-group-item">{{ message|trans ({'%phpversion%' : constant('PHP_VERSION')}) }}</li>
|
||||
{% elseif message == 'mautic.install.php.version.has.only.security.support' %}
|
||||
<li class="list-group-item">{{ message|trans ({'%phpversion%' : constant('PHP_VERSION')}) }}</li>
|
||||
{% elseif message == 'mautic.install.memory.limit' %}
|
||||
<li class="list-group-item">{{ message|trans ({'%min_memory_limit%' : constant('Mautic\\InstallBundle\\Configurator\\Step\\CheckStep::RECOMMENDED_MEMORY_LIMIT')}) }}</li>
|
||||
{% else %}
|
||||
<li class="list-group-item">{{ message|trans }}</li>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if not (majors|length) %}
|
||||
{{ form_start(form) }}
|
||||
|
||||
<div class="row mt-20">
|
||||
<div class="col-sm-12">
|
||||
{{ form_row(form.buttons) }}
|
||||
</div>
|
||||
</div>
|
||||
{{ form_end(form) }}
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endautoescape %}
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,16 @@
|
||||
{#
|
||||
@copyright 2014 Mautic Contributors. All rights reserved
|
||||
@author Mautic
|
||||
@link http://mautic.org
|
||||
@license GNU/GPLv3 http://www.gnu.org/licenses/gpl-3.0.html
|
||||
#}
|
||||
|
||||
{% if not app.getRequest().isXmlHttpRequest() and false == contentOnly|default(false) %}
|
||||
{% extends '@MauticInstall/Install/base.html.twig' %}
|
||||
{% endif %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
{{ parent() }}
|
||||
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,82 @@
|
||||
{#
|
||||
@copyright 2014 Mautic Contributors. All rights reserved
|
||||
@author Mautic
|
||||
@link http://mautic.org
|
||||
@license GNU/GPLv3 http://www.gnu.org/licenses/gpl-3.0.html
|
||||
#}
|
||||
|
||||
{% if 'index' == tmpl %}
|
||||
{% extends '@MauticInstall/Install/content.html.twig' %}
|
||||
{% endif %}
|
||||
|
||||
{% block content %}
|
||||
<div class="panel-heading">
|
||||
<h2 class="panel-title">
|
||||
{{ 'mautic.install.heading.database.configuration'|trans }}
|
||||
</h2>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
{{ form_start(form) }}
|
||||
<div class="alert alert-mautic">
|
||||
<h6>{{ 'mautic.install.database.introtext'|trans }}</h6>
|
||||
</div>
|
||||
|
||||
{{ form_row(form.driver) }}
|
||||
|
||||
{% set driver = form.driver.vars.data %}
|
||||
|
||||
<div id="DatabaseSettings"{% if 'pdo_sqlite' == driver %}{{ ' class="hide"' }}{% endif %}>
|
||||
<div class="row">
|
||||
<div class="col-sm-6">
|
||||
{{ form_row(form.host) }}
|
||||
</div>
|
||||
<div class="col-sm-6">
|
||||
{{ form_row(form.port) }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-sm-6">
|
||||
{{ form_row(form.name) }}
|
||||
</div>
|
||||
<div class="col-sm-6">
|
||||
{{ form_row(form.table_prefix) }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-sm-6">
|
||||
{{ form_row(form.user) }}
|
||||
</div>
|
||||
<div class="col-sm-6">
|
||||
{{ form_row(form.password) }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-sm-6">
|
||||
{{ form_row(form.backup_tables) }}
|
||||
</div>
|
||||
{% set hide = (not form.backup_tables.vars.data) ? ' hide' : '' %}
|
||||
<div class="col-sm-6{{ hide }}" id="backupPrefix">
|
||||
{{ form_row(form.backup_prefix) }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row mt-20">
|
||||
<div class="col-sm-9">
|
||||
<div class="hide" id="waitMessage">
|
||||
<div class="alert alert-info">
|
||||
<strong>{{ 'mautic.install.database.installing'|trans }}</strong>
|
||||
</div>
|
||||
</div>
|
||||
{{- include('@MauticInstall/Install/navbar.html.twig', {'step' : index, 'count' : count, 'completedSteps' : completedSteps}) -}}
|
||||
</div>
|
||||
<div class="col-sm-3">
|
||||
{{ form_row(form.buttons) }}
|
||||
</div>
|
||||
</div>
|
||||
{{ form_end(form) }}
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,28 @@
|
||||
{#
|
||||
@copyright 2014 Mautic Contributors. All rights reserved
|
||||
@author Mautic
|
||||
@link http://mautic.org
|
||||
@license GNU/GPLv3 http://www.gnu.org/licenses/gpl-3.0.html
|
||||
#}
|
||||
|
||||
{% if 'index' == tmpl %}
|
||||
{% extends '@MauticInstall/Install/content.html.twig' %}
|
||||
{% endif %}
|
||||
|
||||
{% block content %}
|
||||
<div class="panel-heading">
|
||||
<h2 class="panel-title">
|
||||
{{ 'mautic.install.heading.final'|trans }}
|
||||
</h2>
|
||||
</div>
|
||||
<div class="panel-body text-center">
|
||||
<div><i class="ri-check-line ri-5x mb-20 text-success"></i></div>
|
||||
<h4 class="mb-3">{{ 'mautic.install.heading.finished'|trans }}</h4>
|
||||
<h5>{{ 'mautic.install.heading.configured'|trans|raw }}</h5>
|
||||
{% if welcome_url %}
|
||||
<a href="{{ welcome_url }}" role="button" class="btn btn-primary mt-20">
|
||||
{{ 'mautic.install.sentence.proceed.to.mautic'|trans }}
|
||||
</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,61 @@
|
||||
{#
|
||||
@copyright 2014 Mautic Contributors. All rights reserved
|
||||
@author Mautic
|
||||
@link http://mautic.org
|
||||
@license GNU/GPLv3 http://www.gnu.org/licenses/gpl-3.0.html
|
||||
#}
|
||||
|
||||
{% if 'index' == tmpl %}
|
||||
{% extends '@MauticInstall/Install/content.html.twig' %}
|
||||
{% endif %}
|
||||
|
||||
{% block content %}
|
||||
<div class="panel-heading">
|
||||
<h2 class="panel-title">
|
||||
{{ 'mautic.install.heading.misc.configuration'|trans }}
|
||||
</h2>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
{{ form_start(form) }}
|
||||
<h4>{{ 'mautic.install.misc.header.url'|trans }}</h4>
|
||||
<h6>{{ 'mautic.install.misc.subheader.url'|trans }}</h6>
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
{{ form_row(form.site_url) }}
|
||||
</div>
|
||||
</div>
|
||||
<h4>{{ 'mautic.install.misc.header.paths'|trans }}</h4>
|
||||
<h6>{{ 'mautic.install.misc.subheader.paths'|trans }}</h6>
|
||||
<div class="row">
|
||||
<div class="col-sm-6">
|
||||
{{ form_row(form.log_path) }}
|
||||
</div>
|
||||
<div class="col-sm-6">
|
||||
{{ form_row(form.cache_path) }}
|
||||
</div>
|
||||
</div>
|
||||
{% if constant('AppKernel::EXTRA_VERSION') %}
|
||||
<h4>{{ 'mautic.install.misc.header.stability'|trans }}</h4>
|
||||
<h6>{{ 'mautic.install.misc.subheader.stability'|trans }}</h6>
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
{{ form_row(form.update_stability) }}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
<div class="row mt-20">
|
||||
<div class="col-sm-9">
|
||||
<div class="hide" id="waitMessage">
|
||||
<div class="alert alert-info">
|
||||
<strong>{{ 'mautic.install.finalizing'|trans }}</strong>
|
||||
</div>
|
||||
</div>
|
||||
{{- include('@MauticInstall/Install/navbar.html.twig', {'step' : index, 'count' : count, 'completedSteps' : completedSteps}) -}}
|
||||
</div>
|
||||
<div class="col-sm-3">
|
||||
{{ form_row(form.buttons) }}
|
||||
</div>
|
||||
</div>
|
||||
{{ form_end(form) }}
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,22 @@
|
||||
{#
|
||||
@copyright 2014 Mautic Contributors. All rights reserved
|
||||
@author Mautic
|
||||
@link http://mautic.org
|
||||
@license GNU/GPLv3 http://www.gnu.org/licenses/gpl-3.0.html
|
||||
#}
|
||||
|
||||
{% set start = 1 %}
|
||||
<div id="stepNavigation" class="hidden-xs">
|
||||
<ul class="horizontal-step">
|
||||
{% for start in 1..count-1 %}
|
||||
<li{{ (start == step) ? ' class="active"' : '' }}>
|
||||
{% set url = (start == step or (start in completedSteps) or (start - 1 in completedSteps)) ? path('mautic_installer_step', {'index' : start}) : '#' %}
|
||||
<a href="{{ url }}" class="steps{{ ('#' == url) ? ' disabled' : '' }}">
|
||||
<span class="steps-figure">{{ ('mautic.install.step.'~start)|trans }}</span>
|
||||
</a>
|
||||
</li>
|
||||
{% set start = start + 1 %}
|
||||
{% endfor %}
|
||||
</ul>
|
||||
<div class="clearfix"></div>
|
||||
</div>
|
||||
@@ -0,0 +1,59 @@
|
||||
{#
|
||||
@copyright 2014 Mautic Contributors. All rights reserved
|
||||
@author Mautic
|
||||
@link http://mautic.org
|
||||
@license GNU/GPLv3 http://www.gnu.org/licenses/gpl-3.0.html
|
||||
#}
|
||||
|
||||
{% if 'index' == tmpl %}
|
||||
{% extends '@MauticInstall/Install/content.html.twig' %}
|
||||
{% endif %}
|
||||
|
||||
{% block content %}
|
||||
<div class="panel-heading">
|
||||
<h2 class="panel-title">
|
||||
{{ 'mautic.install.heading.user.configuration'|trans }}
|
||||
</h2>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
{{ form_start(form) }}
|
||||
<div class="alert alert-mautic">
|
||||
{% trans %}mautic.install.user.introtext{% endtrans %}
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-sm-6">
|
||||
{{ form_row(form.username) }}
|
||||
</div>
|
||||
<div class="col-sm-6">
|
||||
{{ form_row(form.password) }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr class="text-secondary" />
|
||||
|
||||
<div class="row mt-lg">
|
||||
<div class="col-sm-6">
|
||||
{{ form_row(form.firstname) }}
|
||||
</div>
|
||||
<div class="col-sm-6">
|
||||
{{ form_row(form.lastname) }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
{{ form_row(form.email) }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row mt-20">
|
||||
<div class="col-sm-9">
|
||||
{{- include('@MauticInstall/Install/navbar.html.twig', {'step' : index, 'count' : count, 'completedSteps' : completedSteps}) -}}
|
||||
</div>
|
||||
<div class="col-sm-3">
|
||||
{{ form_row(form.buttons) }}
|
||||
</div>
|
||||
</div>
|
||||
{{ form_end(form) }}
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,86 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mautic\InstallBundle\Tests\Command;
|
||||
|
||||
use Doctrine\Bundle\DoctrineBundle\Registry;
|
||||
use Mautic\CoreBundle\Doctrine\Connection\ConnectionWrapper;
|
||||
use Mautic\InstallBundle\Command\InstallCommand;
|
||||
use Mautic\InstallBundle\Install\InstallService;
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\Console\Application;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Helper\HelperSet;
|
||||
use Symfony\Component\Console\Input\ArrayInput;
|
||||
use Symfony\Component\Console\Input\InputDefinition;
|
||||
use Symfony\Component\Console\Output\BufferedOutput;
|
||||
|
||||
class InstallCommandTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @var MockObject&InstallService
|
||||
*/
|
||||
private MockObject $installer;
|
||||
|
||||
/**
|
||||
* @var MockObject&Registry
|
||||
*/
|
||||
private MockObject $doctrineRegistry;
|
||||
|
||||
private InstallCommand $command;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
$this->installer = $this->createMock(InstallService::class);
|
||||
$this->doctrineRegistry = $this->createMock(Registry::class);
|
||||
$application = $this->createMock(Application::class);
|
||||
$inputDefinition = $this->createMock(InputDefinition::class);
|
||||
$command = $this->createMock(Command::class);
|
||||
|
||||
$inputDefinition->method('getOptions')->willReturn([]);
|
||||
$inputDefinition->method('getArguments')->willReturn([]);
|
||||
|
||||
$application->method('getHelperSet')->willReturn($this->createMock(HelperSet::class));
|
||||
$application->method('getDefinition')->willReturn($inputDefinition);
|
||||
$application->method('find')->willReturn($command);
|
||||
|
||||
$this->command = new InstallCommand($this->installer, $this->doctrineRegistry);
|
||||
$this->command->setApplication($application);
|
||||
}
|
||||
|
||||
public function testCommandWhenSiteInstalled(): void
|
||||
{
|
||||
$this->installer->method('checkIfInstalled')->willReturnOnConsecutiveCalls(true);
|
||||
|
||||
$input = new ArrayInput(['site_url' => 'localhost']);
|
||||
$output = new BufferedOutput();
|
||||
$this->command->run($input, $output);
|
||||
|
||||
$this->assertSame('Mautic already installed'.PHP_EOL, $output->fetch());
|
||||
}
|
||||
|
||||
public function testCommandWhenSiteNotInstalled(): void
|
||||
{
|
||||
$this->installer->method('checkIfInstalled')->willReturnOnConsecutiveCalls(false);
|
||||
|
||||
$this->doctrineRegistry->method('getConnection')->willReturn($this->createMock(ConnectionWrapper::class));
|
||||
|
||||
$input = new ArrayInput(
|
||||
[
|
||||
'site_url' => 'localhost',
|
||||
'--admin_firstname' => 'Admin',
|
||||
'--admin_lastname' => 'Mautic',
|
||||
'--admin_username' => 'admin',
|
||||
'--admin_email' => 'admin@example.com',
|
||||
'--admin_password' => 'password',
|
||||
]
|
||||
);
|
||||
$output = new BufferedOutput();
|
||||
$this->command->run($input, $output);
|
||||
|
||||
$this->assertStringContainsString('Install complete'.PHP_EOL, $output->fetch());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\InstallBundle\Tests\Controller;
|
||||
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Doctrine\Persistence\ManagerRegistry;
|
||||
use Mautic\CoreBundle\Configurator\Configurator;
|
||||
use Mautic\CoreBundle\Factory\ModelFactory;
|
||||
use Mautic\CoreBundle\Helper\CoreParametersHelper;
|
||||
use Mautic\CoreBundle\Helper\PathsHelper;
|
||||
use Mautic\CoreBundle\Helper\UserHelper;
|
||||
use Mautic\CoreBundle\Security\Permissions\CorePermissions;
|
||||
use Mautic\CoreBundle\Service\FlashBag;
|
||||
use Mautic\CoreBundle\Translation\Translator;
|
||||
use Mautic\InstallBundle\Controller\InstallController;
|
||||
use Mautic\InstallBundle\Install\InstallService;
|
||||
use Symfony\Component\DependencyInjection\Container;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\RequestStack;
|
||||
use Symfony\Component\HttpFoundation\Session\Flash\FlashBagInterface;
|
||||
use Symfony\Component\HttpFoundation\Session\Session;
|
||||
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
|
||||
use Symfony\Component\Routing\Router;
|
||||
|
||||
class InstallControllerTest extends \PHPUnit\Framework\TestCase
|
||||
{
|
||||
private \PHPUnit\Framework\MockObject\MockObject $translatorMock;
|
||||
|
||||
private \PHPUnit\Framework\MockObject\MockObject $sessionMock;
|
||||
|
||||
private \PHPUnit\Framework\MockObject\MockObject $containerMock;
|
||||
|
||||
private \PHPUnit\Framework\MockObject\MockObject $routerMock;
|
||||
|
||||
private \PHPUnit\Framework\MockObject\MockObject $flashBagMock;
|
||||
|
||||
private InstallController $controller;
|
||||
|
||||
private \PHPUnit\Framework\MockObject\MockObject $pathsHelper;
|
||||
|
||||
private \PHPUnit\Framework\MockObject\MockObject $configurator;
|
||||
|
||||
private \PHPUnit\Framework\MockObject\MockObject $installer;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->sessionMock = $this->createMock(Session::class);
|
||||
$this->containerMock = $this->createMock(Container::class);
|
||||
$this->routerMock = $this->createMock(Router::class);
|
||||
$this->flashBagMock = $this->createMock(FlashBagInterface::class);
|
||||
$this->pathsHelper = $this->createMock(PathsHelper::class);
|
||||
|
||||
$this->configurator = $this->createMock(Configurator::class);
|
||||
$this->installer = $this->createMock(InstallService::class);
|
||||
$doctrine = $this->createMock(ManagerRegistry::class);
|
||||
$modelFactory = $this->createMock(ModelFactory::class);
|
||||
$userHelper = $this->createMock(UserHelper::class);
|
||||
$coreParametersHelper = $this->createMock(CoreParametersHelper::class);
|
||||
$dispatcher = $this->createMock(EventDispatcherInterface::class);
|
||||
$this->translatorMock = $this->createMock(Translator::class);
|
||||
$flashBag = $this->createMock(FlashBag::class);
|
||||
$requestStack = new RequestStack();
|
||||
$security = $this->createMock(CorePermissions::class);
|
||||
|
||||
$this->controller = new InstallController(
|
||||
$this->configurator,
|
||||
$this->installer,
|
||||
$doctrine,
|
||||
$modelFactory,
|
||||
$userHelper,
|
||||
$coreParametersHelper,
|
||||
$dispatcher,
|
||||
$this->translatorMock,
|
||||
$flashBag,
|
||||
$requestStack,
|
||||
$security
|
||||
);
|
||||
$this->controller->setContainer($this->containerMock);
|
||||
$this->sessionMock->method('getFlashBag')->willReturn($this->flashBagMock);
|
||||
|
||||
$this->containerMock->method('get')
|
||||
->with('router')
|
||||
->willReturn($this->routerMock);
|
||||
}
|
||||
|
||||
public function testStepActionWhenInstalled(): void
|
||||
{
|
||||
$this->installer->expects($this->once())
|
||||
->method('checkIfInstalled')
|
||||
->willReturn(
|
||||
true
|
||||
);
|
||||
|
||||
$this->routerMock->expects($this->once())
|
||||
->method('generate')
|
||||
->with('mautic_dashboard_index', [], UrlGeneratorInterface::ABSOLUTE_PATH)
|
||||
->willReturn('http://localhost/');
|
||||
|
||||
$response = $this->controller->stepAction(
|
||||
new Request(),
|
||||
$this->createMock(EntityManagerInterface::class),
|
||||
$this->pathsHelper,
|
||||
InstallService::CHECK_STEP
|
||||
);
|
||||
$this->assertEquals(302, $response->getStatusCode());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mautic\InstallBundle\Tests\EventListener;
|
||||
|
||||
use Doctrine\DBAL\Schema\Column;
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\DBAL\Schema\Table;
|
||||
use Doctrine\DBAL\Types\BigIntType;
|
||||
use Doctrine\DBAL\Types\DateTimeType;
|
||||
use Doctrine\DBAL\Types\TextType;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Doctrine\ORM\Tools\Event\GenerateSchemaEventArgs;
|
||||
use Mautic\InstallBundle\EventListener\DoctrineEventSubscriber;
|
||||
use PHPUnit\Framework\Assert;
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class DoctrineEventSubscriberTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @var MockObject&EntityManagerInterface
|
||||
*/
|
||||
private MockObject $entityManager;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->entityManager = $this->createMock(EntityManagerInterface::class);
|
||||
}
|
||||
|
||||
public function testSubscriberWillAddCorrectIndexes(): void
|
||||
{
|
||||
$idColumn = new Column('id', new BigIntType());
|
||||
$textColumn = new Column('firstname', new TextType());
|
||||
$dateColumn = new Column('date_added', new DateTimeType());
|
||||
$table = new Table(MAUTIC_TABLE_PREFIX.'leads', [$idColumn, $textColumn, $dateColumn]);
|
||||
$schema = new Schema([$table]);
|
||||
$args = new GenerateSchemaEventArgs($this->entityManager, $schema);
|
||||
$subscriber = new DoctrineEventSubscriber();
|
||||
$subscriber->postGenerateSchema($args);
|
||||
|
||||
Assert::assertTrue($schema->hasTable(MAUTIC_TABLE_PREFIX.'leads'));
|
||||
$contactsTable = $schema->getTable(MAUTIC_TABLE_PREFIX.'leads');
|
||||
Assert::assertTrue($contactsTable->hasIndex('contact_attribution'));
|
||||
Assert::assertTrue($contactsTable->hasIndex('date_added_country_index'));
|
||||
}
|
||||
|
||||
public function testSubscriberWillNotFailWithTablesFromAPlugin(): void
|
||||
{
|
||||
$table = new Table(MAUTIC_TABLE_PREFIX.'some_plugin_table', [new Column('id', new BigIntType())]);
|
||||
$schema = new Schema([$table]);
|
||||
$args = new GenerateSchemaEventArgs($this->entityManager, $schema);
|
||||
$subscriber = new DoctrineEventSubscriber();
|
||||
$subscriber->postGenerateSchema($args);
|
||||
|
||||
Assert::assertTrue($schema->hasTable(MAUTIC_TABLE_PREFIX.'some_plugin_table'));
|
||||
Assert::assertFalse($schema->hasTable(MAUTIC_TABLE_PREFIX.'leads'));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mautic\InstallBundle\Tests\Functional;
|
||||
|
||||
use Mautic\CoreBundle\Helper\FileHelper;
|
||||
use Mautic\CoreBundle\Test\IsolatedTestTrait;
|
||||
use Mautic\CoreBundle\Test\MauticMysqlTestCase;
|
||||
use Mautic\InstallBundle\Configurator\Step\CheckStep;
|
||||
use Mautic\LeadBundle\Entity\LeadField;
|
||||
use PHPUnit\Framework\Assert;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
|
||||
/**
|
||||
* This test must run in a separate process because it sets the global constant
|
||||
* MAUTIC_INSTALLER which breaks other tests.
|
||||
*/
|
||||
#[\PHPUnit\Framework\Attributes\PreserveGlobalState(false)]
|
||||
#[\PHPUnit\Framework\Attributes\RunTestsInSeparateProcesses]
|
||||
class InstallWorkflowTest extends MauticMysqlTestCase
|
||||
{
|
||||
use IsolatedTestTrait;
|
||||
|
||||
protected $useCleanupRollback = false;
|
||||
|
||||
private string $localConfigPath;
|
||||
|
||||
private string $defaultMemoryLimit;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
$this->localConfigPath = static::getContainer()->get('kernel')->getLocalConfigFile();
|
||||
$this->defaultMemoryLimit = ini_get('memory_limit');
|
||||
|
||||
if (file_exists($this->localConfigPath)) {
|
||||
// Move local.php so we can get to the installer.
|
||||
rename($this->localConfigPath, $this->localConfigPath.'.bak');
|
||||
}
|
||||
}
|
||||
|
||||
protected function beforeTearDown(): void
|
||||
{
|
||||
if (file_exists($this->localConfigPath)) {
|
||||
// Remove the local.php generated by this test.
|
||||
unlink($this->localConfigPath);
|
||||
}
|
||||
if (file_exists($this->localConfigPath.'.bak')) {
|
||||
// Restore the local config file in it's original state.
|
||||
rename($this->localConfigPath.'.bak', $this->localConfigPath);
|
||||
}
|
||||
|
||||
ini_set('memory_limit', $this->defaultMemoryLimit);
|
||||
}
|
||||
|
||||
public function testInstallWorkflow(): void
|
||||
{
|
||||
// Step 0: System checks.
|
||||
$crawler = $this->client->request(Request::METHOD_GET, '/installer');
|
||||
$this->assertResponseIsSuccessful();
|
||||
|
||||
$submitButton = $crawler->selectButton('install_check_step[buttons][next]');
|
||||
$form = $submitButton->form();
|
||||
$crawler = $this->client->submit($form);
|
||||
|
||||
$this->assertResponseIsSuccessful();
|
||||
|
||||
// Step 1: DB.
|
||||
$submitButton = $crawler->selectButton('install_doctrine_step[buttons][next]');
|
||||
$form = $submitButton->form();
|
||||
|
||||
$form['install_doctrine_step[host]']->setValue($this->connection->getParams()['host']);
|
||||
$form['install_doctrine_step[port]']->setValue((string) $this->connection->getParams()['port']);
|
||||
$form['install_doctrine_step[name]']->setValue($this->connection->getParams()['dbname']);
|
||||
$form['install_doctrine_step[user]']->setValue($this->connection->getParams()['user']);
|
||||
$form['install_doctrine_step[password]']->setValue($this->connection->getParams()['password']);
|
||||
$form['install_doctrine_step[backup_tables]']->setValue('0');
|
||||
|
||||
$crawler = $this->client->submit($form);
|
||||
$this->assertResponseIsSuccessful();
|
||||
|
||||
// Step 2: Admin user.
|
||||
$submitButton = $crawler->selectButton('install_user_step[buttons][next]');
|
||||
$form = $submitButton->form();
|
||||
|
||||
$form['install_user_step[username]']->setValue('admin');
|
||||
$form['install_user_step[password]']->setValue('maut!cR000cks');
|
||||
$form['install_user_step[firstname]']->setValue('admin');
|
||||
$form['install_user_step[lastname]']->setValue('mautic');
|
||||
$form['install_user_step[email]']->setValue('mautic@example.com');
|
||||
|
||||
$crawler = $this->client->submit($form);
|
||||
$this->assertResponseIsSuccessful();
|
||||
$heading = $crawler->filter('.panel-body.text-center h5');
|
||||
Assert::assertCount(1, $heading, $this->client->getResponse()->getContent());
|
||||
|
||||
$successText = $heading->text();
|
||||
Assert::assertStringContainsString('Mautic is installed', $successText);
|
||||
|
||||
// Assert that the fixtures were loaded
|
||||
$fieldRepository = $this->em->getRepository(LeadField::class);
|
||||
|
||||
$emailField = $fieldRepository->findOneBy(['alias' => 'email']);
|
||||
\assert($emailField instanceof LeadField);
|
||||
Assert::assertSame('Email', $emailField->getLabel());
|
||||
}
|
||||
|
||||
public function testInstallRequirementsAndRecommendations(): void
|
||||
{
|
||||
$limit = FileHelper::convertPHPSizeToBytes(CheckStep::RECOMMENDED_MEMORY_LIMIT);
|
||||
$expectedMemoryMessage = static::getContainer()->get('translator')->trans('mautic.install.memory.limit', ['%min_memory_limit%' => CheckStep::RECOMMENDED_MEMORY_LIMIT]);
|
||||
|
||||
// set the memory limit lower than the recommended value.
|
||||
ini_set('memory_limit', (string) ($limit - 1));
|
||||
$crawler = $this->client->request(Request::METHOD_GET, '/installer');
|
||||
$this->assertResponseIsSuccessful();
|
||||
|
||||
$details = $crawler->filter('#minorDetails ul')->html();
|
||||
Assert::assertStringContainsString($expectedMemoryMessage, $details);
|
||||
|
||||
// set the memory limit higher than the recommended value.
|
||||
ini_set('memory_limit', (string) ($limit + 1));
|
||||
$crawler = $this->client->request(Request::METHOD_GET, '/installer');
|
||||
$this->assertResponseIsSuccessful();
|
||||
|
||||
$details = $crawler->filter('#minorDetails ul')->html();
|
||||
Assert::assertStringNotContainsString($expectedMemoryMessage, $details);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\InstallBundle\Tests\Install;
|
||||
|
||||
use Doctrine\DBAL\Connection;
|
||||
use Doctrine\DBAL\DriverManager;
|
||||
use Doctrine\DBAL\Platforms\AbstractPlatform;
|
||||
use Doctrine\DBAL\Schema\AbstractSchemaManager;
|
||||
use Doctrine\DBAL\Schema\Table;
|
||||
use Mautic\CoreBundle\Test\EnvLoader;
|
||||
use Mautic\InstallBundle\Helper\SchemaHelper;
|
||||
use PHPUnit\Framework\Assert;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
/**
|
||||
* @template T of AbstractPlatform
|
||||
*/
|
||||
class InstallSchemaTest extends TestCase
|
||||
{
|
||||
private Connection $connection;
|
||||
|
||||
/**
|
||||
* @var array<string, mixed>
|
||||
*/
|
||||
private array $dbParams;
|
||||
|
||||
private string $indexTableName;
|
||||
|
||||
/**
|
||||
* @var AbstractSchemaManager<T>
|
||||
*/
|
||||
private AbstractSchemaManager $schemaManager;
|
||||
|
||||
public function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
EnvLoader::load();
|
||||
|
||||
$this->dbParams = [
|
||||
'driver' => $_ENV['DB_DRIVER'] ?? 'pdo_mysql',
|
||||
'host' => $_ENV['DB_HOST'],
|
||||
'port' => $_ENV['DB_PORT'],
|
||||
'dbname' => $_ENV['DB_NAME'], // Doctrine needs 'dbname', not 'name'
|
||||
'user' => $_ENV['DB_USER'],
|
||||
'password' => $_ENV['DB_PASSWD'],
|
||||
'table_prefix' => MAUTIC_TABLE_PREFIX,
|
||||
'backup_prefix' => 'bak_',
|
||||
];
|
||||
|
||||
$this->connection = DriverManager::getConnection($this->dbParams);
|
||||
|
||||
$this->indexTableName = 'table_with_index';
|
||||
|
||||
$t = new Table($this->indexTableName);
|
||||
$t->addColumn('a_column', 'text');
|
||||
|
||||
// Create an index that has options, e.g. length of the index
|
||||
$indexOptions = [
|
||||
'lengths' => [
|
||||
0 => 128,
|
||||
],
|
||||
];
|
||||
$t->addIndex(['a_column'], 'index_with_options', [], $indexOptions);
|
||||
$this->schemaManager = $this->connection->createSchemaManager();
|
||||
$this->schemaManager->createTable($t);
|
||||
}
|
||||
|
||||
public function tearDown(): void
|
||||
{
|
||||
parent::tearDown();
|
||||
|
||||
if ($this->schemaManager->tablesExist([$this->indexTableName])) {
|
||||
$this->schemaManager->dropTable($this->indexTableName);
|
||||
}
|
||||
if ($this->schemaManager->tablesExist([$this->dbParams['backup_prefix'].$this->indexTableName])) {
|
||||
$this->schemaManager->dropTable($this->dbParams['backup_prefix'].$this->indexTableName);
|
||||
}
|
||||
}
|
||||
|
||||
public function testBackupIndexesWithConfigOptions(): void
|
||||
{
|
||||
$schemaHelper = new SchemaHelper($this->dbParams);
|
||||
|
||||
// Make the backupExistingSchema method public so we can test that functionality without mocking all the SchemaHelper's functionality.
|
||||
$controllerReflection = new \ReflectionClass(SchemaHelper::class);
|
||||
$method = $controllerReflection->getMethod('backupExistingSchema');
|
||||
$method->setAccessible(true);
|
||||
|
||||
// Set the platform property, as that one is only set in the installSchema method, which we want to avoid.
|
||||
$property = $controllerReflection->getProperty('platform');
|
||||
$property->setAccessible(true);
|
||||
$connection = DriverManager::getConnection($this->dbParams);
|
||||
$property->setValue($schemaHelper, $connection->getDatabasePlatform());
|
||||
|
||||
$tables = [$this->indexTableName];
|
||||
$mauticTables = [$this->indexTableName => $this->dbParams['backup_prefix'].$this->indexTableName];
|
||||
|
||||
$sql = $method->invokeArgs($schemaHelper, [$tables, $mauticTables, $this->dbParams['backup_prefix']]);
|
||||
|
||||
$exceptions = [];
|
||||
if (!empty($sql)) {
|
||||
foreach ($sql as $q) {
|
||||
try {
|
||||
$this->connection->executeStatement($q);
|
||||
} catch (\Exception $exception) {
|
||||
$exceptions[] = $exception->getMessage();
|
||||
}
|
||||
}
|
||||
}
|
||||
$this->connection->close();
|
||||
|
||||
Assert::assertSame([], $exceptions);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,379 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mautic\InstallBundle\Tests\Install;
|
||||
|
||||
use Doctrine\ORM\EntityManager;
|
||||
use Doctrine\ORM\EntityRepository;
|
||||
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\PathsHelper;
|
||||
use Mautic\InstallBundle\Install\InstallService;
|
||||
use Mautic\UserBundle\Entity\User;
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
use Symfony\Component\HttpKernel\KernelInterface;
|
||||
use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasher;
|
||||
use Symfony\Component\Validator\ConstraintViolationInterface;
|
||||
use Symfony\Component\Validator\ConstraintViolationList;
|
||||
use Symfony\Component\Validator\Validator\ValidatorInterface;
|
||||
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||
|
||||
class InstallServiceTest extends \PHPUnit\Framework\TestCase
|
||||
{
|
||||
private MockObject $configurator;
|
||||
|
||||
private MockObject $cacheHelper;
|
||||
|
||||
private MockObject $pathsHelper;
|
||||
|
||||
/**
|
||||
* @var EntityManager&MockObject
|
||||
*/
|
||||
private MockObject $entityManager;
|
||||
|
||||
private MockObject $translator;
|
||||
|
||||
private MockObject $kernel;
|
||||
|
||||
private MockObject $validator;
|
||||
|
||||
private UserPasswordHasher $hasher;
|
||||
|
||||
/**
|
||||
* @var MockObject&FixturesLoaderInterface
|
||||
*/
|
||||
private MockObject $fixtureLoader;
|
||||
|
||||
private InstallService $installer;
|
||||
|
||||
public function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->configurator = $this->createMock(Configurator::class);
|
||||
$this->cacheHelper = $this->createMock(CacheHelper::class);
|
||||
$this->pathsHelper = $this->createMock(PathsHelper::class);
|
||||
$this->entityManager = $this->createMock(EntityManager::class);
|
||||
$this->translator = $this->createMock(TranslatorInterface::class);
|
||||
$this->kernel = $this->createMock(KernelInterface::class);
|
||||
$this->validator = $this->createMock(ValidatorInterface::class);
|
||||
$this->hasher = $this->createMock(UserPasswordHasher::class);
|
||||
$this->fixtureLoader = $this->createMock(FixturesLoaderInterface::class);
|
||||
|
||||
$this->installer = new InstallService(
|
||||
$this->configurator,
|
||||
$this->cacheHelper,
|
||||
$this->pathsHelper,
|
||||
$this->entityManager,
|
||||
$this->translator,
|
||||
$this->kernel,
|
||||
$this->validator,
|
||||
$this->hasher,
|
||||
$this->fixtureLoader
|
||||
);
|
||||
}
|
||||
|
||||
public function testCheckIfInstalledWhenNoLocalConfig(): void
|
||||
{
|
||||
$this->pathsHelper->expects($this->once())
|
||||
->method('getSystemPath')
|
||||
->with('root', false)
|
||||
->willReturn(
|
||||
__DIR__.'/../../../../../',
|
||||
);
|
||||
|
||||
$this->assertFalse($this->installer->checkIfInstalled());
|
||||
}
|
||||
|
||||
public function testGetStepWhenNoLocalConfig(): void
|
||||
{
|
||||
$this->pathsHelper->expects($this->once())
|
||||
->method('getSystemPath')
|
||||
->with('root', false)
|
||||
->willReturn(
|
||||
__DIR__.'/../../../../../',
|
||||
);
|
||||
|
||||
$this->configurator->expects($this->exactly(2))
|
||||
->method('getParameters')
|
||||
->willReturn(
|
||||
[]
|
||||
);
|
||||
|
||||
$index = 0;
|
||||
$step = $this->createMock(StepInterface::class);
|
||||
|
||||
$this->configurator->expects($this->once())
|
||||
->method('getStep')
|
||||
->with($index)
|
||||
->willReturn([$step]);
|
||||
|
||||
$this->assertEquals($step, $this->installer->getStep($index));
|
||||
}
|
||||
|
||||
public function testGetStepWhenDbDriverSet(): void
|
||||
{
|
||||
$this->pathsHelper->expects($this->once())
|
||||
->method('getSystemPath')
|
||||
->with('root', false)
|
||||
->willReturn(
|
||||
__DIR__.'/../../../../../',
|
||||
);
|
||||
|
||||
$this->configurator->expects($this->exactly(2))
|
||||
->method('getParameters')
|
||||
->willReturn(
|
||||
['db_driver' => 'test']
|
||||
);
|
||||
|
||||
$index = 0;
|
||||
$step = $this->createMock(StepInterface::class);
|
||||
|
||||
$this->configurator->expects($this->once())
|
||||
->method('getStep')
|
||||
->with($index)
|
||||
->willReturn([$step]);
|
||||
|
||||
$this->assertEquals($step, $this->installer->getStep($index));
|
||||
}
|
||||
|
||||
public function testCheckRequirements(): void
|
||||
{
|
||||
$step = $this->createMock(StepInterface::class);
|
||||
$messages = ['dummy' => 'test'];
|
||||
|
||||
$step->expects($this->once())
|
||||
->method('checkRequirements')
|
||||
->willReturn($messages);
|
||||
|
||||
$this->translator->expects($this->once())
|
||||
->method('trans')
|
||||
->with('test', [], null, null)
|
||||
->willReturn('test');
|
||||
|
||||
$this->assertEquals($messages, $this->installer->checkRequirements($step));
|
||||
}
|
||||
|
||||
public function testCheckOptionalSettings(): void
|
||||
{
|
||||
$step = $this->createMock(StepInterface::class);
|
||||
$messages = ['dummy' => 'test'];
|
||||
|
||||
$step->expects($this->once())
|
||||
->method('checkOptionalSettings')
|
||||
->willReturn($messages);
|
||||
|
||||
$this->translator->expects($this->once())
|
||||
->method('trans')
|
||||
->with('test', [], null, null)
|
||||
->willReturn('test');
|
||||
|
||||
$this->assertEquals($messages, $this->installer->checkOptionalSettings($step));
|
||||
}
|
||||
|
||||
public function testSaveConfigurationWhenNoCacheClear(): void
|
||||
{
|
||||
$params = [];
|
||||
$step = $this->createMock(StepInterface::class);
|
||||
$clearCache = false;
|
||||
|
||||
$messages = [];
|
||||
|
||||
$step->expects($this->once())
|
||||
->method('update')
|
||||
->with($step)
|
||||
->willReturn($params);
|
||||
|
||||
$this->configurator->expects($this->once())
|
||||
->method('write');
|
||||
|
||||
$this->configurator->expects($this->once())
|
||||
->method('mergeParameters');
|
||||
|
||||
$this->assertEquals($messages, $this->installer->saveConfiguration($params, $step, $clearCache));
|
||||
}
|
||||
|
||||
public function testSaveConfigurationWhenCacheClear(): void
|
||||
{
|
||||
$params = [];
|
||||
$step = $this->createMock(StepInterface::class);
|
||||
$clearCache = true;
|
||||
|
||||
$messages = [];
|
||||
|
||||
$step->expects($this->once())
|
||||
->method('update')
|
||||
->with($step)
|
||||
->willReturn($params);
|
||||
|
||||
$this->configurator->expects($this->once())
|
||||
->method('mergeParameters');
|
||||
|
||||
$this->configurator->expects($this->once())
|
||||
->method('write');
|
||||
|
||||
$this->cacheHelper->expects($this->once())
|
||||
->method('refreshConfig');
|
||||
|
||||
$this->assertEquals($messages, $this->installer->saveConfiguration($params, $step, $clearCache));
|
||||
}
|
||||
|
||||
public function testValidateDatabaseParamsWhenNoRequired(): void
|
||||
{
|
||||
$dbParams = [];
|
||||
$messages = [
|
||||
'driver' => null,
|
||||
'host' => null,
|
||||
'port' => null,
|
||||
'name' => null,
|
||||
'user' => null,
|
||||
];
|
||||
|
||||
$this->assertEquals($messages, $this->installer->validateDatabaseParams($dbParams));
|
||||
}
|
||||
|
||||
public function testValidateDatabaseParamsWhenPortNotValid(): void
|
||||
{
|
||||
$dbParams = [
|
||||
'driver' => 'pdo_mysql',
|
||||
'host' => 'localhost',
|
||||
'port' => '-1',
|
||||
'name' => 'mautic',
|
||||
'user' => 'mautic',
|
||||
];
|
||||
$messages = [
|
||||
'port' => null,
|
||||
];
|
||||
|
||||
$this->assertEquals($messages, $this->installer->validateDatabaseParams($dbParams));
|
||||
}
|
||||
|
||||
public function testValidateDatabaseParamsWhenAllValid(): void
|
||||
{
|
||||
$dbParams = [
|
||||
'driver' => 'pdo_mysql',
|
||||
'host' => 'localhost',
|
||||
'port' => '3306',
|
||||
'name' => 'mautic',
|
||||
'user' => 'mautic',
|
||||
];
|
||||
|
||||
$this->assertEquals([], $this->installer->validateDatabaseParams($dbParams));
|
||||
}
|
||||
|
||||
public function testValidateDatabaseParamsWhenDriverNotValid(): void
|
||||
{
|
||||
$dbParams = [
|
||||
'driver' => 'pdo_sqlite',
|
||||
'host' => 'localhost',
|
||||
'port' => '3306',
|
||||
'name' => 'mautic',
|
||||
'user' => 'mautic',
|
||||
];
|
||||
$messages = [
|
||||
'driver' => null,
|
||||
];
|
||||
|
||||
$this->assertEquals($messages, $this->installer->validateDatabaseParams($dbParams));
|
||||
}
|
||||
|
||||
/**
|
||||
* When an exception is raised while creating a database, there must be an array returned.
|
||||
*/
|
||||
public function testCreateDatabaseStepWithErrors(): void
|
||||
{
|
||||
$dbParams = [
|
||||
'driver' => 'pdo_mysql',
|
||||
'host' => 'localhost',
|
||||
'port' => '3306',
|
||||
'name' => 'mautic',
|
||||
'user' => 'mautic',
|
||||
'table_prefix' => 'mautic_',
|
||||
];
|
||||
|
||||
$step = $this->createMock(StepInterface::class);
|
||||
$this->assertEquals(['error' => null], $this->installer->createDatabaseStep($step, $dbParams));
|
||||
}
|
||||
|
||||
/**
|
||||
* When an exception is raised while creating the schema, there must be an array returned.
|
||||
*/
|
||||
public function testCreateSchemaStepWithErrors(): void
|
||||
{
|
||||
$dbParams = [
|
||||
'driver' => 'pdo_mysql',
|
||||
'host' => 'localhost',
|
||||
'port' => '3306',
|
||||
'name' => 'mautic',
|
||||
'user' => 'mautic',
|
||||
'table_prefix' => 'mautic_',
|
||||
];
|
||||
|
||||
$this->assertEquals(['error' => null], $this->installer->createSchemaStep($dbParams));
|
||||
}
|
||||
|
||||
public function testCreateAdminUserStepWhenPasswordIsMissing(): void
|
||||
{
|
||||
$mockRepo = $this->createMock(EntityRepository::class);
|
||||
$mockRepo->expects($this->once())
|
||||
->method('find')
|
||||
->willReturn(0);
|
||||
|
||||
$this->entityManager->expects($this->once())
|
||||
->method('getRepository')
|
||||
->willReturn($mockRepo);
|
||||
|
||||
$data = [
|
||||
'firstname' => 'Demo',
|
||||
'lastname' => 'User',
|
||||
'username' => 'admin',
|
||||
'email' => 'demo@demo.com',
|
||||
];
|
||||
|
||||
$this->assertEquals(['password' => null], $this->installer->createAdminUserStep($data));
|
||||
}
|
||||
|
||||
public function testCreateAdminUserStepWhenPasswordIsNotLongEnough(): void
|
||||
{
|
||||
$mockRepo = $this->createMock(EntityRepository::class);
|
||||
$mockRepo->expects($this->once())
|
||||
->method('find')
|
||||
->willReturn(new User());
|
||||
|
||||
$this->entityManager->expects($this->once())
|
||||
->method('getRepository')
|
||||
->willReturn($mockRepo);
|
||||
|
||||
$data = [
|
||||
'firstname' => 'Demo',
|
||||
'lastname' => 'User',
|
||||
'username' => 'admin',
|
||||
'password' => '1',
|
||||
'email' => 'demo@demo.com',
|
||||
];
|
||||
|
||||
$mockValidation = $this->createMock(ConstraintViolationInterface::class);
|
||||
$mockValidation->expects($this->once())
|
||||
->method('getMessage')
|
||||
->willReturn('password');
|
||||
$matcher = $this->exactly(2);
|
||||
|
||||
$this->validator->expects($matcher)->method('validate')->willReturnCallback(function (...$parameters) use ($matcher, $data, $mockValidation) {
|
||||
if (1 === $matcher->numberOfInvocations()) {
|
||||
$this->assertSame($data['email'], $parameters[0]);
|
||||
|
||||
return new ConstraintViolationList([]);
|
||||
}
|
||||
if (2 === $matcher->numberOfInvocations()) {
|
||||
$this->assertSame($data['password'], $parameters[0]);
|
||||
|
||||
return new ConstraintViolationList([$mockValidation]);
|
||||
}
|
||||
});
|
||||
|
||||
$this->assertEquals([0 => 'password'], $this->installer->createAdminUserStep($data));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
mautic.lead.field.attribution="Attribution"
|
||||
mautic.lead.field.attribution_date="Attribution Date"
|
||||
mautic.lead.field.last_active="Date Last Active"
|
||||
mautic.lead.field.firstname="First Name"
|
||||
mautic.lead.field.lastname="Last Name"
|
||||
mautic.lead.field.company="Primary company"
|
||||
mautic.lead.field.email="Email"
|
||||
mautic.lead.field.position="Position"
|
||||
mautic.lead.field.points="Points"
|
||||
mautic.lead.field.title="Title"
|
||||
mautic.lead.field.phone="Phone"
|
||||
mautic.lead.field.mobile="Mobile"
|
||||
mautic.lead.field.fax="Fax"
|
||||
mautic.lead.field.address1="Address Line 1"
|
||||
mautic.lead.field.address2="Address Line 2"
|
||||
mautic.lead.field.website="Website"
|
||||
mautic.lead.field.twitter="Twitter"
|
||||
mautic.lead.field.foursquare="Foursquare"
|
||||
mautic.lead.field.facebook="Facebook"
|
||||
mautic.lead.field.instagram="Instagram"
|
||||
mautic.lead.field.skype="Skype"
|
||||
mautic.lead.field.city="City"
|
||||
mautic.lead.field.state="State"
|
||||
mautic.lead.field.zipcode="Zip Code"
|
||||
mautic.lead.field.country="Country"
|
||||
mautic.lead.field.gender="Gender"
|
||||
mautic.lead.field.preferred_locale="Preferred Locale"
|
||||
mautic.lead.field.timezone="Preferred Timezone"
|
||||
mautic.user.role.admin.name="Administrator"
|
||||
mautic.user.role.admin.description="Full system access"
|
||||
mautic.lead.field.companyemail="Company Email"
|
||||
mautic.lead.field.companyname="Company Name"
|
||||
mautic.lead.field.companyaddress1="Address 1"
|
||||
mautic.lead.field.companyaddress2="Address 2"
|
||||
mautic.lead.field.companycity="City"
|
||||
mautic.lead.field.companystate="State"
|
||||
mautic.lead.field.companycountry="Country"
|
||||
mautic.lead.field.companyzipcode="Zip Code"
|
||||
mautic.lead.field.companyindustry="Industry"
|
||||
mautic.lead.field.companyphone="Phone"
|
||||
mautic.lead.field.companydescription="Description"
|
||||
mautic.lead.field.companywebsite="Website"
|
||||
mautic.lead.field.companynumber_of_employees="Number of Employees"
|
||||
mautic.lead.field.companyfax="Fax"
|
||||
mautic.lead.field.companyannual_revenue="Annual Revenue"
|
||||
mautic.lead.field.timezone="Preferred Timezone"
|
||||
@@ -0,0 +1,11 @@
|
||||
mautic.installer.error.adding.fields="An error occurred while attempting to populate the contact fields table: %exception%"
|
||||
mautic.installer.error.adding.fixtures="An error occurred while attempting to add default data: %exception%"
|
||||
mautic.installer.error.connecting.database="An error occured while attempting to connect to the database: %exception%"
|
||||
mautic.installer.error.creating.database="The database, %name%, could not be found or created due to permissions restrictions. Please manually create the database then try again."
|
||||
mautic.installer.error.getting.role="An error occurred while attempting to get the admin role: %exception%"
|
||||
mautic.installer.error.creating.user="An error occurred while attempting to create the admin user: %exception%"
|
||||
mautic.installer.error.database.exists="The database you've specified already exists and contains Mautic data."
|
||||
mautic.installer.error.database.version="Your database version (%currentversion%) is too old for Mautic to work correctly. Supported versions are MySQL %mysqlminversion% (or higher) and MariaDB %mariadbminversion% (or higher)."
|
||||
mautic.installer.error.installing.data="An error occurred while attempting to install the data: %exception%"
|
||||
mautic.installer.error.no.metadata="No metadata was found to create the database."
|
||||
mautic.installer.error.writing.configuration="An error occurred while attempting to write the configuration to the filesystem."
|
||||
@@ -0,0 +1,89 @@
|
||||
mautic.install.composer.dependencies="Vendor libraries must be installed. If you are running Mautic from source, please run 'composer install'. If you downloaded Mautic, please check for the 'vendor' folder."
|
||||
mautic.install.config.unwritable="Mautic is unable to write your configuration file to the appropriate location. Please check your filesystem permissions to ensure the following path is writable: <strong>%path%</strong>."
|
||||
mautic.install.database.installing="Verifying the details and creating the database. This may take a few seconds."
|
||||
mautic.install.database.introtext="To function properly, Mautic requires a connection to a database server. Use this form to provide the required details for this connection."
|
||||
mautic.install.date.timezone.not.set="The <strong>date.timezone</strong> setting is not set in your PHP configuration. Mautic has set the default timezone to 'UTC' but we suggest setting this in your PHP configuration."
|
||||
mautic.install.detect.unicode="The detect_unicode setting should be disabled in your PHP configuration."
|
||||
mautic.install.directory.unwritable="The %path% directory must be writable. Change the permissions of the <strong>%path%</strong> directory so that the web server can write into it."
|
||||
mautic.install.extension.curl="Install and enable the <strong>curl</strong> extension."
|
||||
mautic.install.extension.fileinfo="Install and enable the <strong>fileinfo</strong> extension."
|
||||
mautic.install.extension.imap="Install and enable the <strong>imap</strong> extension to support monitored email."
|
||||
mautic.install.extension.mbstring="Install and enable the <strong>mbstring</string> extension."
|
||||
mautic.install.extension.openssl="Install and enable the <strong>openssl</strong> extension. If open_basedir is enabled, ensure it has access to /dev/random or /dev/urandom."
|
||||
mautic.install.extension.zip="Install and enable the <strong>zip</strong> extension. This is required to allow Mautic to install language packages and perform updates within the application."
|
||||
mautic.install.final.step="Finish"
|
||||
mautic.install.finalizing="Finalizing the installation. This may take a few seconds."
|
||||
mautic.install.form.backup_prefix="Prefix for backup tables"
|
||||
mautic.install.form.cache_path="Cache directory path"
|
||||
mautic.install.form.database.driver="Database Driver"
|
||||
mautic.install.form.database.host="Database Host"
|
||||
mautic.install.form.database.name="Database Name"
|
||||
mautic.install.form.database.password="Database Password"
|
||||
mautic.install.form.database.port="Database Port"
|
||||
mautic.install.form.database.secret="Site Secret Value"
|
||||
mautic.install.form.database.table.prefix="Database Table Prefix"
|
||||
mautic.install.form.database.user="Database Username"
|
||||
mautic.install.form.existing_tables="Backup existing tables?"
|
||||
mautic.install.form.existing_tables_descr="If set to no, conflicting tables will be dropped."
|
||||
mautic.install.form.log_path="Log directory path"
|
||||
mautic.install.form.none="None"
|
||||
mautic.install.form.update_stability="Minimum Stability"
|
||||
mautic.install.form.user.email="E-mail Address"
|
||||
mautic.install.form.user.password="Admin Password"
|
||||
mautic.install.form.user.username="Admin Username"
|
||||
mautic.install.function.ctypealpha="Install and enable the <strong>ctype</strong> extension."
|
||||
mautic.install.function.iconv="Install and enable the <strong>iconv</strong> extension."
|
||||
mautic.install.function.jsonencode="Install and enable the <strong>JSON</strong> extension."
|
||||
mautic.install.function.mbstring="Install and enable the <strong>mbstring</strong> extension."
|
||||
mautic.install.function.posix.enable="Enable the <strong>php_posix</strong> extension (used to colorize command line output)."
|
||||
mautic.install.function.sessionstart="Install and enable the <strong>session</strong> extension."
|
||||
mautic.install.function.simplexml="Install and enable the <strong>SimpleXML</strong> extension."
|
||||
mautic.install.function.tokengetall="Install and enable the <strong>Tokenizer</strong> extension."
|
||||
mautic.install.function.xml="Install and enable the <strong>XML</strong> extension."
|
||||
mautic.install.memory.limit="The <strong>memory_limit</strong> setting in your PHP configuration is lower than the suggested minimum limit of %min_memory_limit%. Mautic can have performance issues with large datasets without sufficient memory."
|
||||
mautic.install.heading.check.environment="Mautic Installation - Environment Check"
|
||||
mautic.install.heading.configured="Mautic is installed! Visit <a href='https://www.mautic.org/getting-started' target='_blank'>Getting Started</a> for the next steps."
|
||||
mautic.install.heading.database.configuration="Mautic Installation - Database Setup"
|
||||
mautic.install.heading.final="Mautic Installation - Final Steps"
|
||||
mautic.install.heading.finished="Well Done!"
|
||||
mautic.install.heading.major.problems="Major Problems that need to be fixed now"
|
||||
mautic.install.heading.minor.problems="Some Recommendations"
|
||||
mautic.install.heading.misc.configuration="Mautic Installation - Miscellaneous"
|
||||
mautic.install.heading.ready="Ready to Install!"
|
||||
mautic.install.heading.user.configuration="Mautic Installation - Administrative User"
|
||||
mautic.install.intl.config="The intl extension is not performing as expected. Please check that PHP's intl extension is installed and configured properly."
|
||||
mautic.install.zend_assertions="Configure <code>zend.assertions = -1</code> in php.ini for production environments for faster and more stable execution."
|
||||
mautic.install.magic_quotes_enabled="Your server has PHP's <strong>magic_quotes_gpc</strong> option enabled, please disable this option to use Mautic."
|
||||
mautic.install.minimum.php.version="Your server does not meet the minimum PHP requirements. Mautic requires PHP version %minimum% while your server has %installed%. Please contact your host to update your PHP installation."
|
||||
mautic.install.misc.header.paths="Where should the logs and cache be stored?"
|
||||
mautic.install.misc.header.stability="Set the update stability path"
|
||||
mautic.install.misc.header.url="Full URL to this site"
|
||||
mautic.install.misc.subheader.paths="Set the absolute path to a couple directories Mautic uses. It is recommended that these be outside the publicly available web root."
|
||||
mautic.install.misc.subheader.stability="You are installing a non-stable version of Mautic (such as a beta release). Mautic defaults to showing only stable releases. You can optionally choose to fetch updates for non-stable releases here by choosing a minimum upgrade stability. For example, by selecting the Beta channel, you will be shown beta releases as well as stable releases."
|
||||
mautic.install.misc.subheader.url="Some features, such as scheduled campaigns and/or campaigns that are based on a contact's lack of interaction, will need to be triggered by a cron job. Mautic will need to know the site URL for these instances in order to properly generate landing page, asset, and clickthrough (redirect) URLs."
|
||||
mautic.install.module.intl="Install and enable the <strong>intl</strong> extension."
|
||||
mautic.install.module.phpxml="Install and enable the <strong>PHP-XML</strong> module."
|
||||
mautic.install.next.step="Next Step"
|
||||
mautic.install.password.minlengt="Your password must be at least six characters long"
|
||||
mautic.install.pdo.drivers="Please install the mysql PDO Driver"
|
||||
mautic.install.pdo.mandatory="PDO Extension is mandatory"
|
||||
mautic.install.php.version.has.only.security.support="You have PHP version %phpversion% installed which is only receiving security fixes at this time. This means your PHP version will soon no longer be supported. We recommend planning to upgrade to a newer PHP version. You can view which PHP versions are actively supported <a href='http://php.net/supported-versions.php' target='_blank'>at PHP.net</a>."
|
||||
mautic.install.php.version.not.supported="You have PHP version installed which is no longer supported by the PHP project or Mautic. We highly recommend upgrading your server to a newer PHP version. You can view which PHP versions are actively supported <a href='http://php.net/supported-versions.php' target='_blank'>at PHP.net</a>."
|
||||
mautic.install.please.wait="Please wait"
|
||||
mautic.install.sentence.config.written="Your local.php configuration file has been overwritten with these parameters (in <em>%path%</em>):"
|
||||
mautic.install.sentence.major.problems="We have detected <strong>%majors%</strong> major problems. You <em>must</em> fix them before continuing:"
|
||||
mautic.install.sentence.minor.problems="We have detected some minor things that we <em>recommend</em> changing in order to have a better Mautic experience:"
|
||||
mautic.install.sentence.proceed.to.mautic="Proceed to Mautic"
|
||||
mautic.install.sentence.ready="Great! Your environment is ready for Mautic."
|
||||
mautic.install.ssl.certificate="It is recommended to secure your installation with an SSL certificate (https). Starting February 2020 Google Chrome will stop sending third-party cookies in cross-site requests unless the cookies are secure. Tracking will stop working for Mautic instances running on HTTP. Use HTTPS."
|
||||
mautic.install.step.1="1"
|
||||
mautic.install.step.2="2"
|
||||
mautic.install.step.3="3"
|
||||
mautic.install.step.4="4"
|
||||
mautic.install.step.5="5"
|
||||
mautic.install.suhosin.whitelist="Suhosin is not properly configured, add <strong>phar</strong> to <strong>suhosin.executor.include.whitelist</strong> in your PHP configuration."
|
||||
mautic.install.timezone.not.supported="Your default timezone is not supported by PHP. Check for typos in your PHP configuration."
|
||||
mautic.install.user.introtext="Create the admin user for your Mautic installation."
|
||||
mautic.install.xdebug.exception.trace="Xdebug's 'show_exception_trace' option should be disabled in your PHP configuration."
|
||||
mautic.install.xdebug.nesting="Set <strong>xdebug.max_nesting_level</strong> to at least <strong>250</strong> in your PHP configuration to stop Xdebug's infinite recursion protection from erroneously throwing a fatal error."
|
||||
mautic.install.xdebug.scream="Xdebug's 'scream' option should be disabled in your PHP configuration."
|
||||
@@ -0,0 +1,5 @@
|
||||
mautic.install.database.driver.invalid="Invalid database driver given. Must be one of %drivers%"
|
||||
mautic.install.database.path.invalid="The path is invalid or not writable."
|
||||
mautic.install.database.path.warning="WARNING!! The path is within the public web root. This is not recommended due to the security risks. Consider changing the path to be outside of %root%."
|
||||
mautic.install.database.port.invalid="The port must be an integer greater than 0."
|
||||
mautic.install.password.minlength="Password must be at least 6 characters."
|
||||
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
namespace Mautic\InstallBundle\Twig;
|
||||
|
||||
use Twig\Extension\AbstractExtension;
|
||||
use Twig\TwigFilter;
|
||||
|
||||
/**
|
||||
* TwigExtension class.
|
||||
*/
|
||||
class TwigExtension extends AbstractExtension
|
||||
{
|
||||
/**
|
||||
* getFilters function.
|
||||
*
|
||||
* @return mixed[]
|
||||
*/
|
||||
public function getFilters()
|
||||
{
|
||||
return [
|
||||
new TwigFilter('phpversion', [$this, 'phpversion']),
|
||||
];
|
||||
}
|
||||
|
||||
public function phpversion(string $value = ''): string|bool
|
||||
{
|
||||
return phpversion($value);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user