diff --git a/src/Command/Import/UrlCommand.php b/src/Command/Import/UrlCommand.php new file mode 100644 index 000000000..9f40d7cde --- /dev/null +++ b/src/Command/Import/UrlCommand.php @@ -0,0 +1,118 @@ +setName('wallabag:import:url') + ->setDescription('Import a single URL') + ->addArgument('username', InputArgument::REQUIRED, 'User to add the URL to (value: username or id)') + ->addArgument('url', InputArgument::REQUIRED, 'URL to import') + ->addArgument('tags', InputArgument::OPTIONAL, 'Comma-separated list of tags to add') + ->addOption('markAsRead', null, InputOption::VALUE_OPTIONAL, 'Mark entry as read', false) + ->addOption('useUserId', null, InputOption::VALUE_NONE, 'Use user id instead of username to find account') + ; + } + + protected function execute(InputInterface $input, OutputInterface $output): int + { + // Turning off doctrine default logs queries for saving memory + $middlewares = $this->entityManager->getConnection()->getConfiguration()->getMiddlewares(); + $middlewaresWithoutLogging = array_filter($middlewares, fn (Middleware $middleware) => !$middleware instanceof LoggingMiddleware); + $this->entityManager->getConnection()->getConfiguration()->setMiddlewares($middlewaresWithoutLogging); + + if ($input->getOption('useUserId')) { + $entityUser = $this->userRepository->findOneById($input->getArgument('username')); + } else { + $entityUser = $this->userRepository->findOneByUsername($input->getArgument('username')); + } + + if (!\is_object($entityUser)) { + throw new Exception(\sprintf('User "%s" not found', $input->getArgument('username'))); + } + + // Authenticate user for paywalled websites + $token = new UsernamePasswordToken( + $entityUser, + 'main', + $entityUser->getRoles() + ); + + $this->tokenStorage->setToken($token); + $user = $this->tokenStorage->getToken()->getUser(); + \assert($user instanceof User); + + $url = $input->getArgument('url'); + + $existingEntry = $this->entityManager + ->getRepository(Entry::class) + ->findByUrlAndUserId($url, $user->getId()); + + if (false !== $existingEntry) { + $output->writeln(\sprintf('The URL %s is already in user’s entries.', $url)); + + return 1; + } + + $entry = new Entry($user); + + try { + $this->contentProxy->updateEntry($entry, $url); + } catch (\Exception $e) { + $output->writeln(\sprintf('Error trying to import the URL %s: %s.', $url, $e->getMessage())); + + return 1; + } + + if ($input->getOption('markAsRead')) { + $entry->updateArchived(true); + } + + $this->entityManager->persist($entry); + + $tags = explode(',', $input->getArgument('tags')); + if (\count($tags) > 1) { + $this->tagsAssigner->assignTagsToEntry( + $entry, + $tags, + $this->entityManager->getUnitOfWork()->getScheduledEntityInsertions() + ); + } + + $this->entityManager->flush(); + + $output->writeln(\sprintf('URL %s successfully imported.', $url)); + + return 0; + } +} diff --git a/tests/Command/Import/UrlCommandTest.php b/tests/Command/Import/UrlCommandTest.php new file mode 100644 index 000000000..c71c69ba7 --- /dev/null +++ b/tests/Command/Import/UrlCommandTest.php @@ -0,0 +1,99 @@ +expectException(RuntimeException::class); + $this->expectExceptionMessage('Not enough arguments'); + + $application = new Application($this->getTestClient()->getKernel()); + + $command = $application->find('wallabag:import:url'); + + $tester = new CommandTester($command); + $tester->execute([]); + } + + public function testRunUrlCommandWithWrongUsername() + { + $this->expectException(NoResultException::class); + + $application = new Application($this->getTestClient()->getKernel()); + + $command = $application->find('wallabag:import:url'); + + $tester = new CommandTester($command); + $tester->execute([ + 'username' => 'random', + 'url' => $this->url, + ]); + } + + public function testRunUrlCommand() + { + $application = new Application($this->getTestClient()->getKernel()); + + $command = $application->find('wallabag:import:url'); + + $tester = new CommandTester($command); + $tester->execute([ + 'username' => 'admin', + 'url' => $this->url, + ]); + + $this->assertStringContainsString('successfully imported', $tester->getDisplay()); + } + + public function testRunUrlCommandWithTags() + { + $application = new Application($this->getTestClient()->getKernel()); + + $command = $application->find('wallabag:import:url'); + + $tester = new CommandTester($command); + $tester->execute([ + 'username' => 'admin', + 'url' => $this->url, + 'tags' => 'sport, football', + ]); + + $this->assertStringContainsString('successfully imported', $tester->getDisplay()); + + $client = $this->getTestClient(); + $em = $client->getContainer()->get(EntityManagerInterface::class); + $entry = $em->getRepository(Entry::class)->findByUrlAndUserId($this->url, $this->getLoggedInUserId()); + $this->assertContains('football', $entry->getTagsLabel()); + $this->assertNotContains('basketball', $entry->getTagsLabel()); + } + + public function testRunUrlCommandWithUserId() + { + $this->logInAs('admin'); + + $application = new Application($this->getTestClient()->getKernel()); + + $command = $application->find('wallabag:import:url'); + + $tester = new CommandTester($command); + $tester->execute([ + 'username' => $this->getLoggedInUserId(), + 'url' => $this->url, + '--useUserId' => true, + ]); + + $this->assertStringContainsString('successfully imported', $tester->getDisplay()); + } +}