diff options
author | Sean Molenaar <sean@seanmolenaar.eu> | 2020-10-04 20:45:33 +0200 |
---|---|---|
committer | Benjamin Brahmer <info@b-brahmer.de> | 2020-11-03 19:58:37 +0100 |
commit | 10e8c28feaf6d858948285a291231f651ef74728 (patch) | |
tree | 5c48216dc1ce6a6dd8f292d489bb4a7d03cb5595 /lib | |
parent | 361cfa55b7302b29e158647060085550b9a3dfe1 (diff) |
Add migration with foreign keys
Closes #829
Signed-off-by: Sean Molenaar <sean@seanmolenaar.eu>
Diffstat (limited to 'lib')
-rw-r--r-- | lib/Command/Config/FeedAdd.php | 24 | ||||
-rw-r--r-- | lib/Command/Config/FolderAdd.php | 10 | ||||
-rw-r--r-- | lib/Command/Config/FolderDelete.php | 4 | ||||
-rw-r--r-- | lib/Controller/FeedApiController.php | 21 | ||||
-rw-r--r-- | lib/Controller/FeedController.php | 32 | ||||
-rw-r--r-- | lib/Controller/FolderApiController.php | 27 | ||||
-rw-r--r-- | lib/Controller/FolderController.php | 39 | ||||
-rw-r--r-- | lib/Controller/UserApiController.php | 74 | ||||
-rw-r--r-- | lib/Db/Feed.php | 12 | ||||
-rw-r--r-- | lib/Db/FeedMapper.php | 4 | ||||
-rw-r--r-- | lib/Db/FeedMapperV2.php | 18 | ||||
-rw-r--r-- | lib/Db/Folder.php | 2 | ||||
-rw-r--r-- | lib/Db/FolderMapperV2.php | 1 | ||||
-rw-r--r-- | lib/Db/ItemMapper.php | 22 | ||||
-rw-r--r-- | lib/Migration/Version150005Date20201009192341.php | 98 | ||||
-rw-r--r-- | lib/Service/FeedService.php | 26 | ||||
-rw-r--r-- | lib/Service/FeedServiceV2.php | 8 | ||||
-rw-r--r-- | lib/Service/FolderService.php | 18 | ||||
-rw-r--r-- | lib/Service/FolderServiceV2.php | 2 | ||||
-rw-r--r-- | lib/Service/ItemService.php | 44 | ||||
-rw-r--r-- | lib/Utility/OPMLExporter.php | 2 |
21 files changed, 299 insertions, 189 deletions
diff --git a/lib/Command/Config/FeedAdd.php b/lib/Command/Config/FeedAdd.php index 3c4820437..86121ad06 100644 --- a/lib/Command/Config/FeedAdd.php +++ b/lib/Command/Config/FeedAdd.php @@ -2,6 +2,8 @@ declare(strict_types=1); namespace OCA\News\Command\Config; +use OCA\News\Service\Exceptions\ServiceConflictException; +use OCA\News\Service\Exceptions\ServiceNotFoundException; use OCA\News\Service\FeedServiceV2; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputArgument; @@ -56,13 +58,31 @@ class FeedAdd extends Command { $user = $input->getArgument('user-id'); $url = $input->getArgument('feed'); - $folder = (int) $input->getOption('folder') ?? 0; + $folder = $input->getOption('folder'); $title = $input->getOption('title'); $full_text = (bool) $input->getOption('full-text'); $username = $input->getOption('username'); $password = $input->getOption('password'); - $feed = $this->feedService->create($user, $url, $folder, $full_text, $title, $username, $password); + if ($folder !== null) { + $folder = intval($folder); + } + + try { + $feed = $this->feedService->create( + $user, + $url, + $folder, + $full_text, + $title, + $username, + $password + ); + } catch (ServiceNotFoundException|ServiceConflictException $e) { + $output->write($e->getMessage()); + return 1; + } + $this->feedService->fetch($feed); $output->writeln(json_encode($feed->toAPI(), JSON_PRETTY_PRINT)); diff --git a/lib/Command/Config/FolderAdd.php b/lib/Command/Config/FolderAdd.php index 257cf2dd6..03ad72535 100644 --- a/lib/Command/Config/FolderAdd.php +++ b/lib/Command/Config/FolderAdd.php @@ -47,9 +47,13 @@ class FolderAdd extends Command */ protected function execute(InputInterface $input, OutputInterface $output): int { - $user = $input->getArgument('user-id'); - $name = $input->getArgument('name'); - $parent = (int) $input->getOption('parent') ?? 0; + $user = $input->getArgument('user-id'); + $name = $input->getArgument('name'); + $parent = $input->getOption('parent'); + + if ($parent !== null) { + $parent = intval($parent); + } $this->folderService->create($user, $name, $parent); diff --git a/lib/Command/Config/FolderDelete.php b/lib/Command/Config/FolderDelete.php index bf7608b02..397eda999 100644 --- a/lib/Command/Config/FolderDelete.php +++ b/lib/Command/Config/FolderDelete.php @@ -50,11 +50,11 @@ class FolderDelete extends Command $user = $input->getArgument('user-id'); $id = $input->getArgument('folder-id'); - if ($id === '0') { + if ($id === null) { throw new ServiceException('Can not remove root folder!'); } - $this->folderService->delete($user, $id); + $this->folderService->delete($user, intval($id)); return 0; } diff --git a/lib/Controller/FeedApiController.php b/lib/Controller/FeedApiController.php index cb3d5e645..2c96bbc6e 100644 --- a/lib/Controller/FeedApiController.php +++ b/lib/Controller/FeedApiController.php @@ -27,6 +27,7 @@ use \OCP\AppFramework\Http; use \OCA\News\Service\FeedService; use \OCA\News\Service\ItemService; use Psr\Log\LoggerInterface; +use function GuzzleHttp\Psr7\uri_for; class FeedApiController extends ApiController { @@ -99,13 +100,17 @@ class FeedApiController extends ApiController * @NoCSRFRequired * @CORS * - * @param string $url - * @param int $folderId + * @param string $url + * @param int|null $folderId * * @return array|mixed|JSONResponse */ - public function create(string $url, int $folderId = 0) + public function create(string $url, ?int $folderId = null) { + if ($folderId === 0) { + $folderId = null; + } + try { $this->feedService->purgeDeleted($this->getUserId(), false); @@ -169,13 +174,17 @@ class FeedApiController extends ApiController * @NoCSRFRequired * @CORS * - * @param int $feedId - * @param int $folderId + * @param int $feedId + * @param int|null $folderId * * @return array|JSONResponse */ - public function move(int $feedId, int $folderId) + public function move(int $feedId, ?int $folderId) { + if ($folderId === 0) { + $folderId = null; + } + try { $this->feedService->patch( $feedId, diff --git a/lib/Controller/FeedController.php b/lib/Controller/FeedController.php index c09096bd9..78df7be0c 100644 --- a/lib/Controller/FeedController.php +++ b/lib/Controller/FeedController.php @@ -105,6 +105,9 @@ class FeedController extends Controller // check if feed or folder exists try { if ($feedType === FeedType::FOLDER) { + if ($feedId === 0) { + $feedId = null; + } $this->folderService->find($this->userId, $feedId); } elseif ($feedType === FeedType::FEED) { $this->feedService->find($this->userId, $feedId); @@ -131,20 +134,23 @@ class FeedController extends Controller * @NoAdminRequired * * @param string $url - * @param int $parentFolderId - * @param string $title - * @param string $user - * @param string $password + * @param int|null $parentFolderId + * @param string|null $title + * @param string|null $user + * @param string|null $password * * @return array|JSONResponse */ public function create( string $url, - int $parentFolderId, + ?int $parentFolderId, ?string $title = null, ?string $user = null, ?string $password = null ) { + if ($parentFolderId === 0) { + $parentFolderId = null; + } try { // we need to purge deleted feeds if a feed is created to // prevent already exists exceptions @@ -290,13 +296,13 @@ class FeedController extends Controller /** * @NoAdminRequired * - * @param int $feedId - * @param bool $pinned - * @param bool $fullTextEnabled - * @param int $updateMode - * @param int $ordering - * @param int $folderId - * @param string $title + * @param int $feedId + * @param bool $pinned + * @param bool $fullTextEnabled + * @param int|null $updateMode + * @param int|null $ordering + * @param int|null $folderId + * @param string|null $title * * @return array|JSONResponse */ @@ -315,7 +321,7 @@ class FeedController extends Controller 'updateMode' => $updateMode, 'ordering' => $ordering, 'title' => $title, - 'folderId' => $folderId + 'folderId' => $folderId === 0 ? null : $folderId ]; $diff = array_filter( diff --git a/lib/Controller/FolderApiController.php b/lib/Controller/FolderApiController.php index 1f79d7cf9..691c67139 100644 --- a/lib/Controller/FolderApiController.php +++ b/lib/Controller/FolderApiController.php @@ -90,12 +90,16 @@ class FolderApiController extends ApiController * @NoCSRFRequired * @CORS * - * @param int $folderId + * @param int|null $folderId * * @return array|JSONResponse */ - public function delete(int $folderId) + public function delete(?int $folderId) { + if (empty($folderId)) { + return new JSONResponse([], Http::STATUS_BAD_REQUEST); + } + try { $this->folderService->delete($folderId, $this->getUserId()); } catch (ServiceNotFoundException $ex) { @@ -111,13 +115,17 @@ class FolderApiController extends ApiController * @NoCSRFRequired * @CORS * - * @param int $folderId - * @param string $name + * @param int|null $folderId + * @param string $name * * @return array|JSONResponse */ - public function update(int $folderId, string $name) + public function update(?int $folderId, string $name) { + if (empty($folderId)) { + return new JSONResponse([], Http::STATUS_BAD_REQUEST); + } + try { $this->folderService->rename($folderId, $name, $this->getUserId()); } catch (ServiceValidationException $ex) { @@ -137,11 +145,14 @@ class FolderApiController extends ApiController * @NoCSRFRequired * @CORS * - * @param int $folderId - * @param int $newestItemId + * @param int|null $folderId + * @param int $newestItemId */ - public function read(int $folderId, int $newestItemId): void + public function read(?int $folderId, int $newestItemId): void { + if ($folderId === 0) { + $folderId = null; + } $this->itemService->readFolder($folderId, $newestItemId, $this->getUserId()); } } diff --git a/lib/Controller/FolderController.php b/lib/Controller/FolderController.php index d19726963..09900f5b2 100644 --- a/lib/Controller/FolderController.php +++ b/lib/Controller/FolderController.php @@ -64,13 +64,15 @@ class FolderController extends Controller /** * @NoAdminRequired * - * @param int $folderId - * @param bool $open + * @param int|null $folderId + * @param bool $open * * @return array|JSONResponse */ - public function open(int $folderId, bool $open) + public function open(?int $folderId, bool $open) { + $folderId = $folderId === 0 ? null : $folderId; + try { $this->folderService->open($folderId, $open, $this->userId); } catch (ServiceException $ex) { @@ -108,12 +110,15 @@ class FolderController extends Controller /** * @NoAdminRequired * - * @param int $folderId + * @param int|null $folderId * * @return array|JSONResponse */ - public function delete(int $folderId) + public function delete(?int $folderId) { + if (empty($folderId)) { + return new JSONResponse([], Http::STATUS_BAD_REQUEST); + } try { $this->folderService->markDeleted($folderId, $this->userId); } catch (ServiceNotFoundException $ex) { @@ -127,13 +132,16 @@ class FolderController extends Controller /** * @NoAdminRequired * - * @param string $folderName - * @param int $folderId + * @param string $folderName + * @param int|null $folderId * * @return array|JSONResponse */ - public function rename(string $folderName, int $folderId) + public function rename(string $folderName, ?int $folderId) { + if (empty($folderId)) { + return new JSONResponse([], Http::STATUS_BAD_REQUEST); + } try { $folder = $this->folderService->rename( $folderId, @@ -154,12 +162,15 @@ class FolderController extends Controller /** * @NoAdminRequired * - * @param int $folderId - * @param int $highestItemId + * @param int|null $folderId + * @param int $highestItemId + * * @return array */ - public function read(int $folderId, int $highestItemId): array + public function read(?int $folderId, int $highestItemId): array { + $folderId = $folderId === 0 ? null : $folderId; + $this->itemService->readFolder( $folderId, $highestItemId, @@ -173,12 +184,14 @@ class FolderController extends Controller /** * @NoAdminRequired * - * @param int $folderId + * @param int|null $folderId * * @return array|JSONResponse */ - public function restore(int $folderId) + public function restore(?int $folderId) { + $folderId = $folderId === 0 ? null : $folderId; + try { $this->folderService->unmarkDeleted($folderId, $this->userId); } catch (ServiceNotFoundException $ex) { diff --git a/lib/Controller/UserApiController.php b/lib/Controller/UserApiController.php deleted file mode 100644 index b644ba1f0..000000000 --- a/lib/Controller/UserApiController.php +++ /dev/null @@ -1,74 +0,0 @@ -<?php -/** - * Nextcloud - News - * - * This file is licensed under the Affero General Public License version 3 or - * later. See the COPYING file. - * - * @author Alessandro Cosentino <cosenal@gmail.com> - * @author Bernhard Posselt <dev@bernhard-posselt.com> - * @author David Guillot <david@guillot.me> - * @copyright 2012 Alessandro Cosentino - * @copyright 2012-2014 Bernhard Posselt - * @copyright 2018 David Guillot - */ - -namespace OCA\News\Controller; - -use \OCP\IRequest; -use \OCP\IUserSession; -use \OCP\IURLGenerator; -use \OCP\Files\IRootFolder; -use \OCP\AppFramework\Http; - -class UserApiController extends ApiController -{ - - private $rootFolder; - - public function __construct( - string $appName, - IRequest $request, - IUserSession $userSession, - IRootFolder $rootFolder - ) { - parent::__construct($appName, $request, $userSession); - $this->rootFolder = $rootFolder; - } - - /** - * @NoAdminRequired - * @NoCSRFRequired - * @CORS - */ - public function index(): array - { - $user = $this->getUser(); - - // find the avatar - $jpgAvatar = '/' . $user->getUID() . '/avatar.jpg'; - $pngAvatar = '/' . $user->getUID() . '/avatar.png'; - $avatar = null; - - if ($this->rootFolder->nodeExists($jpgAvatar)) { - $file = $this->rootFolder->get($jpgAvatar); - $avatar = [ - 'data' => base64_encode($file->getContent()), - 'mime' => 'image/jpeg' - ]; - } elseif ($this->rootFolder->nodeExists($pngAvatar)) { - $file = $this->rootFolder->get($pngAvatar); - $avatar = [ - 'data' => base64_encode($file->getContent()), - 'mime' => 'image/png' - ]; - } - - return [ - 'userId' => $user->getUID(), - 'displayName' => $user->getDisplayName(), - 'lastLoginTimestamp' => $user->getLastLogin(), - 'avatar' => $avatar - ]; - } -} diff --git a/lib/Db/Feed.php b/lib/Db/Feed.php index 852de4c78..473912acc 100644 --- a/lib/Db/Feed.php +++ b/lib/Db/Feed.php @@ -37,7 +37,7 @@ class Feed extends Entity implements IAPI, \JsonSerializable protected $faviconLink = null; /** @var int|null */ protected $added = 0; - /** @var int */ + /** @var int|null */ protected $folderId; /** @var int */ protected $unreadCount; @@ -152,9 +152,9 @@ class Feed extends Entity implements IAPI, \JsonSerializable } /** - * @return int + * @return int|null */ - public function getFolderId(): int + public function getFolderId(): ?int { return $this->folderId; } @@ -416,9 +416,11 @@ class Feed extends Entity implements IAPI, \JsonSerializable } /** - * @param int $folderId + * @param int|null $folderId + * + * @return Feed */ - public function setFolderId(int $folderId): Feed + public function setFolderId(?int $folderId): Feed { if ($this->folderId !== $folderId) { $this->folderId = $folderId; diff --git a/lib/Db/FeedMapper.php b/lib/Db/FeedMapper.php index 867ba982d..193cf1f5d 100644 --- a/lib/Db/FeedMapper.php +++ b/lib/Db/FeedMapper.php @@ -77,7 +77,7 @@ class FeedMapper extends NewsMapper // think twice when changing this 'AND `items`.`unread` = ? ' . 'WHERE `feeds`.`user_id` = ? ' . - 'AND (`feeds`.`folder_id` = 0 ' . + 'AND (`feeds`.`folder_id` IS NULL ' . 'OR `folders`.`deleted_at` = 0 ' . ') ' . 'AND `feeds`.`deleted_at` = 0 ' . @@ -106,7 +106,7 @@ class FeedMapper extends NewsMapper // POSSIBLE SQL INJECTION RISK WHEN MODIFIED WITHOUT THOUGHT. // think twice when changing this 'AND `items`.`unread` = ? ' . - 'WHERE (`feeds`.`folder_id` = 0 ' . + 'WHERE (`feeds`.`folder_id` IS NULL ' . 'OR `folders`.`deleted_at` = 0 ' . ') ' . 'AND `feeds`.`deleted_at` = 0 ' . diff --git a/lib/Db/FeedMapperV2.php b/lib/Db/FeedMapperV2.php index a7edecd88..05b272112 100644 --- a/lib/Db/FeedMapperV2.php +++ b/lib/Db/FeedMapperV2.php @@ -43,6 +43,7 @@ class FeedMapperV2 extends NewsMapperV2 * Find all feeds for a user. * * @param string $userId The user identifier + * @param array $params Filter parameters * * @return Entity[] */ @@ -62,7 +63,7 @@ class FeedMapperV2 extends NewsMapperV2 * Find all feeds for a user. * * @param string $userId The user identifier - * @param int $id The feed identifier + * @param int $id The feed identifier * * @return Entity * @@ -124,17 +125,22 @@ class FeedMapperV2 extends NewsMapperV2 /** * Find all feeds in a folder * - * @param int $id ID of the folder + * @param int|null $id ID of the folder * * @return Feed[] */ - public function findAllFromFolder(int $id): array + public function findAllFromFolder(?int $id): array { $builder = $this->db->getQueryBuilder(); $builder->addSelect('*') - ->from($this->tableName) - ->where('folder_id = :folder_id') - ->setParameter(':folder_id', $id); + ->from($this->tableName); + + if (is_null($id)) { + $builder->where('folder_id IS NULL'); + } else { + $builder->where('folder_id = :folder_id') + ->setParameter(':folder_id', $id); + } return $this->findEntities($builder); } diff --git a/lib/Db/Folder.php b/lib/Db/Folder.php index 2efd65e96..d452bb7d4 100644 --- a/lib/Db/Folder.php +++ b/lib/Db/Folder.php @@ -141,7 +141,7 @@ class Folder extends Entity implements IAPI, \JsonSerializable return $this; } - public function setParentId(int $parentId = 0): self + public function setParentId(?int $parentId = null): self { if ($this->parentId !== $parentId) { $this->parentId = $parentId; diff --git a/lib/Db/FolderMapperV2.php b/lib/Db/FolderMapperV2.php index 7d0536607..913bd9d70 100644 --- a/lib/Db/FolderMapperV2.php +++ b/lib/Db/FolderMapperV2.php @@ -41,6 +41,7 @@ class FolderMapperV2 extends NewsMapperV2 * Find all feeds for a user. * * @param string $userId The user identifier + * @param array $params Filter parameters * * @return Entity[] */ diff --git a/lib/Db/ItemMapper.php b/lib/Db/ItemMapper.php index 270919b44..65c6e0b15 100644 --- a/lib/Db/ItemMapper.php +++ b/lib/Db/ItemMapper.php @@ -55,7 +55,7 @@ class ItemMapper extends NewsMapper $prependTo . 'LEFT OUTER JOIN `*PREFIX*news_folders` `folders` ' . 'ON `folders`.`id` = `feeds`.`folder_id` ' . - 'WHERE `feeds`.`folder_id` = 0 ' . + 'WHERE `feeds`.`folder_id` IS NULL ' . 'OR `folders`.`deleted_at` = 0 ' . 'ORDER BY `items`.`id` ' . $ordering; } @@ -125,7 +125,7 @@ class ItemMapper extends NewsMapper 'AND `items`.`starred` = ? ' . 'LEFT OUTER JOIN `*PREFIX*news_folders` `folders` ' . 'ON `folders`.`id` = `feeds`.`folder_id` ' . - 'WHERE `feeds`.`folder_id` = 0 ' . + 'WHERE `feeds`.`folder_id` IS NULL ' . 'OR `folders`.`deleted_at` = 0'; $params = [$userId, true]; @@ -151,14 +151,15 @@ class ItemMapper extends NewsMapper } - public function readFolder($folderId, $highestItemId, $time, $userId) + public function readFolder(?int $folderId, $highestItemId, $time, $userId) { + $folderWhere = is_null($folderId) ? 'IS' : '='; $sql = 'UPDATE `*PREFIX*news_items` ' . 'SET unread = ? ' . ', `last_modified` = ? ' . 'WHERE `feed_id` IN (' . 'SELECT `id` FROM `*PREFIX*news_feeds` ' . - 'WHERE `folder_id` = ? ' . + "WHERE `folder_id` ${folderWhere} ? " . 'AND `user_id` = ? ' . ') ' . 'AND `id` <= ?'; @@ -207,11 +208,12 @@ class ItemMapper extends NewsMapper } - public function findAllNewFolder($id, $updatedSince, $showAll, $userId) + public function findAllNewFolder(?int $id, $updatedSince, $showAll, $userId) { $sql = $this->buildStatusQueryPart($showAll); - $sql .= 'AND `feeds`.`folder_id` = ? ' . + $folderWhere = is_null($id) ? 'IS' : '='; + $sql .= "AND `feeds`.`folder_id` ${$folderWhere} ? " . 'AND `items`.`last_modified` >= ? '; $sql = $this->makeSelectQuery($sql); $params = [$userId, $id, $updatedSince]; @@ -270,7 +272,7 @@ class ItemMapper extends NewsMapper public function findAllFolder( - $id, + ?int $id, $limit, $offset, $showAll, @@ -285,10 +287,10 @@ class ItemMapper extends NewsMapper $sql = $this->buildStatusQueryPart($showAll); $sql .= $this->buildSearchQueryPart($search); - $sql .= 'AND `feeds`.`folder_id` = ? '; + $folderWhere = is_null($id) ? 'IS' : '='; + $sql .= "AND `feeds`.`folder_id` ${folderWhere} ? "; if ($offset !== 0) { - $sql .= 'AND `items`.`id` ' . - $this->getOperator($oldestFirst) . ' ? '; + $sql .= 'AND `items`.`id` ' . $this->getOperator($oldestFirst) . ' ? '; $params[] = $offset; } $sql = $this->makeSelectQuery($sql, $oldestFirst, $search); diff --git a/lib/Migration/Version150005Date20201009192341.php b/lib/Migration/Version150005Date20201009192341.php new file mode 100644 index 000000000..937d3f50b --- /dev/null +++ b/lib/Migration/Version150005Date20201009192341.php @@ -0,0 +1,98 @@ +<?php + +declare(strict_types=1); + +namespace OCA\News\Migration; + +use Closure; +use OCP\DB\ISchemaWrapper; +use OCP\DB\QueryBuilder\IQueryBuilder; +use OCP\IDBConnection; +use OCP\Migration\IOutput; +use OCP\Migration\SimpleMigrationStep; + +/** + * Auto-generated migration step: Please modify to your needs! + */ +class Version150005Date20201009192341 extends SimpleMigrationStep { + + protected $connection; + + public function __construct(IDBConnection $connection) + { + $this->connection = $connection; + } + + /** + * @param IOutput $output + * @param Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper` + * @param array $options + */ + public function preSchemaChange(IOutput $output, Closure $schemaClosure, array $options) { + $qb = $this->connection->getQueryBuilder(); + + $qb->update('news_feeds') + ->set('folder_id', $qb->createPositionalParameter(null, IQueryBuilder::PARAM_NULL)) + ->where('folder_id = 0') + ->execute(); + + $feed_name = $this->connection->getQ |