<?php
namespace AppBundle\Controller;
use AppBundle\Component\Plugin\PluginManager;
use Monolog\Logger;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\Filesystem\Filesystem;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Console\Input\ArrayInput;
use Symfony\Bundle\FrameworkBundle\Console\Application;
use Symfony\Component\Console\Output\BufferedOutput;
use Symfony\Component\HttpKernel\Event\FilterControllerEvent;
use Symfony\Component\HttpKernel\Event\PostResponseEvent;
use Symfony\Component\HttpKernel\Exception\HttpException;
use Symfony\Component\HttpKernel\KernelEvents;
class SetupController extends Controller implements EventSubscriberInterface
{
private $commandAfterResponse = false;
public function before(FilterControllerEvent $event)
{
set_time_limit(60 * 30);
$controller = $event->getController();
$action = $controller[1];
// ロックを掛けないアクション(BASIC認証をかけること)
$allowedActions = array(
'utilAction', 'cacheClearAction', 'schemaUpdateAction'
);
// ロックファイルがあればロックをかける
$currentEnv = CMS__ENVIRONMENT;
$lockFile = str_replace('/', \DIRECTORY_SEPARATOR, CMS__ADMIN_DIR . 'setup/install_' . $currentEnv . '.lock');
if(file_exists($lockFile) && !in_array($action, $allowedActions)){
throw new HttpException(500, 'Remove "' . $lockFile . '" before install.');
}
}
public function indexAction(Request $request)
{
return $this->render('setup/index.html.php', [
]);
}
public function dbSettingAction(Request $request)
{
$env = CMS__ENVIRONMENT;
$xml = str_replace('/\\', \DIRECTORY_SEPARATOR, CMS__ADMIN_DIR . 'config/env_database_setting_' . $env . '.xml');
$db_settings = $this->loadDatabaseSettings($xml);
return $this->render('setup/db-setting.html.php', [
'db_settings' => isset($db_settings[CMS__ENVIRONMENT]) ? $db_settings[CMS__ENVIRONMENT] : [],
'env' => $env
]);
}
public function mailSettingAction(Request $request)
{
$env = CMS__ENVIRONMENT;
$xml = str_replace('/\\', \DIRECTORY_SEPARATOR, CMS__ADMIN_DIR . 'config/env_mail_setting_' . $env . '.xml');
$mail_settings = $this->loadMailSettings($xml);
return $this->render('setup/mail-setting.html.php', [
'mail_settings' => isset($mail_settings[CMS__ENVIRONMENT]) ? $mail_settings[CMS__ENVIRONMENT] : [],
'env' => $env
]);
}
public function migrateAction(Request $request)
{
$result = '';
$result .= $this->runCommand(new ArrayInput([
'command' => 'doctrine:migration:diff',
'--no-interaction' => true
]));
$result .= $this->runCommand(new ArrayInput([
'command' => 'doctrine:migration:migrate',
'--no-interaction' => true
]));
return $this->render('setup/migrate.html.php', [
'result' => $result
]);
}
public function databaseAction(Request $request)
{
$data = $request->request->get('db');
$env = $request->request->get('env');
$this->saveDatabaseSettings($env, [$env => $data]);
return $this->redirectToRoute('setup_mail_setting');
}
protected function loadDatabaseSettings($file)
{
$settings = [
'prod' => [
'database_host' => '127.0.0.1',
'database_port' => 3306,
'database_name' => null,
'database_user' => null,
'database_password' => null
],
'staging' => [
'database_host' => '127.0.0.1',
'database_port' => 3306,
'database_name' => null,
'database_user' => null,
'database_password' => null
],
'dev' => [
'database_host' => '203.138.100.49',
'database_port' => 3306,
'database_name' => null,
'database_user' => 'ability',
'database_password' => 'ability'
]
];
if(file_exists($file)){
$dom = new \DOMDocument();
$dom->load($file);
$root = $dom->documentElement;
$settingNodes = $root->getElementsByTagName('setting');
foreach($settingNodes as $settingNode){
$mode = $settingNode->getAttribute('mode');
$host = $settingNode->getElementsByTagName('host')->item(0)->nodeValue;
$port = $settingNode->getElementsByTagName('port')->item(0)->nodeValue;
$name = $settingNode->getElementsByTagName('name')->item(0)->nodeValue;
$user = $settingNode->getElementsByTagName('user')->item(0)->nodeValue;
$password = $settingNode->getElementsByTagName('password')->item(0)->nodeValue;
$settings[$mode] = [
'database_host' => $host,
'database_port' => $port,
'database_name' => $name,
'database_user' => $user,
'database_password' => $password
];
}
}
return $settings;
}
protected function saveDatabaseSettings($env, $data){
$dom = new \DOMDocument();
$dom->preserveWhiteSpace = false;
$dom->formatOutput = true;
$root = $dom->createElement('settings');
$dom->appendChild($root);
$file = str_replace('/\\', \DIRECTORY_SEPARATOR, CMS__ADMIN_DIR . 'config/env_database_setting_' . $env . '.xml');
foreach ($data as $mode => $values) {
$setting = $this->createDatabaseSetting($dom, $mode, $values);
if($setting){
$root->appendChild($setting);
}
}
$dom->save($file);
}
protected function createDatabaseSetting($dom, $mode, $values)
{
$name = !empty($values['name']) ? $values['name'] : null;
$user = !empty($values['user']) ? $values['user'] : null;
$host = !empty($values['host']) ? $values['host'] : '127.0.0.1';
$port = !empty($values['port']) ? $values['port'] : 3306;
$password = !empty($values['password']) ? $values['password'] : null;
$setting = $dom->createElement('setting');
$setting->setAttribute('mode', $mode);
$hostElem = $dom->createElement('host');
$portElem = $dom->createElement('port');
$nameElem = $dom->createElement('name');
$userElem = $dom->createElement('user');
$passwordElem = $dom->createElement('password');
$hostText = $dom->createTextNode($host);
$portText = $dom->createTextNode($port);
$nameText = $dom->createTextNode($name);
$userText = $dom->createTextNode($user);
$passwordText = $dom->createTextNode($password);
$hostElem->appendChild($hostText);
$portElem->appendChild($portText);
$nameElem->appendChild($nameText);
$userElem->appendChild($userText);
$passwordElem->appendChild($passwordText);
$setting->appendChild($hostElem);
$setting->appendChild($portElem);
$setting->appendChild($nameElem);
$setting->appendChild($userElem);
$setting->appendChild($passwordElem);
return $setting;
}
public function checkDatabaseConnectionAction(Request $request)
{
$name = $request->query->get('name', null);
$user = $request->query->get('user', null);
$host = $request->query->get('host', '127.0.0.1');
$port = $request->query->get('port', 3306);
$password = $request->query->get('password', null);
$dsn = sprintf('mysql:dbname=%s;host=%s;port=%s',
$name, $host, $port);
try {
$db = new \PDO($dsn, $user, $password);
return new JsonResponse([
'status' => 'success'
]);
} catch(\PDOException $e){
return new JsonResponse([
'status' => 'failed'
]);
}
}
public function mailAction(Request $request)
{
$data = $request->request->get('mailer');
$env = $request->request->get('env');
$this->saveMailSettings($env, [$env => $data]);
$this->commandAfterResponse = true;
return $this->redirectToRoute('setup_install');
}
public function mailTestAction(Request $request)
{
$container = $this->container;
$user = $container->getParameter('mailer_user');
$password = $container->getParameter('mailer_password');
$transport = $container->getParameter('mailer_transport');
$host = $container->getParameter('mailer_host');
$message = '';
if('POST' === $request->getMethod()){
$to = $request->request->get('to');
if(!empty($to)){
$log = $this->runCommand(new ArrayInput([
'command' => 'mail:test',
'to' => $to
]));
$message = '送信しました';
}
}
return $this->render('setup/mail-test.html.php', [
'message' => $message,
'user' => $user,
'password' => $password,
'transport' => $transport,
'host' => $host
]);
}
public function checkMailServerConnectionAction(Request $request)
{
$host = $request->query->get('host', '127.0.0.1');
$transport = $request->query->get('transport', 'smtp');
$user = $request->query->get('user', '');
$password = $request->query->get('password', '');
$port = 25;
try {
$swiftTransport = new \Swift_SmtpTransport($host, $port);
$swiftTransport->setUsername($user);
$swiftTransport->setPassword($password);
$mailer = new \Swift_Mailer($swiftTransport);
$mailer->getTransport()->start();
} catch(\Swift_TransportException $ex){
return new JsonResponse([
'status' => 'failed',
'error' => $ex->getMessage()
]);
}
return new JsonResponse([
'status' => 'success'
]);
}
protected function loadMailSettings($file)
{
$settings = [
'prod' => [
'mailer_transport' => 'smtp',
'mailer_host' => '127.0.0.1',
'mailer_user' => null,
'mailer_password' => null,
'mailer_port' => 25,
],
'staging' => [
'mailer_transport' => 'smtp',
'mailer_host' => '127.0.0.1',
'mailer_user' => null,
'mailer_password' => null,
'mailer_port' => 25,
],
'dev' => [
'mailer_transport' => 'smtp',
'mailer_host' => 'smtp.mailtrap.io',
'mailer_user' => 'b45544fee7a1a6',
'mailer_password' => 'dc41fd4e41494d',
'mailer_port' => 25
]
];
if(file_exists($file)){
$dom = new \DOMDocument();
$dom->load($file);
$root = $dom->documentElement;
$settingNodes = $root->getElementsByTagName('setting');
foreach($settingNodes as $settingNode){
$mode = $settingNode->getAttribute('mode');
$transport = $settingNode->getElementsByTagName('transport')->item(0)->nodeValue;
$host = $settingNode->getElementsByTagName('host')->item(0)->nodeValue;
$user = $settingNode->getElementsByTagName('user')->item(0)->nodeValue;
$password = $settingNode->getElementsByTagName('password')->item(0)->nodeValue;
$portNode = $settingNode->getElementsByTagName('port')->item(0);
if($portNode){
$port = $portNode->nodeValue;
} else {
$port = 25;
}
$settings[$mode] = [
'mailer_host' => $host,
'mailer_transport' => $transport,
'mailer_user' => $user,
'mailer_password' => $password,
'mailer_port' => $port,
];
}
}
return $settings;
}
protected function saveMailSettings($env, $data){
$dom = new \DOMDocument();
$dom->preserveWhiteSpace = false;
$dom->formatOutput = true;
$root = $dom->createElement('settings');
$dom->appendChild($root);
$file = str_replace('/\\', \DIRECTORY_SEPARATOR, CMS__ADMIN_DIR . 'config/env_mail_setting_' . $env . '.xml');
foreach ($data as $mode => $values) {
$setting = $this->createMailSetting($dom, $mode, $values);
if($setting){
$root->appendChild($setting);
}
}
$dom->save($file);
}
protected function createMailSetting($dom, $mode, $values)
{
$host = !empty($values['host']) ? $values['host'] : '127.0.0.1';
$transport = !empty($values['transport']) ? $values['transport'] : 'smtp';
$user = !empty($values['user']) ? $values['user'] : null;
$password = !empty($values['password']) ? $values['password'] : null;
$port = !empty($values['port']) ? $values['port'] : 25;
$setting = $dom->createElement('setting');
$setting->setAttribute('mode', $mode);
$hostElem = $dom->createElement('host');
$hostElem->appendChild($dom->createTextNode($host));
$setting->appendChild($hostElem);
$portElem = $dom->createElement('port');
$portElem->appendChild($dom->createTextNode($port));
$setting->appendChild($portElem);
$transportElem = $dom->createElement('transport');
$transportElem->appendChild($dom->createTextNode($transport));
$setting->appendChild($transportElem);
$userElem = $dom->createElement('user');
$userElem->appendChild($dom->createTextNode($user));
$setting->appendChild($userElem);
$passwordElem = $dom->createElement('password');
$passwordElem->appendChild($dom->createTextNode($password));
$setting->appendChild($passwordElem);
return $setting;
}
public function installAction(Request $request)
{
$dbHost = $this->getParameter('database_host');
$dbName = $this->getParameter('database_name');
return $this->render('setup/install.html.php', [
'dbHost' => $dbHost,
'dbName' => $dbName
]);
}
public function ajaxInstallAction(Request $request)
{
set_time_limit(60 * 30);
if(!$request->isXmlHttpRequest()){
return new JsonResponse([
'status' => 'failed',
'log' => null
]);
}
$rootDir = $this->container->get('kernel')->getRootDir();
$modDir = str_replace('/', \DIRECTORY_SEPARATOR, $rootDir . '/../src/Mod');
if(!file_exists($modDir)){
mkdir($modDir, 0755, true);
}
$migrationDir = str_replace('/', \DIRECTORY_SEPARATOR, CMS__ADMIN_DIR . 'cms/app/DoctrineMigrations/');
if(file_exists($migrationDir)){
$files = glob($migrationDir . '*.php');
foreach($files as $file){
@unlink($file);
}
}
$log = '';
$this->runCommand(new ArrayInput([
'command' => 'cache:clear',
'--no-warmup' => true,
'--no-interaction' => true,
'-v' => true
]));
// 注意:データベースの内容が削除されます
$log .= $this->runCommand(new ArrayInput([
'command' => 'doctrine:schema:drop',
'--full-database' => true,
'--force' => true,
'--no-interaction' => true,
'-v' => true
]));
$log .= $this->runCommand(new ArrayInput([
'command' => 'doctrine:migration:diff',
'--no-interaction' => true,
'-v' => true
]));
$log .= $this->runCommand(new ArrayInput([
'command' => 'doctrine:migration:migrate',
'--no-interaction' => true,
'-v' => true
]));
$log .= $this->runCommand(new ArrayInput([
'command' => 'doctrine:fixtures:load',
'--no-interaction' => true,
'-v' => true
]));
$log .= 'Installing modules...' . "\n";
$this->installModAll();
$log .= 'Modules installed.' . "\n";
$this->runCommand(new ArrayInput([
'command' => 'log:clear',
'--no-interaction' => true,
'-v' => true
]));
$data = [
'status' => 'success',
'log' => $log
];
$setupDir = str_replace('/', \DIRECTORY_SEPARATOR, CMS__ADMIN_DIR . 'setup/');
$currentEnv = CMS__ENVIRONMENT;
$lockFile = str_replace('/', \DIRECTORY_SEPARATOR, $setupDir . 'install_' . $currentEnv . '.lock');
$logFile = str_replace('/', \DIRECTORY_SEPARATOR, $setupDir . 'install_' . $currentEnv . '.log');
if(!file_exists($setupDir)){
mkdir($setupDir, 0777, true);
}
// ロックファイルの作成
file_put_contents($lockFile, 'インストールの際はこのファイルを削除してください');
file_put_contents($logFile, $log);
return new JsonResponse($data);
}
public function completeAction(Request $request)
{
return $this->render('setup/complete.html.php', []);
}
public function schemaUpdateAction(Request $request)
{
$result = '';
$result .= $this->runCommand(new ArrayInput([
'command' => 'doctrine:schema:update',
'--force' => true
]));
return $this->render('setup/schema-update.html.php', [
'result' => $result
]);
}
public function cacheClearAction(Request $request)
{
$result = '';
$this->commandAfterResponse = true;
$this->addFlash('success', 'コマンド実行しました。');
return $this->render('setup/cache-clear.html.php', [
'result' => $result
]);
}
/**
* アプリケーションコマンドの実行
*
* @param ArrayInput $input
* @return string
* @throws \Exception
*/
protected function runCommand(ArrayInput $input)
{
$app = new Application($this->get('kernel'));
$app->setAutoExit(false);
$output = new BufferedOutput();
$exitCode = $app->run($input, $output);
return $output->fetch();
}
public function ajaxClearModAction()
{
$fs = new Filesystem();
$log = '';
$rootDir = $this->container->get('kernel')->getRootDir();
$configDir = str_replace('/', \DIRECTORY_SEPARATOR, $rootDir . '/config');
$modDir = str_replace('/', \DIRECTORY_SEPARATOR, $rootDir . '/cms/src/Mod');
$routingFile = str_replace('/', \DIRECTORY_SEPARATOR, $configDir . '/_mod_routing.yml');
$bundleRegisterFile = str_replace('/', \DIRECTORY_SEPARATOR, $configDir. '/_bundles.xml');
file_put_contents($routingFile, '');
file_put_contents($bundleRegisterFile, '<?xml version="1.0" encoding="UTF-8"?><bundles />');
if(file_exists($modDir)){
$fs->remove($modDir);
}
mkdir($modDir, 0755, true);
return new JsonResponse([
'status' => 'success'
]);
}
public function ajaxRegisterModAction(Request $request)
{
$log = '';
$log .= $this->runCommand(new ArrayInput([
'command' => 'bundle:register:exists',
'-v' => true
]));
$data = [
'status' => 'success',
'log' => $log
];
return new JsonResponse($data);
}
public function ajaxInstallModAction(Request $request)
{
$log = '';
$log .= $this->runCommand(new ArrayInput([
'command' => 'bundle:install',
'-v' => true
]));
$data = [
'status' => 'success',
'log' => $log
];
return new JsonResponse($data);
}
/**
* Mod フォルダを空にする
*/
protected function clearMods()
{
$fs = new Filesystem();
$configDir = $this->container->get('kernel')->getRootDir() . '/config';
$routingFile = str_replace('/', \DIRECTORY_SEPARATOR, $configDir . '/mod_routing.yml');
$bundleRegisterFile = str_replace('/', \DIRECTORY_SEPARATOR, $configDir. '/bundles.xml');
if(file_exists($routingFile)){
file_put_contents($routingFile, '');
}
if(file_exists($bundleRegisterFile)){
unlink($bundleRegisterFile);
}
$dir = str_replace('/', \DIRECTORY_SEPARATOR, CMS__ADMIN_DIR . 'cms/src/Mod/');
if(!file_exists($dir)){
mkdir($dir, 0755, true);
}
$copy = str_replace('/', \DIRECTORY_SEPARATOR, CMS__ADMIN_DIR . 'cms/src/Mod_' . microtime(true) . '/');
if(file_exists($dir) && is_dir($dir)){
rename($dir, $copy);
$fs->remove($copy);
mkdir($dir);
}
}
public function utilAction()
{
return $this->render('::setup/util.html.php', []);
}
/**
* bundles.xml をもとにプラグインを再インストール
*/
public function installModAll()
{
$projectDir = $this->get('kernel')->getProjectDir();
$modDir = str_replace('/', \DIRECTORY_SEPARATOR, $projectDir . '/src/Mod');
$bundleConfig = str_replace('', \DIRECTORY_SEPARATOR, $projectDir . '/app/config/bundles.xml');
$manager = new PluginManager($this->container);
if(file_exists($bundleConfig)){
$dom = new \DOMDocument('1.0', 'UTF-8');
$dom->preserveWhiteSpace = false;
$dom->formatOutput = true;
$dom->load($bundleConfig);
$bundles = $dom->documentElement->getElementsByTagName('bundle');
foreach($bundles as $bundleNode){
$alias = $bundleNode->getAttribute('alias');
$directory = $bundleNode->getAttribute('directory');
$configFile = str_replace('/', \DIRECTORY_SEPARATOR, $modDir . '/' . trim($directory, '/') . '/Resources/config/bundle.xml');
// バンドルが読み込まれている場合はインストーラーを実行
if(file_exists($configFile) && $this->container->has($alias . '.bundle_setup')){
$manager->installWithConfig($configFile);
$setup = $this->container->get($alias . '.bundle_setup');
$setup->install();
}
}
}
}
public function forceCacheClear(PostResponseEvent $event)
{
set_time_limit(60 * 3);
if ($this->commandAfterResponse === false) {
return '';
}
$commands = [];
$log = '';
/** @var Logger $logger */
$logger = $this->container->get('monolog.logger.custom');
$logger->info('command cache clear start');
$commands[] = new ArrayInput([
'command' => 'cache:clear',
'--no-interaction' => true,
'-v' => true
]);
foreach($commands as $command){
/** ArrayInput $command */
$log .= (string)$command . \PHP_EOL;
$log .= $this->runCommand($command);
}
$this->commandAfterResponse = false;
$logger->info($log);
$logger->info('command cache clear finish');
return $log;
}
/**
* {@inheritdoc}
*/
public static function getSubscribedEvents()
{
return [KernelEvents::TERMINATE => 'forceCacheClear'];
}
}