port 1776 databox create to master

This commit is contained in:
aina-esokia
2018-03-01 17:41:08 +04:00
parent 71f6e5d964
commit 9226e5a478
18 changed files with 411 additions and 166 deletions

View File

@@ -75,7 +75,7 @@ $app->command(new PluginsReset());
$app->command(new EnablePlugin());
$app->command(new DisablePlugin());
$app->command(new CheckEnvironment('check:system'));
$app->command(new Install('system:install'));
$app->command(new Install('system:install', $app['phraseanet.structure-template']));
$app->command(new CrossDomainGenerator());
$app->command(new FixAutoincrements('system:fix-autoincrements'));

View File

@@ -47,7 +47,7 @@ database:
- mysql -u ubuntu -e 'CREATE DATABASE update39_test;CREATE DATABASE ab_test;CREATE DATABASE db_test;SET @@global.sql_mode=STRICT_ALL_TABLES;SET @@global.max_allowed_packet=33554432;SET @@global.wait_timeout=999999;';
post:
- "./bin/developer system:uninstall -v"
- "./bin/setup system:install -v --email=test@phraseanet.com --password=test --db-host=127.0.0.1 --db-user=ubuntu --db-template=fr --db-password= --databox=db_test --appbox=ab_test --server-name=http://127.0.0.1 -y;"
- "./bin/setup system:install -v --email=test@phraseanet.com --password=test --db-host=127.0.0.1 --db-user=ubuntu --db-template=fr-simple --db-password= --databox=db_test --appbox=ab_test --server-name=http://127.0.0.1 -y;"
- "./bin/developer ini:setup-tests-dbs -v"
- "./bin/console searchengine:index:create -v"
- "./bin/developer phraseanet:regenerate-sqlite -v"

View File

@@ -3,6 +3,7 @@
namespace Alchemy\Phrasea\Command\Databox;
use Alchemy\Phrasea\Command\Command;
use Alchemy\Phrasea\Core\Configuration\StructureTemplate;
use Alchemy\Phrasea\Databox\DataboxConnectionSettings;
use Alchemy\Phrasea\Databox\DataboxService;
use Alchemy\Phrasea\Model\Repositories\UserRepository;
@@ -10,6 +11,7 @@ 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\Helper\DialogHelper;
class CreateDataboxCommand extends Command
{
@@ -18,7 +20,7 @@ class CreateDataboxCommand extends Command
{
$this->setName('databox:create')
->addArgument('databox', InputArgument::REQUIRED, 'Database name for the databox', null)
->addArgument('owner', InputArgument::REQUIRED, 'Email of the databox admin user', null)
->addArgument('owner', InputArgument::REQUIRED, 'Login of the databox admin user', null)
->addOption('connection', 'c', InputOption::VALUE_NONE, 'Flag to set new database settings')
->addOption('db-host', null, InputOption::VALUE_OPTIONAL, 'MySQL server host', 'localhost')
->addOption('db-port', null, InputOption::VALUE_OPTIONAL, 'MySQL server port', 3306)
@@ -28,13 +30,16 @@ class CreateDataboxCommand extends Command
'db-template',
null,
InputOption::VALUE_OPTIONAL,
'Metadata structure language template (available are fr (french) and en (english))',
'fr'
'Databox template',
null
);
}
protected function doExecute(InputInterface $input, OutputInterface $output)
{
/** @var DialogHelper $dialog */
$dialog = $this->getHelperSet()->get('dialog');
$databoxName = $input->getArgument('databox');
$connectionSettings = $input->getOption('connection') == false ? null : new DataboxConnectionSettings(
$input->getOption('db-host'),
@@ -45,18 +50,63 @@ class CreateDataboxCommand extends Command
/** @var UserRepository $userRepository */
$userRepository = $this->container['repo.users'];
$owner = $userRepository->findByLogin($input->getArgument('owner'));
if(!$owner) {
$output->writeln(sprintf("<error>Unknown user \"%s\"</error>", $input->getArgument('owner')));
return 1;
}
/** @var DataboxService $databoxService */
$databoxService = $this->container['databox.service'];
$owner = $userRepository->findByEmail($input->getArgument('owner'));
if($databoxService->exists($databoxName, $connectionSettings)) {
$output->writeln(sprintf("<error>Database \"%s\" already exists</error>", $databoxName));
$databoxService->createDatabox(
$databoxName,
$input->getOption('db-template') . '-simple',
$owner,
$connectionSettings
);
return 1;
}
/** @var StructureTemplate $templates */
$templates = $this->container['phraseanet.structure-template'];
// if a template name is provided, check that this template exists
$templateName = $input->getOption('db-template');
if($templateName && !$templates->getByName($templateName)) {
throw new \Exception_InvalidArgument(sprintf("Databox template \"%s\" not found.", $templateName));
}
if(!$templateName) {
// propose a default template : the first available if "en-simple" does not exists.
$defaultDBoxTemplate = $templates->getDefault();
do {
$templateName = $dialog->ask($output, 'Choose a template from ('.$templates->toString().') for metadata structure <comment>[default: "'.$defaultDBoxTemplate.'"]</comment> : ', $defaultDBoxTemplate);
if(!$templates->getByName($templateName)){
$output->writeln(" <error>Data-Box template : Template not found, try again.</error>");
}
}
while (!$templates->getByName($templateName));
}
try {
$databoxService->createDatabox(
$databoxName,
$templateName,
$owner,
$connectionSettings
);
}
catch(\Exception $e) {
$output->writeln(sprintf("<error>Failed to create database \"%s\", error=\"%s\"</error>"
, $databoxName
, $e->getMessage()
));
return 1;
}
$output->writeln('Databox created');
return 0;
}
}

