From e4c31b362ec724f83cd2fb0205ea4321496d8d0c Mon Sep 17 00:00:00 2001 From: Bart Visscher Date: Sat, 7 Dec 2013 14:44:23 +0100 Subject: [PATCH 001/236] Add command for converting sqlite database to server based one --- core/command/db/convertfromsqlite.php | 164 ++++++++++++++++++++++++++ core/register_command.php | 1 + 2 files changed, 165 insertions(+) create mode 100644 core/command/db/convertfromsqlite.php diff --git a/core/command/db/convertfromsqlite.php b/core/command/db/convertfromsqlite.php new file mode 100644 index 0000000000..85cf1e38c1 --- /dev/null +++ b/core/command/db/convertfromsqlite.php @@ -0,0 +1,164 @@ + + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + * + */ + +namespace OC\Core\Command\Db; + +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\OutputInterface; + +class ConvertFromSqlite extends Command { + protected function configure() { + $this + ->setName('db:convert-from-sqlite') + ->setDescription('Convert the owncloud sqlite database to the newly configured one') + ->addArgument( + 'type', + InputArgument::REQUIRED, + 'the type of the database to convert to' + ) + ->addArgument( + 'username', + InputArgument::REQUIRED, + 'the username of the database to convert to' + ) + ->addArgument( + 'hostname', + InputArgument::REQUIRED, + 'the hostname of the database to convert to' + ) + ->addArgument( + 'database', + InputArgument::REQUIRED, + 'the name of the database to convert to' + ) + ->addOption( + 'port', + null, + InputOption::VALUE_REQUIRED, + 'the port of the database to convert to' + ) + ->addOption( + 'password', + null, + InputOption::VALUE_REQUIRED, + 'the password of the database to convert to. Will be asked when not specified' + ) + ; + } + + private static $type2driver = array( + 'mysql' => 'pdo_mysql', + 'pgsql' => 'pdo_pgsql', + 'oci' => 'oci8', + 'mssql' => 'pdo_sqlsrv', + ); + protected function execute(InputInterface $input, OutputInterface $output) { + // connect 'from' database + $datadir = \OC_Config::getValue( "datadirectory", \OC::$SERVERROOT.'/data' ); + $name = \OC_Config::getValue( "dbname", "owncloud" ); + $dbfile = $datadir.'/'.$name.'.db'; + $connectionParams = array( + 'path' => $dbfile, + 'driver' => 'pdo_sqlite', + ); + $fromDB = \Doctrine\DBAL\DriverManager::getConnection($connectionParams); + + // connect 'to' database + $type = $input->getArgument('type'); + $username = $input->getArgument('username'); + $hostname = $input->getArgument('hostname'); + $dbname = $input->getArgument('database'); + + if ($input->getOption('password')) { + $password = $input->getOption('password'); + } else { + // TODO: should be moved to the interact function + $dialog = $this->getHelperSet()->get('dialog'); + $password = $dialog->askHiddenResponse( + $output, + 'What is the database password?', + false + ); + } + $connectionParams = array( + 'driver' => self::$type2driver[$type], + 'user' => $username, + 'password' => $password, + 'host' => $hostname, + 'dbname' => $dbname, + ); + if ($input->getOption('port')) { + $connectionParams['port'] = $input->getOption('port'); + } + $toDB = \Doctrine\DBAL\DriverManager::getConnection($connectionParams); + + // create tables in new database + $schemaManager = new \OC\DB\MDB2SchemaManager($toDB); + $schemaManager->createDbFromStructure(\OC::$SERVERROOT.'/db_structure.xml'); + $apps = \OC_App::getEnabledApps(); + foreach($apps as $app) { + if(file_exists(\OC_App::getAppPath($app).'/appinfo/database.xml')) { + $schemaManager->createDbFromStructure(\OC_App::getAppPath($app).'/appinfo/database.xml'); + } + } + + // get tables from 'to' database + $toTables = $this->getTables($toDB); + // get tables from 'from' database + $fromTables = $this->getTables($fromDB); + // warn/fail if there are more tables in 'from' database + $tables = array_diff($fromTables, $toTables); + if (!empty($tables)) { + $output->writeln('The following tables do NOT exist any more: '.join(', ', $tables).''); + $dialog = $this->getHelperSet()->get('dialog'); + if (!$dialog->askConfirmation( + $output, + 'Continue with the convertion?', + false + )) { + return; + } + } + // copy table rows + $tables = array_intersect($toTables, $fromTables); + foreach($tables as $table) { + $output->writeln($table); + $this->copyTable($fromDB, $toDB, $table, $output); + } + } + + private function getTables($db) { + $schemaManager = $db->getSchemaManager(); + return $schemaManager->listTableNames(); + } + + private function copyTable($fromDB, $toDB, $table, $output) { + $progress = $this->getHelperSet()->get('progress'); + $query = 'SELECT COUNT(*) from '.$table; + $count = $fromDB->fetchColumn($query); + $query = 'SELECT * from '.$table; + $statement = $fromDB->executeQuery($query); + $query = 'DELETE FROM '.$table; + $toDB->executeUpdate($query); + $progress->start($output, $count); + if ($count > 100) { + $progress->setRedrawFrequency(5); + } else { + $progress->setRedrawFrequency(1); + } + while($row = $statement->fetch()) { + $progress->advance(); + $toDB->insert($table, $row); + } + $progress->finish(); + } +} \ No newline at end of file diff --git a/core/register_command.php b/core/register_command.php index e4f3b12436..419747ebb4 100644 --- a/core/register_command.php +++ b/core/register_command.php @@ -9,6 +9,7 @@ /** @var $application Symfony\Component\Console\Application */ $application->add(new OC\Core\Command\Status); $application->add(new OC\Core\Command\Db\GenerateChangeScript()); +$application->add(new OC\Core\Command\Db\ConvertFromSqlite()); $application->add(new OC\Core\Command\Upgrade()); $application->add(new OC\Core\Command\Maintenance\SingleUser()); $application->add(new OC\Core\Command\App\Disable()); From 22edc42f548a148b323a375ecaf82141f18bc293 Mon Sep 17 00:00:00 2001 From: Bart Visscher Date: Sat, 7 Dec 2013 15:09:36 +0100 Subject: [PATCH 002/236] Add UTF8 charset to connection params --- core/command/db/convertfromsqlite.php | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/core/command/db/convertfromsqlite.php b/core/command/db/convertfromsqlite.php index 85cf1e38c1..d5003edd6e 100644 --- a/core/command/db/convertfromsqlite.php +++ b/core/command/db/convertfromsqlite.php @@ -99,6 +99,16 @@ class ConvertFromSqlite extends Command { if ($input->getOption('port')) { $connectionParams['port'] = $input->getOption('port'); } + switch ($type) { + case 'mysql': + case 'mssql': + $connectionParams['charset'] = 'UTF8'; + break; + case 'oci': + $connectionParams['charset'] = 'AL32UTF8'; + break; + } + $toDB = \Doctrine\DBAL\DriverManager::getConnection($connectionParams); // create tables in new database From 731e83c35a11657248f1d73c0a68e74573a1c3e8 Mon Sep 17 00:00:00 2001 From: Bart Visscher Date: Sat, 7 Dec 2013 15:14:54 +0100 Subject: [PATCH 003/236] Save the new database config --- core/command/db/convertfromsqlite.php | 28 ++++++++++++++++++++++----- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/core/command/db/convertfromsqlite.php b/core/command/db/convertfromsqlite.php index d5003edd6e..fb11409d2c 100644 --- a/core/command/db/convertfromsqlite.php +++ b/core/command/db/convertfromsqlite.php @@ -138,12 +138,30 @@ class ConvertFromSqlite extends Command { return; } } - // copy table rows - $tables = array_intersect($toTables, $fromTables); - foreach($tables as $table) { - $output->writeln($table); - $this->copyTable($fromDB, $toDB, $table, $output); + // enable maintenance mode to prevent changes + \OC_Config::setValue('maintenance', true); + try { + // copy table rows + $tables = array_intersect($toTables, $fromTables); + foreach($tables as $table) { + $output->writeln($table); + $this->copyTable($fromDB, $toDB, $table, $output); + } + // save new database config + $dbhost = $hostname; + if ($input->getOption('port')) { + $dbhost = $hostname.':'.$input->getOption('port'); + } + \OC_Config::setValue('dbtype', $type); + \OC_Config::setValue('dbname', $dbname); + \OC_Config::setValue('dbhost', $dbhost); + \OC_Config::setValue('dbuser', $username); + \OC_Config::setValue('dbpassword', $password); + } catch(Exception $e) { + \OC_Config::setValue('maintenance', false); + throw $e; } + \OC_Config::setValue('maintenance', false); } private function getTables($db) { From 202e26647e534065271db153fde7207922fa2ecd Mon Sep 17 00:00:00 2001 From: Bart Visscher Date: Tue, 24 Dec 2013 13:36:32 +0100 Subject: [PATCH 004/236] Inject config object --- core/command/db/convertfromsqlite.php | 33 +++++++++++++++++++-------- core/register_command.php | 2 +- 2 files changed, 24 insertions(+), 11 deletions(-) diff --git a/core/command/db/convertfromsqlite.php b/core/command/db/convertfromsqlite.php index fb11409d2c..de65fe9a88 100644 --- a/core/command/db/convertfromsqlite.php +++ b/core/command/db/convertfromsqlite.php @@ -16,6 +16,19 @@ use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; class ConvertFromSqlite extends Command { + /** + * @var \OC\Config $config + */ + protected $config; + + /** + * @param \OC\Config $config + */ + public function __construct($config) { + $this->config = $config; + parent::__construct(); + } + protected function configure() { $this ->setName('db:convert-from-sqlite') @@ -63,8 +76,8 @@ class ConvertFromSqlite extends Command { ); protected function execute(InputInterface $input, OutputInterface $output) { // connect 'from' database - $datadir = \OC_Config::getValue( "datadirectory", \OC::$SERVERROOT.'/data' ); - $name = \OC_Config::getValue( "dbname", "owncloud" ); + $datadir = $this->config->getValue( "datadirectory", \OC::$SERVERROOT.'/data' ); + $name = $this->config->getValue( "dbname", "owncloud" ); $dbfile = $datadir.'/'.$name.'.db'; $connectionParams = array( 'path' => $dbfile, @@ -139,7 +152,7 @@ class ConvertFromSqlite extends Command { } } // enable maintenance mode to prevent changes - \OC_Config::setValue('maintenance', true); + $this->config->setValue('maintenance', true); try { // copy table rows $tables = array_intersect($toTables, $fromTables); @@ -152,16 +165,16 @@ class ConvertFromSqlite extends Command { if ($input->getOption('port')) { $dbhost = $hostname.':'.$input->getOption('port'); } - \OC_Config::setValue('dbtype', $type); - \OC_Config::setValue('dbname', $dbname); - \OC_Config::setValue('dbhost', $dbhost); - \OC_Config::setValue('dbuser', $username); - \OC_Config::setValue('dbpassword', $password); + $this->config->setValue('dbtype', $type); + $this->config->setValue('dbname', $dbname); + $this->config->setValue('dbhost', $dbhost); + $this->config->setValue('dbuser', $username); + $this->config->setValue('dbpassword', $password); } catch(Exception $e) { - \OC_Config::setValue('maintenance', false); + $this->config->setValue('maintenance', false); throw $e; } - \OC_Config::setValue('maintenance', false); + $this->config->setValue('maintenance', false); } private function getTables($db) { diff --git a/core/register_command.php b/core/register_command.php index 419747ebb4..350a817c02 100644 --- a/core/register_command.php +++ b/core/register_command.php @@ -9,7 +9,7 @@ /** @var $application Symfony\Component\Console\Application */ $application->add(new OC\Core\Command\Status); $application->add(new OC\Core\Command\Db\GenerateChangeScript()); -$application->add(new OC\Core\Command\Db\ConvertFromSqlite()); +$application->add(new OC\Core\Command\Db\ConvertFromSqlite(OC_Config::getObject())); $application->add(new OC\Core\Command\Upgrade()); $application->add(new OC\Core\Command\Maintenance\SingleUser()); $application->add(new OC\Core\Command\App\Disable()); From 4e1a3f212f0c3f3f9e412b67b8deeec0963f15c9 Mon Sep 17 00:00:00 2001 From: Bart Visscher Date: Tue, 24 Dec 2013 13:44:35 +0100 Subject: [PATCH 005/236] Review points --- core/command/db/convertfromsqlite.php | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/core/command/db/convertfromsqlite.php b/core/command/db/convertfromsqlite.php index de65fe9a88..ed0443e980 100644 --- a/core/command/db/convertfromsqlite.php +++ b/core/command/db/convertfromsqlite.php @@ -184,22 +184,18 @@ class ConvertFromSqlite extends Command { private function copyTable($fromDB, $toDB, $table, $output) { $progress = $this->getHelperSet()->get('progress'); - $query = 'SELECT COUNT(*) from '.$table; + $query = 'SELECT COUNT(*) FROM '.$table; $count = $fromDB->fetchColumn($query); - $query = 'SELECT * from '.$table; + $query = 'SELECT * FROM '.$table; $statement = $fromDB->executeQuery($query); $query = 'DELETE FROM '.$table; $toDB->executeUpdate($query); $progress->start($output, $count); - if ($count > 100) { - $progress->setRedrawFrequency(5); - } else { - $progress->setRedrawFrequency(1); - } + $progress->setRedrawFrequency($count > 100 ? 5 : 1); while($row = $statement->fetch()) { $progress->advance(); $toDB->insert($table, $row); } $progress->finish(); } -} \ No newline at end of file +} From 1b7eb4dc6cfce382c7a3192bdc8bf6fd52791de1 Mon Sep 17 00:00:00 2001 From: Bart Visscher Date: Sat, 11 Jan 2014 12:22:23 +0100 Subject: [PATCH 006/236] Quote column names on insert --- core/command/db/convertfromsqlite.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/core/command/db/convertfromsqlite.php b/core/command/db/convertfromsqlite.php index ed0443e980..5ae54b6893 100644 --- a/core/command/db/convertfromsqlite.php +++ b/core/command/db/convertfromsqlite.php @@ -194,7 +194,11 @@ class ConvertFromSqlite extends Command { $progress->setRedrawFrequency($count > 100 ? 5 : 1); while($row = $statement->fetch()) { $progress->advance(); - $toDB->insert($table, $row); + $data = array(); + foreach ($row as $columnName => $value) { + $data[$toDB->quoteIdentifier($columnName)] = $value; + } + $toDB->insert($table, $data); } $progress->finish(); } From af3bedf9858b117b676c915829229fb8745a5e36 Mon Sep 17 00:00:00 2001 From: Bart Visscher Date: Sat, 11 Jan 2014 12:23:28 +0100 Subject: [PATCH 007/236] Add option to remove all the tables from the destination database --- core/command/db/convertfromsqlite.php | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/core/command/db/convertfromsqlite.php b/core/command/db/convertfromsqlite.php index 5ae54b6893..4d5122ca2d 100644 --- a/core/command/db/convertfromsqlite.php +++ b/core/command/db/convertfromsqlite.php @@ -65,6 +65,12 @@ class ConvertFromSqlite extends Command { InputOption::VALUE_REQUIRED, 'the password of the database to convert to. Will be asked when not specified' ) + ->addOption( + 'clear-schema', + null, + InputOption::VALUE_NONE, + 'remove all tables from the destination database' + ) ; } @@ -124,7 +130,20 @@ class ConvertFromSqlite extends Command { $toDB = \Doctrine\DBAL\DriverManager::getConnection($connectionParams); + // Clearing schema in new database + if ($input->getOption('clear-schema')) { + $schemaManager = $toDB->getSchemaManager(); + $toTables = $schemaManager->listTableNames(); + if (!empty($toTables)) { + $output->writeln('Clearing schema in new database'); + } + foreach($toTables as $table) { + $schemaManager->dropTable($table); + } + } + // create tables in new database + $output->writeln('Creating schema in new database'); $schemaManager = new \OC\DB\MDB2SchemaManager($toDB); $schemaManager->createDbFromStructure(\OC::$SERVERROOT.'/db_structure.xml'); $apps = \OC_App::getEnabledApps(); @@ -188,8 +207,6 @@ class ConvertFromSqlite extends Command { $count = $fromDB->fetchColumn($query); $query = 'SELECT * FROM '.$table; $statement = $fromDB->executeQuery($query); - $query = 'DELETE FROM '.$table; - $toDB->executeUpdate($query); $progress->start($output, $count); $progress->setRedrawFrequency($count > 100 ? 5 : 1); while($row = $statement->fetch()) { From 2638fd47674deaeb630b87f01a46ad94446a2097 Mon Sep 17 00:00:00 2001 From: Bart Visscher Date: Sat, 11 Jan 2014 12:25:35 +0100 Subject: [PATCH 008/236] Postgresql needs the sequences adjusted after the inserts --- core/command/db/convertfromsqlite.php | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/core/command/db/convertfromsqlite.php b/core/command/db/convertfromsqlite.php index 4d5122ca2d..cd3e494d7f 100644 --- a/core/command/db/convertfromsqlite.php +++ b/core/command/db/convertfromsqlite.php @@ -179,6 +179,18 @@ class ConvertFromSqlite extends Command { $output->writeln($table); $this->copyTable($fromDB, $toDB, $table, $output); } + if ($type == 'pgsql') { + $sequences = $toDB->getSchemaManager()->listSequences(); + foreach($sequences as $sequence) { + $info = $toDB->fetchAssoc('SELECT table_schema, table_name, column_name ' + .'FROM information_schema.columns ' + .'WHERE column_default = ? AND table_catalog = ?', + array("nextval('".$sequence->getName()."'::regclass)", $dbname)); + $table_name = $info['table_name']; + $column_name = $info['column_name']; + $toDB->executeQuery("SELECT setval('" . $sequence->getName() . "', (SELECT MAX(" . $column_name . ") FROM " . $table_name . ")+1)"); + } + } // save new database config $dbhost = $hostname; if ($input->getOption('port')) { From 21426b7ff6c8b84ea3898aa4a95a7fd3c4376ddb Mon Sep 17 00:00:00 2001 From: Bart Visscher Date: Fri, 17 Jan 2014 14:54:13 +0100 Subject: [PATCH 009/236] Fixed doctrine code --- 3rdparty | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/3rdparty b/3rdparty index 42efd96628..b4db0b302a 160000 --- a/3rdparty +++ b/3rdparty @@ -1 +1 @@ -Subproject commit 42efd966284debadf83b761367e529bc45f806d6 +Subproject commit b4db0b302aa8266b067012d0f862fafe70a665e0 From eede20c5acb4135f96f318a4ad0a146dd562861c Mon Sep 17 00:00:00 2001 From: Bart Visscher Date: Tue, 11 Feb 2014 17:59:50 +0100 Subject: [PATCH 010/236] Check target DB type --- core/command/db/convertfromsqlite.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/core/command/db/convertfromsqlite.php b/core/command/db/convertfromsqlite.php index cd3e494d7f..7170658038 100644 --- a/core/command/db/convertfromsqlite.php +++ b/core/command/db/convertfromsqlite.php @@ -97,6 +97,9 @@ class ConvertFromSqlite extends Command { $hostname = $input->getArgument('hostname'); $dbname = $input->getArgument('database'); + if (!isset(self::$type2driver[$type])) { + throw new InvalidArgumentException('Unknown type: '.$type); + } if ($input->getOption('password')) { $password = $input->getOption('password'); } else { From 3abcd13979660309f9a6d672d3dc64a7c6d784ab Mon Sep 17 00:00:00 2001 From: Bart Visscher Date: Tue, 11 Feb 2014 18:01:41 +0100 Subject: [PATCH 011/236] Allow converting from any db type --- .../db/{convertfromsqlite.php => converttype.php} | 15 ++++----------- core/register_command.php | 2 +- 2 files changed, 5 insertions(+), 12 deletions(-) rename core/command/db/{convertfromsqlite.php => converttype.php} (92%) diff --git a/core/command/db/convertfromsqlite.php b/core/command/db/converttype.php similarity index 92% rename from core/command/db/convertfromsqlite.php rename to core/command/db/converttype.php index 7170658038..38527d3d55 100644 --- a/core/command/db/convertfromsqlite.php +++ b/core/command/db/converttype.php @@ -15,7 +15,7 @@ use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; -class ConvertFromSqlite extends Command { +class ConvertType extends Command { /** * @var \OC\Config $config */ @@ -31,8 +31,8 @@ class ConvertFromSqlite extends Command { protected function configure() { $this - ->setName('db:convert-from-sqlite') - ->setDescription('Convert the owncloud sqlite database to the newly configured one') + ->setName('db:convert-type') + ->setDescription('Convert the owncloud database to the newly configured one') ->addArgument( 'type', InputArgument::REQUIRED, @@ -82,14 +82,7 @@ class ConvertFromSqlite extends Command { ); protected function execute(InputInterface $input, OutputInterface $output) { // connect 'from' database - $datadir = $this->config->getValue( "datadirectory", \OC::$SERVERROOT.'/data' ); - $name = $this->config->getValue( "dbname", "owncloud" ); - $dbfile = $datadir.'/'.$name.'.db'; - $connectionParams = array( - 'path' => $dbfile, - 'driver' => 'pdo_sqlite', - ); - $fromDB = \Doctrine\DBAL\DriverManager::getConnection($connectionParams); + $fromDB = \OC_DB::getConnection(); // connect 'to' database $type = $input->getArgument('type'); diff --git a/core/register_command.php b/core/register_command.php index 736953094b..a3833214c2 100644 --- a/core/register_command.php +++ b/core/register_command.php @@ -9,7 +9,7 @@ /** @var $application Symfony\Component\Console\Application */ $application->add(new OC\Core\Command\Status); $application->add(new OC\Core\Command\Db\GenerateChangeScript()); -$application->add(new OC\Core\Command\Db\ConvertFromSqlite(OC_Config::getObject())); +$application->add(new OC\Core\Command\Db\ConvertType(OC_Config::getObject())); $application->add(new OC\Core\Command\Upgrade()); $application->add(new OC\Core\Command\Maintenance\SingleUser()); $application->add(new OC\Core\Command\App\Disable()); From ae525d1f125483dc14858938c5056f18d63f831e Mon Sep 17 00:00:00 2001 From: Bart Visscher Date: Wed, 12 Feb 2014 17:42:55 +0100 Subject: [PATCH 012/236] Fix namespace for Exception --- core/command/db/converttype.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/command/db/converttype.php b/core/command/db/converttype.php index 38527d3d55..5f59a6be82 100644 --- a/core/command/db/converttype.php +++ b/core/command/db/converttype.php @@ -197,7 +197,7 @@ class ConvertType extends Command { $this->config->setValue('dbhost', $dbhost); $this->config->setValue('dbuser', $username); $this->config->setValue('dbpassword', $password); - } catch(Exception $e) { + } catch(\Exception $e) { $this->config->setValue('maintenance', false); throw $e; } From bcb78e48b283def05d21defaf554f5ce7c578c56 Mon Sep 17 00:00:00 2001 From: Bart Visscher Date: Mon, 17 Feb 2014 18:02:58 +0100 Subject: [PATCH 013/236] Split execute function into multiple functions --- core/command/db/converttype.php | 168 ++++++++++++++++++-------------- 1 file changed, 94 insertions(+), 74 deletions(-) diff --git a/core/command/db/converttype.php b/core/command/db/converttype.php index 5f59a6be82..370ace7fd0 100644 --- a/core/command/db/converttype.php +++ b/core/command/db/converttype.php @@ -85,46 +85,7 @@ class ConvertType extends Command { $fromDB = \OC_DB::getConnection(); // connect 'to' database - $type = $input->getArgument('type'); - $username = $input->getArgument('username'); - $hostname = $input->getArgument('hostname'); - $dbname = $input->getArgument('database'); - - if (!isset(self::$type2driver[$type])) { - throw new InvalidArgumentException('Unknown type: '.$type); - } - if ($input->getOption('password')) { - $password = $input->getOption('password'); - } else { - // TODO: should be moved to the interact function - $dialog = $this->getHelperSet()->get('dialog'); - $password = $dialog->askHiddenResponse( - $output, - 'What is the database password?', - false - ); - } - $connectionParams = array( - 'driver' => self::$type2driver[$type], - 'user' => $username, - 'password' => $password, - 'host' => $hostname, - 'dbname' => $dbname, - ); - if ($input->getOption('port')) { - $connectionParams['port'] = $input->getOption('port'); - } - switch ($type) { - case 'mysql': - case 'mssql': - $connectionParams['charset'] = 'UTF8'; - break; - case 'oci': - $connectionParams['charset'] = 'AL32UTF8'; - break; - } - - $toDB = \Doctrine\DBAL\DriverManager::getConnection($connectionParams); + $toDB = $this->getToDBConnection($input, $output); // Clearing schema in new database if ($input->getOption('clear-schema')) { @@ -167,41 +128,52 @@ class ConvertType extends Command { } } // enable maintenance mode to prevent changes - $this->config->setValue('maintenance', true); - try { - // copy table rows - $tables = array_intersect($toTables, $fromTables); - foreach($tables as $table) { - $output->writeln($table); - $this->copyTable($fromDB, $toDB, $table, $output); - } - if ($type == 'pgsql') { - $sequences = $toDB->getSchemaManager()->listSequences(); - foreach($sequences as $sequence) { - $info = $toDB->fetchAssoc('SELECT table_schema, table_name, column_name ' - .'FROM information_schema.columns ' - .'WHERE column_default = ? AND table_catalog = ?', - array("nextval('".$sequence->getName()."'::regclass)", $dbname)); - $table_name = $info['table_name']; - $column_name = $info['column_name']; - $toDB->executeQuery("SELECT setval('" . $sequence->getName() . "', (SELECT MAX(" . $column_name . ") FROM " . $table_name . ")+1)"); - } - } - // save new database config - $dbhost = $hostname; - if ($input->getOption('port')) { - $dbhost = $hostname.':'.$input->getOption('port'); - } - $this->config->setValue('dbtype', $type); - $this->config->setValue('dbname', $dbname); - $this->config->setValue('dbhost', $dbhost); - $this->config->setValue('dbuser', $username); - $this->config->setValue('dbpassword', $password); - } catch(\Exception $e) { - $this->config->setValue('maintenance', false); - throw $e; + $tables = array_intersect($toTables, $fromTables); + $this->convertDB($fromDB, $toDB, $tables, $input, $output); + } + + private function getToDBConnection($input, $output) { + $type = $input->getArgument('type'); + $username = $input->getArgument('username'); + $hostname = $input->getArgument('hostname'); + $dbname = $input->getArgument('database'); + + if (!isset(self::$type2driver[$type])) { + throw new InvalidArgumentException('Unknown type: '.$type); } - $this->config->setValue('maintenance', false); + if ($input->getOption('password')) { + $password = $input->getOption('password'); + } else { + // TODO: should be moved to the interact function + $dialog = $this->getHelperSet()->get('dialog'); + $password = $dialog->askHiddenResponse( + $output, + 'What is the database password?', + false + ); + $input->setOption('password', $password); + } + $connectionParams = array( + 'driver' => self::$type2driver[$type], + 'user' => $username, + 'password' => $password, + 'host' => $hostname, + 'dbname' => $dbname, + ); + if ($input->getOption('port')) { + $connectionParams['port'] = $input->getOption('port'); + } + switch ($type) { + case 'mysql': + case 'mssql': + $connectionParams['charset'] = 'UTF8'; + break; + case 'oci': + $connectionParams['charset'] = 'AL32UTF8'; + break; + } + + return \Doctrine\DBAL\DriverManager::getConnection($connectionParams); } private function getTables($db) { @@ -227,4 +199,52 @@ class ConvertType extends Command { } $progress->finish(); } + + private function convertDB($fromDB, $toDB, $tables, $input, $output) { + $this->config->setValue('maintenance', true); + $type = $input->getArgument('type'); + try { + // copy table rows + foreach($tables as $table) { + $output->writeln($table); + $this->copyTable($fromDB, $toDB, $table, $output); + } + if ($type == 'pgsql') { + $sequences = $toDB->getSchemaManager()->listSequences(); + $dbname = $input->getArgument('database'); + foreach($sequences as $sequence) { + $info = $toDB->fetchAssoc('SELECT table_schema, table_name, column_name ' + .'FROM information_schema.columns ' + .'WHERE column_default = ? AND table_catalog = ?', + array("nextval('".$sequence->getName()."'::regclass)", $dbname)); + $table_name = $info['table_name']; + $column_name = $info['column_name']; + $toDB->executeQuery("SELECT setval('" . $sequence->getName() . "', (SELECT MAX(" . $column_name . ") FROM " . $table_name . ")+1)"); + } + } + // save new database config + $this->saveDBInfo($input); + } catch(\Exception $e) { + $this->config->setValue('maintenance', false); + throw $e; + } + $this->config->setValue('maintenance', false); + } + + private function saveDBInfo($input) { + $type = $input->getArgument('type'); + $username = $input->getArgument('username'); + $dbhost = $input->getArgument('hostname'); + $dbname = $input->getArgument('database'); + $password = $input->getOption('password'); + if ($input->getOption('port')) { + $dbhost .= ':'.$input->getOption('port'); + } + + $this->config->setValue('dbtype', $type); + $this->config->setValue('dbname', $dbname); + $this->config->setValue('dbhost', $dbhost); + $this->config->setValue('dbuser', $username); + $this->config->setValue('dbpassword', $password); + } } From 8a6dcdf4a47fb45d417ca1fd2aec2901c247d16f Mon Sep 17 00:00:00 2001 From: Bart Visscher Date: Mon, 17 Feb 2014 18:09:42 +0100 Subject: [PATCH 014/236] Move password interaction to interact function --- core/command/db/converttype.php | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/core/command/db/converttype.php b/core/command/db/converttype.php index 370ace7fd0..fb45ab3311 100644 --- a/core/command/db/converttype.php +++ b/core/command/db/converttype.php @@ -29,6 +29,19 @@ class ConvertType extends Command { parent::__construct(); } + protected function interact(InputInterface $input, OutputInterface $output) { + parent::interact($input, $output); + if (!$input->getOption('password')) { + $dialog = $this->getHelperSet()->get('dialog'); + $password = $dialog->askHiddenResponse( + $output, + 'What is the database password?', + false + ); + $input->setOption('password', $password); + } + } + protected function configure() { $this ->setName('db:convert-type') @@ -137,22 +150,11 @@ class ConvertType extends Command { $username = $input->getArgument('username'); $hostname = $input->getArgument('hostname'); $dbname = $input->getArgument('database'); + $password = $input->getOption('password'); if (!isset(self::$type2driver[$type])) { throw new InvalidArgumentException('Unknown type: '.$type); } - if ($input->getOption('password')) { - $password = $input->getOption('password'); - } else { - // TODO: should be moved to the interact function - $dialog = $this->getHelperSet()->get('dialog'); - $password = $dialog->askHiddenResponse( - $output, - 'What is the database password?', - false - ); - $input->setOption('password', $password); - } $connectionParams = array( 'driver' => self::$type2driver[$type], 'user' => $username, From a55c56c9e7c0c78c1592f2501b527b5c977cafb7 Mon Sep 17 00:00:00 2001 From: Bart Visscher Date: Tue, 18 Feb 2014 20:20:58 +0100 Subject: [PATCH 015/236] Use the patches as proposed in doctrine/dbal --- 3rdparty | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/3rdparty b/3rdparty index 3d42b54064..6240224233 160000 --- a/3rdparty +++ b/3rdparty @@ -1 +1 @@ -Subproject commit 3d42b540641cb3f9ce563d5a4ec6b385dbaba742 +Subproject commit 624022423304280dab2cf7ed2367aa7c99ff565c From 6f4ecd32b37cc668ee0d59721c2451f349bb9290 Mon Sep 17 00:00:00 2001 From: Bart Visscher Date: Fri, 21 Feb 2014 22:52:48 +0100 Subject: [PATCH 016/236] Add more caching in the group manager --- lib/private/group/manager.php | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/lib/private/group/manager.php b/lib/private/group/manager.php index 9b433b64fd..451de0c053 100644 --- a/lib/private/group/manager.php +++ b/lib/private/group/manager.php @@ -40,7 +40,12 @@ class Manager extends PublicEmitter { /** * @var \OC\Group\Group[] */ - private $cachedGroups; + private $cachedGroups = array(); + + /** + * @var \OC\Group\Group[] + */ + private $cachedUserGroups = array(); /** * @param \OC\User\Manager $userManager @@ -141,7 +146,7 @@ class Manager extends PublicEmitter { $offset -= count($groupIds); } foreach ($groupIds as $groupId) { - $groups[$groupId] = $this->getGroupObject($groupId); + $groups[$groupId] = $this->get($groupId); } if (!is_null($limit) and $limit <= 0) { return array_values($groups); @@ -155,13 +160,18 @@ class Manager extends PublicEmitter { * @return \OC\Group\Group[] */ public function getUserGroups($user) { + $uid = $user->getUID(); + if (isset($this->cachedUserGroups[$uid])) { + return $this->cachedUserGroups[$uid]; + } $groups = array(); foreach ($this->backends as $backend) { - $groupIds = $backend->getUserGroups($user->getUID()); + $groupIds = $backend->getUserGroups($uid); foreach ($groupIds as $groupId) { - $groups[$groupId] = $this->getGroupObject($groupId); + $groups[$groupId] = $this->get($groupId); } } - return array_values($groups); + $this->cachedUserGroups[$uid] = array_values($groups); + return $this->cachedUserGroups[$uid]; } } From f4f72e77d8bf312b6fc693d43ef5fc831130db3b Mon Sep 17 00:00:00 2001 From: Bart Visscher Date: Fri, 21 Feb 2014 22:53:31 +0100 Subject: [PATCH 017/236] Delay fetching the display name until it is requested --- lib/private/user/user.php | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/lib/private/user/user.php b/lib/private/user/user.php index ef5364cbf7..710f9061b5 100644 --- a/lib/private/user/user.php +++ b/lib/private/user/user.php @@ -55,11 +55,6 @@ class User { */ public function __construct($uid, $backend, $emitter = null, $config = null) { $this->uid = $uid; - if ($backend and $backend->implementsActions(OC_USER_BACKEND_GET_DISPLAYNAME)) { - $this->displayName = $backend->getDisplayName($uid); - } else { - $this->displayName = $uid; - } $this->backend = $backend; $this->emitter = $emitter; $this->config = $config; @@ -86,6 +81,13 @@ class User { * @return string */ public function getDisplayName() { + if (!isset($this->displayName)) { + if ($this->backend and $this->backend->implementsActions(OC_USER_BACKEND_GET_DISPLAYNAME)) { + $this->displayName = $this->backend->getDisplayName($this->uid); + } else { + $this->displayName = $this->uid; + } + } return $this->displayName; } From 1d7564dc2f4ec9e06f9047846cd6bf023a1c26ed Mon Sep 17 00:00:00 2001 From: Bart Visscher Date: Fri, 21 Feb 2014 22:58:29 +0100 Subject: [PATCH 018/236] Only check for existence of shared files when doing shared storage setup The getItemsSharedWith function also retrieves related information, resulting in work that isn't used here. --- apps/files_sharing/lib/sharedstorage.php | 2 +- lib/public/share.php | 23 +++++++++++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/apps/files_sharing/lib/sharedstorage.php b/apps/files_sharing/lib/sharedstorage.php index b922654e5e..18c8a4f42a 100644 --- a/apps/files_sharing/lib/sharedstorage.php +++ b/apps/files_sharing/lib/sharedstorage.php @@ -394,7 +394,7 @@ class Shared extends \OC\Files\Storage\Common { public static function setup($options) { if (!\OCP\User::isLoggedIn() || \OCP\User::getUser() != $options['user'] - || \OCP\Share::getItemsSharedWith('file') + || \OCP\Share::hasFilesSharedWith() ) { $user_dir = $options['user_dir']; \OC\Files\Filesystem::mount('\OC\Files\Storage\Shared', diff --git a/lib/public/share.php b/lib/public/share.php index ebc555dba5..8cfe7417be 100644 --- a/lib/public/share.php +++ b/lib/public/share.php @@ -243,6 +243,29 @@ class Share { return array("users" => array_unique($shares), "public" => $publicShare); } + public static function hasFilesSharedWith() { + if (!self::isEnabled()) { + return false; + } + $shareWith = \OC_User::getUser(); + $where = 'INNER JOIN `*PREFIX*filecache` ON `file_source` = `*PREFIX*filecache`.`fileid`'; + $where .= ' WHERE `file_target` IS NOT NULL'; + $queryArgs = array(); + $where .= ' AND `share_type` IN (?,?,?)'; + $queryArgs[] = self::SHARE_TYPE_USER; + $queryArgs[] = self::SHARE_TYPE_GROUP; + $queryArgs[] = self::$shareTypeGroupUserUnique; + $userAndGroups = array_merge(array($shareWith), \OC_Group::getUserGroups($shareWith)); + $placeholders = join(',', array_fill(0, count($userAndGroups), '?')); + $where .= ' AND `share_with` IN ('.$placeholders.')'; + $queryArgs = array_merge($queryArgs, $userAndGroups); + // Don't include own group shares + $where .= ' AND `uid_owner` != ?'; + $queryArgs[] = $shareWith; + $result = \OC_DB::executeAudited('SELECT COUNT(*) FROM `*PREFIX*share` '.$where, $queryArgs); + return $result->fetchOne() > 0; + } + /** * Get the items of item type shared with the current user * @param string Item type From 00e27d5343dab36380129dd8d30e2699e5121079 Mon Sep 17 00:00:00 2001 From: Bart Visscher Date: Thu, 27 Feb 2014 19:12:03 +0100 Subject: [PATCH 019/236] Clear the cached user groups when a group is deleted --- lib/private/group/manager.php | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/private/group/manager.php b/lib/private/group/manager.php index 451de0c053..deceb8bb92 100644 --- a/lib/private/group/manager.php +++ b/lib/private/group/manager.php @@ -52,12 +52,14 @@ class Manager extends PublicEmitter { */ public function __construct($userManager) { $this->userManager = $userManager; - $cache = & $this->cachedGroups; - $this->listen('\OC\Group', 'postDelete', function ($group) use (&$cache) { + $cachedGroups = & $this->cachedGroups; + $cachedUserGroups = & $this->cachedUserGroups; + $this->listen('\OC\Group', 'postDelete', function ($group) use (&$cachedGroups, &$cachedUserGroups) { /** * @var \OC\Group\Group $group */ - unset($cache[$group->getGID()]); + unset($cachedGroups[$group->getGID()]); + $cachedUserGroups = array(); }); } From cb37a2716a75013dd79f8830a6d074e5afebb767 Mon Sep 17 00:00:00 2001 From: Bart Visscher Date: Thu, 27 Feb 2014 20:09:07 +0100 Subject: [PATCH 020/236] Also clear cached UserGroup when a user is added/removed --- lib/private/group/manager.php | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/lib/private/group/manager.php b/lib/private/group/manager.php index deceb8bb92..151b185dbf 100644 --- a/lib/private/group/manager.php +++ b/lib/private/group/manager.php @@ -61,6 +61,18 @@ class Manager extends PublicEmitter { unset($cachedGroups[$group->getGID()]); $cachedUserGroups = array(); }); + $this->listen('\OC\Group', 'postAddUser', function ($group) use (&$cachedUserGroups) { + /** + * @var \OC\Group\Group $group + */ + $cachedUserGroups = array(); + }); + $this->listen('\OC\Group', 'postRemoveUser', function ($group) use (&$cachedUserGroups) { + /** + * @var \OC\Group\Group $group + */ + $cachedUserGroups = array(); + }); } /** From 3116bede68b07efc3a3bc6c991edf191c918565b Mon Sep 17 00:00:00 2001 From: Bart Visscher Date: Thu, 27 Feb 2014 21:04:44 +0100 Subject: [PATCH 021/236] Add unit tests for getUserGroups with addUser and removeUser --- tests/lib/group/manager.php | 94 +++++++++++++++++++++++++++++++++++++ 1 file changed, 94 insertions(+) diff --git a/tests/lib/group/manager.php b/tests/lib/group/manager.php index 90f0e1b35e..c39a7d6f33 100644 --- a/tests/lib/group/manager.php +++ b/tests/lib/group/manager.php @@ -343,4 +343,98 @@ class Manager extends \PHPUnit_Framework_TestCase { $this->assertEquals('group1', $group1->getGID()); $this->assertEquals('group2', $group2->getGID()); } + + public function testGetUserGroupsWithAddUser() { + /** + * @var \PHPUnit_Framework_MockObject_MockObject | \OC_Group_Backend $backend + */ + $backend = $this->getMock('\OC_Group_Database'); + $expectedGroups = array(); + $backend->expects($this->any()) + ->method('getUserGroups') + ->with('user1') + ->will($this->returnCallback(function () use (&$expectedGroups) { + return $expectedGroups; + })); + $backend->expects($this->any()) + ->method('groupExists') + ->with('group1') + ->will($this->returnValue(true)); + $backend->expects($this->once()) + ->method('implementsActions') + ->will($this->returnValue(true)); + + /** + * @var \OC\User\Manager $userManager + */ + $userManager = $this->getMock('\OC\User\Manager'); + $manager = new \OC\Group\Manager($userManager); + $manager->addBackend($backend); + + // prime cache + $user1 = new User('user1', null); + $groups = $manager->getUserGroups($user1); + $this->assertEquals(array(), $groups); + + // add user + $group = $manager->get('group1'); + $group->addUser($user1); + $expectedGroups = array('group1'); + + // check result + $groups = $manager->getUserGroups($user1); + $this->assertEquals(1, count($groups)); + $group1 = $groups[0]; + $this->assertEquals('group1', $group1->getGID()); + } + + public function testGetUserGroupsWithRemoveUser() { + /** + * @var \PHPUnit_Framework_MockObject_MockObject | \OC_Group_Backend $backend + */ + $backend = $this->getMock('\OC_Group_Database'); + $expectedGroups = array('group1'); + $backend->expects($this->any()) + ->method('getUserGroups') + ->with('user1') + ->will($this->returnCallback(function () use (&$expectedGroups) { + return $expectedGroups; + })); + $backend->expects($this->any()) + ->method('groupExists') + ->with('group1') + ->will($this->returnValue(true)); + $backend->expects($this->once()) + ->method('implementsActions') + ->will($this->returnValue(true)); + $backend->expects($this->once()) + ->method('inGroup') + ->will($this->returnValue(true)); + $backend->expects($this->once()) + ->method('removeFromGroup') + ->will($this->returnValue(true)); + + /** + * @var \OC\User\Manager $userManager + */ + $userManager = $this->getMock('\OC\User\Manager'); + $manager = new \OC\Group\Manager($userManager); + $manager->addBackend($backend); + + // prime cache + $user1 = new User('user1', null); + $groups = $manager->getUserGroups($user1); + $this->assertEquals(1, count($groups)); + $group1 = $groups[0]; + $this->assertEquals('group1', $group1->getGID()); + + // remove user + $group = $manager->get('group1'); + $group->removeUser($user1); + $expectedGroups = array(); + + // check result + $groups = $manager->getUserGroups($user1); + $this->assertEquals(array(), $groups); + } } From 47d70da2f5cb55ad47023b061b68062dd8b8d8e2 Mon Sep 17 00:00:00 2001 From: Bart Visscher Date: Fri, 28 Feb 2014 09:29:20 +0100 Subject: [PATCH 022/236] Use limit=1 so the db can stop searching on the first hit --- lib/public/share.php | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/lib/public/share.php b/lib/public/share.php index 8cfe7417be..cc4bfb67bd 100644 --- a/lib/public/share.php +++ b/lib/public/share.php @@ -243,6 +243,10 @@ class Share { return array("users" => array_unique($shares), "public" => $publicShare); } + /** + * Check if the current user has files shared with + * @return boolean + */ public static function hasFilesSharedWith() { if (!self::isEnabled()) { return false; @@ -262,8 +266,11 @@ class Share { // Don't include own group shares $where .= ' AND `uid_owner` != ?'; $queryArgs[] = $shareWith; - $result = \OC_DB::executeAudited('SELECT COUNT(*) FROM `*PREFIX*share` '.$where, $queryArgs); - return $result->fetchOne() > 0; + $result = \OC_DB::executeAudited(array( + 'sql' => 'SELECT * FROM `*PREFIX*share` '.$where, + 'limit' => 1, + ), $queryArgs); + return (bool)$result->fetchOne(); } /** From b54b0b2153742d0d5376045287e5874929437298 Mon Sep 17 00:00:00 2001 From: Thomas Tanghus Date: Wed, 5 Mar 2014 00:26:06 +0100 Subject: [PATCH 023/236] Yet another cleanup --- lib/private/tags.php | 53 ++++++++++++++++++++++++++------------------ tests/lib/tags.php | 2 +- 2 files changed, 33 insertions(+), 22 deletions(-) diff --git a/lib/private/tags.php b/lib/private/tags.php index 06550068f7..2e786e3fd7 100644 --- a/lib/private/tags.php +++ b/lib/private/tags.php @@ -121,21 +121,7 @@ class Tags implements \OCP\ITags { * @return boolean. */ public function isEmpty() { - $sql = 'SELECT COUNT(*) FROM `' . self::TAG_TABLE . '` ' - . 'WHERE `uid` = ? AND `type` = ?'; - try { - $stmt = \OCP\DB::prepare($sql); - $result = $stmt->execute(array($this->user, $this->type)); - if (\OCP\DB::isError($result)) { - \OCP\Util::writeLog('core', __METHOD__. ', DB error: ' . \OCP\DB::getErrorMessage($result), \OCP\Util::ERROR); - return false; - } - return ((int)$result->fetchOne() === 0); - } catch(\Exception $e) { - \OCP\Util::writeLog('core', __METHOD__.', exception: '.$e->getMessage(), - \OCP\Util::ERROR); - return false; - } + return count($this->tags) === 0; } /** @@ -184,6 +170,10 @@ class Tags implements \OCP\ITags { $tagId = $tag; } elseif(is_string($tag)) { $tag = trim($tag); + if($tag === '') { + \OCP\Util::writeLog('core', __METHOD__.', Cannot use empty tag names', \OCP\Util::DEBUG); + return false; + } $tagId = $this->array_searchi($tag, $this->tags); } @@ -234,11 +224,15 @@ class Tags implements \OCP\ITags { * Add a new tag. * * @param string $name A string with a name of the tag - * @return false|string the id of the added tag or false if it already exists. + * @return false|string the id of the added tag or false on error. */ public function add($name) { $name = trim($name); + if($name === '') { + \OCP\Util::writeLog('core', __METHOD__.', Cannot add an empty tag', \OCP\Util::DEBUG); + return false; + } if($this->hasTag($name)) { \OCP\Util::writeLog('core', __METHOD__.', name: ' . $name. ' exists already', \OCP\Util::DEBUG); return false; @@ -280,6 +274,12 @@ class Tags implements \OCP\ITags { public function rename($from, $to) { $from = trim($from); $to = trim($to); + + if($to === '' || $from === '') { + \OCP\Util::writeLog('core', __METHOD__.', Cannot use empty tag names', \OCP\Util::DEBUG); + return false; + } + $id = $this->array_searchi($from, $this->tags); if($id === false) { \OCP\Util::writeLog('core', __METHOD__.', tag: ' . $from. ' does not exist', \OCP\Util::DEBUG); @@ -318,6 +318,8 @@ class Tags implements \OCP\ITags { $names = array($names); } $names = array_map('trim', $names); + array_filter($names); + $newones = array(); foreach($names as $name) { if(($this->in_arrayi( @@ -492,9 +494,9 @@ class Tags implements \OCP\ITags { */ public function addToFavorites($objid) { if(!$this->hasTag(self::TAG_FAVORITE)) { - $this->add(self::TAG_FAVORITE, true); + $this->add(self::TAG_FAVORITE); } - return $this->tagAs($objid, self::TAG_FAVORITE, $this->type); + return $this->tagAs($objid, self::TAG_FAVORITE); } /** @@ -504,7 +506,7 @@ class Tags implements \OCP\ITags { * @return boolean */ public function removeFromFavorites($objid) { - return $this->unTag($objid, self::TAG_FAVORITE, $this->type); + return $this->unTag($objid, self::TAG_FAVORITE); } /** @@ -512,13 +514,17 @@ class Tags implements \OCP\ITags { * * @param int $objid The id of the object * @param string $tag The id or name of the tag - * @return boolean Returns false on database error. + * @return boolean Returns false on error. */ public function tagAs($objid, $tag) { if(is_string($tag) && !is_numeric($tag)) { $tag = trim($tag); + if($tag === '') { + \OCP\Util::writeLog('core', __METHOD__.', Cannot add an empty tag', \OCP\Util::DEBUG); + return false; + } if(!$this->hasTag($tag)) { - $this->add($tag, true); + $this->add($tag); } $tagId = $this->array_searchi($tag, $this->tags); } else { @@ -549,6 +555,10 @@ class Tags implements \OCP\ITags { public function unTag($objid, $tag) { if(is_string($tag) && !is_numeric($tag)) { $tag = trim($tag); + if($tag === '') { + \OCP\Util::writeLog('core', __METHOD__.', Tag name is empty', \OCP\Util::DEBUG); + return false; + } $tagId = $this->array_searchi($tag, $this->tags); } else { $tagId = $tag; @@ -579,6 +589,7 @@ class Tags implements \OCP\ITags { } $names = array_map('trim', $names); + array_filter($names); \OCP\Util::writeLog('core', __METHOD__ . ', before: ' . print_r($this->tags, true), \OCP\Util::DEBUG); diff --git a/tests/lib/tags.php b/tests/lib/tags.php index 97e3734cfd..976b4b4fdc 100644 --- a/tests/lib/tags.php +++ b/tests/lib/tags.php @@ -130,7 +130,7 @@ class Test_Tags extends PHPUnit_Framework_TestCase { $tagger = $this->tagMgr->load($this->objectType); foreach($objids as $id) { - $tagger->tagAs($id, 'Family'); + $this->assertTrue($tagger->tagAs($id, 'Family')); } $this->assertEquals(1, count($tagger->getTags())); From e768ead82096b4ad885dd15388691e39bb2c55fc Mon Sep 17 00:00:00 2001 From: Andreas Fischer Date: Mon, 31 Mar 2014 17:06:02 +0200 Subject: [PATCH 024/236] Remove whitespace at end of lines. --- core/command/db/converttype.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/command/db/converttype.php b/core/command/db/converttype.php index fb45ab3311..1f3e296acc 100644 --- a/core/command/db/converttype.php +++ b/core/command/db/converttype.php @@ -4,7 +4,7 @@ * This file is licensed under the Affero General Public License version 3 or * later. * See the COPYING-README file. - * + * */ namespace OC\Core\Command\Db; From b002f11771317d2098a9d93758ce7c442a76c203 Mon Sep 17 00:00:00 2001 From: Andreas Fischer Date: Mon, 31 Mar 2014 17:06:06 +0200 Subject: [PATCH 025/236] Use question color. --- core/command/db/converttype.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/command/db/converttype.php b/core/command/db/converttype.php index 1f3e296acc..8b9c72d903 100644 --- a/core/command/db/converttype.php +++ b/core/command/db/converttype.php @@ -35,7 +35,7 @@ class ConvertType extends Command { $dialog = $this->getHelperSet()->get('dialog'); $password = $dialog->askHiddenResponse( $output, - 'What is the database password?', + 'What is the database password?', false ); $input->setOption('password', $password); From 5dbbe6d08b6046777e20761057725f24f841e722 Mon Sep 17 00:00:00 2001 From: Andreas Fischer Date: Mon, 31 Mar 2014 18:20:04 +0200 Subject: [PATCH 026/236] Doc blocks for properties do not repeat the property name. --- core/command/db/converttype.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/command/db/converttype.php b/core/command/db/converttype.php index 8b9c72d903..99f2807fee 100644 --- a/core/command/db/converttype.php +++ b/core/command/db/converttype.php @@ -17,7 +17,7 @@ use Symfony\Component\Console\Output\OutputInterface; class ConvertType extends Command { /** - * @var \OC\Config $config + * @var \OC\Config */ protected $config; From e67cf99cef596da7db5171730523e110a1a055d0 Mon Sep 17 00:00:00 2001 From: Andreas Fischer Date: Mon, 31 Mar 2014 18:20:24 +0200 Subject: [PATCH 027/236] \InvalidArgumentException is in root namespace. --- core/command/db/converttype.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/command/db/converttype.php b/core/command/db/converttype.php index 99f2807fee..b445378783 100644 --- a/core/command/db/converttype.php +++ b/core/command/db/converttype.php @@ -153,7 +153,7 @@ class ConvertType extends Command { $password = $input->getOption('password'); if (!isset(self::$type2driver[$type])) { - throw new InvalidArgumentException('Unknown type: '.$type); + throw new \InvalidArgumentException('Unknown type: '.$type); } $connectionParams = array( 'driver' => self::$type2driver[$type], From a585cec5303a7b3d361559f73bba154fcbe0c978 Mon Sep 17 00:00:00 2001 From: Andreas Fischer Date: Mon, 31 Mar 2014 19:27:18 +0200 Subject: [PATCH 028/236] Remove unnecessary and incorrect comments. --- core/command/db/converttype.php | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/core/command/db/converttype.php b/core/command/db/converttype.php index b445378783..387f873adb 100644 --- a/core/command/db/converttype.php +++ b/core/command/db/converttype.php @@ -94,13 +94,9 @@ class ConvertType extends Command { 'mssql' => 'pdo_sqlsrv', ); protected function execute(InputInterface $input, OutputInterface $output) { - // connect 'from' database $fromDB = \OC_DB::getConnection(); - - // connect 'to' database $toDB = $this->getToDBConnection($input, $output); - // Clearing schema in new database if ($input->getOption('clear-schema')) { $schemaManager = $toDB->getSchemaManager(); $toTables = $schemaManager->listTableNames(); @@ -112,7 +108,6 @@ class ConvertType extends Command { } } - // create tables in new database $output->writeln('Creating schema in new database'); $schemaManager = new \OC\DB\MDB2SchemaManager($toDB); $schemaManager->createDbFromStructure(\OC::$SERVERROOT.'/db_structure.xml'); @@ -123,10 +118,9 @@ class ConvertType extends Command { } } - // get tables from 'to' database $toTables = $this->getTables($toDB); - // get tables from 'from' database $fromTables = $this->getTables($fromDB); + // warn/fail if there are more tables in 'from' database $tables = array_diff($fromTables, $toTables); if (!empty($tables)) { @@ -140,7 +134,6 @@ class ConvertType extends Command { return; } } - // enable maintenance mode to prevent changes $tables = array_intersect($toTables, $fromTables); $this->convertDB($fromDB, $toDB, $tables, $input, $output); } From f9853b253c6ecb9a77a9d1c9006c5f548cdfd04e Mon Sep 17 00:00:00 2001 From: Andreas Fischer Date: Mon, 31 Mar 2014 20:00:44 +0200 Subject: [PATCH 029/236] Deduplicate connection handling code into \OC\DB\ConnectionFactory --- core/command/db/converttype.php | 47 +++------ core/register_command.php | 2 +- lib/private/db.php | 137 ++++++++------------------- lib/private/db/connectionfactory.php | 117 +++++++++++++++++++++++ 4 files changed, 173 insertions(+), 130 deletions(-) create mode 100644 lib/private/db/connectionfactory.php diff --git a/core/command/db/converttype.php b/core/command/db/converttype.php index 387f873adb..2a4e6747e6 100644 --- a/core/command/db/converttype.php +++ b/core/command/db/converttype.php @@ -22,10 +22,17 @@ class ConvertType extends Command { protected $config; /** - * @param \OC\Config $config + * @var \OC\DB\ConnectionFactory */ - public function __construct($config) { + protected $connectionFactory; + + /** + * @param \OC\Config $config + * @param \OC\DB\ConnectionFactory $connectionFactory + */ + public function __construct($config, $connectionFactory) { $this->config = $config; + $this->connectionFactory = $connectionFactory; parent::__construct(); } @@ -87,12 +94,6 @@ class ConvertType extends Command { ; } - private static $type2driver = array( - 'mysql' => 'pdo_mysql', - 'pgsql' => 'pdo_pgsql', - 'oci' => 'oci8', - 'mssql' => 'pdo_sqlsrv', - ); protected function execute(InputInterface $input, OutputInterface $output) { $fromDB = \OC_DB::getConnection(); $toDB = $this->getToDBConnection($input, $output); @@ -140,35 +141,17 @@ class ConvertType extends Command { private function getToDBConnection($input, $output) { $type = $input->getArgument('type'); - $username = $input->getArgument('username'); - $hostname = $input->getArgument('hostname'); - $dbname = $input->getArgument('database'); - $password = $input->getOption('password'); - - if (!isset(self::$type2driver[$type])) { - throw new \InvalidArgumentException('Unknown type: '.$type); - } $connectionParams = array( - 'driver' => self::$type2driver[$type], - 'user' => $username, - 'password' => $password, - 'host' => $hostname, - 'dbname' => $dbname, + 'host' => $input->getArgument('hostname'), + 'user' => $input->getArgument('username'), + 'password' => $input->getOption('password'), + 'dbname' => $input->getArgument('database'), + 'tablePrefix' => $this->config->getValue('dbtableprefix', 'oc_'), ); if ($input->getOption('port')) { $connectionParams['port'] = $input->getOption('port'); } - switch ($type) { - case 'mysql': - case 'mssql': - $connectionParams['charset'] = 'UTF8'; - break; - case 'oci': - $connectionParams['charset'] = 'AL32UTF8'; - break; - } - - return \Doctrine\DBAL\DriverManager::getConnection($connectionParams); + return $this->connectionFactory->getConnection($type, $connectionParams); } private function getTables($db) { diff --git a/core/register_command.php b/core/register_command.php index a3833214c2..f1361c859f 100644 --- a/core/register_command.php +++ b/core/register_command.php @@ -9,7 +9,7 @@ /** @var $application Symfony\Component\Console\Application */ $application->add(new OC\Core\Command\Status); $application->add(new OC\Core\Command\Db\GenerateChangeScript()); -$application->add(new OC\Core\Command\Db\ConvertType(OC_Config::getObject())); +$application->add(new OC\Core\Command\Db\ConvertType(OC_Config::getObject(), new \OC\DB\ConnectionFactory())); $application->add(new OC\Core\Command\Upgrade()); $application->add(new OC\Core\Command\Maintenance\SingleUser()); $application->add(new OC\Core\Command\App\Disable()); diff --git a/lib/private/db.php b/lib/private/db.php index cfdac766bf..11532d9fa5 100644 --- a/lib/private/db.php +++ b/lib/private/db.php @@ -72,102 +72,45 @@ class OC_DB { $port=false; } - // do nothing if the connection already has been established - if (!self::$connection) { - $config = new \Doctrine\DBAL\Configuration(); - $eventManager = new \Doctrine\Common\EventManager(); - switch($type) { - case 'sqlite': - case 'sqlite3': - $datadir=OC_Config::getValue( "datadirectory", OC::$SERVERROOT.'/data' ); - $connectionParams = array( - 'user' => $user, - 'password' => $pass, - 'path' => $datadir.'/'.$name.'.db', - 'driver' => 'pdo_sqlite', - ); - $connectionParams['adapter'] = '\OC\DB\AdapterSqlite'; - $connectionParams['wrapperClass'] = 'OC\DB\Connection'; - break; - case 'mysql': - $connectionParams = array( - 'user' => $user, - 'password' => $pass, - 'host' => $host, - 'port' => $port, - 'dbname' => $name, - 'charset' => 'UTF8', - 'driver' => 'pdo_mysql', - ); - $connectionParams['adapter'] = '\OC\DB\Adapter'; - $connectionParams['wrapperClass'] = 'OC\DB\Connection'; - // Send "SET NAMES utf8". Only required on PHP 5.3 below 5.3.6. - // See http://stackoverflow.com/questions/4361459/php-pdo-charset-set-names#4361485 - $eventManager->addEventSubscriber(new \Doctrine\DBAL\Event\Listeners\MysqlSessionInit); - break; - case 'pgsql': - $connectionParams = array( - 'user' => $user, - 'password' => $pass, - 'host' => $host, - 'port' => $port, - 'dbname' => $name, - 'driver' => 'pdo_pgsql', - ); - $connectionParams['adapter'] = '\OC\DB\AdapterPgSql'; - $connectionParams['wrapperClass'] = 'OC\DB\Connection'; - break; - case 'oci': - $connectionParams = array( - 'user' => $user, - 'password' => $pass, - 'host' => $host, - 'dbname' => $name, - 'charset' => 'AL32UTF8', - 'driver' => 'oci8', - ); - if (!empty($port)) { - $connectionParams['port'] = $port; - } - $connectionParams['adapter'] = '\OC\DB\AdapterOCI8'; - $connectionParams['wrapperClass'] = 'OC\DB\OracleConnection'; - $eventManager->addEventSubscriber(new \Doctrine\DBAL\Event\Listeners\OracleSessionInit); - break; - case 'mssql': - $connectionParams = array( - 'user' => $user, - 'password' => $pass, - 'host' => $host, - 'port' => $port, - 'dbname' => $name, - 'charset' => 'UTF8', - 'driver' => 'pdo_sqlsrv', - ); - $connectionParams['adapter'] = '\OC\DB\AdapterSQLSrv'; - $connectionParams['wrapperClass'] = 'OC\DB\Connection'; - break; - default: - return false; - } - $connectionParams['tablePrefix'] = OC_Config::getValue('dbtableprefix', 'oc_' ); - try { - self::$connection = \Doctrine\DBAL\DriverManager::getConnection($connectionParams, $config, $eventManager); - if ($type === 'sqlite' || $type === 'sqlite3') { - // Sqlite doesn't handle query caching and schema changes - // TODO: find a better way to handle this - self::$connection->disableQueryStatementCaching(); - } - } catch(\Doctrine\DBAL\DBALException $e) { - OC_Log::write('core', $e->getMessage(), OC_Log::FATAL); - OC_User::setUserId(null); + $factory = new \OC\DB\ConnectionFactory(); + if (!$factory->isValidType($type)) { + return false; + } - // send http status 503 - header('HTTP/1.1 503 Service Temporarily Unavailable'); - header('Status: 503 Service Temporarily Unavailable'); - OC_Template::printErrorPage('Failed to connect to database'); - die(); + if ($factory->normalizeType($type) === 'sqlite3') { + $datadir = OC_Config::getValue("datadirectory", OC::$SERVERROOT.'/data'); + $connectionParams = array( + 'user' => $user, + 'password' => $pass, + 'path' => $datadir.'/'.$name.'.db', + ); + } else { + $connectionParams = array( + 'user' => $user, + 'password' => $pass, + 'host' => $host, + 'dbname' => $name, + ); + if (!empty($port)) { + $connectionParams['port'] = $port; } } + + $connectionParams['tablePrefix'] = OC_Config::getValue('dbtableprefix', 'oc_'); + + try { + self::$connection = $factory->getConnection($type, $connectionParams); + } catch(\Doctrine\DBAL\DBALException $e) { + OC_Log::write('core', $e->getMessage(), OC_Log::FATAL); + OC_User::setUserId(null); + + // send http status 503 + header('HTTP/1.1 503 Service Temporarily Unavailable'); + header('Status: 503 Service Temporarily Unavailable'); + OC_Template::printErrorPage('Failed to connect to database'); + die(); + } + return true; } @@ -202,12 +145,12 @@ class OC_DB { */ static public function prepare( $query , $limit = null, $offset = null, $isManipulation = null) { self::connect(); - + if ($isManipulation === null) { //try to guess, so we return the number of rows on manipulations $isManipulation = self::isManipulation($query); } - + // return the result try { $result = self::$connection->prepare($query, $limit, $offset); @@ -222,7 +165,7 @@ class OC_DB { /** * tries to guess the type of statement based on the first 10 characters * the current check allows some whitespace but does not work with IF EXISTS or other more complex statements - * + * * @param string $sql * @return bool */ @@ -245,7 +188,7 @@ class OC_DB { } return false; } - + /** * @brief execute a prepared statement, on error write log and throw exception * @param mixed $stmt OC_DB_StatementWrapper, diff --git a/lib/private/db/connectionfactory.php b/lib/private/db/connectionfactory.php new file mode 100644 index 0000000000..14ffe1a4a5 --- /dev/null +++ b/lib/private/db/connectionfactory.php @@ -0,0 +1,117 @@ + + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +namespace OC\DB; + +/** +* Takes care of creating and configurating Doctrine connections. +*/ +class ConnectionFactory { + /** + * @var array + * + * Array mapping DBMS type to default connection parameters passed to + * \Doctrine\DBAL\DriverManager::getConnection(). + */ + protected $defaultConnectionParams = array( + 'mssql' => array( + 'adapter' => '\OC\DB\AdapterSQLSrv', + 'charset' => 'UTF8', + 'driver' => 'pdo_sqlsrv', + 'wrapperClass' => 'OC\DB\Connection', + ), + 'mysql' => array( + 'adapter' => '\OC\DB\Adapter', + 'charset' => 'UTF8', + 'driver' => 'pdo_mysql', + 'wrapperClass' => 'OC\DB\Connection', + ), + 'oci' => array( + 'adapter' => '\OC\DB\AdapterOCI8', + 'charset' => 'AL32UTF8', + 'driver' => 'oci8', + 'wrapperClass' => 'OC\DB\OracleConnection', + ), + 'pgsql' => array( + 'adapter' => '\OC\DB\AdapterPgSql', + 'driver' => 'pdo_pgsql', + 'wrapperClass' => 'OC\DB\Connection', + ), + 'sqlite3' => array( + 'adapter' => '\OC\DB\AdapterSqlite', + 'driver' => 'pdo_sqlite', + 'wrapperClass' => 'OC\DB\Connection', + ), + ); + + /** + * @brief Get default connection parameters for a given DBMS. + * @param string $type DBMS type + * @throws \InvalidArgumentException If $type is invalid + * @return array Default connection parameters. + */ + public function getDefaultConnectionParams($type) { + $normalizedType = $this->normalizeType($type); + if (!isset($this->defaultConnectionParams[$normalizedType])) { + throw new \InvalidArgumentException("Unsupported type: $type"); + } + return $this->defaultConnectionParams[$normalizedType]; + } + + /** + * @brief Get default connection parameters for a given DBMS. + * @param string $type DBMS type + * @param array $additionalConnectionParams Additional connection parameters + * @return \OC\DB\Connection + */ + public function getConnection($type, $additionalConnectionParams) { + $normalizedType = $this->normalizeType($type); + $eventManager = new \Doctrine\Common\EventManager(); + switch ($normalizedType) { + case 'mysql': + // Send "SET NAMES utf8". Only required on PHP 5.3 below 5.3.6. + // See http://stackoverflow.com/questions/4361459/php-pdo-charset-set-names#4361485 + $eventManager->addEventSubscriber(new \Doctrine\DBAL\Event\Listeners\MysqlSessionInit); + break; + case 'oci': + $eventManager->addEventSubscriber(new \Doctrine\DBAL\Event\Listeners\OracleSessionInit); + break; + } + $connection = \Doctrine\DBAL\DriverManager::getConnection( + array_merge($this->getDefaultConnectionParams($type), $additionalConnectionParams), + new \Doctrine\DBAL\Configuration(), + $eventManager + ); + switch ($normalizedType) { + case 'sqlite3': + // Sqlite doesn't handle query caching and schema changes + // TODO: find a better way to handle this + $connection->disableQueryStatementCaching(); + break; + } + return $connection; + } + + /** + * @brief Normalize DBMS type + * @param string $type DBMS type + * @return string Normalized DBMS type + */ + public function normalizeType($type) { + return $type === 'sqlite' ? 'sqlite3' : $type; + } + + /** + * @brief Checks whether the specififed DBMS type is valid. + * @return bool + */ + public function isValidType($type) { + $normalizedType = $this->normalizeType($type); + return isset($this->defaultConnectionParams[$normalizedType]); + } +} From b39a74ff6cc951e8334462ee08728b0d270d6f3a Mon Sep 17 00:00:00 2001 From: Andreas Fischer Date: Mon, 31 Mar 2014 20:51:30 +0200 Subject: [PATCH 030/236] Some more colors. --- core/command/db/converttype.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/command/db/converttype.php b/core/command/db/converttype.php index 2a4e6747e6..464a5db3cd 100644 --- a/core/command/db/converttype.php +++ b/core/command/db/converttype.php @@ -102,14 +102,14 @@ class ConvertType extends Command { $schemaManager = $toDB->getSchemaManager(); $toTables = $schemaManager->listTableNames(); if (!empty($toTables)) { - $output->writeln('Clearing schema in new database'); + $output->writeln('Clearing schema in new database'); } foreach($toTables as $table) { $schemaManager->dropTable($table); } } - $output->writeln('Creating schema in new database'); + $output->writeln('Creating schema in new database'); $schemaManager = new \OC\DB\MDB2SchemaManager($toDB); $schemaManager->createDbFromStructure(\OC::$SERVERROOT.'/db_structure.xml'); $apps = \OC_App::getEnabledApps(); From 01141e1520bb10c0fe915f1e86d5814004b57277 Mon Sep 17 00:00:00 2001 From: Andreas Fischer Date: Mon, 31 Mar 2014 21:07:48 +0200 Subject: [PATCH 031/236] Type hinting. --- core/command/db/converttype.php | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/core/command/db/converttype.php b/core/command/db/converttype.php index 464a5db3cd..c38270d153 100644 --- a/core/command/db/converttype.php +++ b/core/command/db/converttype.php @@ -9,6 +9,10 @@ namespace OC\Core\Command\Db; +use OC\Config; +use OC\DB\Connection; +use OC\DB\ConnectionFactory; + use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; @@ -30,7 +34,7 @@ class ConvertType extends Command { * @param \OC\Config $config * @param \OC\DB\ConnectionFactory $connectionFactory */ - public function __construct($config, $connectionFactory) { + public function __construct(Config $config, ConnectionFactory $connectionFactory) { $this->config = $config; $this->connectionFactory = $connectionFactory; parent::__construct(); @@ -139,7 +143,7 @@ class ConvertType extends Command { $this->convertDB($fromDB, $toDB, $tables, $input, $output); } - private function getToDBConnection($input, $output) { + private function getToDBConnection(InputInterface $input, OutputInterface $output) { $type = $input->getArgument('type'); $connectionParams = array( 'host' => $input->getArgument('hostname'), @@ -154,12 +158,12 @@ class ConvertType extends Command { return $this->connectionFactory->getConnection($type, $connectionParams); } - private function getTables($db) { + private function getTables(Connection $db) { $schemaManager = $db->getSchemaManager(); return $schemaManager->listTableNames(); } - private function copyTable($fromDB, $toDB, $table, $output) { + private function copyTable(Connection $fromDB, Connection $toDB, $table, OutputInterface $output) { $progress = $this->getHelperSet()->get('progress'); $query = 'SELECT COUNT(*) FROM '.$table; $count = $fromDB->fetchColumn($query); @@ -178,7 +182,7 @@ class ConvertType extends Command { $progress->finish(); } - private function convertDB($fromDB, $toDB, $tables, $input, $output) { + private function convertDB(Connection $fromDB, Connection $toDB, array $tables, InputInterface $input, OutputInterface $output) { $this->config->setValue('maintenance', true); $type = $input->getArgument('type'); try { @@ -209,7 +213,7 @@ class ConvertType extends Command { $this->config->setValue('maintenance', false); } - private function saveDBInfo($input) { + private function saveDBInfo(InputInterface $input) { $type = $input->getArgument('type'); $username = $input->getArgument('username'); $dbhost = $input->getArgument('hostname'); From 5b64a27df9c3f6c64db307753a70515b7123eb5b Mon Sep 17 00:00:00 2001 From: Andreas Fischer Date: Mon, 31 Mar 2014 21:09:54 +0200 Subject: [PATCH 032/236] Undo 3rdparty update. --- 3rdparty | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/3rdparty b/3rdparty index 6240224233..da3c9f651a 160000 --- a/3rdparty +++ b/3rdparty @@ -1 +1 @@ -Subproject commit 624022423304280dab2cf7ed2367aa7c99ff565c +Subproject commit da3c9f651a26cf076249ebf25c477e3791e69ca3 From 86ab1cf476ab03d3c3e64ab9603bc1b6a1f27be5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=BCller?= Date: Tue, 1 Apr 2014 22:17:31 +0200 Subject: [PATCH 033/236] typos fixed PHPDoc comments added --- core/command/db/converttype.php | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/core/command/db/converttype.php b/core/command/db/converttype.php index c38270d153..d419ca61a8 100644 --- a/core/command/db/converttype.php +++ b/core/command/db/converttype.php @@ -43,6 +43,7 @@ class ConvertType extends Command { protected function interact(InputInterface $input, OutputInterface $output) { parent::interact($input, $output); if (!$input->getOption('password')) { + /** @var $dialog \Symfony\Component\Console\Helper\DialogHelper */ $dialog = $this->getHelperSet()->get('dialog'); $password = $dialog->askHiddenResponse( $output, @@ -56,7 +57,7 @@ class ConvertType extends Command { protected function configure() { $this ->setName('db:convert-type') - ->setDescription('Convert the owncloud database to the newly configured one') + ->setDescription('Convert the ownCloud database to the newly configured one') ->addArgument( 'type', InputArgument::REQUIRED, @@ -130,10 +131,11 @@ class ConvertType extends Command { $tables = array_diff($fromTables, $toTables); if (!empty($tables)) { $output->writeln('The following tables do NOT exist any more: '.join(', ', $tables).''); + /** @var $dialog \Symfony\Component\Console\Helper\DialogHelper */ $dialog = $this->getHelperSet()->get('dialog'); if (!$dialog->askConfirmation( $output, - 'Continue with the convertion?', + 'Continue with the conversion?', false )) { return; @@ -164,6 +166,7 @@ class ConvertType extends Command { } private function copyTable(Connection $fromDB, Connection $toDB, $table, OutputInterface $output) { + /** @var $progress \Symfony\Component\Console\Helper\ProgressHelper */ $progress = $this->getHelperSet()->get('progress'); $query = 'SELECT COUNT(*) FROM '.$table; $count = $fromDB->fetchColumn($query); From 9b4643f3865f5f14483b2e4618967f6cc91b0a22 Mon Sep 17 00:00:00 2001 From: josh4trunks Date: Thu, 3 Apr 2014 20:46:54 -0700 Subject: [PATCH 034/236] Send URI instead of filepath to NGINX for X-Accel --- lib/private/files.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/private/files.php b/lib/private/files.php index bfe6d3c02d..a377c196cd 100644 --- a/lib/private/files.php +++ b/lib/private/files.php @@ -152,7 +152,7 @@ class OC_Files { /** @var $storage \OC\Files\Storage\Storage */ list($storage) = $view->resolvePath($filename); if ($storage->isLocal()) { - self::addSendfileHeader(\OC\Files\Filesystem::getLocalFile($filename)); + self::addSendfileHeader($filename); } else { \OC\Files\Filesystem::readfile($filename); } @@ -167,9 +167,11 @@ class OC_Files { */ private static function addSendfileHeader($filename) { if (isset($_SERVER['MOD_X_SENDFILE_ENABLED'])) { + $filename = \OC\Files\Filesystem::getLocalFile($filename); header("X-Sendfile: " . $filename); } if (isset($_SERVER['MOD_X_SENDFILE2_ENABLED'])) { + $filename = \OC\Files\Filesystem::getLocalFile($filename); if (isset($_SERVER['HTTP_RANGE']) && preg_match("/^bytes=([0-9]+)-([0-9]*)$/", $_SERVER['HTTP_RANGE'], $range)) { $filelength = filesize($filename); @@ -185,6 +187,7 @@ class OC_Files { } if (isset($_SERVER['MOD_X_ACCEL_REDIRECT_ENABLED'])) { + $filename = \OC::$WEBROOT . '/data' . \OC\Files\Filesystem::getRoot() . $filename; header("X-Accel-Redirect: " . $filename); } } From 3e0858e51f885badb58b4ea3a7666937b3158bff Mon Sep 17 00:00:00 2001 From: Andreas Fischer Date: Wed, 9 Apr 2014 15:21:57 +0200 Subject: [PATCH 035/236] private -> protected --- core/command/db/converttype.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/core/command/db/converttype.php b/core/command/db/converttype.php index d419ca61a8..81a89de97f 100644 --- a/core/command/db/converttype.php +++ b/core/command/db/converttype.php @@ -145,7 +145,7 @@ class ConvertType extends Command { $this->convertDB($fromDB, $toDB, $tables, $input, $output); } - private function getToDBConnection(InputInterface $input, OutputInterface $output) { + protected function getToDBConnection(InputInterface $input, OutputInterface $output) { $type = $input->getArgument('type'); $connectionParams = array( 'host' => $input->getArgument('hostname'), @@ -160,12 +160,12 @@ class ConvertType extends Command { return $this->connectionFactory->getConnection($type, $connectionParams); } - private function getTables(Connection $db) { + protected function getTables(Connection $db) { $schemaManager = $db->getSchemaManager(); return $schemaManager->listTableNames(); } - private function copyTable(Connection $fromDB, Connection $toDB, $table, OutputInterface $output) { + protected function copyTable(Connection $fromDB, Connection $toDB, $table, OutputInterface $output) { /** @var $progress \Symfony\Component\Console\Helper\ProgressHelper */ $progress = $this->getHelperSet()->get('progress'); $query = 'SELECT COUNT(*) FROM '.$table; @@ -185,7 +185,7 @@ class ConvertType extends Command { $progress->finish(); } - private function convertDB(Connection $fromDB, Connection $toDB, array $tables, InputInterface $input, OutputInterface $output) { + protected function convertDB(Connection $fromDB, Connection $toDB, array $tables, InputInterface $input, OutputInterface $output) { $this->config->setValue('maintenance', true); $type = $input->getArgument('type'); try { @@ -216,7 +216,7 @@ class ConvertType extends Command { $this->config->setValue('maintenance', false); } - private function saveDBInfo(InputInterface $input) { + protected function saveDBInfo(InputInterface $input) { $type = $input->getArgument('type'); $username = $input->getArgument('username'); $dbhost = $input->getArgument('hostname'); From 03a3f668676486644a12261eba7e33f227ea960d Mon Sep 17 00:00:00 2001 From: Andreas Fischer Date: Wed, 9 Apr 2014 15:45:30 +0200 Subject: [PATCH 036/236] Move schema clearing to extra method. --- core/command/db/converttype.php | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/core/command/db/converttype.php b/core/command/db/converttype.php index 81a89de97f..7e65d1fb66 100644 --- a/core/command/db/converttype.php +++ b/core/command/db/converttype.php @@ -9,6 +9,8 @@ namespace OC\Core\Command\Db; +use Doctrine\DBAL\Schema\AbstractSchemaManager; + use OC\Config; use OC\DB\Connection; use OC\DB\ConnectionFactory; @@ -104,14 +106,7 @@ class ConvertType extends Command { $toDB = $this->getToDBConnection($input, $output); if ($input->getOption('clear-schema')) { - $schemaManager = $toDB->getSchemaManager(); - $toTables = $schemaManager->listTableNames(); - if (!empty($toTables)) { - $output->writeln('Clearing schema in new database'); - } - foreach($toTables as $table) { - $schemaManager->dropTable($table); - } + $this->clearSchema($toDB->getSchemaManager(), $input, $output); } $output->writeln('Creating schema in new database'); @@ -160,6 +155,16 @@ class ConvertType extends Command { return $this->connectionFactory->getConnection($type, $connectionParams); } + protected function clearSchema(AbstractSchemaManager $schemaManager, InputInterface $input, OutputInterface $output) { + $toTables = $schemaManager->listTableNames(); + if (!empty($toTables)) { + $output->writeln('Clearing schema in new database'); + } + foreach($toTables as $table) { + $schemaManager->dropTable($table); + } + } + protected function getTables(Connection $db) { $schemaManager = $db->getSchemaManager(); return $schemaManager->listTableNames(); From 5ef7d69d418ef4323cc3dc93f5ec6d24315eef96 Mon Sep 17 00:00:00 2001 From: Andreas Fischer Date: Wed, 9 Apr 2014 15:57:33 +0200 Subject: [PATCH 037/236] Do not attempt to covert to the same DBMS. --- core/command/db/converttype.php | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/core/command/db/converttype.php b/core/command/db/converttype.php index 7e65d1fb66..7e0a41b1b1 100644 --- a/core/command/db/converttype.php +++ b/core/command/db/converttype.php @@ -102,6 +102,14 @@ class ConvertType extends Command { } protected function execute(InputInterface $input, OutputInterface $output) { + if ($input->getArgument('type') === $this->config->getValue('dbtype', '')) { + $output->writeln(sprintf( + 'Can not convert from %1$s to %1$s.', + $input->getArgument('type') + )); + return 1; + } + $fromDB = \OC_DB::getConnection(); $toDB = $this->getToDBConnection($input, $output); From 370593361b89063d7bd65c018ccd537d0ea1c0ab Mon Sep 17 00:00:00 2001 From: Andreas Fischer Date: Wed, 9 Apr 2014 16:42:22 +0200 Subject: [PATCH 038/236] Add option to create all app schemas instead of just installed app. --- core/command/db/converttype.php | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/core/command/db/converttype.php b/core/command/db/converttype.php index 7e0a41b1b1..f406148ebd 100644 --- a/core/command/db/converttype.php +++ b/core/command/db/converttype.php @@ -98,6 +98,12 @@ class ConvertType extends Command { InputOption::VALUE_NONE, 'remove all tables from the destination database' ) + ->addOption( + 'all-apps', + null, + InputOption::VALUE_NONE, + 'whether to create schema for all apps instead of only installed apps' + ) ; } @@ -120,7 +126,7 @@ class ConvertType extends Command { $output->writeln('Creating schema in new database'); $schemaManager = new \OC\DB\MDB2SchemaManager($toDB); $schemaManager->createDbFromStructure(\OC::$SERVERROOT.'/db_structure.xml'); - $apps = \OC_App::getEnabledApps(); + $apps = $input->getOption('all-apps') ? \OC_App::getAllApps() : \OC_App::getEnabledApps(); foreach($apps as $app) { if(file_exists(\OC_App::getAppPath($app).'/appinfo/database.xml')) { $schemaManager->createDbFromStructure(\OC_App::getAppPath($app).'/appinfo/database.xml'); From 56b6504d59be62da618ad654e37faedb88f32242 Mon Sep 17 00:00:00 2001 From: Andreas Fischer Date: Wed, 9 Apr 2014 16:46:21 +0200 Subject: [PATCH 039/236] Extract schema creation code into its own method. --- core/command/db/converttype.php | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/core/command/db/converttype.php b/core/command/db/converttype.php index f406148ebd..170145940d 100644 --- a/core/command/db/converttype.php +++ b/core/command/db/converttype.php @@ -123,15 +123,7 @@ class ConvertType extends Command { $this->clearSchema($toDB->getSchemaManager(), $input, $output); } - $output->writeln('Creating schema in new database'); - $schemaManager = new \OC\DB\MDB2SchemaManager($toDB); - $schemaManager->createDbFromStructure(\OC::$SERVERROOT.'/db_structure.xml'); - $apps = $input->getOption('all-apps') ? \OC_App::getAllApps() : \OC_App::getEnabledApps(); - foreach($apps as $app) { - if(file_exists(\OC_App::getAppPath($app).'/appinfo/database.xml')) { - $schemaManager->createDbFromStructure(\OC_App::getAppPath($app).'/appinfo/database.xml'); - } - } + $this->createSchema($toDB, $input, $output); $toTables = $this->getTables($toDB); $fromTables = $this->getTables($fromDB); @@ -154,6 +146,18 @@ class ConvertType extends Command { $this->convertDB($fromDB, $toDB, $tables, $input, $output); } + protected function createSchema(Connection $toDB, InputInterface $input, OutputInterface $output) { + $output->writeln('Creating schema in new database'); + $schemaManager = new \OC\DB\MDB2SchemaManager($toDB); + $schemaManager->createDbFromStructure(\OC::$SERVERROOT.'/db_structure.xml'); + $apps = $input->getOption('all-apps') ? \OC_App::getAllApps() : \OC_App::getEnabledApps(); + foreach($apps as $app) { + if (file_exists(\OC_App::getAppPath($app).'/appinfo/database.xml')) { + $schemaManager->createDbFromStructure(\OC_App::getAppPath($app).'/appinfo/database.xml'); + } + } + } + protected function getToDBConnection(InputInterface $input, OutputInterface $output) { $type = $input->getArgument('type'); $connectionParams = array( From b10bf72999984d1f6c775fbeb0119c66c0fc38f7 Mon Sep 17 00:00:00 2001 From: jbtbnl Date: Thu, 10 Apr 2014 00:33:55 +0200 Subject: [PATCH 040/236] Vertically align public layout to better fit small mobile screens --- core/css/styles.css | 17 ++++++++++++++++- core/templates/layout.guest.php | 13 +++++++------ 2 files changed, 23 insertions(+), 7 deletions(-) diff --git a/core/css/styles.css b/core/css/styles.css index 57e2c4479a..2f67fa07c8 100644 --- a/core/css/styles.css +++ b/core/css/styles.css @@ -327,7 +327,7 @@ input[type="submit"].enabled { /* Some whitespace to the top */ #body-login #header { - padding-top: 100px; + padding-top: 10px; } /* Fix background gradient */ #body-login { @@ -637,6 +637,21 @@ label.infield { cursor:text !important; top:1.05em; left:.85em; } min-height: 100%; margin: 0 auto -70px; width: 300px; + + display: -webkit-box; + -webkit-box-orient: horizontal; + -webkit-box-pack: center; + -webkit-box-align: center; + + display: -moz-box; + -moz-box-orient: horizontal; + -moz-box-pack: center; + -moz-box-align: center; + + display: box; + box-orient: horizontal; + box-pack: center; + box-align: center; } #body-login footer, #body-login .push { height: 70px; diff --git a/core/templates/layout.guest.php b/core/templates/layout.guest.php index 5788d1d5bd..a6d8d93533 100644 --- a/core/templates/layout.guest.php +++ b/core/templates/layout.guest.php @@ -35,14 +35,15 @@
-
- - +
+
+
+