diff --git a/app/config/services.yml b/app/config/services.yml index 3cca8b30f..f0acf08a2 100644 --- a/app/config/services.yml +++ b/app/config/services.yml @@ -31,6 +31,7 @@ services: $supportUrl: '@=service(''craue_config'').get(''wallabag_support_url'')' $fonts: '%wallabag.fonts%' $defaultIgnoreOriginInstanceRules: '%wallabag.default_ignore_origin_instance_rules%' + $entryDeletionExpirationDays: '%wallabag.entry_deletion_expiration_days%' Wallabag\: resource: '../../src/*' @@ -253,6 +254,11 @@ services: Wallabag\Helper\DownloadImages: arguments: $baseFolder: "%kernel.project_dir%/web/assets/images" + + + Wallabag\Helper\EntryDeletionExpirationConfig: + arguments: + $defaultExpirationDays: '%wallabag.entry_deletion_expiration_days%' Wallabag\Command\InstallCommand: arguments: diff --git a/app/config/wallabag.yml b/app/config/wallabag.yml index 02106a33b..bef3ae4c0 100644 --- a/app/config/wallabag.yml +++ b/app/config/wallabag.yml @@ -32,6 +32,7 @@ parameters: wallabag.action_mark_as_read: 1 wallabag.list_mode: 0 wallabag.display_thumbnails: 1 + wallabag.entry_deletion_expiration_days: 90 wallabag.fetching_error_message_title: 'No title found' wallabag.fetching_error_message: | wallabag can't retrieve contents for this article. Please troubleshoot this issue. diff --git a/src/Command/PurgeEntryDeletionsCommand.php b/src/Command/PurgeEntryDeletionsCommand.php index c9a416437..4d7566eb1 100644 --- a/src/Command/PurgeEntryDeletionsCommand.php +++ b/src/Command/PurgeEntryDeletionsCommand.php @@ -8,6 +8,7 @@ use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Style\SymfonyStyle; +use Wallabag\Helper\EntryDeletionExpirationConfig; use Wallabag\Repository\EntryDeletionRepository; class PurgeEntryDeletionsCommand extends Command @@ -18,6 +19,7 @@ class PurgeEntryDeletionsCommand extends Command public function __construct( private readonly EntityManagerInterface $entityManager, private readonly EntryDeletionRepository $entryDeletionRepository, + private readonly EntryDeletionExpirationConfig $expirationConfig, ) { parent::__construct(); } @@ -25,13 +27,6 @@ class PurgeEntryDeletionsCommand extends Command protected function configure() { $this - ->addOption( - 'older-than', - null, - InputOption::VALUE_REQUIRED, - 'Purge records older than this date (format: YYYY-MM-DD)', - '-30 days' - ) ->addOption( 'dry-run', null, @@ -44,18 +39,10 @@ class PurgeEntryDeletionsCommand extends Command { $io = new SymfonyStyle($input, $output); - $olderThan = $input->getOption('older-than'); $dryRun = (bool) $input->getOption('dry-run'); - try { - $date = new \DateTime($olderThan); - } catch (\Exception $e) { - $io->error(sprintf('Invalid date format: %s.\nYou can use any format supported by PHP (e.g. YYYY-MM-DD).', $olderThan)); - - return 1; - } - - $count = $this->entryDeletionRepository->countAllBefore($date); + $cutoff = $this->expirationConfig->getCutoffDate(); + $count = $this->entryDeletionRepository->countAllBefore($cutoff); if ($dryRun) { $io->text('Dry run mode enabled (no records will be deleted)'); @@ -78,14 +65,14 @@ class PurgeEntryDeletionsCommand extends Command $confirmMessage = sprintf( 'Are you sure you want to delete records older than %s? (count: %d)', - $date->format('Y-m-d'), + $cutoff->format('Y-m-d'), $count, ); if (!$io->confirm($confirmMessage)) { return 0; } - $this->entryDeletionRepository->deleteAllBefore($date); + $this->entryDeletionRepository->deleteAllBefore($cutoff); $io->success(sprintf('Successfully deleted %d records.', $count)); diff --git a/src/Helper/EntryDeletionExpirationConfig.php b/src/Helper/EntryDeletionExpirationConfig.php new file mode 100644 index 000000000..e6d764b24 --- /dev/null +++ b/src/Helper/EntryDeletionExpirationConfig.php @@ -0,0 +1,33 @@ +expirationDays = $defaultExpirationDays; + } + + public function getCutoffDate(): \DateTime + { + return new \DateTime("-{$this->expirationDays} days"); + } + + public function getExpirationDays(): int + { + return $this->expirationDays; + } + + /** + * Override the expiration days parameter. + * This is mostly useful for testing purposes and should not be used in other contexts. + */ + public function setExpirationDays(int $days): self + { + $this->expirationDays = $days; + return $this; + } +} diff --git a/tests/Command/PurgeEntryDeletionsCommandTest.php b/tests/Command/PurgeEntryDeletionsCommandTest.php index e3a07ac96..3fa8872f5 100644 --- a/tests/Command/PurgeEntryDeletionsCommandTest.php +++ b/tests/Command/PurgeEntryDeletionsCommandTest.php @@ -2,10 +2,13 @@ namespace Tests\Wallabag\Command; +use Doctrine\ORM\EntityManagerInterface; use Symfony\Bundle\FrameworkBundle\Console\Application; +use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase; use Symfony\Component\Console\Tester\CommandTester; -use Tests\Wallabag\WallabagTestCase; use Wallabag\Entity\EntryDeletion; +use Wallabag\Helper\EntryDeletionExpirationConfig; +use Wallabag\Repository\EntryDeletionRepository; /** * Test the purge entry deletions command. @@ -15,59 +18,67 @@ use Wallabag\Entity\EntryDeletion; * - Admin user: 1 deletion from 1 day ago (entry_id: 1001) * - Bob user: 1 deletion from 3 days ago (entry_id: 1003) */ -class PurgeEntryDeletionsCommandTest extends WallabagTestCase +class PurgeEntryDeletionsCommandTest extends KernelTestCase { + private EntryDeletionExpirationConfig $expirationConfig; + private EntryDeletionRepository $entryDeletionRepository; + + protected function setUp(): void + { + self::bootKernel(); + + $this->expirationConfig = self::getContainer()->get(EntryDeletionExpirationConfig::class); + $this->expirationConfig->setExpirationDays(2); + + $em = self::getContainer()->get(EntityManagerInterface::class); + $this->entryDeletionRepository = $em->getRepository(EntryDeletion::class); + } + public function testRunPurgeEntryDeletionsCommandWithDryRun() { - $application = new Application($this->getTestClient()->getKernel()); + $application = new Application(self::$kernel); $command = $application->find('wallabag:purge-entry-deletions'); - $dateStr = '-2 days'; $tester = new CommandTester($command); $tester->execute([ - '--older-than' => $dateStr, '--dry-run' => true, ]); $this->assertStringContainsString('Dry run mode enabled', $tester->getDisplay()); $this->assertSame(0, $tester->getStatusCode()); - $em = $this->getEntityManager(); - $count = $em->getRepository(EntryDeletion::class)->countAllBefore(new \DateTime($dateStr)); + $count = $this->entryDeletionRepository->countAllBefore($this->expirationConfig->getCutoffDate()); $this->assertSame(2, $count); } public function testRunPurgeEntryDeletionsCommand() { - $application = new Application($this->getTestClient()->getKernel()); + $application = new Application(self::$kernel); $command = $application->find('wallabag:purge-entry-deletions'); - $dateStr = '-2 days'; $tester = new CommandTester($command); $tester->setInputs(['yes']); // confirm deletion - $tester->execute(['--older-than' => $dateStr]); + $tester->execute([]); $this->assertStringContainsString('Successfully deleted 2 records', $tester->getDisplay()); $this->assertSame(0, $tester->getStatusCode()); - $em = $this->getEntityManager(); - - $count = $em->getRepository(EntryDeletion::class)->countAllBefore(new \DateTime($dateStr)); + $count = $this->entryDeletionRepository->countAllBefore($this->expirationConfig->getCutoffDate()); $this->assertSame(0, $count); - $count = $em->getRepository(EntryDeletion::class)->countAllBefore(new \DateTime('now')); - $this->assertSame(1, $count); + $countAll = $this->entryDeletionRepository->countAllBefore(new \DateTime('now')); + $this->assertSame(1, $countAll); } public function testRunPurgeEntryDeletionsCommandWithNoRecords() { - $application = new Application($this->getTestClient()->getKernel()); + $this->expirationConfig->setExpirationDays(10); + + $application = new Application(self::$kernel); $command = $application->find('wallabag:purge-entry-deletions'); $tester = new CommandTester($command); - $tester->execute([ - '--older-than' => '-1 year', - ]); + $tester->execute([]); $this->assertStringContainsString('No entry deletion records found', $tester->getDisplay()); $this->assertSame(0, $tester->getStatusCode());