View File

@@ -13,6 +13,8 @@ namespace Alchemy\Phrasea\Command\Developer;
use Alchemy\Phrasea\Command\Command;
use Alchemy\Phrasea\Core\Version;
use Alchemy\Phrasea\Exception\RuntimeException;
use Alchemy\Phrasea\Utilities\StringHelper;
use Doctrine\DBAL\Connection;
use Symfony\Component\Console\Input\ArrayInput;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
@@ -112,11 +114,15 @@ class IniReset extends Command
// get data paths
$dataPath = $this->container['conf']->get(['main', 'storage', 'subdefs'], $this->container['root.path'].'/datas');
$schema = $this->container['orm.em']->getConnection()->getSchemaManager();
/** @var Connection $connection */
$connection = $this->container['orm.em']->getConnection();
$schema = $connection->getSchemaManager();
$output->writeln('Creating database "'.$dbs['ab'].'"...<info>OK</info>');
$schema->dropAndCreateDatabase($dbs['ab']);
$schema->dropAndCreateDatabase(StringHelper::SqlQuote($dbs['ab'], StringHelper::SQL_IDENTIFIER));
$output->writeln('Creating database "'.$dbName.'"...<info>OK</info>');
$schema->dropAndCreateDatabase($dbName);
$schema->dropAndCreateDatabase(StringHelper::SqlQuote($dbName, StringHelper::SQL_IDENTIFIER));
// inject v3.1 fixtures
if ($input->getOption('run-patches')) {
@@ -212,7 +218,7 @@ class IniReset extends Command
} else {
$output->write(sprintf('Upgrading... from version <info>%s</info> to <info>%s</info>', $this->app->getApplicationBox()->get_version(), $version->getNumber()), true);
}
$cmd = 'php ' . __DIR__ . '/../../../../../bin/setup system:upgrade -y -f -v';
$process = new Process($cmd);
$process->setTimeout(600);

View File

