1
0
Fork 0
mirror of https://github.com/wallabag/wallabag.git synced 2025-06-22 16:26:01 +00:00

Merge pull request #8189 from ldidry/console-command-to-add-url

 — Add command to import URL for user from command-line
This commit is contained in:
Jérémy Benoist 2025-06-13 12:57:25 +02:00 committed by GitHub
commit e90bbaf540
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 217 additions and 0 deletions

View file

@ -0,0 +1,118 @@
<?php
namespace Wallabag\Command\Import;
use Doctrine\DBAL\Driver\Middleware;
use Doctrine\DBAL\Logging\Middleware as LoggingMiddleware;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\Config\Definition\Exception\Exception;
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;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
use Wallabag\Entity\Entry;
use Wallabag\Entity\User;
use Wallabag\Helper\ContentProxy;
use Wallabag\Helper\TagsAssigner;
use Wallabag\Repository\UserRepository;
class UrlCommand extends Command
{
public function __construct(
private readonly EntityManagerInterface $entityManager,
private readonly TokenStorageInterface $tokenStorage,
private readonly UserRepository $userRepository,
private readonly ContentProxy $contentProxy,
private readonly TagsAssigner $tagsAssigner,
) {
parent::__construct();
}
protected function configure()
{
$this
->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 users 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;
}
}

View file

@ -0,0 +1,99 @@
<?php
namespace Tests\Wallabag\Command\Import;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\NoResultException;
use Symfony\Bundle\FrameworkBundle\Console\Application;
use Symfony\Component\Console\Exception\RuntimeException;
use Symfony\Component\Console\Tester\CommandTester;
use Tests\Wallabag\WallabagTestCase;
use Wallabag\Entity\Entry;
class UrlCommandTest extends WallabagTestCase
{
private $url = 'https://www.20minutes.fr/sport/football/4158082-20250612-euro-espoirs-su-souffrir-ensemble-decimes-bleuets-retiennent-positif-apres-nul-face-portugal';
public function testRunUrlCommandWithoutArguments()
{
$this->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());
}
}