From f3c5662972bcd07df04e37e8e9f9411559083848 Mon Sep 17 00:00:00 2001 From: Jannik Stehle Date: Wed, 14 Apr 2021 11:43:02 +0200 Subject: [PATCH 1/7] Add command to repair notifications and properly handle mail sending with relative urls --- appinfo/info.xml | 1 + lib/Command/RepairNotifications.php | 114 ++++++++++++++++++++++++++++ lib/Mailer/NotificationMailer.php | 12 ++- 3 files changed, 126 insertions(+), 1 deletion(-) create mode 100644 lib/Command/RepairNotifications.php diff --git a/appinfo/info.xml b/appinfo/info.xml index a4990bd9..05fe7ddf 100644 --- a/appinfo/info.xml +++ b/appinfo/info.xml @@ -24,6 +24,7 @@ OCA\Notifications\Command\Generate + OCA\Notifications\Command\RepairNotifications OCA\Notifications\Panels\Personal\NotificationsPanel diff --git a/lib/Command/RepairNotifications.php b/lib/Command/RepairNotifications.php new file mode 100644 index 00000000..2f92a705 --- /dev/null +++ b/lib/Command/RepairNotifications.php @@ -0,0 +1,114 @@ + + * + */ + +namespace OCA\Notifications\Command; + +use OCP\IDBConnection; +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 RepairNotifications extends Command { + + /** @var IDBConnection */ + protected $connection; + + private static $availableSubjects = [ + 'relativeLinks' + ]; + + /** + * @param IDBConnection $connection + */ + public function __construct(IDBConnection $connection) { + parent::__construct(); + $this->connection = $connection; + } + + protected function configure() { + $this + ->setName('notifications:repairNotifications') + ->setDescription('Repair existing notifications') + ->addArgument('subject', InputArgument::REQUIRED, 'Subject to repair') + ; + } + + /** + * @param InputInterface $input + * @param OutputInterface $output + * @return false|int + */ + protected function execute(InputInterface $input, OutputInterface $output) { + $subject = $input->getArgument('subject'); + + if (!\in_array($subject, self::$availableSubjects)) { + throw new \LogicException('Invalid subject'); + } + + $sql = $this->connection->getQueryBuilder(); + $sql->select(['notification_id', 'link', 'actions']) + ->from('notifications') + ->where($sql->expr()->like('link', $sql->createPositionalParameter('http%'))) + ->orWhere($sql->expr()->like('actions', $sql->createPositionalParameter('%"link":"http%'))); + + $result = $sql->execute()->fetchAll(); + + if (!$result) { + $output->writeln('No notifications found to repair.'); + return false; + } + + $output->writeln(\sprintf('%s notification(s) found to repair', \count($result))); + + foreach ($result as $row) { + $output->writeln(\sprintf('Repairing notification with ID %s...', $row['notification_id'])); + + $sql = $this->connection->getQueryBuilder(); + $sql->update('notifications') + ->where($sql->expr()->eq('notification_id', $sql->createNamedParameter($row['notification_id']))); + + $linkUrlComponents = \parse_url($row['link']); + if (\array_key_exists('scheme', $linkUrlComponents)) { + $newLink = \parse_url($row['link'], PHP_URL_PATH); + $sql->set('link', $sql->createNamedParameter($newLink)); + } + + if (\strpos($row['actions'], 'http') !== false) { + $actions = \json_decode($row['actions'], true); + + foreach ($actions as &$action) { + $actionUrlComponents = \parse_url($action['link']); + if (\array_key_exists('scheme', $actionUrlComponents)) { + $action['link'] = \parse_url($action['link'], PHP_URL_PATH); + } + } + + $sql->set('actions', $sql->createNamedParameter(\json_encode($actions))); + } + + $sql->execute(); + } + + $output->writeln('Done'); + return 0; + } +} diff --git a/lib/Mailer/NotificationMailer.php b/lib/Mailer/NotificationMailer.php index c5b01a36..11f99788 100644 --- a/lib/Mailer/NotificationMailer.php +++ b/lib/Mailer/NotificationMailer.php @@ -26,6 +26,7 @@ use OCP\Mail\IMailer; use OCP\Template; use OCA\Notifications\Configuration\OptionsStorage; +use OCP\IURLGenerator; /** * The class will focus on sending notifications via email. In addition, some email-related @@ -41,10 +42,14 @@ class NotificationMailer { /** @var OptionsStorage */ private $optionsStorage; - public function __construct(IManager $manager, IMailer $mailer, OptionsStorage $optionsStorage) { + /** @var IURLGenerator */ + private $urlGenerator; + + public function __construct(IManager $manager, IMailer $mailer, OptionsStorage $optionsStorage, IURLGenerator $urlGenerator) { $this->manager = $manager; $this->mailer = $mailer; $this->optionsStorage = $optionsStorage; + $this->urlGenerator = $urlGenerator; } /** @@ -71,8 +76,13 @@ public function sendNotification(INotification $notification, $serverUrl, $email $emailMessage->setTo([$emailAddress]); $notificationLink = $notification->getLink(); + $components = \parse_url($notificationLink); + $linkIsAbsolute = \array_key_exists('host', $components); + if ($notificationLink === '') { $notificationLink = $serverUrl; + } elseif ($linkIsAbsolute !== true) { + $notificationLink = $this->urlGenerator->getAbsoluteURL($components['path']); } $parsedSubject = $notification->getParsedSubject(); From 164f9dacfec0d820382a8169a461753569c4b746 Mon Sep 17 00:00:00 2001 From: Jannik Stehle Date: Wed, 14 Apr 2021 11:47:08 +0200 Subject: [PATCH 2/7] Add authors and fix phan --- lib/Command/RepairNotifications.php | 3 ++- lib/Mailer/NotificationMailer.php | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/Command/RepairNotifications.php b/lib/Command/RepairNotifications.php index 2f92a705..b000281a 100644 --- a/lib/Command/RepairNotifications.php +++ b/lib/Command/RepairNotifications.php @@ -1,6 +1,7 @@ + * @author Jan Ackermann * * @copyright Copyright (c) 2021, ownCloud GmbH * @license AGPL-3.0 diff --git a/lib/Mailer/NotificationMailer.php b/lib/Mailer/NotificationMailer.php index 11f99788..3b116fa1 100644 --- a/lib/Mailer/NotificationMailer.php +++ b/lib/Mailer/NotificationMailer.php @@ -82,7 +82,7 @@ public function sendNotification(INotification $notification, $serverUrl, $email if ($notificationLink === '') { $notificationLink = $serverUrl; } elseif ($linkIsAbsolute !== true) { - $notificationLink = $this->urlGenerator->getAbsoluteURL($components['path']); + $notificationLink = $this->urlGenerator->getAbsoluteURL($notificationLink); } $parsedSubject = $notification->getParsedSubject(); From e364a147b0b4cd517e76c1f7d84fbaf483427926 Mon Sep 17 00:00:00 2001 From: Jannik Stehle Date: Wed, 14 Apr 2021 11:54:28 +0200 Subject: [PATCH 3/7] Fix unit tests --- tests/Unit/Mailer/NotificationMailerTest.php | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/tests/Unit/Mailer/NotificationMailerTest.php b/tests/Unit/Mailer/NotificationMailerTest.php index c9146e50..b53e8007 100644 --- a/tests/Unit/Mailer/NotificationMailerTest.php +++ b/tests/Unit/Mailer/NotificationMailerTest.php @@ -22,6 +22,7 @@ namespace OCA\Notifications\Tests\Unit\Mailer; use OC\Mail\Mailer; +use OCP\IURLGenerator; use OCP\Notification\IManager; use OCP\Notification\INotification; use OCP\L10N\IFactory; @@ -36,10 +37,10 @@ class NotificationMailerTest extends \Test\TestCase { private $mailer; /** @var OptionsStorage */ private $optionsStorage; - /** @var IFactory */ - private $l10nFactory; /** @var NotificationMailer*/ private $notificationMailer; + /** @var IURLGenerator*/ + private $urlGenerator; protected function setUp(): void { parent::setUp(); @@ -57,11 +58,11 @@ protected function setUp(): void { ->disableOriginalConstructor() ->getMock(); - $this->l10nFactory = $this->getMockBuilder(IFactory::class) + $this->urlGenerator = $this->getMockBuilder(IURLGenerator::class) ->disableOriginalConstructor() ->getMock(); - $this->notificationMailer = new NotificationMailer($this->manager, $this->mailer, $this->optionsStorage, $this->l10nFactory); + $this->notificationMailer = new NotificationMailer($this->manager, $this->mailer, $this->optionsStorage, $this->urlGenerator); } public function emailProvider() { @@ -106,7 +107,6 @@ public function testSendNotification() { return \vsprintf($text, $params); })); - $this->l10nFactory->method('get')->willReturn($mockedL10N); $this->mailer->expects($this->once())->method('send'); $this->optionsStorage->method('getOptions') @@ -144,7 +144,6 @@ public function testSendNotificationFailedRecipients() { return \vsprintf($text, $params); })); - $this->l10nFactory->method('get')->willReturn($mockedL10N); $this->mailer->expects($this->once()) ->method('send') ->willReturn(['userTest1']); From f5e00ccb0bf450188a2b7e2c53f20a923cf46f0e Mon Sep 17 00:00:00 2001 From: Jannik Stehle Date: Wed, 14 Apr 2021 12:19:19 +0200 Subject: [PATCH 4/7] Add unit tests for the newly added command --- lib/Command/RepairNotifications.php | 4 +- .../Unit/Command/RepairNotificationsTest.php | 83 +++++++++++++++++++ 2 files changed, 85 insertions(+), 2 deletions(-) create mode 100644 tests/Unit/Command/RepairNotificationsTest.php diff --git a/lib/Command/RepairNotifications.php b/lib/Command/RepairNotifications.php index b000281a..d9076c24 100644 --- a/lib/Command/RepairNotifications.php +++ b/lib/Command/RepairNotifications.php @@ -33,7 +33,7 @@ class RepairNotifications extends Command { /** @var IDBConnection */ protected $connection; - private static $availableSubjects = [ + public static $availableSubjects = [ 'relativeLinks' ]; @@ -75,7 +75,7 @@ protected function execute(InputInterface $input, OutputInterface $output) { if (!$result) { $output->writeln('No notifications found to repair.'); - return false; + return 0; } $output->writeln(\sprintf('%s notification(s) found to repair', \count($result))); diff --git a/tests/Unit/Command/RepairNotificationsTest.php b/tests/Unit/Command/RepairNotificationsTest.php new file mode 100644 index 00000000..a96d6ae3 --- /dev/null +++ b/tests/Unit/Command/RepairNotificationsTest.php @@ -0,0 +1,83 @@ + + * @author Jan Ackermann + * + * @copyright Copyright (c) 2021, ownCloud GmbH + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * 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, version 3, + * along with this program. If not, see + * + */ + +namespace OCA\Notifications\Tests\Unit\Command; + +use Doctrine\DBAL\Driver\Statement; +use OCA\Notifications\Command\Generate; +use OCA\Notifications\Command\RepairNotifications; +use OCA\Notifications\Tests\Unit\TestCase; +use OCP\DB\QueryBuilder\IExpressionBuilder; +use OCP\DB\QueryBuilder\IQueryBuilder; +use OCP\IDBConnection; +use Symfony\Component\Console\Tester\CommandTester; + +class RepairNotificationsTest extends TestCase { + + /** @var IDBConnection | \PHPUnit\Framework\MockObject\MockObject */ + protected $connection; + /** @var Generate */ + protected $command; + /** @var CommandTester */ + protected $tester; + + protected function setUp(): void { + parent::setUp(); + + $this->connection = $this->createMock(IDBConnection::class); + $this->command = new RepairNotifications($this->connection); + $this->tester = new CommandTester($this->command); + } + + public function testInvalidSubject() { + $this->expectException(\LogicException::class); + + $options = []; + $input = ['subject' => 'test']; + $this->tester->execute($input, $options); + } + + public function testRepairLinks() { + $options = []; + $input = ['subject' => RepairNotifications::$availableSubjects[0]]; + + $dbResult = [ + ['notification_id' => 1, 'link' => 'http://owncloud.com/test', 'actions' => '[]'] + ]; + + $exprBuilder = $this->createMock(IExpressionBuilder::class); + $statementMock = $this->createMock(Statement::class); + $statementMock->method('fetchAll')->willReturn($dbResult); + $qbMock = $this->createMock(IQueryBuilder::class); + $qbMock->method('select')->willReturnSelf(); + $qbMock->method('from')->willReturnSelf(); + $qbMock->method('update')->willReturnSelf(); + $qbMock->method('where')->willReturnSelf(); + $qbMock->method('expr')->willReturn($exprBuilder); + $qbMock->method('execute')->willReturn($statementMock); + + $this->connection->method('getQueryBuilder')->willReturn($qbMock); + + $response = $this->tester->execute($input, $options); + $this->assertEquals(0, $response); + } +} From 4dfa6c23ef51c45aebc9e76cb83a83bde81986d1 Mon Sep 17 00:00:00 2001 From: Jannik Stehle Date: Wed, 14 Apr 2021 14:44:48 +0200 Subject: [PATCH 5/7] Adjustments from the code review --- lib/Command/RepairNotifications.php | 63 ++++--------------- lib/Handler.php | 50 +++++++++++++++ lib/Mailer/NotificationMailer.php | 5 +- .../Unit/Command/RepairNotificationsTest.php | 35 +++-------- 4 files changed, 71 insertions(+), 82 deletions(-) diff --git a/lib/Command/RepairNotifications.php b/lib/Command/RepairNotifications.php index d9076c24..db9092cb 100644 --- a/lib/Command/RepairNotifications.php +++ b/lib/Command/RepairNotifications.php @@ -22,7 +22,7 @@ namespace OCA\Notifications\Command; -use OCP\IDBConnection; +use OCA\Notifications\Handler; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; @@ -30,19 +30,19 @@ class RepairNotifications extends Command { - /** @var IDBConnection */ - protected $connection; + /** @var Handler */ + protected $handler; public static $availableSubjects = [ 'relativeLinks' ]; /** - * @param IDBConnection $connection + * @param Handler $handler */ - public function __construct(IDBConnection $connection) { + public function __construct(Handler $handler) { parent::__construct(); - $this->connection = $connection; + $this->handler = $handler; } protected function configure() { @@ -56,60 +56,19 @@ protected function configure() { /** * @param InputInterface $input * @param OutputInterface $output - * @return false|int + * @return int */ protected function execute(InputInterface $input, OutputInterface $output) { $subject = $input->getArgument('subject'); if (!\in_array($subject, self::$availableSubjects)) { - throw new \LogicException('Invalid subject'); + $output->writeln('Invalid subject'); + return 1; } - $sql = $this->connection->getQueryBuilder(); - $sql->select(['notification_id', 'link', 'actions']) - ->from('notifications') - ->where($sql->expr()->like('link', $sql->createPositionalParameter('http%'))) - ->orWhere($sql->expr()->like('actions', $sql->createPositionalParameter('%"link":"http%'))); + $updatedNotificationsCount = $this->handler->removeBaseUrlFromAbsoluteLinks(); - $result = $sql->execute()->fetchAll(); - - if (!$result) { - $output->writeln('No notifications found to repair.'); - return 0; - } - - $output->writeln(\sprintf('%s notification(s) found to repair', \count($result))); - - foreach ($result as $row) { - $output->writeln(\sprintf('Repairing notification with ID %s...', $row['notification_id'])); - - $sql = $this->connection->getQueryBuilder(); - $sql->update('notifications') - ->where($sql->expr()->eq('notification_id', $sql->createNamedParameter($row['notification_id']))); - - $linkUrlComponents = \parse_url($row['link']); - if (\array_key_exists('scheme', $linkUrlComponents)) { - $newLink = \parse_url($row['link'], PHP_URL_PATH); - $sql->set('link', $sql->createNamedParameter($newLink)); - } - - if (\strpos($row['actions'], 'http') !== false) { - $actions = \json_decode($row['actions'], true); - - foreach ($actions as &$action) { - $actionUrlComponents = \parse_url($action['link']); - if (\array_key_exists('scheme', $actionUrlComponents)) { - $action['link'] = \parse_url($action['link'], PHP_URL_PATH); - } - } - - $sql->set('actions', $sql->createNamedParameter(\json_encode($actions))); - } - - $sql->execute(); - } - - $output->writeln('Done'); + $output->writeln("$updatedNotificationsCount notifications were updated"); return 0; } } diff --git a/lib/Handler.php b/lib/Handler.php index 2097be7e..c610bc32 100644 --- a/lib/Handler.php +++ b/lib/Handler.php @@ -310,4 +310,54 @@ protected function notificationFromRow(array $row) { return $notification; } + + /** + * Remove the base url from absolute links in the database. + * This affects the columns 'link' and 'action'. + * e.g: http://owncloud.com/test -> /test + * + * @return int number of updated notifications + */ + public function removeBaseUrlFromAbsoluteLinks() { + $sql = $this->connection->getQueryBuilder(); + $sql->select(['notification_id', 'link', 'actions']) + ->from('notifications') + ->where($sql->expr()->like('link', $sql->createPositionalParameter('http%'))) + ->orWhere($sql->expr()->like('actions', $sql->createPositionalParameter('%"link":"http%'))); + + $statement = $sql->execute(); + $counter = 0; + + while ($row = $statement->fetch()) { + $sql = $this->connection->getQueryBuilder(); + $sql->update('notifications') + ->where($sql->expr()->eq('notification_id', $sql->createNamedParameter($row['notification_id']))); + + $linkUrlComponents = \parse_url($row['link']); + if (isset($linkUrlComponents['scheme'])) { + $newLink = \parse_url($row['link'], PHP_URL_PATH); + $sql->set('link', $sql->createNamedParameter($newLink)); + } + + if (\strpos($row['actions'], 'http') !== false) { + $actions = \json_decode($row['actions'], true); + + foreach ($actions as $index => $action) { + $actionUrlComponents = \parse_url($action['link']); + if (isset($actionUrlComponents['scheme'])) { + $actions[$index]['link'] = \parse_url($action['link'], PHP_URL_PATH); + } + } + + $sql->set('actions', $sql->createNamedParameter(\json_encode($actions))); + } + + $counter++; + $sql->execute(); + } + + $statement->closeCursor(); + + return $counter; + } } diff --git a/lib/Mailer/NotificationMailer.php b/lib/Mailer/NotificationMailer.php index 3b116fa1..ca41372e 100644 --- a/lib/Mailer/NotificationMailer.php +++ b/lib/Mailer/NotificationMailer.php @@ -76,12 +76,11 @@ public function sendNotification(INotification $notification, $serverUrl, $email $emailMessage->setTo([$emailAddress]); $notificationLink = $notification->getLink(); - $components = \parse_url($notificationLink); - $linkIsAbsolute = \array_key_exists('host', $components); + $urlComponents = \parse_url($notificationLink); if ($notificationLink === '') { $notificationLink = $serverUrl; - } elseif ($linkIsAbsolute !== true) { + } elseif (!isset($urlComponents['host'])) { $notificationLink = $this->urlGenerator->getAbsoluteURL($notificationLink); } diff --git a/tests/Unit/Command/RepairNotificationsTest.php b/tests/Unit/Command/RepairNotificationsTest.php index a96d6ae3..7cf7528a 100644 --- a/tests/Unit/Command/RepairNotificationsTest.php +++ b/tests/Unit/Command/RepairNotificationsTest.php @@ -22,19 +22,16 @@ namespace OCA\Notifications\Tests\Unit\Command; -use Doctrine\DBAL\Driver\Statement; use OCA\Notifications\Command\Generate; use OCA\Notifications\Command\RepairNotifications; +use OCA\Notifications\Handler; use OCA\Notifications\Tests\Unit\TestCase; -use OCP\DB\QueryBuilder\IExpressionBuilder; -use OCP\DB\QueryBuilder\IQueryBuilder; -use OCP\IDBConnection; use Symfony\Component\Console\Tester\CommandTester; class RepairNotificationsTest extends TestCase { - /** @var IDBConnection | \PHPUnit\Framework\MockObject\MockObject */ - protected $connection; + /** @var Handler | \PHPUnit\Framework\MockObject\MockObject */ + protected $handler; /** @var Generate */ protected $command; /** @var CommandTester */ @@ -43,39 +40,23 @@ class RepairNotificationsTest extends TestCase { protected function setUp(): void { parent::setUp(); - $this->connection = $this->createMock(IDBConnection::class); - $this->command = new RepairNotifications($this->connection); + $this->handler = $this->createMock(Handler::class); + $this->command = new RepairNotifications($this->handler); $this->tester = new CommandTester($this->command); } public function testInvalidSubject() { - $this->expectException(\LogicException::class); - $options = []; $input = ['subject' => 'test']; - $this->tester->execute($input, $options); + $response = $this->tester->execute($input, $options); + $this->assertEquals(1, $response); } public function testRepairLinks() { $options = []; $input = ['subject' => RepairNotifications::$availableSubjects[0]]; - $dbResult = [ - ['notification_id' => 1, 'link' => 'http://owncloud.com/test', 'actions' => '[]'] - ]; - - $exprBuilder = $this->createMock(IExpressionBuilder::class); - $statementMock = $this->createMock(Statement::class); - $statementMock->method('fetchAll')->willReturn($dbResult); - $qbMock = $this->createMock(IQueryBuilder::class); - $qbMock->method('select')->willReturnSelf(); - $qbMock->method('from')->willReturnSelf(); - $qbMock->method('update')->willReturnSelf(); - $qbMock->method('where')->willReturnSelf(); - $qbMock->method('expr')->willReturn($exprBuilder); - $qbMock->method('execute')->willReturn($statementMock); - - $this->connection->method('getQueryBuilder')->willReturn($qbMock); + $this->handler->expects($this->once())->method('removeBaseUrlFromAbsoluteLinks'); $response = $this->tester->execute($input, $options); $this->assertEquals(0, $response); From 0bc956e1a57c7c6af4338bcb8971bb6e0555ba23 Mon Sep 17 00:00:00 2001 From: Jannik Stehle Date: Thu, 15 Apr 2021 08:58:07 +0200 Subject: [PATCH 6/7] Avoid parsing URLs multiple times --- lib/Handler.php | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/lib/Handler.php b/lib/Handler.php index c610bc32..b52ae027 100644 --- a/lib/Handler.php +++ b/lib/Handler.php @@ -335,8 +335,7 @@ public function removeBaseUrlFromAbsoluteLinks() { $linkUrlComponents = \parse_url($row['link']); if (isset($linkUrlComponents['scheme'])) { - $newLink = \parse_url($row['link'], PHP_URL_PATH); - $sql->set('link', $sql->createNamedParameter($newLink)); + $sql->set('link', $sql->createNamedParameter($linkUrlComponents['path'])); } if (\strpos($row['actions'], 'http') !== false) { @@ -345,7 +344,7 @@ public function removeBaseUrlFromAbsoluteLinks() { foreach ($actions as $index => $action) { $actionUrlComponents = \parse_url($action['link']); if (isset($actionUrlComponents['scheme'])) { - $actions[$index]['link'] = \parse_url($action['link'], PHP_URL_PATH); + $actions[$index]['link'] = $actionUrlComponents['path']; } } From d1c40b47a68bebdbeb3191fa21f4571e305a0039 Mon Sep 17 00:00:00 2001 From: Jannik Stehle Date: Thu, 15 Apr 2021 09:14:14 +0200 Subject: [PATCH 7/7] Fix phan --- lib/Handler.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/Handler.php b/lib/Handler.php index b52ae027..90c73376 100644 --- a/lib/Handler.php +++ b/lib/Handler.php @@ -334,7 +334,7 @@ public function removeBaseUrlFromAbsoluteLinks() { ->where($sql->expr()->eq('notification_id', $sql->createNamedParameter($row['notification_id']))); $linkUrlComponents = \parse_url($row['link']); - if (isset($linkUrlComponents['scheme'])) { + if (isset($linkUrlComponents['scheme'], $linkUrlComponents['path'])) { $sql->set('link', $sql->createNamedParameter($linkUrlComponents['path'])); } @@ -343,7 +343,7 @@ public function removeBaseUrlFromAbsoluteLinks() { foreach ($actions as $index => $action) { $actionUrlComponents = \parse_url($action['link']); - if (isset($actionUrlComponents['scheme'])) { + if (isset($actionUrlComponents['scheme'], $actionUrlComponents['path'])) { $actions[$index]['link'] = $actionUrlComponents['path']; } }