@@ -13,6 +13,8 @@ namespace Alchemy\Phrasea\Command\Developer;
use Alchemy\Phrasea\Command\Command;
use Alchemy\Phrasea\Exception\RuntimeException;
use Alchemy\Phrasea\Utilities\StringHelper;
use Doctrine\DBAL\Connection;
use Symfony\Component\Console\Input\ArrayInput;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
@@ -44,20 +46,30 @@ class SetupTestsDbs extends Command
$dbs[] = $settings['database']['ab_name'];
$dbs[] = $settings['database']['db_name'];
$schema = $this->container['orm.em']->getConnection()->getSchemaManager();
/** @var Connection $connection */
$connection = $this->container['orm.em']->getConnection();
$schema = $connection->getSchemaManager();
foreach($dbs as $name) {
$output->writeln('Creating database "'.$name.'"...<info>OK</info>');
$name = StringHelper::SqlQuote($name, StringHelper::SQL_IDENTIFIER); // quote as `identifier`
$schema->dropAndCreateDatabase($name);
}
$this->container['orm.em']->getConnection()->executeUpdate('
GRANT ALL PRIVILEGES ON '.$settings['database']['ab_name'].'.* TO \''.$settings['database']['user'].'\'@\''.$settings['database']['host'].'\' IDENTIFIED BY \''.$settings['database']['password'].'\' WITH GRANT OPTION
');
$user = StringHelper::SqlQuote($settings['database']['user'], StringHelper::SQL_VALUE); // quote as 'value'
$host = StringHelper::SqlQuote($settings['database']['host'], StringHelper::SQL_VALUE);
$pass = StringHelper::SqlQuote($settings['database']['password'], StringHelper::SQL_VALUE);
$this->container['orm.em']->getConnection()->executeUpdate('
GRANT ALL PRIVILEGES ON '.$settings['database']['db_name'].'.* TO \''.$settings['database']['user'].'\'@\''.$settings['database']['host'].'\' IDENTIFIED BY \''.$settings['database']['password'].'\' WITH GRANT OPTION
');
$ab_name = StringHelper::SqlQuote($settings['database']['ab_name'], StringHelper::SQL_IDENTIFIER);
$db_name = StringHelper::SqlQuote($settings['database']['db_name'], StringHelper::SQL_IDENTIFIER);
$this->container['orm.em']->getConnection()->executeUpdate(
'GRANT ALL PRIVILEGES ON '.$ab_name.'.* TO '.$user.'@'.$host.' IDENTIFIED BY '.$pass.' WITH GRANT OPTION'
);
$this->container['orm.em']->getConnection()->executeUpdate(
'GRANT ALL PRIVILEGES ON '.$db_name.'.* TO '.$user.'@'.$host.' IDENTIFIED BY '.$pass.' WITH GRANT OPTION'
);
$this->container['orm.em']->getConnection()->executeUpdate('SET @@global.sql_mode= ""');

View File

@@ -12,6 +12,7 @@
namespace Alchemy\Phrasea\Command\Setup;
use Alchemy\Phrasea\Command\Command;
use Alchemy\Phrasea\Core\Configuration\StructureTemplate;
use Doctrine\DBAL\Driver\Connection;
use Symfony\Component\Console\Helper\DialogHelper;
use Symfony\Component\Console\Input\ArrayInput;
@@ -23,11 +24,18 @@ use Symfony\Component\Process\ExecutableFinder;
class Install extends Command
{
private $executableFinder;
/** @var StructureTemplate StructureTemplate */
private $structureTemplate;
public function __construct($name = null)
/**
* @param null|string $name
* @param StructureTemplate $structureTemplate
*/
public function __construct($name, $structureTemplate)
{
parent::__construct($name);
$this->structureTemplate = $structureTemplate;
$this->executableFinder = new ExecutableFinder();
$this
@@ -38,9 +46,9 @@ class Install extends Command
->addOption('db-port', null, InputOption::VALUE_OPTIONAL, 'MySQL server port', 3306)
->addOption('db-user', null, InputOption::VALUE_OPTIONAL, 'MySQL server user', 'phrasea')
->addOption('db-password', null, InputOption::VALUE_OPTIONAL, 'MySQL server password', null)
->addOption('db-template', null, InputOption::VALUE_OPTIONAL, 'Metadata structure language template (available are fr (french) and en (english))', null)
->addOption('databox', null, InputOption::VALUE_OPTIONAL, 'Database name for the DataBox', null)
->addOption('appbox', null, InputOption::VALUE_OPTIONAL, 'Database name for the ApplicationBox', null)
->addOption('databox', null, InputOption::VALUE_OPTIONAL, 'Database name for the DataBox', null)
->addOption('db-template', null, InputOption::VALUE_OPTIONAL, 'Databox template (' . $this->structureTemplate->toString() . ')', null)
->addOption('data-path', null, InputOption::VALUE_OPTIONAL, 'Path to data repository', realpath(__DIR__ . '/../../../../../datas'))
->addOption('server-name', null, InputOption::VALUE_OPTIONAL, 'Server name')
->addOption('indexer', null, InputOption::VALUE_OPTIONAL, 'Path to Phraseanet Indexer', 'auto')
@@ -49,11 +57,22 @@ class Install extends Command
return $this;
}
private function serverNameToAppBoxName($serverName)
{
return "ab_" . $serverName;
}
private function serverNameToDataBoxName($serverName)
{
return "db_" . $serverName;
}
/**
* {@inheritdoc}
*/
protected function doExecute(InputInterface $input, OutputInterface $output)
{
/** @var DialogHelper $dialog */
$dialog = $this->getHelperSet()->get('dialog');
$output->writeln("<comment>
@@ -91,12 +110,16 @@ class Install extends Command
}
}
$abConn = $this->getABConn($input, $output, $dialog);
$serverName = $this->getServerName($input, $output, $dialog);
list($dbConn, $template) = $this->getDBConn($input, $output, $abConn, $dialog);
$abConn = $this->getABConn($input, $output, $dialog, $serverName);
if(!$abConn) {
return 1; // no ab is fatal
}
list($dbConn, $templateName) = $this->getDBConn($input, $output, $abConn, $dialog);
list($email, $password) = $this->getCredentials($input, $output, $dialog);
$dataPath = $this->getDataPath($input, $output, $dialog);
$serverName = $this->getServerName($input, $output, $dialog);
if (!$input->getOption('yes')) {
$continue = $dialog->askConfirmation($output, "<question>Phraseanet is going to be installed, continue ? (N/y)</question>", false);
@@ -108,32 +131,32 @@ class Install extends Command
}
}
$this->container['phraseanet.installer']->install($email, $password, $abConn, $serverName, $dataPath, $dbConn, $template, $this->detectBinaries());
$this->container['phraseanet.installer']->install($email, $password, $abConn, $serverName, $dataPath, $dbConn, $templateName, $this->detectBinaries());
if (null !== $this->getApplication()) {
$command = $this->getApplication()->find('crossdomain:generate');
$command->run(new ArrayInput(array(
$command->run(new ArrayInput([
'command' => 'crossdomain:generate'
)), $output);
]), $output);
}
$output->writeln("<info>Install successful !</info>");
return;
return 0;
}
private function getABConn(InputInterface $input, OutputInterface $output, DialogHelper $dialog)
{
$abConn = $info = null;
if (!$input->getOption('appbox')) {
$output->writeln("\n<info>--- Database credentials ---</info>\n");
$output->writeln("<info>--- Database credentials ---</info>");
do {
$hostname = $dialog->ask($output, "DB hostname (localhost) : ", 'localhost');
$port = $dialog->ask($output, "DB port (3306) : ", 3306);
$dbUser = $dialog->ask($output, "DB user : ");
$dbPassword = $dialog->askHiddenResponse($output, "DB password (hidden) : ");
$abName = $dialog->ask($output, "DB name (phraseanet) : ", 'phraseanet');
$hostname = $dialog->ask($output, 'DB hostname <comment>[default: "localhost"]</comment> : ', 'localhost');
$port = $dialog->ask($output, 'DB port <comment>[default: "3306"]</comment> : ', '3306');
$dbUser = $dialog->ask($output, 'DB user : ');
$dbPassword = $dialog->askHiddenResponse($output, 'DB password (hidden) : ');
$abName = $dialog->ask($output, 'ApplicationBox name <comment>[default: "phraseanet"]</comment> : ', 'phraseanet');
$info = [
'host' => $hostname,
@@ -145,9 +168,10 @@ class Install extends Command
try {
$abConn = $this->container['dbal.provider']($info);
$abConn->connect();
$output->writeln("\n\t<info>Application-Box : Connection successful !</info>\n");
$output->writeln("<info>Application-Box : Connection successful !</info>");
} catch (\Exception $e) {
$output->writeln("\n\t<error>Invalid connection parameters</error>\n");
$output->writeln("<error>Application-Box : Failed to connect, try again.</error>");
$abConn = null;
}
} while (!$abConn);
} else {
@@ -161,7 +185,7 @@ class Install extends Command
$abConn = $this->container['dbal.provider']($info);
$abConn->connect();
$output->writeln("\n\t<info>Application-Box : Connection successful !</info>\n");
$output->writeln("<info>Application-Box : Connection successful !</info>");
}
// add dbs.option & orm.options services to use orm.em later
@@ -175,12 +199,13 @@ class Install extends Command
private function getDBConn(InputInterface $input, OutputInterface $output, Connection $abConn, DialogHelper $dialog)
{
$dbConn = $template = $info = null;
$templates = $this->container['phraseanet.structure-template']->getAvailable();
$dbConn = $info = null;
$templateName = null;
if (!$input->getOption('databox')) {
do {
$retry = false;
$dbName = $dialog->ask($output, 'DataBox name, will not be created if empty : ', null);
$dbName = $dialog->ask($output, 'Data-Box name, will not be created if empty : ', null);
if ($dbName) {
try {
@@ -194,19 +219,13 @@ class Install extends Command
$dbConn = $this->container['dbal.provider']($info);
$dbConn->connect();
$output->writeln("\n\t<info>Data-Box : Connection successful !</info>\n");
do {
$template = $dialog->ask($output, "Choose a language template for metadata structure, available are {$templates->__toString()} : ", 'en');
}
while (!in_array($template, array_keys($templates->getTemplates())));
$output->writeln("\n\tLanguage selected is <info>'$template'</info>\n");
$output->writeln("<info>Data-Box : Connection successful !</info>");
} catch (\Exception $e) {
$output->writeln(" <error>Data-Box : Failed to connect, try again.</error>");
$retry = true;
}
} else {
$output->writeln("\n\tNo databox will be created\n");
$output->writeln("No databox will be created");
}
} while ($retry);
} else {
@@ -220,17 +239,37 @@ class Install extends Command
$dbConn = $this->container['dbal.provider']($info);
$dbConn->connect();
$output->writeln("\n\t<info>Data-Box : Connection successful !</info>\n");
$template = $input->getOption('db-template') ? : 'en';
$output->writeln("<info>Data-Box : Connection successful !</info>");
}
// add dbs.option & orm.options services to use orm.em later
if ($dbConn && $info) {
/** @var StructureTemplate $templates */
$templates = $this->container['phraseanet.structure-template'];
// if a template name is provided, check that this template exists
$templateName = $input->getOption('db-template');
if($templateName && !$templates->getByName($templateName)) {
throw new \Exception_InvalidArgument(sprintf("Databox template \"%s\" not found.", $templateName));
}
if(!$templateName) {
// propose a default template : the first available if "en-simple" does not exists.
$defaultDBoxTemplate = $this->structureTemplate->getDefault();
do {
$templateName = $dialog->ask($output, 'Choose a template from ('.$templates->toString().') for metadata structure <comment>[default: "'.$defaultDBoxTemplate.'"]</comment> : ', $defaultDBoxTemplate);
if(!$templates->getByName($templateName)) {
$output->writeln("<error>Data-Box template : Template not found, try again.</error>");
}
}
while (!$templates->getByName($templateName));
}
$this->container['dbs.options'] = array_merge($this->container['db.options.from_info']($info), $this->container['dbs.options']);
$this->container['orm.ems.options'] = array_merge($this->container['orm.em.options.from_info']($info), $this->container['orm.ems.options']);
}
return [$dbConn, $template];
return [$dbConn, $templateName];
}
private function getCredentials(InputInterface $input, OutputInterface $output, DialogHelper $dialog)
@@ -238,7 +277,7 @@ class Install extends Command
$email = $password = null;
if (!$input->getOption('email') && !$input->getOption('password')) {
$output->writeln("\n<info>--- Account Informations ---</info>\n");
$output->writeln("<info>--- Account Informations ---</info>");
do {
$email = $dialog->ask($output, 'Please provide a valid e-mail address : ');
@@ -248,7 +287,7 @@ class Install extends Command
$password = $dialog->askHiddenResponse($output, 'Please provide a password (hidden, 6 character min) : ');
} while (strlen($password) < 6);
$output->writeln("\n\t<info>Email / Password successfully set</info>\n");
$output->writeln("<info>Email / Password successfully set</info>");
} elseif ($input->getOption('email') && $input->getOption('password')) {
if (!\Swift_Validate::email($input->getOption('email'))) {
throw new \RuntimeException('Invalid email addess');

View File

@@ -11,6 +11,7 @@
namespace Alchemy\Phrasea\Controller;
use Alchemy\Phrasea\Application;
use Alchemy\Phrasea\Core\Configuration\StructureTemplate;
use Alchemy\Phrasea\Setup\RequirementCollectionInterface;
use Alchemy\Phrasea\Setup\Requirements\BinariesRequirements;
use Alchemy\Phrasea\Setup\Requirements\FilesystemRequirements;
@@ -74,10 +75,13 @@ class SetupController extends Controller
$warnings[] = $this->app->trans('It is not recommended to install Phraseanet without HTTPS support');
}
/** @var StructureTemplate $st */
$st = $this->app['phraseanet.structure-template'];
return $this->render('/setup/step2.html.twig', [
'locale' => $this->app['locale'],
'available_locales' => Application::getAvailableLanguages(),
'available_templates' => $this->app['phraseanet.structure-template']->getAvailable()->getTemplates(),
'available_templates' => $st->getNames(),
'warnings' => $warnings,
'error' => $request->query->get('error'),
'current_servername' => $request->getScheme() . '://' . $request->getHttpHost() . '/',
@@ -92,7 +96,7 @@ class SetupController extends Controller
$servername = $request->getScheme() . '://' . $request->getHttpHost() . '/';
$dbConn = null;
$dbConn = null;
$database_host = $request->request->get('hostname');
$database_port = $request->request->get('port');

View File

@@ -10,8 +10,6 @@
namespace Alchemy\Phrasea\Core\Configuration;
use Alchemy\Phrasea\Application;
/**
* Class StructureTemplate
* @package Alchemy\Phrasea\Core\Configuration
@@ -19,25 +17,36 @@ use Alchemy\Phrasea\Application;
class StructureTemplate
{
const TEMPLATE_EXTENSION = 'xml';
private $templates;
const DEFAULT_TEMPLATE = 'en-simple';
public function __construct(Application $app)
{
$this->app = $app;
}
/** @var string */
private $rootPath;
/** @var \SplFileInfo[] */
private $templates;
/** @var string[] */
private $names;
/**
* @return $this
* @throws \Exception
* @param string $rootPath
*/
public function getAvailable()
public function __construct($rootPath)
{
$templateList = new \DirectoryIterator($this->app['root.path'] . '/lib/conf.d/data_templates');
if (empty($templateList)) {
throw new \Exception('No available structure template');
$this->rootPath = $rootPath;
$this->names = $this->templates = null; // lazy loaded, not yet set
}
private function load()
{
if(!is_null($this->templates)) {
return; // already loaded
}
$templates = [];
$abbreviationLength = 2;
$templateList = new \DirectoryIterator($this->rootPath . '/lib/conf.d/data_templates');
$this->templates = [];
$this->names = [];
foreach ($templateList as $template) {
if ($template->isDot()
|| !$template->isFile()
@@ -45,65 +54,64 @@ class StructureTemplate
) {
continue;
}
$name = $template->getFilename();
$abbreviation = strtolower(substr($name, 0, $abbreviationLength));
if (array_key_exists($abbreviation, $templates)) {
$abbreviation = strtolower(substr($name, 0, ++$abbreviationLength));
}
$templates[$abbreviation] = $template->getBasename('.' . self::TEMPLATE_EXTENSION);
}
$this->templates = $templates;
return $this;
$name = $template->getBasename('.' . self::TEMPLATE_EXTENSION);
// beware that the directoryiterator returns a reference on a static, so clone()
$this->templates[$name] = clone($template);
$this->names[] = $name;
}
}
/**
* @param $templateName
* @return null|\SplFileInfo
*/
public function getByName($templateName)
{
$this->load();
if (!array_key_exists($templateName, $this->templates)) {
return null;
}
return $this->templates[$templateName];
}
/**
* @param $index
* @return null|\SplFileInfo
*/
public function getNameByIndex($index)
{
$this->load();
return $this->names[$index];
}
/**
* @return \string[]
*/
public function getNames()
{
$this->load();
return $this->names;
}
public function toString()
{
$this->load();
return implode(', ', $this->names);
}
/**
* @return string
*/
public function __toString()
public function getDefault()
{
if (!$this->templates) {
return '';
}
$templateToString = '';
$cpt = 1;
$templateLength = count($this->templates);
foreach ($this->templates as $key => $value) {
if (($templateLength - 1) == $cpt) {
$separator = ' and ';
}
elseif (end($this->templates) == $value) {
$separator = '';
}
else {
$separator = ', ';
}
$templateToString .= $key . ' (' . $value . ')' . $separator;
$cpt++;
}
$this->load();
return $templateToString;
}
/**
* @param $template
* @return mixed
* @throws \Exception
*/
public function getTemplateName($template = 'en')
{
if (!array_key_exists($template, $this->templates)) {
throw new \Exception('Not found template : ' . $template);
}
return $this->templates[$template];
}
/**
* @return mixed
*/
public function getTemplates()
{
return $this->templates;
return $this->getByName(self::DEFAULT_TEMPLATE) ? self::DEFAULT_TEMPLATE : $this->getNameByIndex(0);
}
}

View File

@@ -75,7 +75,7 @@ class ConfigurationServiceProvider implements ServiceProviderInterface
});
$app['phraseanet.structure-template'] = $app->share(function (Application $app) {
return new StructureTemplate($app);
return new StructureTemplate($app['root.path']);
});
}

View File

@@ -4,7 +4,9 @@ namespace Alchemy\Phrasea\Databox;
use Alchemy\Phrasea\Application;
use Alchemy\Phrasea\Core\Configuration\PropertyAccess;
use Alchemy\Phrasea\Core\Configuration\StructureTemplate;
use Alchemy\Phrasea\Model\Entities\User;
use Alchemy\Phrasea\Utilities\StringHelper;
use Doctrine\DBAL\Connection;
/**
@@ -72,26 +74,101 @@ class DataboxService
}
/**
* @param $databaseName
* @param DataboxConnectionSettings|null $connectionSettings
* @return bool
*/
public function exists($databaseName, DataboxConnectionSettings $connectionSettings = null)
{
$connectionSettings = $connectionSettings ?: DataboxConnectionSettings::fromArray(
$this->configuration->get(['main', 'database'])
);
$factory = $this->connectionFactory;
// do not simply try to connect to the database, list
/** @var Connection $connection */
$connection = $factory([
'host' => $connectionSettings->getHost(),
'port' => $connectionSettings->getPort(),
'user' => $connectionSettings->getUser(),
'password' => $connectionSettings->getPassword(),
'dbname' => null,
]);
$ret = false;
$databaseName = strtolower($databaseName);
$sm = $connection->getSchemaManager();
$databases = $sm->listDatabases();
foreach($databases as $database) {
if(strtolower($database) == $databaseName) {
$ret = true;
break;
}
}
return $ret;
}
/**
* @param Connection $connection
* @param \SplFileInfo $template
* @return \databox
*/
public function createDataboxFromConnection($connection, $template)
{
return \databox::create($this->app, $connection, $template);
}
/**
* @param $databaseName
* @param $templateName
* @param User $owner
* @param string $databaseName
* @param string $dataTemplate
* @param DataboxConnectionSettings|null $connectionSettings
* @return \databox
* @throws \Exception_InvalidArgument
*/
public function createDatabox(
$databaseName,
$dataTemplate,
$templateName,
User $owner,
DataboxConnectionSettings $connectionSettings = null
) {
$this->validateDatabaseName($databaseName);
$dataTemplate = new \SplFileInfo($this->rootPath . '/lib/conf.d/data_templates/' . $dataTemplate . '.xml');
/** @var StructureTemplate $st */
$st = $this->app['phraseanet.structure-template'];
$template = $st->getByName($templateName);
if(is_null($template)) {
throw new \Exception_InvalidArgument(sprintf('Databox template "%s" not found.', $templateName));
}
// if no connectionSettings (host, user, ...) are provided, create dbox beside appBox
$connectionSettings = $connectionSettings ?: DataboxConnectionSettings::fromArray(
$this->configuration->get(['main', 'database'])
);
$factory = $this->connectionFactory;
if(!$this->exists($databaseName, $connectionSettings)) {
// use a tmp connection to create the database
/** @var Connection $connection */
$connection = $factory([
'host' => $connectionSettings->getHost(),
'port' => $connectionSettings->getPort(),
'user' => $connectionSettings->getUser(),
'password' => $connectionSettings->getPassword(),
'dbname' => null
]);
// the schemeManager does NOT quote identifiers, we MUST do it
// see : http://docs.doctrine-project.org/projects/doctrine-dbal/en/latest/reference/schema-manager.html
$connection->getSchemaManager()->createDatabase(StringHelper::SqlQuote($databaseName, StringHelper::SQL_IDENTIFIER));
$connection->close();
unset($connection);
}
/** @var Connection $connection */
$connection = $factory([
'host' => $connectionSettings->getHost(),
@@ -103,7 +180,8 @@ class DataboxService
$connection->connect();
$databox = \databox::create($this->app, $connection, $dataTemplate);
$databox = $this->createDataboxFromConnection($connection, $template);
$databox->registerAdmin($owner);
$connection->close();

View File

@@ -12,6 +12,7 @@
namespace Alchemy\Phrasea\Setup;
use Alchemy\Phrasea\Application;
use Alchemy\Phrasea\Core\Configuration\StructureTemplate;
use Alchemy\Phrasea\Core\PhraseaEvents;
use Alchemy\Phrasea\Core\Event\InstallFinishEvent;
use Alchemy\Phrasea\Model\Entities\User;
@@ -29,7 +30,7 @@ class Installer
$this->app = $app;
}
public function install($email, $password, Connection $abConn, $serverName, $dataPath, Connection $dbConn = null, $template = null, array $binaryData = [])
public function install($email, $password, Connection $abConn, $serverName, $dataPath, Connection $dbConn = null, $templateName = null, array $binaryData = [])
{
$this->rollbackInstall($abConn, $dbConn);
@@ -39,7 +40,7 @@ class Installer
$user = $this->createUser($email, $password);
$this->createDefaultUsers();
if (null !== $dbConn) {
$this->createDB($dbConn, $template, $user);
$this->createDB($dbConn, $templateName, $user);
}
} catch (\Exception $e) {
$this->rollbackInstall($abConn, $dbConn);
@@ -51,9 +52,15 @@ class Installer
return $user;
}
private function createDB(Connection $dbConn = null, $template, User $admin)
private function createDB(Connection $dbConn = null, $templateName, User $admin)
{
$template = new \SplFileInfo(__DIR__ . '/../../../conf.d/data_templates/' . $this->app['phraseanet.structure-template']->getAvailable()->getTemplateName($template) . '.xml');
/** @var StructureTemplate $st */
$st = $this->app['phraseanet.structure-template'];
$template = $st->getByName($templateName);
if(is_null($template)) {
throw new \Exception_InvalidArgument(sprintf('Databox template "%s" not found.', $templateName));
}
$databox = \databox::create($this->app, $dbConn, $template);
$this->app->getAclForUser($admin)
@@ -66,7 +73,7 @@ class Installer
\ACL::BAS_MODIF_TH => true,
\ACL::BAS_CHUPUB => true
]
);
);
$collection = \collection::create($this->app, $databox, $this->app['phraseanet.appbox'], 'test', $admin);

View File

@@ -13,6 +13,9 @@ namespace Alchemy\Phrasea\Utilities;
class StringHelper
{
const SQL_VALUE = '\'';
const SQL_IDENTIFIER = '`';
/**
* @param string $str
* @return string
@@ -35,4 +38,14 @@ class StringHelper
return $pascalCase ? $transformStr : lcfirst($transformStr);
}
/**
* @param $s
* @param $quote
* @return string
*/
public static function SqlQuote($s, $quote)
{
return $quote . str_replace($quote, $quote.$quote, $s) . $quote;
}
}

View File

@@ -63,14 +63,15 @@ class databox extends base implements ThumbnailedElement
/**
* @param Application $app
* @param Connection $databoxConnection
* @param SplFileInfo $data_template
* @param SplFileInfo $template
* @return databox
* @throws \Doctrine\DBAL\DBALException
*/
public static function create(Application $app, Connection $databoxConnection, \SplFileInfo $data_template)
public static function create(Application $app, Connection $databoxConnection, \SplFileInfo $template)
{
if ( ! file_exists($data_template->getRealPath())) {
throw new \InvalidArgumentException($data_template->getRealPath() . " does not exist");
$rp = $template->getRealPath();
if (!$rp || !file_exists($rp)) {
throw new \InvalidArgumentException(sprintf('Databox template "%s" not found.', $template->getFilename()));
}
$host = $databoxConnection->getHost();
@@ -113,7 +114,7 @@ class databox extends base implements ThumbnailedElement
$databox->insert_datas();
$databox->setNewStructure(
$data_template, $app['conf']->get(['main', 'storage', 'subdefs'])
$template, $app['conf']->get(['main', 'storage', 'subdefs'])
);
$app['dispatcher']->dispatch(DataboxEvents::CREATED, new CreatedEvent($databox));
@@ -371,9 +372,9 @@ class databox extends base implements ThumbnailedElement
DataboxEvents::STRUCTURE_CHANGED,
new StructureChangedEvent(
$this,
array(
[
'dom_before'=>$old_structure
)
]
)
);
@@ -441,13 +442,13 @@ class databox extends base implements ThumbnailedElement
$type = isset($field['type']) ? $field['type'] : 'string';
$type = in_array($type
, [
, [
databox_field::TYPE_DATE
, databox_field::TYPE_NUMBER
, databox_field::TYPE_STRING
, databox_field::TYPE_TEXT
]
) ? $type : databox_field::TYPE_STRING;
]
) ? $type : databox_field::TYPE_STRING;
$multi = isset($field['multi']) ? (Boolean) (string) $field['multi'] : false;
@@ -769,14 +770,14 @@ class databox extends base implements ThumbnailedElement
$rs = $stmt->fetchAll(PDO::FETCH_ASSOC);
$stmt->closeCursor();
$ret = array(
$ret = [
'records' => 0,
'records_indexed' => 0, // jetons = 0;0
'records_to_index' => 0, // jetons = 0;1
'records_not_indexed' => 0, // jetons = 1;0
'records_indexing' => 0, // jetons = 1;1
'subdefs_todo' => array() // by type "image", "video", ...
);
'subdefs_todo' => [] // by type "image", "video", ...
];
foreach ($rs as $row) {
$ret['records'] += ($n = (int)($row['n']));
$status = $row['status'];
@@ -870,9 +871,9 @@ class databox extends base implements ThumbnailedElement
DataboxEvents::UNMOUNTED,
new UnmountedEvent(
null,
array(
[
'dbname'=>$old_dbname
)
]
)
);
@@ -935,9 +936,9 @@ class databox extends base implements ThumbnailedElement
DataboxEvents::DELETED,
new DeletedEvent(
null,
array(
[
'dbname'=>$old_dbname
)
]
)
);
@@ -1141,7 +1142,7 @@ class databox extends base implements ThumbnailedElement
\ACL::BAS_MODIF_TH => true,
\ACL::BAS_CHUPUB => true
]
);
);
$sql = "SELECT * FROM coll";
$stmt = $this->get_connection()->prepare($sql);
@@ -1363,9 +1364,9 @@ class databox extends base implements ThumbnailedElement
DataboxEvents::TOU_CHANGED,
new TouChangedEvent(
$this,
array(
[
'tou_before'=>$old_tou,
)
]
)
);
@@ -1481,6 +1482,7 @@ class databox extends base implements ThumbnailedElement
$rs = $stmt->fetchAll(PDO::FETCH_ASSOC);
$stmt->closeCursor();
$TOU = [];
foreach ($rs as $row) {
$TOU[$row['locale']] = ['updated_on' => $row['updated_on'], 'value' => $row['value']];
}

