diff --git a/.php-cs-fixer.php b/.php-cs-fixer.php index 39974f827a..0a02c452d6 100644 --- a/.php-cs-fixer.php +++ b/.php-cs-fixer.php @@ -4,7 +4,8 @@ ->exclude('assets') ->exclude('utils') ->in(__DIR__) - ->name('*.phtml'); + ->name('*.phtml') + ->name('selfoss'); $rules = [ '@Symfony' => true, diff --git a/bin/selfoss b/bin/selfoss new file mode 100755 index 0000000000..aec9f7b940 --- /dev/null +++ b/bin/selfoss @@ -0,0 +1,20 @@ +#!/usr/bin/env php +popHandler(); +$handler = new StreamHandler('php://stderr'); +$log->pushHandler($handler); + +$application->add($dice->create(DatabaseExportCommand::class)); +$application->add($dice->create(DatabaseImportCommand::class)); + +$application->run(); diff --git a/composer.json b/composer.json index 5e8b66e638..93cbdfd5d7 100644 --- a/composer.json +++ b/composer.json @@ -22,6 +22,7 @@ "php-http/guzzle6-adapter": "^1.0", "simplepie/simplepie": "^1.3", "smottt/wideimage": "^1.1", + "symfony/console": "^3.4", "violet/streaming-json-encoder": "^1.1", "willwashburn/phpamo": "^1.0" }, @@ -39,6 +40,7 @@ ], "autoload": { "psr-4": { + "Commands\\": "src/Commands/", "controllers\\": "src/controllers/", "daos\\": "src/daos/", "helpers\\": "src/helpers/", diff --git a/composer.lock b/composer.lock index 484bc1a6af..a7b98ed84f 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "8b4e7b3be490c10b0bd0c4d63df28d61", + "content-hash": "4a5de52ba0c4702029fecf2d24acd6f2", "packages": [ { "name": "bcosca/fatfree-core", @@ -2209,6 +2209,158 @@ }, "time": "2021-01-09T15:26:41+00:00" }, + { + "name": "symfony/console", + "version": "v3.4.47", + "source": { + "type": "git", + "url": "https://github.com/symfony/console.git", + "reference": "a10b1da6fc93080c180bba7219b5ff5b7518fe81" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/console/zipball/a10b1da6fc93080c180bba7219b5ff5b7518fe81", + "reference": "a10b1da6fc93080c180bba7219b5ff5b7518fe81", + "shasum": "" + }, + "require": { + "php": "^5.5.9|>=7.0.8", + "symfony/debug": "~2.8|~3.0|~4.0", + "symfony/polyfill-mbstring": "~1.0" + }, + "conflict": { + "symfony/dependency-injection": "<3.4", + "symfony/process": "<3.3" + }, + "provide": { + "psr/log-implementation": "1.0" + }, + "require-dev": { + "psr/log": "~1.0", + "symfony/config": "~3.3|~4.0", + "symfony/dependency-injection": "~3.4|~4.0", + "symfony/event-dispatcher": "~2.8|~3.0|~4.0", + "symfony/lock": "~3.4|~4.0", + "symfony/process": "~3.3|~4.0" + }, + "suggest": { + "psr/log": "For using the console logger", + "symfony/event-dispatcher": "", + "symfony/lock": "", + "symfony/process": "" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Console\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Console Component", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/console/tree/v3.4.47" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2020-10-24T10:57:07+00:00" + }, + { + "name": "symfony/debug", + "version": "v3.4.47", + "source": { + "type": "git", + "url": "https://github.com/symfony/debug.git", + "reference": "ab42889de57fdfcfcc0759ab102e2fd4ea72dcae" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/debug/zipball/ab42889de57fdfcfcc0759ab102e2fd4ea72dcae", + "reference": "ab42889de57fdfcfcc0759ab102e2fd4ea72dcae", + "shasum": "" + }, + "require": { + "php": "^5.5.9|>=7.0.8", + "psr/log": "~1.0" + }, + "conflict": { + "symfony/http-kernel": ">=2.3,<2.3.24|~2.4.0|>=2.5,<2.5.9|>=2.6,<2.6.2" + }, + "require-dev": { + "symfony/http-kernel": "~2.8|~3.0|~4.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Debug\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Debug Component", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/debug/tree/v3.4.47" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2020-10-24T10:57:07+00:00" + }, { "name": "symfony/finder", "version": "v3.4.47", @@ -4717,158 +4869,6 @@ }, "time": "2016-10-03T07:35:21+00:00" }, - { - "name": "symfony/console", - "version": "v3.4.47", - "source": { - "type": "git", - "url": "https://github.com/symfony/console.git", - "reference": "a10b1da6fc93080c180bba7219b5ff5b7518fe81" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/a10b1da6fc93080c180bba7219b5ff5b7518fe81", - "reference": "a10b1da6fc93080c180bba7219b5ff5b7518fe81", - "shasum": "" - }, - "require": { - "php": "^5.5.9|>=7.0.8", - "symfony/debug": "~2.8|~3.0|~4.0", - "symfony/polyfill-mbstring": "~1.0" - }, - "conflict": { - "symfony/dependency-injection": "<3.4", - "symfony/process": "<3.3" - }, - "provide": { - "psr/log-implementation": "1.0" - }, - "require-dev": { - "psr/log": "~1.0", - "symfony/config": "~3.3|~4.0", - "symfony/dependency-injection": "~3.4|~4.0", - "symfony/event-dispatcher": "~2.8|~3.0|~4.0", - "symfony/lock": "~3.4|~4.0", - "symfony/process": "~3.3|~4.0" - }, - "suggest": { - "psr/log": "For using the console logger", - "symfony/event-dispatcher": "", - "symfony/lock": "", - "symfony/process": "" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\Console\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony Console Component", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/console/tree/v3.4.47" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2020-10-24T10:57:07+00:00" - }, - { - "name": "symfony/debug", - "version": "v3.4.47", - "source": { - "type": "git", - "url": "https://github.com/symfony/debug.git", - "reference": "ab42889de57fdfcfcc0759ab102e2fd4ea72dcae" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/debug/zipball/ab42889de57fdfcfcc0759ab102e2fd4ea72dcae", - "reference": "ab42889de57fdfcfcc0759ab102e2fd4ea72dcae", - "shasum": "" - }, - "require": { - "php": "^5.5.9|>=7.0.8", - "psr/log": "~1.0" - }, - "conflict": { - "symfony/http-kernel": ">=2.3,<2.3.24|~2.4.0|>=2.5,<2.5.9|>=2.6,<2.6.2" - }, - "require-dev": { - "symfony/http-kernel": "~2.8|~3.0|~4.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\Debug\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony Debug Component", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/debug/tree/v3.4.47" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2020-10-24T10:57:07+00:00" - }, { "name": "symfony/event-dispatcher", "version": "v3.4.47", diff --git a/src/Commands/Database/ExportCommand.php b/src/Commands/Database/ExportCommand.php new file mode 100644 index 0000000000..879b5d91c5 --- /dev/null +++ b/src/Commands/Database/ExportCommand.php @@ -0,0 +1,41 @@ +connection = $connection; + + parent::__construct(); + } + + protected function configure() { + $this->addArgument('path', InputArgument::REQUIRED, 'Path to file to export the database to.'); + } + + protected function execute(InputInterface $input, OutputInterface $output) { + $tags = $this->connection->exec('select * from tags'); + $sources = $this->connection->exec('select * from sources'); + $items = $this->connection->exec('select * from items'); + + $data = [ + 'schemaVersion' => $this->database->getSchemaVersion(), + 'tags' => $tags, + 'sources' => $sources, + 'items' => $items, + ]; + + file_put_contents($input->getArgument('path'), json_encode($data)); + } +} diff --git a/src/Commands/Database/ImportCommand.php b/src/Commands/Database/ImportCommand.php new file mode 100644 index 0000000000..bce089022f --- /dev/null +++ b/src/Commands/Database/ImportCommand.php @@ -0,0 +1,65 @@ +exec("insert into $table($fieldsSql) values($valuesSql)", $values); +} + +class ImportCommand extends Command { + protected static $defaultName = 'db:import'; + protected static $defaultDescription = 'Imports the database contents from a JSON file.'; + + /** @var \DB\SQL database connection */ + private $connection; + + /** @var \daos\Database database object to create the tables */ + private $database; + + public function __construct(\DB\SQL $connection, \daos\Database $database) { + $this->connection = $connection; + $this->database = $database; + + parent::__construct(); + } + + protected function configure() { + $this->addArgument('path', InputArgument::REQUIRED, 'Path to file to import the database from.'); + } + + protected function execute(InputInterface $input, OutputInterface $output) { + $data = json_decode(file_get_contents($input->getArgument('path')), true); + + $currentDbSchemaVersion = $this->database->getSchemaVersion(); + + if ($data['schemaVersion'] !== $currentDbSchemaVersion) { + $output->writeln('The database schema version of the current selfoss installation does not match the installation the data was exported from, which may cause issues. For best compatibility, upgrade ' . ($data['schemaVersion'] < $currentDbSchemaVersion ? 'the source selfoss' : 'this selfoss') . ' installation before importing.'); + } + + foreach ($data['tags'] as $tag) { + insert($this->connection, 'tags', ['tag', 'color'], $tag); + } + + foreach ($data['sources'] as $source) { + insert($this->connection, 'sources', ['id', 'title', 'tags', 'filter', 'spout', 'params', 'error', 'lastupdate', 'lastentry'], $source); + } + + foreach ($data['items'] as $item) { + insert($this->connection, 'items', ['id', 'datetime', 'title', 'content', 'thumbnail', 'icon', 'unread', 'starred', 'source', 'uid', 'link', 'updatetime', 'author', 'shared', 'lastseen'], $item); + } + } +}