setName('wallabag:install') ->setDescription('wallabag installer.') ->addOption( 'reset', null, InputOption::VALUE_NONE, 'Reset current database' ) ; } protected function execute(InputInterface $input, OutputInterface $output) { $this->defaultInput = $input; $this->defaultOutput = $output; $output->writeln('Installing wallabag...'); $output->writeln(''); $this ->checkRequirements() ->setupDatabase() ->setupAdmin() ->setupConfig() ; $output->writeln('wallabag has been successfully installed.'); $output->writeln('Just execute `php bin/console server:run --env=prod` for using wallabag: http://localhost:8000'); } protected function checkRequirements() { $this->defaultOutput->writeln('Step 1 of 4. Checking system requirements.'); $rows = []; // testing if database driver exists $fulfilled = true; $label = 'PDO Driver (%s)'; $status = 'OK!'; $help = ''; if (!extension_loaded($this->getContainer()->getParameter('database_driver'))) { $fulfilled = false; $status = 'ERROR!'; $help = 'Database driver "'.$this->getContainer()->getParameter('database_driver').'" is not installed.'; } $rows[] = [sprintf($label, $this->getContainer()->getParameter('database_driver')), $status, $help]; // testing if connection to the database can be etablished $label = 'Database connection'; $status = 'OK!'; $help = ''; try { $conn = $this->getContainer()->get('doctrine')->getManager()->getConnection(); $conn->connect(); } catch (\Exception $e) { if (false === strpos($e->getMessage(), 'Unknown database') && false === strpos($e->getMessage(), 'database "'.$this->getContainer()->getParameter('database_name').'" does not exist')) { $fulfilled = false; $status = 'ERROR!'; $help = 'Can\'t connect to the database: '.$e->getMessage(); } } $rows[] = [$label, $status, $help]; // now check if MySQL isn't too old to handle utf8mb4 if ($conn->isConnected() && $conn->getDatabasePlatform() instanceof \Doctrine\DBAL\Platforms\MySqlPlatform) { $version = $conn->query('select version()')->fetchColumn(); $minimalVersion = '5.5.4'; if (false === version_compare($version, $minimalVersion, '>')) { $fulfilled = false; $rows[] = [ 'Database version', 'ERROR!', 'Your MySQL version ('.$version.') is too old, consider upgrading ('.$minimalVersion.'+).', ]; } } foreach ($this->functionExists as $functionRequired) { $label = ''.$functionRequired.''; $status = 'OK!'; $help = ''; if (!function_exists($functionRequired)) { $fulfilled = false; $status = 'ERROR!'; $help = 'You need the '.$functionRequired.' function activated'; } $rows[] = [$label, $status, $help]; } $table = new Table($this->defaultOutput); $table ->setHeaders(['Checked', 'Status', 'Recommendation']) ->setRows($rows) ->render(); if (!$fulfilled) { throw new \RuntimeException('Some system requirements are not fulfilled. Please check output messages and fix them.'); } $this->defaultOutput->writeln('Success! Your system can run wallabag properly.'); $this->defaultOutput->writeln(''); return $this; } protected function setupDatabase() { $this->defaultOutput->writeln('Step 2 of 4. Setting up database.'); // user want to reset everything? Don't care about what is already here if (true === $this->defaultInput->getOption('reset')) { $this->defaultOutput->writeln('Droping database, creating database and schema, clearing the cache'); $this ->runCommand('doctrine:database:drop', ['--force' => true]) ->runCommand('doctrine:database:create') ->runCommand('doctrine:schema:create') ->runCommand('cache:clear') ; $this->defaultOutput->writeln(''); return $this; } if (!$this->isDatabasePresent()) { $this->defaultOutput->writeln('Creating database and schema, clearing the cache'); $this ->runCommand('doctrine:database:create') ->runCommand('doctrine:schema:create') ->runCommand('cache:clear') ; $this->defaultOutput->writeln(''); return $this; } $questionHelper = $this->getHelper('question'); $question = new ConfirmationQuestion('It appears that your database already exists. Would you like to reset it? (y/N)', false); if ($questionHelper->ask($this->defaultInput, $this->defaultOutput, $question)) { $this->defaultOutput->writeln('Droping database, creating database and schema'); $this ->runCommand('doctrine:database:drop', ['--force' => true]) ->runCommand('doctrine:database:create') ->runCommand('doctrine:schema:create') ; } elseif ($this->isSchemaPresent()) { $question = new ConfirmationQuestion('Seems like your database contains schema. Do you want to reset it? (y/N)', false); if ($questionHelper->ask($this->defaultInput, $this->defaultOutput, $question)) { $this->defaultOutput->writeln('Droping schema and creating schema'); $this ->runCommand('doctrine:schema:drop', ['--force' => true]) ->runCommand('doctrine:schema:create') ; } } else { $this->defaultOutput->writeln('Creating schema'); $this ->runCommand('doctrine:schema:create') ; } $this->defaultOutput->writeln('Clearing the cache'); $this->runCommand('cache:clear'); $this->defaultOutput->writeln(''); return $this; } protected function setupAdmin() { $this->defaultOutput->writeln('Step 3 of 4. Administration setup.'); $questionHelper = $this->getHelperSet()->get('question'); $question = new ConfirmationQuestion('Would you like to create a new admin user (recommended) ? (Y/n)', true); if (!$questionHelper->ask($this->defaultInput, $this->defaultOutput, $question)) { return $this; } $em = $this->getContainer()->get('doctrine.orm.entity_manager'); $userManager = $this->getContainer()->get('fos_user.user_manager'); $user = $userManager->createUser(); $question = new Question('Username (default: wallabag) :', 'wallabag'); $user->setUsername($questionHelper->ask($this->defaultInput, $this->defaultOutput, $question)); $question = new Question('Password (default: wallabag) :', 'wallabag'); $user->setPlainPassword($questionHelper->ask($this->defaultInput, $this->defaultOutput, $question)); $question = new Question('Email:', ''); $user->setEmail($questionHelper->ask($this->defaultInput, $this->defaultOutput, $question)); $user->setEnabled(true); $user->addRole('ROLE_SUPER_ADMIN'); $em->persist($user); // dispatch a created event so the associated config will be created $event = new UserEvent($user); $this->getContainer()->get('event_dispatcher')->dispatch(FOSUserEvents::USER_CREATED, $event); $this->defaultOutput->writeln(''); return $this; } protected function setupConfig() { $this->defaultOutput->writeln('Step 4 of 4. Config setup.'); $em = $this->getContainer()->get('doctrine.orm.entity_manager'); // cleanup before insert new stuff $em->createQuery('DELETE FROM CraueConfigBundle:Setting')->execute(); $settings = [ [ 'name' => 'share_public', 'value' => '1', 'section' => 'entry', ], [ 'name' => 'carrot', 'value' => '1', 'section' => 'entry', ], [ 'name' => 'share_diaspora', 'value' => '1', 'section' => 'entry', ], [ 'name' => 'diaspora_url', 'value' => 'http://diasporapod.com', 'section' => 'entry', ], [ 'name' => 'share_shaarli', 'value' => '1', 'section' => 'entry', ], [ 'name' => 'shaarli_url', 'value' => 'http://myshaarli.com', 'section' => 'entry', ], [ 'name' => 'share_mail', 'value' => '1', 'section' => 'entry', ], [ 'name' => 'share_twitter', 'value' => '1', 'section' => 'entry', ], [ 'name' => 'export_epub', 'value' => '1', 'section' => 'export', ], [ 'name' => 'export_mobi', 'value' => '1', 'section' => 'export', ], [ 'name' => 'export_pdf', 'value' => '1', 'section' => 'export', ], [ 'name' => 'export_csv', 'value' => '1', 'section' => 'export', ], [ 'name' => 'export_json', 'value' => '1', 'section' => 'export', ], [ 'name' => 'export_txt', 'value' => '1', 'section' => 'export', ], [ 'name' => 'export_xml', 'value' => '1', 'section' => 'export', ], [ 'name' => 'import_with_redis', 'value' => '0', 'section' => 'import', ], [ 'name' => 'import_with_rabbitmq', 'value' => '0', 'section' => 'import', ], [ 'name' => 'show_printlink', 'value' => '1', 'section' => 'entry', ], [ 'name' => 'wallabag_support_url', 'value' => 'https://www.wallabag.org/pages/support.html', 'section' => 'misc', ], [ 'name' => 'wallabag_url', 'value' => '', 'section' => 'misc', ], [ 'name' => 'piwik_enabled', 'value' => '0', 'section' => 'analytics', ], [ 'name' => 'piwik_host', 'value' => 'v2.wallabag.org', 'section' => 'analytics', ], [ 'name' => 'piwik_site_id', 'value' => '1', 'section' => 'analytics', ], [ 'name' => 'demo_mode_enabled', 'value' => '0', 'section' => 'misc', ], [ 'name' => 'demo_mode_username', 'value' => 'wallabag', 'section' => 'misc', ], [ 'name' => 'download_images_enabled', 'value' => '0', 'section' => 'misc', ], ]; foreach ($settings as $setting) { $newSetting = new Setting(); $newSetting->setName($setting['name']); $newSetting->setValue($setting['value']); $newSetting->setSection($setting['section']); $em->persist($newSetting); } $em->flush(); $this->defaultOutput->writeln(''); return $this; } /** * Run a command. * * @param string $command * @param array $parameters Parameters to this command (usually 'force' => true) */ protected function runCommand($command, $parameters = []) { $parameters = array_merge( ['command' => $command], $parameters, [ '--no-debug' => true, '--env' => $this->defaultInput->getOption('env') ?: 'dev', ] ); if ($this->defaultInput->getOption('no-interaction')) { $parameters = array_merge($parameters, ['--no-interaction' => true]); } $this->getApplication()->setAutoExit(false); $output = new BufferedOutput(); $exitCode = $this->getApplication()->run(new ArrayInput($parameters), $output); if (0 !== $exitCode) { $this->getApplication()->setAutoExit(true); $this->defaultOutput->writeln(''); $this->defaultOutput->writeln('The command "'.$command.'" generates some errors: '); $this->defaultOutput->writeln($output->fetch()); die(); } // PDO does not always close the connection after Doctrine commands. // See https://github.com/symfony/symfony/issues/11750. $this->getContainer()->get('doctrine')->getManager()->getConnection()->close(); return $this; } /** * Check if the database already exists. * * @return bool */ private function isDatabasePresent() { $connection = $this->getContainer()->get('doctrine')->getManager()->getConnection(); $databaseName = $connection->getDatabase(); try { $schemaManager = $connection->getSchemaManager(); } catch (\Exception $exception) { // mysql & sqlite if (false !== strpos($exception->getMessage(), sprintf("Unknown database '%s'", $databaseName))) { return false; } // pgsql if (false !== strpos($exception->getMessage(), sprintf('database "%s" does not exist', $databaseName))) { return false; } throw $exception; } // custom verification for sqlite, since `getListDatabasesSQL` doesn't work for sqlite if ('sqlite' === $schemaManager->getDatabasePlatform()->getName()) { $params = $this->getContainer()->get('doctrine.dbal.default_connection')->getParams(); if (isset($params['path']) && file_exists($params['path'])) { return true; } return false; } try { return in_array($databaseName, $schemaManager->listDatabases()); } catch (\Doctrine\DBAL\Exception\DriverException $e) { // it means we weren't able to get database list, assume the database doesn't exist return false; } } /** * Check if the schema is already created. * If we found at least oen table, it means the schema exists. * * @return bool */ private function isSchemaPresent() { $schemaManager = $this->getContainer()->get('doctrine')->getManager()->getConnection()->getSchemaManager(); return count($schemaManager->listTableNames()) > 0 ? true : false; } }