View File

@@ -32,7 +32,7 @@
- name: Run application setup
become: yes
become_user: vagrant
shell: 'bin/setup system:install --email=admin@{{ hostname }}.vb --password=admin --db-host=127.0.0.1 --db-port=3306 --db-user={{ mariadb.user }} --db-password={{ mariadb.password }} --db-template=fr --appbox={{ mariadb.appbox_db }} --databox={{ mariadb.databox_db }} --server-name=www.{{ hostname }}.vb --data-path=/vagrant/datas -y'
shell: 'bin/setup system:install --email=admin@{{ hostname }}.vb --password=admin --db-host=127.0.0.1 --db-port=3306 --db-user={{ mariadb.user }} --db-password={{ mariadb.password }} --db-template=en-simple --appbox={{ mariadb.appbox_db }} --databox={{ mariadb.databox_db }} --server-name=www.{{ hostname }}.vb --data-path=/vagrant/datas -y'
args:
chdir: /vagrant/

View File

@@ -738,8 +738,8 @@
<td><label>{{ 'Modele de donnees' | trans }}</label></td>
<td>
<select name="db_template" class="databox_creation_input">
{% for key,template in available_templates %}
<option value="{{ key }}">{{ template }}</option>
{% for name in available_templates %}
<option value="{{ name|escape('js') }}">{{ name }}</option>
{% endfor %}
</select>
</td>

