diff --git a/appinfo/info.xml b/appinfo/info.xml
index e4702d18..7597ad76 100644
--- a/appinfo/info.xml
+++ b/appinfo/info.xml
@@ -52,6 +52,7 @@
OCA\AnnouncementCenter\Command\Announce
OCA\AnnouncementCenter\Command\AnnouncementList
OCA\AnnouncementCenter\Command\AnnouncementDelete
+ OCA\AnnouncementCenter\Command\RemoveNotifications
diff --git a/lib/Command/RemoveNotifications.php b/lib/Command/RemoveNotifications.php
new file mode 100644
index 00000000..a1945cbb
--- /dev/null
+++ b/lib/Command/RemoveNotifications.php
@@ -0,0 +1,79 @@
+
+ *
+ * @author Marvin Winkens
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ *
+ */
+namespace OCA\AnnouncementCenter\Command;
+
+use InvalidArgumentException;
+use OCA\AnnouncementCenter\Manager;
+use OCA\AnnouncementCenter\Model\AnnouncementDoesNotExistException;
+use Psr\Log\LoggerInterface;
+use Symfony\Component\Console\Command\Command;
+use Symfony\Component\Console\Input\InputArgument;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Output\OutputInterface;
+
+class RemoveNotifications extends Command {
+ public function __construct(
+ protected Manager $manager,
+ protected LoggerInterface $logger,
+ ) {
+ parent::__construct();
+ }
+
+ protected function configure(): void {
+ $this
+ ->setName('announcementcenter:remove-notifications') # others use minus sign as well
+ ->setDescription('Remove notifications of announcement by id')
+ ->addArgument(
+ 'id',
+ InputArgument::REQUIRED,
+ 'Id of announcement to remove notifications from',
+ );
+ }
+
+ protected function execute(InputInterface $input, OutputInterface $output): int {
+ try {
+ $removeNotId = $this->parseId($input->getArgument('id'));
+ $this->manager->getAnnouncement($removeNotId, true);
+ $this->manager->removeNotifications($removeNotId);
+ } catch (AnnouncementDoesNotExistException) {
+ $output->writeln('Announcement with #' . $removeNotId . ' does not exist!');
+ return 1;
+ } catch (InvalidArgumentException $e) {
+ $output->writeln($e->getMessage());
+ return 1;
+ }
+ $output->writeln('Successfully removed notifications from accouncement #' . $removeNotId);
+ $this->logger->info('Admin removed notifications from announcement #' . $removeNotId . ' over CLI');
+ return 0;
+ }
+
+ private function parseId(mixed $value) {
+ if (is_numeric($value)) {
+ return (int)$value;
+ }
+ throw new InvalidArgumentException('Id "' . $value . '" is not an integer');
+ }
+}
diff --git a/tests/Command/RemoveNotificationsTest.php b/tests/Command/RemoveNotificationsTest.php
new file mode 100644
index 00000000..6fd3d2e7
--- /dev/null
+++ b/tests/Command/RemoveNotificationsTest.php
@@ -0,0 +1,116 @@
+
+ *
+ * @author Marvin Winkens
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ *
+ */
+namespace OCA\AnnouncementCenter\Tests\Command;
+
+use OCA\AnnouncementCenter\Command\RemoveNotifications;
+use OCA\AnnouncementCenter\Manager;
+use OCA\AnnouncementCenter\Model\AnnouncementDoesNotExistException;
+use OCA\AnnouncementCenter\Tests\TestCase;
+use PHPUnit\Framework\MockObject\MockObject;
+use Psr\Log\LoggerInterface;
+use Symfony\Component\Console\Command\Command;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Output\OutputInterface;
+
+class RemoveNotificationsTest extends TestCase {
+ protected Manager|MockObject $manager;
+ protected LoggerInterface|MockObject $logger;
+ protected Command $removeNotificationsCommand;
+ protected InputInterface|MockObject $input;
+ protected OutputInterface|MockObject $output;
+
+ protected function setUp(): void {
+ parent::setUp();
+
+ $this->manager = $this->createMock(Manager::class);
+ $this->logger = $this->createMock(LoggerInterface::class);
+
+ $this->input = $this->getMockBuilder(InputInterface::class)
+ ->setMethods([
+ 'getArgument',
+ 'getOption',
+ 'getFirstArgument',
+ 'hasParameterOption',
+ 'getParameterOption',
+ 'bind',
+ 'validate',
+ 'getArguments',
+ 'setArgument',
+ 'hasArgument',
+ 'getOptions',
+ 'isInteractive',
+ 'hasOption',
+ 'setOption',
+ 'setInteractive',
+ ])
+ ->getMock();
+ $this->output = $this->createMock(OutputInterface::class);
+
+ $this->removeNotificationsCommand = new RemoveNotifications(
+ $this->manager,
+ $this->logger,
+ );
+ }
+
+ public function testRemoveNotificationsSuccessfully() {
+ $this->input->expects($this->once())
+ ->method('getArgument')
+ ->with('id')
+ ->willReturn(42);
+ $this->manager->expects($this->once())
+ ->method('removeNotifications');
+ $this->manager->expects($this->once())
+ ->method('getAnnouncement');
+ $this->output->expects($this->atLeastOnce())
+ ->method('writeln');
+ $this->logger->expects($this->atLeastOnce())
+ ->method('info');
+ $result = self::invokePrivate($this->removeNotificationsCommand, 'execute', [$this->input, $this->output]);
+ self::assertEquals(0, $result);
+ }
+
+ public function testRemoveNotificationsInvalidId() {
+ $this->input->expects($this->once())
+ ->method('getArgument')
+ ->with('id')
+ ->willReturn('invalid');
+ $result = self::invokePrivate($this->removeNotificationsCommand, 'execute', [$this->input, $this->output]);
+ self::assertEquals(true, $result > 0);
+ }
+
+ public function testRemoveNotificationsDoesNotExist() {
+ $this->input->expects($this->once())
+ ->method('getArgument')
+ ->with('id')
+ ->willReturn(42);
+ $this->manager->expects($this->once())
+ ->method('getAnnouncement')
+ ->willThrowException(new AnnouncementDoesNotExistException('message'));
+ $this->output->expects($this->atLeastOnce())
+ ->method('writeln');
+ $result = self::invokePrivate($this->removeNotificationsCommand, 'execute', [$this->input, $this->output]);
+ self::assertEquals(true, $result > 0);
+ }
+}