View File

@@ -4,6 +4,7 @@ namespace Alchemy\Tests\Phrasea\Command\Setup;
use Alchemy\Phrasea\Command\Setup\Install;
use Symfony\Component\Yaml\Yaml;
use Alchemy\Phrasea\Core\Configuration\StructureTemplate;
/**
* @group functional
@@ -20,7 +21,7 @@ class InstallTest extends \PhraseanetTestCase
$password = 'sup4ssw0rd';
$serverName = 'http://phrasea.io';
$dataPath = '/tmp';
$template = 'fr';
$template = 'fr-simple';
$infoDb = Yaml::parse(file_get_contents(__DIR__ . '/../../../../../../resources/hudson/InstallDBs.yml'));
@@ -81,7 +82,7 @@ class InstallTest extends \PhraseanetTestCase
return true;
break;
default:
return;
return '';
}
}));
@@ -93,7 +94,9 @@ class InstallTest extends \PhraseanetTestCase
->method('install')
->with($email, $password, $this->isInstanceOf('Doctrine\DBAL\Driver\Connection'), $serverName, $dataPath, $this->isInstanceOf('Doctrine\DBAL\Driver\Connection'), $template, $this->anything());
$command = new Install('system:check');
$structureTemplate = self::$DI['cli']['phraseanet.structure-template'];
$command = new Install('system:check', $structureTemplate);
$command->setHelperSet($helperSet);
$command->setContainer(self::$DI['cli']);
$this->assertEquals(0, $command->execute($input, $output));

View File

@@ -75,7 +75,7 @@ class InstallerTest extends \PhraseanetTestCase
$dataPath = __DIR__ . '/../../../../../datas/';
$installer = new Installer($app);
$installer->install(uniqid('admin') . '@example.com', 'sdfsdsd', $abConn, 'http://local.phrasea.test.installer/', $dataPath, $dbConn, 'en');
$installer->install(uniqid('admin') . '@example.com', 'sdfsdsd', $abConn, 'http://local.phrasea.test.installer/', $dataPath, $dbConn, 'en-simple');
$this->assertTrue($app['configuration.store']->isSetup());
$this->assertTrue($app['phraseanet.configuration-tester']->isUpToDate());

View File

@@ -52,4 +52,27 @@ class StringHelperTest extends \PhraseanetTestCase
["ABC\n\rDEF", "ABC\n\nDEF"],
];
}
/**
* @dataProvider provideStringsForSqlQuote
* @covers Alchemy\Phrasea\Utilities\StringHelper::SqlQuote
*/
public function testSqlQuote($string, $mode, $expected)
{
$result = StringHelper::SqlQuote($string, $mode);
$this->assertEquals($expected, $result);
}
public function provideStringsForSqlQuote()
{
return [
["azerty", StringHelper::SQL_VALUE, "'azerty'"],
["aze'rty", StringHelper::SQL_VALUE, "'aze''rty'"],
["aze`rty", StringHelper::SQL_VALUE, "'aze`rty'"],
["azerty", StringHelper::SQL_IDENTIFIER, "`azerty`"],
["aze'rty", StringHelper::SQL_IDENTIFIER, "`aze'rty`"],
["aze`rty", StringHelper::SQL_IDENTIFIER, "`aze``rty`"],
];
}
}