From 7bddcc091d5fe0f5e01325e16524d44fe8c1fb74 Mon Sep 17 00:00:00 2001
From: Thomas Citharel
Date: Sat, 16 Mar 2019 16:19:25 +0100
Subject: [PATCH 01/10] Support event reminders (email and notifications)
Signed-off-by: Thomas Citharel
---
apps/dav/appinfo/app.php | 3 +
apps/dav/appinfo/info.xml | 1 +
.../composer/composer/autoload_classmap.php | 11 +
.../dav/composer/composer/autoload_static.php | 11 +
apps/dav/js/settings-admin-caldav.js | 6 +
apps/dav/lib/AppInfo/Application.php | 58 +++-
.../lib/BackgroundJob/EventReminderJob.php | 59 ++++
apps/dav/lib/CalDAV/CalDavBackend.php | 1 -
.../Reminder/AbstractNotificationProvider.php | 208 ++++++++++++
apps/dav/lib/CalDAV/Reminder/Backend.php | 139 ++++++++
.../NotificationProvider/EmailProvider.php | 157 +++++++++
.../ProviderNotAvailableException.php | 39 +++
.../NotificationProvider/PushProvider.php | 101 ++++++
.../Reminder/NotificationProviderManager.php | 59 ++++
.../NotificationTypeDoesNotExistException.php | 39 +++
apps/dav/lib/CalDAV/Reminder/Notifier.php | 143 ++++++++
.../lib/CalDAV/Reminder/ReminderService.php | 185 +++++++++++
.../Version1007Date20181005133326.php | 82 +++++
apps/dav/lib/Settings/CalDAVSettings.php | 1 +
apps/dav/templates/settings-admin-caldav.php | 21 ++
.../BackgroundJob/EventReminderJobTest.php | 68 ++++
.../AbstractNotificationProviderTest.php | 87 +++++
.../unit/CalDAV/Reminder/BackendTest.php | 313 ++++++++++++++++++
.../EmailProviderTest.php | 226 +++++++++++++
.../NotificationProvider/PushProviderTest.php | 139 ++++++++
.../NotificationProviderManagerTest.php | 100 ++++++
.../unit/CalDAV/Reminder/NotifierTest.php | 184 ++++++++++
.../CalDAV/Reminder/ReminderServiceTest.php | 276 +++++++++++++++
28 files changed, 2711 insertions(+), 6 deletions(-)
create mode 100644 apps/dav/lib/BackgroundJob/EventReminderJob.php
create mode 100644 apps/dav/lib/CalDAV/Reminder/AbstractNotificationProvider.php
create mode 100644 apps/dav/lib/CalDAV/Reminder/Backend.php
create mode 100644 apps/dav/lib/CalDAV/Reminder/NotificationProvider/EmailProvider.php
create mode 100644 apps/dav/lib/CalDAV/Reminder/NotificationProvider/ProviderNotAvailableException.php
create mode 100644 apps/dav/lib/CalDAV/Reminder/NotificationProvider/PushProvider.php
create mode 100644 apps/dav/lib/CalDAV/Reminder/NotificationProviderManager.php
create mode 100644 apps/dav/lib/CalDAV/Reminder/NotificationTypeDoesNotExistException.php
create mode 100644 apps/dav/lib/CalDAV/Reminder/Notifier.php
create mode 100644 apps/dav/lib/CalDAV/Reminder/ReminderService.php
create mode 100644 apps/dav/lib/Migration/Version1007Date20181005133326.php
create mode 100644 apps/dav/tests/unit/BackgroundJob/EventReminderJobTest.php
create mode 100644 apps/dav/tests/unit/CalDAV/Reminder/AbstractNotificationProviderTest.php
create mode 100644 apps/dav/tests/unit/CalDAV/Reminder/BackendTest.php
create mode 100644 apps/dav/tests/unit/CalDAV/Reminder/NotificationProvider/EmailProviderTest.php
create mode 100644 apps/dav/tests/unit/CalDAV/Reminder/NotificationProvider/PushProviderTest.php
create mode 100644 apps/dav/tests/unit/CalDAV/Reminder/NotificationProviderManagerTest.php
create mode 100644 apps/dav/tests/unit/CalDAV/Reminder/NotifierTest.php
create mode 100644 apps/dav/tests/unit/CalDAV/Reminder/ReminderServiceTest.php
diff --git a/apps/dav/appinfo/app.php b/apps/dav/appinfo/app.php
index 70d01088be..dd9e0e9c09 100644
--- a/apps/dav/appinfo/app.php
+++ b/apps/dav/appinfo/app.php
@@ -108,3 +108,6 @@ $calendarManager->register(function() use ($calendarManager, $app) {
$app->setupCalendarProvider($calendarManager, $user->getUID());
}
});
+
+$app->registerNotifier();
+$app->registerCalendarReminders();
diff --git a/apps/dav/appinfo/info.xml b/apps/dav/appinfo/info.xml
index dc90ac5818..91617e9002 100644
--- a/apps/dav/appinfo/info.xml
+++ b/apps/dav/appinfo/info.xml
@@ -23,6 +23,7 @@
OCA\DAV\BackgroundJob\CleanupDirectLinksJobOCA\DAV\BackgroundJob\UpdateCalendarResourcesRoomsBackgroundJobOCA\DAV\BackgroundJob\CleanupInvitationTokenJob
+ OCA\DAV\BackgroundJob\EventReminderJob
diff --git a/apps/dav/composer/composer/autoload_classmap.php b/apps/dav/composer/composer/autoload_classmap.php
index 694231eebd..33977f3166 100644
--- a/apps/dav/composer/composer/autoload_classmap.php
+++ b/apps/dav/composer/composer/autoload_classmap.php
@@ -13,6 +13,7 @@ return array(
'OCA\\DAV\\Avatars\\RootCollection' => $baseDir . '/../lib/Avatars/RootCollection.php',
'OCA\\DAV\\BackgroundJob\\CleanupDirectLinksJob' => $baseDir . '/../lib/BackgroundJob/CleanupDirectLinksJob.php',
'OCA\\DAV\\BackgroundJob\\CleanupInvitationTokenJob' => $baseDir . '/../lib/BackgroundJob/CleanupInvitationTokenJob.php',
+ 'OCA\\DAV\\BackgroundJob\\EventReminderJob' => $baseDir . '/../lib/BackgroundJob/EventReminderJob.php',
'OCA\\DAV\\BackgroundJob\\GenerateBirthdayCalendarBackgroundJob' => $baseDir . '/../lib/BackgroundJob/GenerateBirthdayCalendarBackgroundJob.php',
'OCA\\DAV\\BackgroundJob\\RefreshWebcalJob' => $baseDir . '/../lib/BackgroundJob/RefreshWebcalJob.php',
'OCA\\DAV\\BackgroundJob\\RegisterRegenerateBirthdayCalendars' => $baseDir . '/../lib/BackgroundJob/RegisterRegenerateBirthdayCalendars.php',
@@ -51,6 +52,15 @@ return array(
'OCA\\DAV\\CalDAV\\PublicCalendarRoot' => $baseDir . '/../lib/CalDAV/PublicCalendarRoot.php',
'OCA\\DAV\\CalDAV\\Publishing\\PublishPlugin' => $baseDir . '/../lib/CalDAV/Publishing/PublishPlugin.php',
'OCA\\DAV\\CalDAV\\Publishing\\Xml\\Publisher' => $baseDir . '/../lib/CalDAV/Publishing/Xml/Publisher.php',
+ 'OCA\\DAV\\CalDAV\\Reminder\\AbstractNotificationProvider' => $baseDir . '/../lib/CalDAV/Reminder/AbstractNotificationProvider.php',
+ 'OCA\\DAV\\CalDAV\\Reminder\\Backend' => $baseDir . '/../lib/CalDAV/Reminder/Backend.php',
+ 'OCA\\DAV\\CalDAV\\Reminder\\NotificationProviderManager' => $baseDir . '/../lib/CalDAV/Reminder/NotificationProviderManager.php',
+ 'OCA\\DAV\\CalDAV\\Reminder\\NotificationProvider\\EmailProvider' => $baseDir . '/../lib/CalDAV/Reminder/NotificationProvider/EmailProvider.php',
+ 'OCA\\DAV\\CalDAV\\Reminder\\NotificationProvider\\ProviderNotAvailableException' => $baseDir . '/../lib/CalDAV/Reminder/NotificationProvider/ProviderNotAvailableException.php',
+ 'OCA\\DAV\\CalDAV\\Reminder\\NotificationProvider\\PushProvider' => $baseDir . '/../lib/CalDAV/Reminder/NotificationProvider/PushProvider.php',
+ 'OCA\\DAV\\CalDAV\\Reminder\\NotificationTypeDoesNotExistException' => $baseDir . '/../lib/CalDAV/Reminder/NotificationTypeDoesNotExistException.php',
+ 'OCA\\DAV\\CalDAV\\Reminder\\Notifier' => $baseDir . '/../lib/CalDAV/Reminder/Notifier.php',
+ 'OCA\\DAV\\CalDAV\\Reminder\\ReminderService' => $baseDir . '/../lib/CalDAV/Reminder/ReminderService.php',
'OCA\\DAV\\CalDAV\\ResourceBooking\\AbstractPrincipalBackend' => $baseDir . '/../lib/CalDAV/ResourceBooking/AbstractPrincipalBackend.php',
'OCA\\DAV\\CalDAV\\ResourceBooking\\ResourcePrincipalBackend' => $baseDir . '/../lib/CalDAV/ResourceBooking/ResourcePrincipalBackend.php',
'OCA\\DAV\\CalDAV\\ResourceBooking\\RoomPrincipalBackend' => $baseDir . '/../lib/CalDAV/ResourceBooking/RoomPrincipalBackend.php',
@@ -176,6 +186,7 @@ return array(
'OCA\\DAV\\Migration\\Version1005Date20180530124431' => $baseDir . '/../lib/Migration/Version1005Date20180530124431.php',
'OCA\\DAV\\Migration\\Version1006Date20180619154313' => $baseDir . '/../lib/Migration/Version1006Date20180619154313.php',
'OCA\\DAV\\Migration\\Version1006Date20180628111625' => $baseDir . '/../lib/Migration/Version1006Date20180628111625.php',
+ 'OCA\\DAV\\Migration\\Version1007Date20181005133326' => $baseDir . '/../lib/Migration/Version1007Date20181005133326.php',
'OCA\\DAV\\Migration\\Version1008Date20181030113700' => $baseDir . '/../lib/Migration/Version1008Date20181030113700.php',
'OCA\\DAV\\Migration\\Version1008Date20181105104826' => $baseDir . '/../lib/Migration/Version1008Date20181105104826.php',
'OCA\\DAV\\Migration\\Version1008Date20181105104833' => $baseDir . '/../lib/Migration/Version1008Date20181105104833.php',
diff --git a/apps/dav/composer/composer/autoload_static.php b/apps/dav/composer/composer/autoload_static.php
index 6f10491607..8ca83bb8b7 100644
--- a/apps/dav/composer/composer/autoload_static.php
+++ b/apps/dav/composer/composer/autoload_static.php
@@ -28,6 +28,7 @@ class ComposerStaticInitDAV
'OCA\\DAV\\Avatars\\RootCollection' => __DIR__ . '/..' . '/../lib/Avatars/RootCollection.php',
'OCA\\DAV\\BackgroundJob\\CleanupDirectLinksJob' => __DIR__ . '/..' . '/../lib/BackgroundJob/CleanupDirectLinksJob.php',
'OCA\\DAV\\BackgroundJob\\CleanupInvitationTokenJob' => __DIR__ . '/..' . '/../lib/BackgroundJob/CleanupInvitationTokenJob.php',
+ 'OCA\\DAV\\BackgroundJob\\EventReminderJob' => __DIR__ . '/..' . '/../lib/BackgroundJob/EventReminderJob.php',
'OCA\\DAV\\BackgroundJob\\GenerateBirthdayCalendarBackgroundJob' => __DIR__ . '/..' . '/../lib/BackgroundJob/GenerateBirthdayCalendarBackgroundJob.php',
'OCA\\DAV\\BackgroundJob\\RefreshWebcalJob' => __DIR__ . '/..' . '/../lib/BackgroundJob/RefreshWebcalJob.php',
'OCA\\DAV\\BackgroundJob\\RegisterRegenerateBirthdayCalendars' => __DIR__ . '/..' . '/../lib/BackgroundJob/RegisterRegenerateBirthdayCalendars.php',
@@ -66,6 +67,15 @@ class ComposerStaticInitDAV
'OCA\\DAV\\CalDAV\\PublicCalendarRoot' => __DIR__ . '/..' . '/../lib/CalDAV/PublicCalendarRoot.php',
'OCA\\DAV\\CalDAV\\Publishing\\PublishPlugin' => __DIR__ . '/..' . '/../lib/CalDAV/Publishing/PublishPlugin.php',
'OCA\\DAV\\CalDAV\\Publishing\\Xml\\Publisher' => __DIR__ . '/..' . '/../lib/CalDAV/Publishing/Xml/Publisher.php',
+ 'OCA\\DAV\\CalDAV\\Reminder\\AbstractNotificationProvider' => __DIR__ . '/..' . '/../lib/CalDAV/Reminder/AbstractNotificationProvider.php',
+ 'OCA\\DAV\\CalDAV\\Reminder\\Backend' => __DIR__ . '/..' . '/../lib/CalDAV/Reminder/Backend.php',
+ 'OCA\\DAV\\CalDAV\\Reminder\\NotificationProviderManager' => __DIR__ . '/..' . '/../lib/CalDAV/Reminder/NotificationProviderManager.php',
+ 'OCA\\DAV\\CalDAV\\Reminder\\NotificationProvider\\EmailProvider' => __DIR__ . '/..' . '/../lib/CalDAV/Reminder/NotificationProvider/EmailProvider.php',
+ 'OCA\\DAV\\CalDAV\\Reminder\\NotificationProvider\\ProviderNotAvailableException' => __DIR__ . '/..' . '/../lib/CalDAV/Reminder/NotificationProvider/ProviderNotAvailableException.php',
+ 'OCA\\DAV\\CalDAV\\Reminder\\NotificationProvider\\PushProvider' => __DIR__ . '/..' . '/../lib/CalDAV/Reminder/NotificationProvider/PushProvider.php',
+ 'OCA\\DAV\\CalDAV\\Reminder\\NotificationTypeDoesNotExistException' => __DIR__ . '/..' . '/../lib/CalDAV/Reminder/NotificationTypeDoesNotExistException.php',
+ 'OCA\\DAV\\CalDAV\\Reminder\\Notifier' => __DIR__ . '/..' . '/../lib/CalDAV/Reminder/Notifier.php',
+ 'OCA\\DAV\\CalDAV\\Reminder\\ReminderService' => __DIR__ . '/..' . '/../lib/CalDAV/Reminder/ReminderService.php',
'OCA\\DAV\\CalDAV\\ResourceBooking\\AbstractPrincipalBackend' => __DIR__ . '/..' . '/../lib/CalDAV/ResourceBooking/AbstractPrincipalBackend.php',
'OCA\\DAV\\CalDAV\\ResourceBooking\\ResourcePrincipalBackend' => __DIR__ . '/..' . '/../lib/CalDAV/ResourceBooking/ResourcePrincipalBackend.php',
'OCA\\DAV\\CalDAV\\ResourceBooking\\RoomPrincipalBackend' => __DIR__ . '/..' . '/../lib/CalDAV/ResourceBooking/RoomPrincipalBackend.php',
@@ -191,6 +201,7 @@ class ComposerStaticInitDAV
'OCA\\DAV\\Migration\\Version1005Date20180530124431' => __DIR__ . '/..' . '/../lib/Migration/Version1005Date20180530124431.php',
'OCA\\DAV\\Migration\\Version1006Date20180619154313' => __DIR__ . '/..' . '/../lib/Migration/Version1006Date20180619154313.php',
'OCA\\DAV\\Migration\\Version1006Date20180628111625' => __DIR__ . '/..' . '/../lib/Migration/Version1006Date20180628111625.php',
+ 'OCA\\DAV\\Migration\\Version1007Date20181005133326' => __DIR__ . '/..' . '/../lib/Migration/Version1007Date20181005133326.php',
'OCA\\DAV\\Migration\\Version1008Date20181030113700' => __DIR__ . '/..' . '/../lib/Migration/Version1008Date20181030113700.php',
'OCA\\DAV\\Migration\\Version1008Date20181105104826' => __DIR__ . '/..' . '/../lib/Migration/Version1008Date20181105104826.php',
'OCA\\DAV\\Migration\\Version1008Date20181105104833' => __DIR__ . '/..' . '/../lib/Migration/Version1008Date20181105104833.php',
diff --git a/apps/dav/js/settings-admin-caldav.js b/apps/dav/js/settings-admin-caldav.js
index 1a40c208df..ad30ba6ad3 100644
--- a/apps/dav/js/settings-admin-caldav.js
+++ b/apps/dav/js/settings-admin-caldav.js
@@ -36,3 +36,9 @@ $('#caldavGenerateBirthdayCalendar').change(function() {
$.post(OC.generateUrl('/apps/dav/disableBirthdayCalendar'));
}
});
+
+$('#caldavSendRemindersNotifications').change(function() {
+ var val = $(this)[0].checked;
+
+ OCP.AppConfig.setValue('dav', 'sendEventReminders', val ? 'yes' : 'no');
+});
diff --git a/apps/dav/lib/AppInfo/Application.php b/apps/dav/lib/AppInfo/Application.php
index 41570ee744..2e3b95d8bf 100644
--- a/apps/dav/lib/AppInfo/Application.php
+++ b/apps/dav/lib/AppInfo/Application.php
@@ -30,6 +30,12 @@ use OCA\DAV\CalDAV\Activity\Backend;
use OCA\DAV\CalDAV\Activity\Provider\Event;
use OCA\DAV\CalDAV\BirthdayService;
use OCA\DAV\CalDAV\CalendarManager;
+use OCA\DAV\CalDAV\Reminder\Backend as ReminderBackend;
+use OCA\DAV\CalDAV\Reminder\NotificationProvider\EmailProvider;
+use OCA\DAV\CalDAV\Reminder\NotificationProvider\PushProvider;
+use OCA\DAV\CalDAV\Reminder\NotificationProviderManager;
+use OCA\DAV\CalDAV\Reminder\Notifier;
+use OCA\DAV\CalDAV\Reminder\ReminderService;
use OCA\DAV\Capabilities;
use OCA\DAV\CardDAV\ContactsManager;
use OCA\DAV\CardDAV\PhotoCache;
@@ -43,6 +49,8 @@ use Symfony\Component\EventDispatcher\GenericEvent;
class Application extends App {
+ const APP_ID = 'dav';
+
/**
* Application constructor.
*/
@@ -109,8 +117,7 @@ class Application extends App {
}
});
- // carddav/caldav sync event setup
- $listener = function($event) {
+ $birthdayListener = function ($event) {
if ($event instanceof GenericEvent) {
/** @var BirthdayService $b */
$b = $this->getContainer()->query(BirthdayService::class);
@@ -122,9 +129,9 @@ class Application extends App {
}
};
- $dispatcher->addListener('\OCA\DAV\CardDAV\CardDavBackend::createCard', $listener);
- $dispatcher->addListener('\OCA\DAV\CardDAV\CardDavBackend::updateCard', $listener);
- $dispatcher->addListener('\OCA\DAV\CardDAV\CardDavBackend::deleteCard', function($event) {
+ $dispatcher->addListener('\OCA\DAV\CardDAV\CardDavBackend::createCard', $birthdayListener);
+ $dispatcher->addListener('\OCA\DAV\CardDAV\CardDavBackend::updateCard', $birthdayListener);
+ $dispatcher->addListener('\OCA\DAV\CardDAV\CardDavBackend::deleteCard', function ($event) {
if ($event instanceof GenericEvent) {
/** @var BirthdayService $b */
$b = $this->getContainer()->query(BirthdayService::class);
@@ -177,6 +184,11 @@ class Application extends App {
$event->getArgument('calendarData'),
$event->getArgument('shares')
);
+
+ $reminderBackend = $this->getContainer()->query(ReminderBackend::class);
+ $reminderBackend->cleanRemindersForCalendar(
+ $event->getArgument('calendarId')
+ );
});
$dispatcher->addListener('\OCA\DAV\CalDAV\CalDavBackend::updateShares', function(GenericEvent $event) {
/** @var Backend $backend */
@@ -187,6 +199,8 @@ class Application extends App {
$event->getArgument('add'),
$event->getArgument('remove')
);
+
+ // Here we should recalculate if reminders should be sent to new or old sharees
});
$dispatcher->addListener('\OCA\DAV\CalDAV\CalDavBackend::publishCalendar', function(GenericEvent $event) {
@@ -214,6 +228,16 @@ class Application extends App {
$event->getArgument('shares'),
$event->getArgument('objectData')
);
+
+ /** @var ReminderService $reminderBackend */
+ $reminderService= $this->getContainer()->query(ReminderService::class);
+
+ $reminderService->onTouchCalendarObject(
+ $eventName,
+ $event->getArgument('calendarData'),
+ $event->getArgument('shares'),
+ $event->getArgument('objectData')
+ );
};
$dispatcher->addListener('\OCA\DAV\CalDAV\CalDavBackend::createCalendarObject', $listener);
$dispatcher->addListener('\OCA\DAV\CalDAV\CalDavBackend::updateCalendarObject', $listener);
@@ -224,4 +248,28 @@ class Application extends App {
return $this->getContainer()->query(SyncService::class);
}
+ public function registerNotifier() {
+ $this->getContainer()->getServer()->getNotificationManager()->registerNotifier(function() {
+ return $this->getContainer()->query(Notifier::class);
+ }, function() {
+ $l = $this->getContainer()->getServer()->getL10NFactory()->get(self::APP_ID);
+ return [
+ 'id' => self::APP_ID,
+ 'name' => $l->t('Calendars and Contacts'),
+ ];
+ });
+ }
+
+ public function registerCalendarReminders(): void
+ {
+ try {
+ /** @var NotificationProviderManager $notificationProviderManager */
+ $notificationProviderManager = $this->getContainer()->query(NotificationProviderManager::class);
+ $notificationProviderManager->registerProvider(EmailProvider::class);
+ $notificationProviderManager->registerProvider(PushProvider::class);
+ } catch(\Exception $ex) {
+ $this->getContainer()->getServer()->getLogger()->logException($ex);
+ }
+ }
+
}
diff --git a/apps/dav/lib/BackgroundJob/EventReminderJob.php b/apps/dav/lib/BackgroundJob/EventReminderJob.php
new file mode 100644
index 0000000000..e0a147e420
--- /dev/null
+++ b/apps/dav/lib/BackgroundJob/EventReminderJob.php
@@ -0,0 +1,59 @@
+
+ *
+ * @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\DAV\BackgroundJob;
+
+use OC\BackgroundJob\TimedJob;
+use OCA\DAV\CalDAV\Reminder\ReminderService;
+use OCP\IConfig;
+
+class EventReminderJob extends TimedJob {
+
+ /** @var ReminderService */
+ private $reminderService;
+
+ /** @var IConfig */
+ private $config;
+
+ /**
+ * EventReminderJob constructor.
+ *
+ * @param ReminderService $reminderService
+ * @param IConfig $config
+ */
+ public function __construct(ReminderService $reminderService, IConfig $config) {
+ $this->reminderService = $reminderService;
+ $this->config = $config;
+ /** Run every 5 minutes */
+ $this->setInterval(5);
+ }
+
+ /**
+ * @param $arg
+ * @throws \OCA\DAV\CalDAV\Reminder\NotificationProvider\ProviderNotAvailableException
+ * @throws \OCA\DAV\CalDAV\Reminder\NotificationTypeDoesNotExistException
+ * @throws \OC\User\NoUserException
+ */
+ public function run($arg): void
+ {
+ if ($this->config->getAppValue('dav', 'sendEventReminders', 'yes') === 'yes') {
+ $this->reminderService->processReminders();
+ }
+ }
+}
diff --git a/apps/dav/lib/CalDAV/CalDavBackend.php b/apps/dav/lib/CalDAV/CalDavBackend.php
index 91281dc0cb..62d3909ce3 100644
--- a/apps/dav/lib/CalDAV/CalDavBackend.php
+++ b/apps/dav/lib/CalDAV/CalDavBackend.php
@@ -1135,7 +1135,6 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription
*/
function updateCalendarObject($calendarId, $objectUri, $calendarData, $calendarType=self::CALENDAR_TYPE_CALENDAR) {
$extraData = $this->getDenormalizedData($calendarData);
-
$query = $this->db->getQueryBuilder();
$query->update('calendarobjects')
->set('calendardata', $query->createNamedParameter($calendarData, IQueryBuilder::PARAM_LOB))
diff --git a/apps/dav/lib/CalDAV/Reminder/AbstractNotificationProvider.php b/apps/dav/lib/CalDAV/Reminder/AbstractNotificationProvider.php
new file mode 100644
index 0000000000..ba928fac80
--- /dev/null
+++ b/apps/dav/lib/CalDAV/Reminder/AbstractNotificationProvider.php
@@ -0,0 +1,208 @@
+
+ *
+ * @author Thomas Citharel
+ *
+ * @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\DAV\CalDAV\Reminder;
+
+use \DateTime;
+use \DateTimeImmutable;
+use OCP\IConfig;
+use OCP\IL10N;
+use OCP\ILogger;
+use OCP\IURLGenerator;
+use OCP\L10N\IFactory as L10NFactory;
+use OCP\IUser;
+use Sabre\VObject\Component\VCalendar;
+use Sabre\VObject\Component\VEvent;
+use Sabre\VObject\DateTimeParser;
+use Sabre\VObject\Parameter;
+use Sabre\VObject\Property;
+
+abstract class AbstractNotificationProvider
+{
+
+ public const NOTIFICATION_TYPE = '';
+
+ /** @var ILogger */
+ protected $logger;
+
+ /** @var L10NFactory */
+ protected $l10nFactory;
+
+ /** @var IL10N */
+ protected $l10n;
+
+ /** @var IURLGenerator */
+ protected $urlGenerator;
+
+ /** @var IConfig */
+ protected $config;
+
+ /**
+ * @param ILogger $logger
+ * @param L10NFactory $l10nFactory
+ * @param IConfig $config
+ * @param IUrlGenerator $urlGenerator
+ */
+ public function __construct(ILogger $logger, L10NFactory $l10nFactory, IURLGenerator $urlGenerator, IConfig $config) {
+ $this->logger = $logger;
+ $this->l10nFactory = $l10nFactory;
+ $this->urlGenerator = $urlGenerator;
+ $this->config = $config;
+ }
+
+ /**
+ * Send notification
+ *
+ * @param VCalendar $vcalendar
+ * @param string $calendarDisplayName
+ * @param IUser $user
+ * @return void
+ */
+ public function send(VCalendar $vcalendar, string $calendarDisplayName, IUser $user): void {}
+
+ /**
+ * @var VCalendar $vcalendar
+ * @var string $defaultValue
+ * @return array
+ * @throws \Exception
+ */
+ protected function extractEventDetails(VCalendar $vcalendar, $defaultValue = ''): array
+ {
+ /** @var VEvent $vevent */
+ $vevent = $vcalendar->VEVENT;
+
+ /** @var Property $start */
+ $start = $vevent->DTSTART;
+ if (isset($vevent->DTEND)) {
+ $end = $vevent->DTEND;
+ } elseif (isset($vevent->DURATION)) {
+ $isFloating = $vevent->DTSTART->isFloating();
+ $end = clone $vevent->DTSTART;
+ $endDateTime = $end->getDateTime();
+ $endDateTime = $endDateTime->add(DateTimeParser::parse($vevent->DURATION->getValue()));
+ $end->setDateTime($endDateTime, $isFloating);
+ } elseif (!$vevent->DTSTART->hasTime()) {
+ $isFloating = $vevent->DTSTART->isFloating();
+ $end = clone $vevent->DTSTART;
+ $endDateTime = $end->getDateTime();
+ $endDateTime = $endDateTime->modify('+1 day');
+ $end->setDateTime($endDateTime, $isFloating);
+ } else {
+ $end = clone $vevent->DTSTART;
+ }
+
+ return [
+ 'title' => (string) $vevent->SUMMARY ?: $defaultValue,
+ 'description' => (string) $vevent->DESCRIPTION ?: $defaultValue,
+ 'start'=> $start->getDateTime(),
+ 'end' => $end->getDateTime(),
+ 'when' => $this->generateWhenString($start, $end),
+ 'url' => (string) $vevent->URL ?: $defaultValue,
+ 'location' => (string) $vevent->LOCATION ?: $defaultValue,
+ 'uid' => (string) $vevent->UID,
+ ];
+ }
+
+ /**
+ * @param Property $dtstart
+ * @param Property $dtend
+ * @return string
+ * @throws \Exception
+ */
+ private function generateWhenString(Property $dtstart, Property $dtend): string
+ {
+ $isAllDay = $dtstart instanceof Property\ICalendar\Date;
+
+ /** @var Property\ICalendar\Date | Property\ICalendar\DateTime $dtstart */
+ /** @var Property\ICalendar\Date | Property\ICalendar\DateTime $dtend */
+ /** @var DateTimeImmutable $dtstartDt */
+ $dtstartDt = $dtstart->getDateTime();
+ /** @var DateTimeImmutable $dtendDt */
+ $dtendDt = $dtend->getDateTime();
+
+ $diff = $dtstartDt->diff($dtendDt);
+
+ $dtstartDt = new DateTime($dtstartDt->format(DateTime::ATOM));
+ $dtendDt = new DateTime($dtendDt->format(DateTime::ATOM));
+
+ if ($isAllDay) {
+ // One day event
+ if ($diff->days === 1) {
+ return $this->l10n->l('date', $dtstartDt, ['width' => 'medium']);
+ }
+
+ //event that spans over multiple days
+ $localeStart = $this->l10n->l('date', $dtstartDt, ['width' => 'medium']);
+ $localeEnd = $this->l10n->l('date', $dtendDt, ['width' => 'medium']);
+
+ return $localeStart . ' - ' . $localeEnd;
+ }
+
+ /** @var Property\ICalendar\DateTime $dtstart */
+ /** @var Property\ICalendar\DateTime $dtend */
+ $isFloating = $dtstart->isFloating();
+ $startTimezone = $endTimezone = null;
+ if (!$isFloating) {
+ $prop = $dtstart->offsetGet('TZID');
+ if ($prop instanceof Parameter) {
+ $startTimezone = $prop->getValue();
+ }
+
+ $prop = $dtend->offsetGet('TZID');
+ if ($prop instanceof Parameter) {
+ $endTimezone = $prop->getValue();
+ }
+ }
+
+ $localeStart = $this->l10n->l('weekdayName', $dtstartDt, ['width' => 'abbreviated']) . ', ' .
+ $this->l10n->l('datetime', $dtstartDt, ['width' => 'medium|short']);
+
+ // always show full date with timezone if timezones are different
+ if ($startTimezone !== $endTimezone) {
+ $localeEnd = $this->l10n->l('datetime', $dtendDt, ['width' => 'medium|short']);
+
+ return $localeStart . ' (' . $startTimezone . ') - ' .
+ $localeEnd . ' (' . $endTimezone . ')';
+ }
+
+ // show only end time if date is the same
+ if ($this->isDayEqual($dtstartDt, $dtendDt)) {
+ $localeEnd = $this->l10n->l('time', $dtendDt, ['width' => 'short']);
+ } else {
+ $localeEnd = $this->l10n->l('weekdayName', $dtendDt, ['width' => 'abbreviated']) . ', ' .
+ $this->l10n->l('datetime', $dtendDt, ['width' => 'medium|short']);
+ }
+
+ return $localeStart . ' - ' . $localeEnd . ' (' . $startTimezone . ')';
+ }
+
+ /**
+ * @param DateTime $dtStart
+ * @param DateTime $dtEnd
+ * @return bool
+ */
+ private function isDayEqual(DateTime $dtStart, DateTime $dtEnd): bool
+ {
+ return $dtStart->format('Y-m-d') === $dtEnd->format('Y-m-d');
+ }
+}
diff --git a/apps/dav/lib/CalDAV/Reminder/Backend.php b/apps/dav/lib/CalDAV/Reminder/Backend.php
new file mode 100644
index 0000000000..c85e7c365d
--- /dev/null
+++ b/apps/dav/lib/CalDAV/Reminder/Backend.php
@@ -0,0 +1,139 @@
+
+ *
+ * @author Thomas Citharel
+ *
+ * @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\DAV\CalDAV\Reminder;
+
+use OCP\IDBConnection;
+use OCP\AppFramework\Utility\ITimeFactory;
+
+/**
+ * Class Backend
+ *
+ * @package OCA\DAV\CalDAV\Reminder
+ */
+class Backend {
+
+ /** @var IDBConnection */
+ protected $db;
+
+ /** @var ITimeFactory */
+ private $timeFactory;
+
+ /**
+ * @param IDBConnection $db
+ * @param ITimeFactory $timeFactory
+ */
+ public function __construct(IDBConnection $db, ITimeFactory $timeFactory) {
+ $this->db = $db;
+ $this->timeFactory = $timeFactory;
+ }
+
+ /**
+ * @param string $uid
+ * @param string $calendarId
+ * @param string $uri
+ * @param string $type
+ * @param int $notificationDate
+ * @param int $eventStartDate
+ */
+ public function insertReminder(string $uid, string $calendarId, string $uri, string $type, int $notificationDate, int $eventStartDate): void
+ {
+ $query = $this->db->getQueryBuilder();
+ $query->insert('calendar_reminders')
+ ->values([
+ 'uid' => $query->createNamedParameter($uid),
+ 'calendarid' => $query->createNamedParameter($calendarId),
+ 'objecturi' => $query->createNamedParameter($uri),
+ 'type' => $query->createNamedParameter($type),
+ 'notificationdate' => $query->createNamedParameter($notificationDate),
+ 'eventstartdate' => $query->createNamedParameter($eventStartDate),
+ ])->execute();
+ }
+
+ /**
+ * Cleans reminders in database
+ *
+ * @param int $calendarId
+ * @param string $objectUri
+ */
+ public function cleanRemindersForEvent(int $calendarId, string $objectUri): void
+ {
+ $query = $this->db->getQueryBuilder();
+
+ $query->delete('calendar_reminders')
+ ->where($query->expr()->eq('calendarid', $query->createNamedParameter($calendarId)))
+ ->andWhere($query->expr()->eq('objecturi', $query->createNamedParameter($objectUri)))
+ ->execute();
+ }
+
+ /**
+ * Remove all reminders for a calendar
+ *
+ * @param integer $calendarId
+ * @return void
+ */
+ public function cleanRemindersForCalendar(int $calendarId): void
+ {
+ $query = $this->db->getQueryBuilder();
+
+ $query->delete('calendar_reminders')
+ ->where($query->expr()->eq('calendarid', $query->createNamedParameter($calendarId)))
+ ->execute();
+ }
+
+ /**
+ * Remove a reminder by it's id
+ *
+ * @param integer $reminderId
+ * @return void
+ */
+ public function removeReminder(int $reminderId): void
+ {
+ $query = $this->db->getQueryBuilder();
+
+ $query->delete('calendar_reminders')
+ ->where($query->expr()->eq('id', $query->createNamedParameter($reminderId)))
+ ->execute();
+ }
+
+ /**
+ * Get all reminders with a notification date before now
+ *
+ * @return array
+ * @throws \Exception
+ */
+ public function getRemindersToProcess(): array
+ {
+ $query = $this->db->getQueryBuilder();
+ $fields = ['cr.id', 'cr.calendarid', 'cr.objecturi', 'cr.type', 'cr.notificationdate', 'cr.uid', 'co.calendardata', 'c.displayname'];
+ $stmt = $query->select($fields)
+ ->from('calendar_reminders', 'cr')
+ ->where($query->expr()->lte('cr.notificationdate', $query->createNamedParameter($this->timeFactory->getTime())))
+ ->andWhere($query->expr()->gte('cr.eventstartdate', $query->createNamedParameter($this->timeFactory->getTime()))) # We check that DTSTART isn't before
+ ->leftJoin('cr', 'calendars', 'c', $query->expr()->eq('cr.calendarid', 'c.id'))
+ ->leftJoin('cr', 'calendarobjects', 'co', $query->expr()->andX($query->expr()->eq('cr.calendarid', 'c.id'), $query->expr()->eq('co.uri', 'cr.objecturi')))
+ ->execute();
+
+ return $stmt->fetchAll();
+ }
+}
diff --git a/apps/dav/lib/CalDAV/Reminder/NotificationProvider/EmailProvider.php b/apps/dav/lib/CalDAV/Reminder/NotificationProvider/EmailProvider.php
new file mode 100644
index 0000000000..81d4474011
--- /dev/null
+++ b/apps/dav/lib/CalDAV/Reminder/NotificationProvider/EmailProvider.php
@@ -0,0 +1,157 @@
+
+ *
+ * @author Thomas Citharel
+ *
+ * @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\DAV\CalDAV\Reminder\NotificationProvider;
+
+use OCA\DAV\CalDAV\Reminder\AbstractNotificationProvider;
+use OCP\IConfig;
+use OCP\ILogger;
+use OCP\IURLGenerator;
+use OCP\L10N\IFactory as L10NFactory;
+use OCP\Mail\IEMailTemplate;
+use OCP\Mail\IMailer;
+use OCP\IUser;
+use Sabre\VObject\Component\VCalendar;
+
+class EmailProvider extends AbstractNotificationProvider
+{
+ /** @var IMailer */
+ private $mailer;
+
+ public const NOTIFICATION_TYPE = 'EMAIL';
+
+ /**
+ * @param IConfig $config
+ * @param IMailer $mailer
+ * @param ILogger $logger
+ * @param L10NFactory $l10nFactory
+ * @param IUrlGenerator $urlGenerator
+ */
+ public function __construct(IConfig $config, IMailer $mailer, ILogger $logger,
+ L10NFactory $l10nFactory,
+ IURLGenerator $urlGenerator) {
+ parent::__construct($logger, $l10nFactory, $urlGenerator, $config);
+ $this->mailer = $mailer;
+ }
+
+ /**
+ * Send notification
+ *
+ * @param VCalendar $vcalendar
+ * @param string $calendarDisplayName
+ * @param IUser $user
+ * @return void
+ * @throws \Exception
+ */
+ public function send(VCalendar $vcalendar, string $calendarDisplayName, IUser $user): void
+ {
+ if ($user->getEMailAddress() === null) {
+ return;
+ }
+
+ $lang = $this->config->getUserValue($user->getUID(), 'core', 'lang', $this->l10nFactory->findLanguage());
+ $this->l10n = $this->l10nFactory->get('dav', $lang);
+
+ $event = $this->extractEventDetails($vcalendar);
+ $fromEMail = \OCP\Util::getDefaultEmailAddress('invitations-noreply');
+
+ $message = $this->mailer->createMessage()
+ ->setFrom([$fromEMail => 'Nextcloud'])
+ // TODO: Set reply to from event creator
+ // ->setReplyTo([$sender => $senderName])
+ ->setTo([$user->getEMailAddress() => $user->getDisplayName()]);
+
+ $template = $this->mailer->createEMailTemplate('dav.calendarReminder', $event);
+ $template->addHeader();
+
+ $this->addSubjectAndHeading($template, $event['title']);
+ $this->addBulletList($template, $event, $calendarDisplayName);
+
+ $template->addFooter();
+ $message->useTemplate($template);
+
+ $attachment = $this->mailer->createAttachment(
+ $vcalendar->serialize(),
+ $event['uid'].'.ics',// TODO(leon): Make file name unique, e.g. add event id
+ 'text/calendar'
+ );
+ $message->attach($attachment);
+
+ try {
+ $failed = $this->mailer->send($message);
+ if ($failed) {
+ $this->logger->error('Unable to deliver message to {failed}', ['app' => 'dav', 'failed' => implode(', ', $failed)]);
+ }
+ } catch(\Exception $ex) {
+ $this->logger->logException($ex, ['app' => 'dav']);
+ }
+ }
+
+ /**
+ * @param IEMailTemplate $template
+ * @param string $summary
+ */
+ private function addSubjectAndHeading(IEMailTemplate $template, string $summary): void
+ {
+ $template->setSubject('Notification: ' . $summary);
+ $template->addHeading($summary);
+ }
+
+ /**
+ * @param IEMailTemplate $template
+ * @param array $eventData
+ * @param string $calendarDisplayName
+ */
+ private function addBulletList(IEMailTemplate $template, array $eventData, string $calendarDisplayName): void
+ {
+ $template->addBodyListItem($calendarDisplayName, $this->l10n->t('Calendar:'),
+ $this->getAbsoluteImagePath('actions/info.svg'));
+
+ $template->addBodyListItem($eventData['when'], $this->l10n->t('Date:'),
+ $this->getAbsoluteImagePath('places/calendar.svg'));
+
+ if ($eventData['location']) {
+ $template->addBodyListItem((string) $eventData['location'], $this->l10n->t('Where:'),
+ $this->getAbsoluteImagePath('actions/address.svg'));
+ }
+ if ($eventData['description']) {
+ $template->addBodyListItem((string) $eventData['description'], $this->l10n->t('Description:'),
+ $this->getAbsoluteImagePath('actions/more.svg'));
+ }
+ if ($eventData['url']) {
+ $template->addBodyListItem((string) $eventData['url'], $this->l10n->t('Link:'),
+ $this->getAbsoluteImagePath('places/link.svg'));
+ }
+ }
+
+ /**
+ * @param string $path
+ * @return string
+ */
+ private function getAbsoluteImagePath($path): string
+ {
+ return $this->urlGenerator->getAbsoluteURL(
+ $this->urlGenerator->imagePath('core', $path)
+ );
+ }
+}
diff --git a/apps/dav/lib/CalDAV/Reminder/NotificationProvider/ProviderNotAvailableException.php b/apps/dav/lib/CalDAV/Reminder/NotificationProvider/ProviderNotAvailableException.php
new file mode 100644
index 0000000000..bf736db8a3
--- /dev/null
+++ b/apps/dav/lib/CalDAV/Reminder/NotificationProvider/ProviderNotAvailableException.php
@@ -0,0 +1,39 @@
+
+ *
+ * @author Thomas Citharel
+ *
+ * @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\DAV\CalDAV\Reminder\NotificationProvider;
+
+class ProviderNotAvailableException extends \Exception {
+
+ /**
+ * ProviderNotAvailableException constructor.
+ *
+ * @since 16.0.0
+ *
+ * @param string $type ReminderType
+ */
+ public function __construct(string $type) {
+ parent::__construct("No notification provider for type $type available");
+ }
+
+}
diff --git a/apps/dav/lib/CalDAV/Reminder/NotificationProvider/PushProvider.php b/apps/dav/lib/CalDAV/Reminder/NotificationProvider/PushProvider.php
new file mode 100644
index 0000000000..1bb0e5c68b
--- /dev/null
+++ b/apps/dav/lib/CalDAV/Reminder/NotificationProvider/PushProvider.php
@@ -0,0 +1,101 @@
+
+ *
+ * @author Thomas Citharel
+ *
+ * @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\DAV\CalDAV\Reminder\NotificationProvider;
+
+use OCA\DAV\AppInfo\Application;
+use OCA\DAV\CalDAV\Reminder\AbstractNotificationProvider;
+use OCP\IConfig;
+use OCP\ILogger;
+use OCP\IURLGenerator;
+use OCP\L10N\IFactory as L10NFactory;
+use OCP\Notification\IManager;
+use OCP\IUser;
+use OCP\Notification\INotification;
+use Sabre\VObject\Component\VCalendar;
+use OCP\AppFramework\Utility\ITimeFactory;
+
+class PushProvider extends AbstractNotificationProvider
+{
+
+ public const NOTIFICATION_TYPE = 'DISPLAY';
+
+ /**
+ * @var IManager
+ */
+ private $manager;
+
+ /**
+ * @var ITimeFactory
+ */
+ private $timeFactory;
+
+ /**
+ * @param IConfig $config
+ * @param IManager $manager
+ * @param ILogger $logger
+ * @param L10NFactory $l10nFactory
+ * @param IUrlGenerator $urlGenerator
+ * @param ITimeFactory $timeFactory
+ */
+ public function __construct(IConfig $config, IManager $manager, ILogger $logger,
+ L10NFactory $l10nFactory,
+ IURLGenerator $urlGenerator, ITimeFactory $timeFactory) {
+ parent::__construct($logger, $l10nFactory, $urlGenerator, $config);
+ $this->manager = $manager;
+ $this->timeFactory = $timeFactory;
+ }
+
+ /**
+ * Send notification
+ *
+ * @param VCalendar $vcalendar
+ * @param string $calendarDisplayName
+ * @param IUser $user
+ * @return void
+ * @throws \Exception
+ */
+ public function send(VCalendar $vcalendar, string $calendarDisplayName, IUser $user): void
+ {
+
+ $lang = $this->config->getUserValue($user->getUID(), 'core', 'lang', $this->l10nFactory->findLanguage());
+ $this->l10n = $this->l10nFactory->get('dav', $lang);
+
+ $event = $this->extractEventDetails($vcalendar);
+ /** @var INotification $notification */
+ $notification = $this->manager->createNotification();
+ $notification->setApp(Application::APP_ID)
+ ->setUser($user->getUID())
+ ->setDateTime($this->timeFactory->getDateTime())
+ ->setObject(Application::APP_ID, $event['uid']) // $type and $id
+ ->setSubject('calendar_reminder', ['title' => $event['title'], 'start' => $event['start']->getTimestamp()]) // $subject and $parameters
+ ->setMessage('calendar_reminder', [
+ 'when' => $event['when'],
+ 'description' => $event['description'],
+ 'location' => $event['location'],
+ 'calendar' => $calendarDisplayName
+ ])
+ ;
+ $this->manager->notify($notification);
+ }
+}
diff --git a/apps/dav/lib/CalDAV/Reminder/NotificationProviderManager.php b/apps/dav/lib/CalDAV/Reminder/NotificationProviderManager.php
new file mode 100644
index 0000000000..389cbbd2bf
--- /dev/null
+++ b/apps/dav/lib/CalDAV/Reminder/NotificationProviderManager.php
@@ -0,0 +1,59 @@
+
+ *
+ * @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\DAV\CalDAV\Reminder;
+
+use OCA\DAV\CalDAV\Reminder\NotificationProvider\ProviderNotAvailableException;
+
+class NotificationProviderManager {
+
+ /** @var array */
+ private $providers = [];
+ /**
+ * @var string $type
+ * @return AbstractNotificationProvider
+ * @throws ProviderNotAvailableException
+ * @throws NotificationTypeDoesNotExistException
+ */
+ public function getProvider(string $type): AbstractNotificationProvider
+ {
+ if (in_array($type, ReminderService::REMINDER_TYPES, true)) {
+ if (isset($this->providers[$type])) {
+ return $this->providers[$type];
+ }
+ throw new ProviderNotAvailableException($type);
+ }
+ throw new NotificationTypeDoesNotExistException($type);
+ }
+
+ /**
+ * @param string $providerClassName
+ * @throws \OCP\AppFramework\QueryException
+ */
+ public function registerProvider(string $providerClassName): void
+ {
+ $provider = \OC::$server->query($providerClassName);
+
+ if (!$provider instanceof AbstractNotificationProvider) {
+ throw new \InvalidArgumentException('Invalid notification provider registered');
+ }
+
+ $this->providers[$provider::NOTIFICATION_TYPE] = $provider;
+ }
+}
diff --git a/apps/dav/lib/CalDAV/Reminder/NotificationTypeDoesNotExistException.php b/apps/dav/lib/CalDAV/Reminder/NotificationTypeDoesNotExistException.php
new file mode 100644
index 0000000000..ae4ec3bd3b
--- /dev/null
+++ b/apps/dav/lib/CalDAV/Reminder/NotificationTypeDoesNotExistException.php
@@ -0,0 +1,39 @@
+
+ *
+ * @author Thomas Citharel
+ *
+ * @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\DAV\CalDAV\Reminder;
+
+class NotificationTypeDoesNotExistException extends \Exception {
+
+ /**
+ * NotificationTypeDoesNotExistException constructor.
+ *
+ * @since 16.0.0
+ *
+ * @param string $type ReminderType
+ */
+ public function __construct(string $type) {
+ parent::__construct("Type $type is not an accepted type of notification");
+ }
+
+}
diff --git a/apps/dav/lib/CalDAV/Reminder/Notifier.php b/apps/dav/lib/CalDAV/Reminder/Notifier.php
new file mode 100644
index 0000000000..d95774e019
--- /dev/null
+++ b/apps/dav/lib/CalDAV/Reminder/Notifier.php
@@ -0,0 +1,143 @@
+
+ *
+ * @author Thomas Citharel
+ *
+ * @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\DAV\CalDAV\Reminder;
+
+use OCA\DAV\AppInfo\Application;
+use OCP\IL10N;
+use OCP\L10N\IFactory;
+use OCP\Notification\INotification;
+use OCP\Notification\INotifier;
+use OCP\IURLGenerator;
+
+class Notifier implements INotifier {
+
+ public static $units = array(
+ 'y' => 'year',
+ 'm' => 'month',
+ 'd' => 'day',
+ 'h' => 'hour',
+ 'i' => 'minute',
+ 's' => 'second',
+ );
+
+ /** @var IFactory */
+ protected $factory;
+
+ /** @var IURLGenerator */
+ protected $urlGenerator;
+
+ /** @var IL10N */
+ protected $l;
+
+ public function __construct(IFactory $factory, IURLGenerator $urlGenerator) {
+ $this->factory = $factory;
+ $this->urlGenerator = $urlGenerator;
+ }
+
+ /**
+ * @param INotification $notification
+ * @param string $languageCode The code of the language that should be used to prepare the notification
+ * @return INotification
+ * @throws \Exception
+ */
+ public function prepare(INotification $notification, $languageCode): INotification
+ {
+ if ($notification->getApp() !== Application::APP_ID) {
+ throw new \InvalidArgumentException('Notification not from this app');
+ }
+
+ // Read the language from the notification
+ $this->l = $this->factory->get('dav', $languageCode);
+
+ if ($notification->getSubject() === 'calendar_reminder') {
+ $subjectParameters = $notification->getSubjectParameters();
+ $notification->setParsedSubject($this->processEventTitle($subjectParameters));
+
+ $messageParameters = $notification->getMessageParameters();
+ $notification->setParsedMessage($this->processEventDescription($messageParameters));
+ $notification->setIcon($this->urlGenerator->getAbsoluteURL($this->urlGenerator->imagePath('core', 'places/calendar.svg')));
+ return $notification;
+ }
+ // Unknown subject => Unknown notification => throw
+ throw new \InvalidArgumentException('Unknown subject');
+ }
+
+ /**
+ * @param array $event
+ * @return string
+ * @throws \Exception
+ */
+ private function processEventTitle(array $event): string
+ {
+ $event_datetime = new \DateTime();
+ $event_datetime->setTimestamp($event['start']);
+ $now = new \DateTime();
+
+ $diff = $event_datetime->diff($now);
+
+ foreach (self::$units as $attribute => $unit) {
+ $count = $diff->$attribute;
+ if (0 !== $count) {
+ return $this->getPluralizedTitle($count, $diff->invert, $unit, $event['title']);
+ }
+ }
+ return '';
+ }
+
+ /**
+ *
+ * @param int $count
+ * @param int $invert
+ * @param string $unit
+ * @param string $title
+ * @return string
+ */
+ private function getPluralizedTitle(int $count, int $invert, string $unit, string $title): string
+ {
+ if ($invert) {
+ return $this->l->n('%s (in one %s)', '%s (in %n %ss)', $count, [$title, $unit]);
+ }
+ // This should probably not show up
+ return $this->l->n('%s (one %s ago)', '%s (%n %ss ago)', $count, [$title, $unit]);
+ }
+
+ /**
+ * @param array $event
+ * @return string
+ */
+ private function processEventDescription(array $event): string
+ {
+ $description = [
+ $this->l->t('Calendar: %s', $event['calendar']),
+ $this->l->t('Date: %s', $event['when']),
+ ];
+
+ if ($event['description']) {
+ $description[] = $this->l->t('Description: %s', $event['description']);
+ }
+ if ($event['location']) {
+ $description[] = $this->l->t('Where: %s', $event['location']);
+ }
+ return implode(' ', $description);
+ }
+}
diff --git a/apps/dav/lib/CalDAV/Reminder/ReminderService.php b/apps/dav/lib/CalDAV/Reminder/ReminderService.php
new file mode 100644
index 0000000000..87c2ce1067
--- /dev/null
+++ b/apps/dav/lib/CalDAV/Reminder/ReminderService.php
@@ -0,0 +1,185 @@
+
+ *
+ * @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\DAV\CalDAV\Reminder;
+
+use OC\User\NoUserException;
+use OCP\IGroup;
+use OCP\IGroupManager;
+use OCP\IUserManager;
+use OCP\IUserSession;
+use Sabre\VObject;
+use Sabre\VObject\Component\VAlarm;
+use Sabre\VObject\Reader;
+
+class ReminderService {
+
+ /** @var Backend */
+ private $backend;
+
+ /** @var NotificationProviderManager */
+ private $notificationProviderManager;
+
+ /** @var IUserManager */
+ private $userManager;
+
+ /** @var IGroupManager */
+ private $groupManager;
+
+ /** @var IUserSession */
+ private $userSession;
+
+ public const REMINDER_TYPE_EMAIL = 'EMAIL';
+ public const REMINDER_TYPE_DISPLAY = 'DISPLAY';
+ public const REMINDER_TYPE_AUDIO = 'AUDIO';
+
+ public const REMINDER_TYPES = [self::REMINDER_TYPE_EMAIL, self::REMINDER_TYPE_DISPLAY, self::REMINDER_TYPE_AUDIO];
+
+ public function __construct(Backend $backend,
+ NotificationProviderManager $notificationProviderManager,
+ IUserManager $userManager,
+ IGroupManager $groupManager,
+ IUserSession $userSession) {
+ $this->backend = $backend;
+ $this->notificationProviderManager = $notificationProviderManager;
+ $this->userManager = $userManager;
+ $this->groupManager = $groupManager;
+ $this->userSession = $userSession;
+ }
+
+ /**
+ * Process reminders to activate
+ *
+ * @throws NoUserException
+ * @throws NotificationProvider\ProviderNotAvailableException
+ * @throws NotificationTypeDoesNotExistException
+ */
+ public function processReminders(): void
+ {
+
+ $reminders = $this->backend->getRemindersToProcess();
+
+ foreach ($reminders as $reminder) {
+ $calendarData = Reader::read($reminder['calendardata']);
+
+ $user = $this->userManager->get($reminder['uid']);
+
+ if ($user === null) {
+ throw new NoUserException('User not found for calendar');
+ }
+
+ $notificationProvider = $this->notificationProviderManager->getProvider($reminder['type']);
+ $notificationProvider->send($calendarData, $reminder['displayname'], $user);
+ $this->backend->removeReminder($reminder['id']);
+ }
+ }
+
+ /**
+ * Saves reminders when a calendar object with some alarms was created/updated/deleted
+ *
+ * @param string $action
+ * @param array $calendarData
+ * @param array $shares
+ * @param array $objectData
+ * @return void
+ * @throws VObject\InvalidDataException
+ * @throws NoUserException
+ */
+ public function onTouchCalendarObject(string $action, array $calendarData, array $shares, array $objectData): void
+ {
+ if (!isset($calendarData['principaluri'])) {
+ return;
+ }
+
+ // Always remove existing reminders for this event
+ $this->backend->cleanRemindersForEvent($objectData['calendarid'], $objectData['uri']);
+
+ /**
+ * If we are deleting the event, no need to go further
+ */
+ if ($action === '\OCA\DAV\CalDAV\CalDavBackend::deleteCalendarObject') {
+ return;
+ }
+
+ $user = $this->userSession->getUser();
+
+ if ($user === null) {
+ throw new NoUserException('No user in session');
+ }
+
+ $users = $this->getUsersForShares($shares);
+
+ $users[] = $user->getUID();
+
+ $vobject = VObject\Reader::read($objectData['calendardata']);
+
+ foreach ($vobject->VEVENT->VALARM as $alarm) {
+ if ($alarm instanceof VAlarm) {
+ $type = strtoupper($alarm->ACTION->getValue());
+ if (in_array($type, self::REMINDER_TYPES, true)) {
+ $time = $alarm->getEffectiveTriggerTime();
+
+ foreach ($users as $uid) {
+ $this->backend->insertReminder(
+ $uid,
+ $objectData['calendarid'],
+ $objectData['uri'],
+ $type,
+ $time->getTimestamp(),
+ $vobject->VEVENT->DTSTART->getDateTime()->getTimestamp());
+
+ }
+ }
+ }
+ }
+ }
+
+
+ /**
+ * Get all users that have access to a given calendar
+ *
+ * @param array $shares
+ * @return string[]
+ */
+ private function getUsersForShares(array $shares): array
+ {
+ $users = $groups = [];
+ foreach ($shares as $share) {
+ $principal = explode('/', $share['{http://owncloud.org/ns}principal']);
+ if ($principal[1] === 'users') {
+ $users[] = $principal[2];
+ } else if ($principal[1] === 'groups') {
+ $groups[] = $principal[2];
+ }
+ }
+
+ if (!empty($groups)) {
+ foreach ($groups as $gid) {
+ $group = $this->groupManager->get($gid);
+ if ($group instanceof IGroup) {
+ foreach ($group->getUsers() as $user) {
+ $users[] = $user->getUID();
+ }
+ }
+ }
+ }
+
+ return array_unique($users);
+ }
+}
diff --git a/apps/dav/lib/Migration/Version1007Date20181005133326.php b/apps/dav/lib/Migration/Version1007Date20181005133326.php
new file mode 100644
index 0000000000..1e4cce950a
--- /dev/null
+++ b/apps/dav/lib/Migration/Version1007Date20181005133326.php
@@ -0,0 +1,82 @@
+hasTable('calendar_reminders')) {
+ $table = $schema->createTable('calendar_reminders');
+
+ $table->addColumn('id', Type::BIGINT, [
+ 'autoincrement' => true,
+ 'notnull' => true,
+ 'length' => 11,
+ 'unsigned' => true,
+ ]);
+ $table->addColumn('uid', Type::STRING, [
+ 'notnull' => true,
+ 'length' => 255,
+ ]);
+ $table->addColumn('calendarid', Type::BIGINT, [
+ 'notnull' => false,
+ 'length' => 11,
+ ]);
+ $table->addColumn('objecturi', Type::STRING, [
+ 'notnull' => true,
+ 'length' => 255,
+ ]);
+ $table->addColumn('type', Type::STRING, [
+ 'notnull' => true,
+ 'length' => 255,
+ ]);
+ $table->addColumn('notificationdate', Type::DATETIME, [
+ 'notnull' => false,
+ ]);
+ $table->addColumn('eventstartdate', Type::DATETIME, [
+ 'notnull' => false,
+ ]);
+
+ $table->setPrimaryKey(['id']);
+ $table->addIndex(['calendarid'], 'calendar_reminder_calendars');
+
+ return $schema;
+ }
+ }
+
+ /**
+ * @param IOutput $output
+ * @param Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper`
+ * @param array $options
+ */
+ public function postSchemaChange(IOutput $output, Closure $schemaClosure, array $options) {
+ }
+}
diff --git a/apps/dav/lib/Settings/CalDAVSettings.php b/apps/dav/lib/Settings/CalDAVSettings.php
index f38143b5b4..958c463b1d 100644
--- a/apps/dav/lib/Settings/CalDAVSettings.php
+++ b/apps/dav/lib/Settings/CalDAVSettings.php
@@ -48,6 +48,7 @@ class CalDAVSettings implements ISettings {
$parameters = [
'send_invitations' => $this->config->getAppValue('dav', 'sendInvitations', 'yes'),
'generate_birthday_calendar' => $this->config->getAppValue('dav', 'generateBirthdayCalendar', 'yes'),
+ 'send_reminders_notifications' => $this->config->getAppValue('dav', 'sendEventReminders', 'yes'),
];
return new TemplateResponse('dav', 'settings-admin-caldav', $parameters);
diff --git a/apps/dav/templates/settings-admin-caldav.php b/apps/dav/templates/settings-admin-caldav.php
index 87b159923d..ba55a88470 100644
--- a/apps/dav/templates/settings-admin-caldav.php
+++ b/apps/dav/templates/settings-admin-caldav.php
@@ -72,4 +72,25 @@ script('dav', [
t('Birthday calendars will be generated by a background job.')); ?> t('Hence they will not be available immediately after enabling but will show up after some time.')); ?>
+
+ />
+
+
+
+ ',
+ '',
+ ],
+ $l->t('Please make sure to properly set up {emailopen}the email server{linkclose}.')
+ )); ?>
+
+
+ t('Notifications will be send through background jobs, so these need to happen often enough.')); ?>
+
diff --git a/apps/dav/tests/unit/BackgroundJob/EventReminderJobTest.php b/apps/dav/tests/unit/BackgroundJob/EventReminderJobTest.php
new file mode 100644
index 0000000000..960dd481dd
--- /dev/null
+++ b/apps/dav/tests/unit/BackgroundJob/EventReminderJobTest.php
@@ -0,0 +1,68 @@
+
+ *
+ * @author Thomas Citharel
+ *
+ * @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\DAV\Tests\unit\BackgroundJob;
+
+use OCA\DAV\BackgroundJob\EventReminderJob;
+use OCA\DAV\CalDAV\Reminder\ReminderService;
+use OCP\IConfig;
+use Test\TestCase;
+
+class EventReminderJobTest extends TestCase {
+
+ /** @var ReminderService|\PHPUnit\Framework\MockObject\MockObject */
+ private $reminderService;
+
+ /** @var IConfig|\PHPUnit\Framework\MockObject\MockObject */
+ private $config;
+
+ /** @var EventReminderJob|\PHPUnit\Framework\MockObject\MockObject */
+ private $backgroundJob;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->reminderService = $this->createMock(ReminderService::class);
+ $this->config = $this->createMock(IConfig::class);
+
+ $this->backgroundJob = new EventReminderJob($this->reminderService, $this->config);
+ }
+
+ public function data(): array
+ {
+ return [[true], [false]];
+ }
+
+ /**
+ * @dataProvider data
+ * @param bool $sendEventReminders
+ */
+ public function testRun(bool $sendEventReminders): void
+ {
+ $this->config->expects($this->once())->method('getAppValue')->with('dav', 'sendEventReminders', 'yes')->willReturn($sendEventReminders ? 'yes' : 'no');
+ $this->reminderService->expects($this->exactly($sendEventReminders ? 1 : 0))->method('processReminders');
+
+ $this->backgroundJob->run([]);
+ }
+}
diff --git a/apps/dav/tests/unit/CalDAV/Reminder/AbstractNotificationProviderTest.php b/apps/dav/tests/unit/CalDAV/Reminder/AbstractNotificationProviderTest.php
new file mode 100644
index 0000000000..ba2e54af33
--- /dev/null
+++ b/apps/dav/tests/unit/CalDAV/Reminder/AbstractNotificationProviderTest.php
@@ -0,0 +1,87 @@
+
+ *
+ * @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\DAV\Tests\unit\CalDAV\Reminder;
+
+use OCA\DAV\CalDAV\Reminder\AbstractNotificationProvider;
+use OCP\IConfig;
+use OCP\IL10N;
+use OCP\ILogger;
+use OCP\IURLGenerator;
+use OCP\L10N\IFactory as L10NFactory;
+use OCP\IUser;
+use Test\TestCase;
+use Sabre\VObject\Component\VCalendar;
+
+abstract class AbstractNotificationProviderTest extends TestCase {
+
+ /** @var ILogger|\PHPUnit\Framework\MockObject\MockObject */
+ protected $logger;
+
+ /** @var L10NFactory|\PHPUnit\Framework\MockObject\MockObject */
+ protected $l10nFactory;
+
+ /** @var IL10N|\PHPUnit\Framework\MockObject\MockObject */
+ protected $l10n;
+
+ /** @var IURLGenerator|\PHPUnit\Framework\MockObject\MockObject */
+ protected $urlGenerator;
+
+ /** @var IConfig|\PHPUnit\Framework\MockObject\MockObject */
+ protected $config;
+
+ /** @var AbstractNotificationProvider|\PHPUnit\Framework\MockObject\MockObject */
+ protected $provider;
+
+ /**
+ * @var VCalendar
+ */
+ protected $vcalendar;
+
+ /**
+ * @var string
+ */
+ protected $calendarDisplayName;
+
+ /**
+ * @var IUser|\PHPUnit\Framework\MockObject\MockObject
+ */
+ protected $user;
+
+ public function setUp() {
+ parent::setUp();
+
+ $this->logger = $this->createMock(ILogger::class);
+ $this->l10nFactory = $this->createMock(L10NFactory::class);
+ $this->l10n = $this->createMock(IL10N::class);
+ $this->urlGenerator = $this->createMock(IURLGenerator::class);
+ $this->config = $this->createMock(IConfig::class);
+
+ $this->vcalendar = new VCalendar();
+ $this->vcalendar->add('VEVENT', [
+ 'SUMMARY' => 'Fellowship meeting',
+ 'DTSTART' => new \DateTime('2017-01-01 00:00:00') // 1483228800
+ ]);
+ $this->calendarDisplayName = 'Personal';
+
+ $this->user = $this->createMock(IUser::class);
+ }
+}
diff --git a/apps/dav/tests/unit/CalDAV/Reminder/BackendTest.php b/apps/dav/tests/unit/CalDAV/Reminder/BackendTest.php
new file mode 100644
index 0000000000..1cd979dc5d
--- /dev/null
+++ b/apps/dav/tests/unit/CalDAV/Reminder/BackendTest.php
@@ -0,0 +1,313 @@
+
+ *
+ * @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\DAV\Tests\unit\CalDAV\Reminder;
+
+use OCP\IDBConnection;
+use OCP\DB\QueryBuilder\IQueryBuilder;
+use OCP\AppFramework\Utility\ITimeFactory;
+use OCA\DAV\CalDAV\Reminder\Backend as ReminderBackend;
+use Test\TestCase;
+
+class BackendTest extends TestCase {
+
+ /**
+ * Reminder Backend
+ *
+ * @var ReminderBackend|\PHPUnit\Framework\MockObject\MockObject
+ */
+ private $reminderBackend;
+
+ /** @var IDBConnection|\PHPUnit\Framework\MockObject\MockObject */
+ private $dbConnection;
+
+ /** @var ITimeFactory|\PHPUnit\Framework\MockObject\MockObject */
+ private $timeFactory;
+
+ public function setUp() {
+ parent::setUp();
+
+ $this->dbConnection = $this->createMock(IDBConnection::class);
+ $this->timeFactory = $this->createMock(ITimeFactory::class);
+ $this->reminderBackend = new ReminderBackend($this->dbConnection, $this->timeFactory);
+ }
+
+ public function testCleanRemindersForEvent(): void
+ {
+ /** @var IQueryBuilder|\PHPUnit\Framework\MockObject\MockObject $queryBuilder */
+ $queryBuilder = $this->createMock(IQueryBuilder::class);
+ $stmt = $this->createMock(\Doctrine\DBAL\Driver\Statement::class);
+ $expr = $this->createMock(\OCP\DB\QueryBuilder\IExpressionBuilder::class);
+
+ $this->dbConnection->expects($this->once())
+ ->method('getQueryBuilder')
+ ->with()
+ ->will($this->returnValue($queryBuilder));
+ $queryBuilder->method('expr')
+ ->will($this->returnValue($expr));
+
+ $expr->method('eq')
+ ->will($this->returnValueMap([
+ ['calendarid', 'createNamedParameter-1', null, 'WHERE_CLAUSE_1'],
+ ['objecturi', 'createNamedParameter-2', null, 'WHERE_CLAUSE_2'],
+ ]));
+ $queryBuilder->method('createNamedParameter')
+ ->will($this->returnValueMap([
+ [1, \PDO::PARAM_STR, null, 'createNamedParameter-1'],
+ ['object.ics', \PDO::PARAM_STR, null, 'createNamedParameter-2'],
+ ]));
+
+ $queryBuilder->expects($this->at(0))
+ ->method('delete')
+ ->with('calendar_reminders')
+ ->willReturn($queryBuilder);
+ $queryBuilder->expects($this->at(3))
+ ->method('where')
+ ->with('WHERE_CLAUSE_1')
+ ->will($this->returnValue($queryBuilder));
+ $queryBuilder->expects($this->at(6))
+ ->method('andWhere')
+ ->with('WHERE_CLAUSE_2')
+ ->will($this->returnValue($queryBuilder));
+ $queryBuilder->expects($this->at(7))
+ ->method('execute')
+ ->with()
+ ->willReturn($stmt);
+
+ $this->reminderBackend->cleanRemindersForEvent(1, 'object.ics');
+ }
+
+ public function testCleanRemindersForCalendar(): void
+ {
+ /** @var IQueryBuilder|\PHPUnit\Framework\MockObject\MockObject $queryBuilder */
+ $queryBuilder = $this->createMock(IQueryBuilder::class);
+ $stmt = $this->createMock(\Doctrine\DBAL\Driver\Statement::class);
+ $expr = $this->createMock(\OCP\DB\QueryBuilder\IExpressionBuilder::class);
+
+ $this->dbConnection->expects($this->once())
+ ->method('getQueryBuilder')
+ ->with()
+ ->will($this->returnValue($queryBuilder));
+ $queryBuilder->method('expr')
+ ->will($this->returnValue($expr));
+
+ $expr->method('eq')
+ ->will($this->returnValueMap([
+ ['calendarid', 'createNamedParameter-1', null, 'WHERE_CLAUSE_1'],
+ ]));
+ $queryBuilder->method('createNamedParameter')
+ ->will($this->returnValueMap([
+ [1337, \PDO::PARAM_STR, null, 'createNamedParameter-1'],
+ ]));
+
+ $queryBuilder->expects($this->at(0))
+ ->method('delete')
+ ->with('calendar_reminders')
+ ->willReturn($queryBuilder);
+ $queryBuilder->expects($this->at(3))
+ ->method('where')
+ ->with('WHERE_CLAUSE_1')
+ ->will($this->returnValue($queryBuilder));
+ $queryBuilder->expects($this->at(4))
+ ->method('execute')
+ ->with()
+ ->willReturn($stmt);
+
+ $this->reminderBackend->cleanRemindersForCalendar(1337);
+ }
+
+ public function testRemoveReminder(): void
+ {
+ /** @var IQueryBuilder|\PHPUnit\Framework\MockObject\MockObject $queryBuilder */
+ $queryBuilder = $this->createMock(IQueryBuilder::class);
+ $stmt = $this->createMock(\Doctrine\DBAL\Driver\Statement::class);
+ $expr = $this->createMock(\OCP\DB\QueryBuilder\IExpressionBuilder::class);
+
+ $this->dbConnection->expects($this->once())
+ ->method('getQueryBuilder')
+ ->with()
+ ->will($this->returnValue($queryBuilder));
+ $queryBuilder->method('expr')
+ ->will($this->returnValue($expr));
+
+ $expr->method('eq')
+ ->will($this->returnValueMap([
+ ['id', 'createNamedParameter-1', null, 'WHERE_CLAUSE_1'],
+ ]));
+ $queryBuilder->method('createNamedParameter')
+ ->will($this->returnValueMap([
+ [16, \PDO::PARAM_STR, null, 'createNamedParameter-1'],
+ ]));
+
+ $queryBuilder->expects($this->at(0))
+ ->method('delete')
+ ->with('calendar_reminders')
+ ->willReturn($queryBuilder);
+ $queryBuilder->expects($this->at(3))
+ ->method('where')
+ ->with('WHERE_CLAUSE_1')
+ ->will($this->returnValue($queryBuilder));
+ $queryBuilder->expects($this->at(4))
+ ->method('execute')
+ ->with()
+ ->willReturn($stmt);
+
+ $this->reminderBackend->removeReminder(16);
+ }
+
+ public function testGetRemindersToProcess(): void
+ {
+ $dbData = [[
+ 'cr.id' => 30,
+ 'cr.calendarid' => 3,
+ 'cr.objecturi' => 'object.ics',
+ 'cr.type' => 'EMAIL',
+ 'cr.notificationdate' => 1337,
+ 'cr.uid' => 'user1',
+ 'co.calendardata' => 'BEGIN:VCALENDAR',
+ 'c.displayname' => 'My Calendar'
+ ]];
+
+ $this->timeFactory->expects($this->exactly(2))
+ ->method('getTime')
+ ->with()
+ ->willReturn(1337);
+
+ /** @var IQueryBuilder|\PHPUnit\Framework\MockObject\MockObject $queryBuilder */
+ $queryBuilder = $this->createMock(IQueryBuilder::class);
+ $stmt = $this->createMock(\Doctrine\DBAL\Driver\Statement::class);
+ $expr = $this->createMock(\OCP\DB\QueryBuilder\IExpressionBuilder::class);
+
+ $this->dbConnection->expects($this->once())
+ ->method('getQueryBuilder')
+ ->with()
+ ->willReturn($queryBuilder);
+ $queryBuilder->method('expr')
+ ->willReturn($expr);
+
+ $expr->method('eq')
+ ->willReturnMap([
+ ['cr.calendarid', 'c.id', null, 'EQ_CLAUSE_1'],
+ ['co.uri', 'cr.objecturi', null, 'EQ_CLAUSE_2'],
+ ]);
+ $expr->method('andX')
+ ->willReturnMap([
+ ['EQ_CLAUSE_1', 'EQ_CLAUSE_2', 'ANDX_CLAUSE'],
+ ]);
+
+ $expr->method('lte')
+ ->with('cr.notificationdate', 'createNamedParameter-1', null)
+ ->willReturn('LTE_CLAUSE_1');
+
+ $expr->method('gte')
+ ->with('cr.eventstartdate', 'createNamedParameter-1', null)
+ ->willReturn('GTE_CLAUSE_2');
+
+ $queryBuilder->method('createNamedParameter')
+ ->willReturnMap([
+ [1337, \PDO::PARAM_STR, null, 'createNamedParameter-1'],
+ ]);
+
+ $queryBuilder->expects($this->at(0))
+ ->method('select')
+ ->with(['cr.id', 'cr.calendarid', 'cr.objecturi', 'cr.type', 'cr.notificationdate', 'cr.uid', 'co.calendardata', 'c.displayname'])
+ ->willReturn($queryBuilder);
+ $queryBuilder->expects($this->at(1))
+ ->method('from')
+ ->with('calendar_reminders', 'cr')
+ ->willReturn($queryBuilder);
+ $queryBuilder->expects($this->at(4))
+ ->method('where')
+ ->with('LTE_CLAUSE_1')
+ ->willReturn($queryBuilder);
+ $queryBuilder->expects($this->at(7))
+ ->method('andWhere')
+ ->with('GTE_CLAUSE_2')
+ ->willReturn($queryBuilder);
+ $queryBuilder->expects($this->at(9))
+ ->method('leftJoin')
+ ->with('cr', 'calendars', 'c', 'EQ_CLAUSE_1')
+ ->willReturn($queryBuilder);
+ $queryBuilder->expects($this->at(13))
+ ->method('leftJoin')
+ ->with('cr', 'calendarobjects', 'co', 'ANDX_CLAUSE')
+ ->willReturn($queryBuilder);
+ $queryBuilder->expects($this->at(14))
+ ->method('execute')
+ ->with()
+ ->willReturn($stmt);
+
+ $stmt->expects($this->once())
+ ->method('fetchAll')
+ ->with()
+ ->willReturn($dbData);
+
+ $actual = $this->reminderBackend->getRemindersToProcess();
+ $this->assertEquals($dbData, $actual);
+ }
+
+ public function testInsertReminder(): void
+ {
+ /** @var IQueryBuilder|\PHPUnit\Framework\MockObject\MockObject $queryBuilder */
+ $queryBuilder = $this->createMock(IQueryBuilder::class);
+ $stmt = $this->createMock(\Doctrine\DBAL\Driver\Statement::class);
+ $expr = $this->createMock(\OCP\DB\QueryBuilder\IExpressionBuilder::class);
+
+ $this->dbConnection->expects($this->once())
+ ->method('getQueryBuilder')
+ ->with()
+ ->will($this->returnValue($queryBuilder));
+ $queryBuilder->method('expr')
+ ->will($this->returnValue($expr));
+
+ $queryBuilder->method('createNamedParameter')
+ ->will($this->returnValueMap([
+ ['user1', \PDO::PARAM_STR, null, 'createNamedParameter-1'],
+ ['1', \PDO::PARAM_STR, null, 'createNamedParameter-2'],
+ ['object.ics', \PDO::PARAM_STR, null, 'createNamedParameter-3'],
+ ['EMAIL', \PDO::PARAM_STR, null, 'createNamedParameter-4'],
+ [1227, \PDO::PARAM_STR, null, 'createNamedParameter-5'],
+ [1337, \PDO::PARAM_STR, null, 'createNamedParameter-6'],
+ ]));
+
+ $queryBuilder->expects($this->at(0))
+ ->method('insert')
+ ->with('calendar_reminders')
+ ->willReturn($queryBuilder);
+ $queryBuilder->expects($this->at(7))
+ ->method('values')
+ ->with([
+ 'uid' => 'createNamedParameter-1',
+ 'calendarid' => 'createNamedParameter-2',
+ 'objecturi' => 'createNamedParameter-3',
+ 'type' => 'createNamedParameter-4',
+ 'notificationdate' => 'createNamedParameter-5',
+ 'eventstartdate' => 'createNamedParameter-6',
+ ])
+ ->willReturn($queryBuilder);
+ $queryBuilder->expects($this->at(8))
+ ->method('execute')
+ ->with()
+ ->willReturn($stmt);
+
+ $actual = $this->reminderBackend->insertReminder('user1', '1', 'object.ics', 'EMAIL', 1227, 1337);
+ }
+}
diff --git a/apps/dav/tests/unit/CalDAV/Reminder/NotificationProvider/EmailProviderTest.php b/apps/dav/tests/unit/CalDAV/Reminder/NotificationProvider/EmailProviderTest.php
new file mode 100644
index 0000000000..34a61b34fc
--- /dev/null
+++ b/apps/dav/tests/unit/CalDAV/Reminder/NotificationProvider/EmailProviderTest.php
@@ -0,0 +1,226 @@
+
+ *
+ * @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\DAV\Tests\unit\CalDAV\Reminder\NotificationProvider;
+
+use OCA\DAV\CalDAV\Reminder\NotificationProvider\EmailProvider;
+use OCA\DAV\CalDAV\Reminder\AbstractNotificationProvider;
+use OCP\IConfig;
+use OCP\IL10N;
+use OCP\ILogger;
+use OCP\IURLGenerator;
+use OCP\L10N\IFactory as L10NFactory;
+use OCP\IUser;
+use OCP\Mail\IEMailTemplate;
+use OCP\Mail\IMailer;
+use OCP\Mail\IAttachment;
+use OCP\Mail\IMessage;
+use Test\TestCase;
+use OCA\DAV\Tests\unit\CalDAV\Reminder\AbstractNotificationProviderTest;
+
+class EmailProviderTest extends AbstractNotificationProviderTest {
+
+ const USER_EMAIL = 'frodo@hobb.it';
+
+ /** @var ILogger|\PHPUnit\Framework\MockObject\MockObject */
+ protected $logger;
+
+ /** @var L10NFactory|\PHPUnit\Framework\MockObject\MockObject */
+ protected $l10nFactory;
+
+ /** @var IL10N|\PHPUnit\Framework\MockObject\MockObject */
+ protected $l10n;
+
+ /** @var IURLGenerator|\PHPUnit\Framework\MockObject\MockObject */
+ protected $urlGenerator;
+
+ /** @var IConfig|\PHPUnit\Framework\MockObject\MockObject */
+ protected $config;
+
+ /** @var IMailer|\PHPUnit\Framework\MockObject\MockObject */
+ private $mailer;
+
+ public function setUp() {
+ parent::setUp();
+
+ $this->mailer = $this->createMock(IMailer::class);
+
+ $this->provider = new EmailProvider(
+ $this->config,
+ $this->mailer,
+ $this->logger,
+ $this->l10nFactory,
+ $this->urlGenerator
+ );
+ }
+
+ public function testSendWithNoUserEmail(): void
+ {
+ $this->user->expects($this->once())
+ ->method('getEMailAddress')
+ ->with()
+ ->willReturn(null);
+
+ $this->mailer
+ ->expects($this->never())
+ ->method('send');
+
+ $this->provider->send($this->vcalendar, $this->calendarDisplayName, $this->user);
+ }
+
+ public function testSendWithFailedRecipients(): void
+ {
+ $this->user->expects($this->exactly(2))
+ ->method('getEMailAddress')
+ ->with()
+ ->willReturn(self::USER_EMAIL);
+
+ $this->mailer
+ ->expects($this->once())
+ ->method('send')
+ ->willReturn([self::USER_EMAIL])
+ ;
+
+ $this->logger
+ ->expects($this->once())
+ ->method('error');
+
+ $l10n = $this->createMock(IL10N::class);
+ $this->l10nFactory
+ ->method('get')
+ ->willReturn($l10n);
+
+ $this->provider->send($this->vcalendar, $this->calendarDisplayName, $this->user);
+ }
+
+ public function testSendWithMailerFailure(): void
+ {
+ $this->user->expects($this->exactly(2))
+ ->method('getEMailAddress')
+ ->with()
+ ->willReturn(self::USER_EMAIL);
+
+ $ex = new \Exception();
+
+ $this->mailer
+ ->expects($this->once())
+ ->method('send')
+ ->will($this->throwException($ex))
+ ;
+
+ $this->logger
+ ->expects($this->once())
+ ->method('logException')
+ ->with($ex, ['app' => 'dav']);
+
+ $l10n = $this->createMock(IL10N::class);
+ $this->l10nFactory
+ ->method('get')
+ ->willReturn($l10n);
+
+ $this->provider->send($this->vcalendar, $this->calendarDisplayName, $this->user);
+ }
+
+ public function testSend(): void
+ {
+ $this->user->expects($this->exactly(2))
+ ->method('getEMailAddress')
+ ->with()
+ ->willReturn(self::USER_EMAIL);
+
+ $this->user->expects($this->once())
+ ->method('getDisplayName')
+ ->with()
+ ->willReturn('Frodo');
+
+ $this->urlGenerator
+ ->expects($this->exactly(2))
+ ->method('getAbsoluteURL');
+
+ $this->urlGenerator
+ ->expects($this->exactly(2))
+ ->method('imagePath');
+
+ $mailMessage = $this->createMock(IMessage::class);
+ $mailMessage->expects($this->once())
+ ->method('setFrom')
+ ->with([\OCP\Util::getDefaultEmailAddress('invitations-noreply') => 'Nextcloud'])
+ ->willReturn($mailMessage);
+
+ $mailMessage->expects($this->once())
+ ->method('setTo')
+ ->with([self::USER_EMAIL => 'Frodo'])
+ ->willReturn($mailMessage);
+
+ $mailMessage
+ ->expects($this->never())
+ ->method('setReplyTo')
+ ->willReturn($mailMessage);
+
+ $emailTemplate = $this->createMock(IEMailTemplate::class);
+ $this->mailer
+ ->expects($this->once())
+ ->method('createEMailTemplate')
+ ->willReturn($emailTemplate);
+
+ $emailTemplate->expects($this->once())
+ ->method('setSubject')
+ ->with('Notification: Fellowship meeting');
+
+ $emailTemplate->expects($this->once())
+ ->method('addHeader');
+
+ $emailTemplate->expects($this->once())
+ ->method('addHeading');
+
+ $emailTemplate->expects($this->exactly(2))
+ ->method('addBodyListItem');
+
+ $emailTemplate->expects($this->once())
+ ->method('addFooter');
+
+ $mailMessage->expects($this->once())
+ ->method('useTemplate')
+ ->with($emailTemplate);
+
+ $this->mailer
+ ->expects($this->once())
+ ->method('createMessage')
+ ->willReturn($mailMessage);
+
+ $emailAttachment = $this->createMock(IAttachment::class);
+ $this->mailer
+ ->expects($this->once())
+ ->method('createAttachment')
+ ->willReturn($emailAttachment);
+
+ $this->mailer
+ ->expects($this->once())
+ ->method('send');
+
+ $l10n = $this->createMock(IL10N::class);
+ $this->l10nFactory
+ ->method('get')
+ ->willReturn($l10n);
+
+ $this->provider->send($this->vcalendar, $this->calendarDisplayName, $this->user);
+ }
+}
diff --git a/apps/dav/tests/unit/CalDAV/Reminder/NotificationProvider/PushProviderTest.php b/apps/dav/tests/unit/CalDAV/Reminder/NotificationProvider/PushProviderTest.php
new file mode 100644
index 0000000000..e10afb44d2
--- /dev/null
+++ b/apps/dav/tests/unit/CalDAV/Reminder/NotificationProvider/PushProviderTest.php
@@ -0,0 +1,139 @@
+
+ *
+ * @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\DAV\Tests\unit\CalDAV\Reminder\NotificationProvider;
+
+use OCA\DAV\AppInfo\Application;
+use OCA\DAV\CalDAV\Reminder\NotificationProvider\PushProvider;
+use OCA\DAV\CalDAV\Reminder\AbstractNotificationProvider;
+use OCP\IConfig;
+use OCP\IL10N;
+use OCP\ILogger;
+use OCP\IURLGenerator;
+use OCP\L10N\IFactory as L10NFactory;
+use OCP\IUser;
+use OCP\Notification\IManager;
+use OCP\Notification\INotification;
+use OCP\AppFramework\Utility\ITimeFactory;
+use Test\TestCase;
+use OCA\DAV\Tests\unit\CalDAV\Reminder\AbstractNotificationProviderTest;
+
+class PushProviderTest extends AbstractNotificationProviderTest {
+
+ /** @var ILogger|\PHPUnit\Framework\MockObject\MockObject */
+ protected $logger;
+
+ /** @var L10NFactory|\PHPUnit\Framework\MockObject\MockObject */
+ protected $l10nFactory;
+
+ /** @var IL10N|\PHPUnit\Framework\MockObject\MockObject */
+ protected $l10n;
+
+ /** @var IURLGenerator|\PHPUnit\Framework\MockObject\MockObject */
+ protected $urlGenerator;
+
+ /** @var IConfig|\PHPUnit\Framework\MockObject\MockObject */
+ protected $config;
+
+ /** @var IManager|\PHPUnit\Framework\MockObject\MockObject */
+ private $manager;
+
+ /** @var ITimeFactory|\PHPUnit\Framework\MockObject\MockObject */
+ private $timeFactory;
+
+ public function setUp() {
+ parent::setUp();
+
+ $this->manager = $this->createMock(IManager::class);
+ $this->timeFactory = $this->createMock(ITimeFactory::class);
+
+ $this->provider = new PushProvider(
+ $this->config,
+ $this->manager,
+ $this->logger,
+ $this->l10nFactory,
+ $this->urlGenerator,
+ $this->timeFactory
+ );
+ }
+
+ public function testSend(): void
+ {
+ $notification = $this->createMock(INotification::class);
+ $notification
+ ->expects($this->once())
+ ->method('setApp')
+ ->with(Application::APP_ID)
+ ->willReturn($notification);
+
+ $notification
+ ->expects($this->once())
+ ->method('setUser')
+ ->willReturn($notification)
+ ;
+
+ $notification
+ ->expects($this->once())
+ ->method('setDateTime')
+ ->willReturn($notification)
+ ;
+
+ $notification
+ ->expects($this->once())
+ ->method('setObject')
+ ->willReturn($notification)
+ ;
+
+ $notification
+ ->expects($this->once())
+ ->method('setSubject')
+ ->willReturn($notification)
+ ;
+
+ $notification
+ ->expects($this->once())
+ ->method('setMessage')
+ ->willReturn($notification)
+ ;
+
+ $this->manager
+ ->expects($this->once())
+ ->method('createNotification')
+ ->willReturn($notification);
+
+ $this->manager
+ ->expects($this->once())
+ ->method('notify')
+ ->with($notification);
+
+ $l10n = $this->createMock(IL10N::class);
+ $this->l10nFactory
+ ->method('get')
+ ->willReturn($l10n);
+
+ $this->timeFactory->expects($this->once())
+ ->method('getDateTime')
+ ->with()
+ ->willReturn(new \DateTime());
+
+ $this->provider->send($this->vcalendar, $this->calendarDisplayName, $this->user);
+ }
+}
diff --git a/apps/dav/tests/unit/CalDAV/Reminder/NotificationProviderManagerTest.php b/apps/dav/tests/unit/CalDAV/Reminder/NotificationProviderManagerTest.php
new file mode 100644
index 0000000000..d962b631c1
--- /dev/null
+++ b/apps/dav/tests/unit/CalDAV/Reminder/NotificationProviderManagerTest.php
@@ -0,0 +1,100 @@
+
+ *
+ * @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\DAV\Tests\unit\CalDAV\Reminder;
+
+use OCA\DAV\CalDAV\Reminder\NotificationProvider\EmailProvider;
+use OCA\DAV\CalDAV\Reminder\NotificationProvider\ProviderNotAvailableException;
+use OCA\DAV\CalDAV\Reminder\NotificationProvider\PushProvider;
+use OCA\DAV\CalDAV\Reminder\NotificationProviderManager;
+use OCA\DAV\CalDAV\Reminder\NotificationTypeDoesNotExistException;
+use OCA\DAV\Capabilities;
+use Test\TestCase;
+
+class NotificationProviderManagerTest extends TestCase {
+
+ /** @var NotificationProviderManager|\PHPUnit\Framework\MockObject\MockObject */
+ private $providerManager;
+
+ /**
+ * @throws \OCP\AppFramework\QueryException
+ */
+ public function setUp() {
+ parent::setUp();
+
+ $this->providerManager = new NotificationProviderManager();
+ $this->providerManager->registerProvider(EmailProvider::class);
+ }
+
+ /**
+ * @expectedException OCA\DAV\CalDAV\Reminder\NotificationTypeDoesNotExistException
+ * @expectedExceptionMessage Type NOT EXISTENT is not an accepted type of notification
+ * @throws ProviderNotAvailableException
+ * @throws NotificationTypeDoesNotExistException
+ */
+ public function testGetProviderForUnknownType(): void
+ {
+ $this->providerManager->getProvider('NOT EXISTENT');
+ }
+
+ /**
+ * @expectedException OCA\DAV\CalDAV\Reminder\NotificationProvider\ProviderNotAvailableException
+ * @expectedExceptionMessage No notification provider for type AUDIO available
+ * @throws NotificationTypeDoesNotExistException
+ * @throws ProviderNotAvailableException
+ */
+ public function testGetProviderForUnRegisteredType(): void
+ {
+ $this->providerManager->getProvider('AUDIO');
+ }
+
+ /**
+ * @throws NotificationTypeDoesNotExistException
+ * @throws ProviderNotAvailableException
+ */
+ public function testGetProvider(): void
+ {
+ $provider = $this->providerManager->getProvider('EMAIL');
+ $this->assertInstanceOf(EmailProvider::class, $provider);
+ }
+
+ /**
+ * @throws NotificationTypeDoesNotExistException
+ * @throws ProviderNotAvailableException
+ * @throws \OCP\AppFramework\QueryException
+ */
+ public function testRegisterProvider(): void
+ {
+ $this->providerManager->registerProvider(PushProvider::class);
+ $provider = $this->providerManager->getProvider('DISPLAY');
+ $this->assertInstanceOf(PushProvider::class, $provider);
+ }
+
+ /**
+ * @expectedExceptionMessage Invalid notification provider registered
+ * @expectedException \InvalidArgumentException
+ * @throws \OCP\AppFramework\QueryException
+ */
+ public function testRegisterBadProvider(): void
+ {
+ $this->providerManager->registerProvider(Capabilities::class);
+ }
+}
diff --git a/apps/dav/tests/unit/CalDAV/Reminder/NotifierTest.php b/apps/dav/tests/unit/CalDAV/Reminder/NotifierTest.php
new file mode 100644
index 0000000000..b9695b33fd
--- /dev/null
+++ b/apps/dav/tests/unit/CalDAV/Reminder/NotifierTest.php
@@ -0,0 +1,184 @@
+
+ *
+ * @author Thomas Citharel
+ *
+ * @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\DAV\Tests\unit\CalDAV\Reminder;
+
+use OCA\DAV\AppInfo\Application;
+use OCA\DAV\CalDAV\Reminder\Notifier;
+use OCP\IL10N;
+use OCP\IURLGenerator;
+use OCP\L10N\IFactory;
+use OCP\Notification\INotification;
+use Test\TestCase;
+
+class NotifierTest extends TestCase {
+ /** @var Notifier */
+ protected $notifier;
+
+ /** @var IFactory|\PHPUnit\Framework\MockObject\MockObject */
+ protected $factory;
+ /** @var IURLGenerator|\PHPUnit\Framework\MockObject\MockObject */
+ protected $urlGenerator;
+ /** @var IL10N|\PHPUnit\Framework\MockObject\MockObject */
+ protected $l;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->urlGenerator = $this->createMock(IURLGenerator::class);
+ $this->l = $this->createMock(IL10N::class);
+ $this->l->expects($this->any())
+ ->method('t')
+ ->willReturnCallback(function($string, $args) {
+ return vsprintf($string, $args);
+ });
+ $this->l->expects($this->any())
+ ->method('n')
+ ->willReturnCallback(function($textSingular, $textPlural, $count, $args) {
+ $text = $count === 1 ? $textSingular : $textPlural;
+ $text = str_replace('%n', (string)$count, $text);
+ return vsprintf($text, $args);
+ });
+ $this->factory = $this->createMock(IFactory::class);
+ $this->factory->expects($this->any())
+ ->method('get')
+ ->willReturn($this->l);
+
+ $this->notifier = new Notifier(
+ $this->factory,
+ $this->urlGenerator
+ );
+ }
+
+ /**
+ * @expectedException \InvalidArgumentException
+ * @expectedExceptionMessage Notification not from this app
+ */
+ public function testPrepareWrongApp(): void
+ {
+ /** @var INotification|\PHPUnit\Framework\MockObject\MockObject $notification */
+ $notification = $this->createMock(INotification::class);
+
+ $notification->expects($this->once())
+ ->method('getApp')
+ ->willReturn('notifications');
+ $notification->expects($this->never())
+ ->method('getSubject');
+
+ $this->notifier->prepare($notification, 'en');
+ }
+
+ /**
+ * @expectedException \InvalidArgumentException
+ * @expectedExceptionMessage Unknown subject
+ */
+ public function testPrepareWrongSubject() {
+ /** @var INotification|\PHPUnit\Framework\MockObject\MockObject $notification */
+ $notification = $this->createMock(INotification::class);
+
+ $notification->expects($this->once())
+ ->method('getApp')
+ ->willReturn(Application::APP_ID);
+ $notification->expects($this->once())
+ ->method('getSubject')
+ ->willReturn('wrong subject');
+
+ $this->notifier->prepare($notification, 'en');
+ }
+
+ public function dataPrepare(): array
+ {
+ return [
+ [
+ 'calendar_reminder',
+ [
+ 'title' => 'foo',
+ 'start' => time() - 60 * 60 * 24
+ ],
+ 'foo (one day ago)',
+ [
+ 'when' => 'foo',
+ 'description' => 'bar',
+ 'location' => 'NC Headquarters',
+ 'calendar' => 'Personal'
+ ],
+ 'Calendar: Personal Date: foo Description: bar Where: NC Headquarters'
+ ],
+ ];
+ }
+
+ /**
+ * @dataProvider dataPrepare
+ *
+ * @param string $subjectType
+ * @param array $subjectParams
+ * @param string $subject
+ * @param array $messageParams
+ * @param string $message
+ * @throws \Exception
+ */
+ public function testPrepare(string $subjectType, array $subjectParams, string $subject, array $messageParams, string $message): void
+ {
+ /** @var INotification|\PHPUnit\Framework\MockObject\MockObject $notification */
+ $notification = $this->createMock(INotification::class);
+
+ $notification->expects($this->once())
+ ->method('getApp')
+ ->willReturn(Application::APP_ID);
+ $notification->expects($this->once())
+ ->method('getSubject')
+ ->willReturn($subjectType);
+ $notification->expects($this->once())
+ ->method('getSubjectParameters')
+ ->willReturn($subjectParams);
+ $notification->expects($this->once())
+ ->method('getMessageParameters')
+ ->willReturn($messageParams);
+
+ $notification->expects($this->once())
+ ->method('setParsedSubject')
+ ->with($subject)
+ ->willReturnSelf();
+
+ $notification->expects($this->once())
+ ->method('setParsedMessage')
+ ->with($message)
+ ->willReturnSelf();
+
+ $this->urlGenerator->expects($this->once())
+ ->method('imagePath')
+ ->with('core', 'places/calendar.svg')
+ ->willReturn('icon-url');
+ $this->urlGenerator->expects($this->once())
+ ->method('getAbsoluteURL')
+ ->with('icon-url')
+ ->willReturn('absolute-icon-url');
+ $notification->expects($this->once())
+ ->method('setIcon')
+ ->with('absolute-icon-url')
+ ->willReturnSelf();
+
+ $return = $this->notifier->prepare($notification, 'en');
+
+ $this->assertEquals($notification, $return);
+ }
+}
diff --git a/apps/dav/tests/unit/CalDAV/Reminder/ReminderServiceTest.php b/apps/dav/tests/unit/CalDAV/Reminder/ReminderServiceTest.php
new file mode 100644
index 0000000000..061c3f16e3
--- /dev/null
+++ b/apps/dav/tests/unit/CalDAV/Reminder/ReminderServiceTest.php
@@ -0,0 +1,276 @@
+
+ *
+ * @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\DAV\Tests\unit\CalDAV\Reminder;
+
+use OCA\DAV\CalDAV\Reminder\AbstractNotificationProvider;
+use OCA\DAV\CalDAV\Reminder\Backend;
+use OCA\DAV\CalDAV\Reminder\NotificationProviderManager;
+use OCA\DAV\CalDAV\Reminder\NotificationProvider\EmailProvider;
+use OCA\DAV\CalDAV\Reminder\NotificationProvider\PushProvider;
+use OCA\DAV\CalDAV\Reminder\ReminderService;
+use OCP\IGroup;
+use OCP\IGroupManager;
+use OCP\IUser;
+use OCP\IUserManager;
+use OCP\IUserSession;
+use Test\TestCase;
+
+class ReminderServiceTest extends TestCase {
+
+ /** @var Backend|\PHPUnit\Framework\MockObject\MockObject */
+ private $backend;
+
+ /** @var NotificationProviderManager|\PHPUnit\Framework\MockObject\MockObject */
+ private $notificationProviderManager;
+
+ /** @var IUserManager|\PHPUnit\Framework\MockObject\MockObject */
+ private $userManager;
+
+ /** @var IGroupManager|\PHPUnit\Framework\MockObject\MockObject*/
+ private $groupManager;
+
+ /** @var IUserSession|\PHPUnit\Framework\MockObject\MockObject */
+ private $userSession;
+
+ public const CALENDAR_DATA = <<backend = $this->createMock(Backend::class);
+ $this->notificationProviderManager = $this->createMock(NotificationProviderManager::class);
+ $this->userManager = $this->createMock(IUserManager::class);
+ $this->groupManager = $this->createMock(IGroupManager::class);
+ $this->userSession = $this->createMock(IUserSession::class);
+ }
+
+ public function dataTestProcessReminders(): array
+ {
+ return [
+ [
+ [], null
+ ],
+ [
+ [
+ [
+ 'calendardata' => self::CALENDAR_DATA,
+ 'displayname' => 'Personal',
+ 'type' => 'EMAIL',
+ 'uid' => 1,
+ 'id' => 1,
+ ],
+ ],
+ $this->createMock(EmailProvider::class),
+ ],
+ [
+ [
+ [
+ 'calendardata' => self::CALENDAR_DATA,
+ 'displayname' => 'Personal',
+ 'type' => 'DISPLAY',
+ 'uid' => 1,
+ 'id' => 1,
+ ],
+ ],
+ $this->createMock(PushProvider::class),
+ ]
+ ];
+ }
+
+ /**
+ * @dataProvider dataTestProcessReminders
+ * @param array $reminders
+ * @param AbstractNotificationProvider|null $notificationProvider
+ * @throws \OCA\DAV\CalDAV\Reminder\NotificationProvider\ProviderNotAvailableException
+ * @throws \OCA\DAV\CalDAV\Reminder\NotificationTypeDoesNotExistException
+ * @throws \OC\User\NoUserException
+ */
+ public function testProcessReminders(array $reminders, ?AbstractNotificationProvider $notificationProvider): void
+ {
+ $user = $this->createMock(IUser::class);
+
+ $this->backend->expects($this->once())->method('getRemindersToProcess')->willReturn($reminders);
+ if (count($reminders) > 0) {
+ $this->userManager->expects($this->exactly(count($reminders)))->method('get')->willReturn($user);
+ $this->backend->expects($this->exactly(count($reminders)))->method('removeReminder');
+ $this->notificationProviderManager->expects($this->exactly(count($reminders)))->method('getProvider')->willReturn($notificationProvider);
+ }
+
+ $reminderService = new ReminderService($this->backend, $this->notificationProviderManager, $this->userManager, $this->groupManager, $this->userSession);
+ $reminderService->processReminders();
+ }
+
+ /**
+ * @expectedException OC\User\NoUserException
+ */
+ public function testProcessReminderWithBadUser(): void
+ {
+ $this->backend->expects($this->once())->method('getRemindersToProcess')->willReturn([
+ [
+ 'calendardata' => self::CALENDAR_DATA,
+ 'type' => 'DISPLAY',
+ 'uid' => 1,
+ 'id' => 1,
+ ]
+ ]);
+ $this->userManager->expects($this->once())->method('get')->with(1)->willReturn(null);
+ $reminderService = new ReminderService($this->backend, $this->notificationProviderManager, $this->userManager, $this->groupManager, $this->userSession);
+ $reminderService->processReminders();
+ }
+
+ public function providesTouchCalendarObject(): array
+ {
+ return [
+ [
+ '\OCA\DAV\CalDAV\CalDavBackend::deleteCalendarObject',
+ [
+ 'principaluri' => 'principals/users/personal'
+ ],
+ [],
+ [
+ 'calendarid' => 1,
+ 'uri' => 'something.ics',
+ ],
+ 0
+ ],
+ [
+ '\OCA\DAV\CalDAV\CalDavBackend::createCalendarObject',
+ [
+ 'principaluri' => 'principals/users/personal'
+ ],
+ [],
+ [
+ 'calendarid' => 1,
+ 'uri' => 'something.ics',
+ 'calendardata' => self::CALENDAR_DATA
+ ],
+ 0
+ ],
+ [
+ '\OCA\DAV\CalDAV\CalDavBackend::updateCalendarObject',
+ [
+ 'principaluri' => 'principals/users/someone',
+ 'uri' => 'personal'
+ ],
+ [
+ [
+ '{http://owncloud.org/ns}principal' => 'principals/users/someone'
+ ]
+ ],
+ [
+ 'calendarid' => 1,
+ 'uri' => 'something.ics',
+ 'calendardata' => self::CALENDAR_DATA
+ ],
+ 0
+ ],
+ [
+ '\OCA\DAV\CalDAV\CalDavBackend::createCalendarObject',
+ [
+ 'principaluri' => 'principals/users/someone',
+ 'uri' => 'personal'
+ ],
+ [
+ [
+ '{http://owncloud.org/ns}principal' => 'principals/groups/somegroup'
+ ]
+ ],
+ [
+ 'calendarid' => 1,
+ 'uri' => 'something.ics',
+ 'calendardata' => self::CALENDAR_DATA
+ ],
+ 1
+ ]
+ ];
+ }
+
+ /**
+ * @dataProvider providesTouchCalendarObject
+ * @param string $action
+ * @param array $calendarData
+ * @param array $shares
+ * @param array $objectData
+ * @param int $numberOfGroups
+ * @throws \OC\User\NoUserException
+ * @throws \Sabre\VObject\InvalidDataException
+ */
+ public function testOnTouchCalendarObject(string $action, array $calendarData, array $shares, array $objectData, int $numberOfGroups): void
+ {
+ $this->backend->expects($this->once())->method('cleanRemindersForEvent')->with($objectData['calendarid'], $objectData['uri']);
+
+ if ($action !== '\OCA\DAV\CalDAV\CalDavBackend::deleteCalendarObject') {
+ $user = $this->createMock(IUser::class);
+ $user->expects($this->once())->method('getUID')->willReturn('user');
+
+ $this->userSession->expects($this->once())->method('getUser')->willReturn($user);
+ if ($numberOfGroups === 0) {
+ $this->backend->expects($this->exactly(count($shares) + 1))->method('insertReminder');
+ } else {
+ $group = $this->createMock(IGroup::class);
+ $groupUser = $this->createMock(IUser::class);
+ $groupUser->expects($this->once())->method('getUID')->willReturn('groupuser');
+ $group->expects($this->once())->method('getUsers')->willReturn([$groupUser]);
+ $this->groupManager->expects($this->exactly($numberOfGroups))->method('get')->willReturn($group);
+ }
+ }
+ $reminderService = new ReminderService($this->backend, $this->notificationProviderManager, $this->userManager, $this->groupManager, $this->userSession);
+ $reminderService->onTouchCalendarObject($action, $calendarData, $shares, $objectData);
+ }
+
+ /**
+ * @expectedException OC\User\NoUserException
+ */
+ public function testOnTouchCalendarObjectWithNoSession(): void
+ {
+ $this->backend->expects($this->once())->method('cleanRemindersForEvent');
+ $this->userSession->expects($this->once())->method('getUser')->willReturn(null);
+
+ $reminderService = new ReminderService($this->backend, $this->notificationProviderManager, $this->userManager, $this->groupManager, $this->userSession);
+ $reminderService->onTouchCalendarObject('', ['principaluri' => 'foo'], [], ['calendarid' => 1, 'uri' => 'bar']);
+ }
+
+ public function testOnTouchCalendarObjectWithNoCalendarURI(): void
+ {
+ $reminderService = new ReminderService($this->backend, $this->notificationProviderManager, $this->userManager, $this->groupManager, $this->userSession);
+ $this->assertNull($reminderService->onTouchCalendarObject('', [], [], []));
+ }
+}
From d74315ac3d31d7ec30fa7fa13ef87c34bd4f6181 Mon Sep 17 00:00:00 2001
From: Georg Ehrke
Date: Fri, 2 Aug 2019 16:25:35 +0200
Subject: [PATCH 02/10] add occ dav:send-event-reminders, so you don't have to
rely on the background-job
Signed-off-by: Georg Ehrke
---
apps/dav/appinfo/info.xml | 1 +
.../composer/composer/autoload_classmap.php | 1 +
.../dav/composer/composer/autoload_static.php | 1 +
apps/dav/lib/Command/SendEventReminders.php | 74 +++++++++++++++++++
4 files changed, 77 insertions(+)
create mode 100644 apps/dav/lib/Command/SendEventReminders.php
diff --git a/apps/dav/appinfo/info.xml b/apps/dav/appinfo/info.xml
index 91617e9002..6e0219e91d 100644
--- a/apps/dav/appinfo/info.xml
+++ b/apps/dav/appinfo/info.xml
@@ -46,6 +46,7 @@
OCA\DAV\Command\CreateCalendarOCA\DAV\Command\MoveCalendarOCA\DAV\Command\ListCalendars
+ OCA\DAV\Command\SendEventRemindersOCA\DAV\Command\SyncBirthdayCalendarOCA\DAV\Command\SyncSystemAddressBookOCA\DAV\Command\RemoveInvalidShares
diff --git a/apps/dav/composer/composer/autoload_classmap.php b/apps/dav/composer/composer/autoload_classmap.php
index 33977f3166..59bcf7caf4 100644
--- a/apps/dav/composer/composer/autoload_classmap.php
+++ b/apps/dav/composer/composer/autoload_classmap.php
@@ -96,6 +96,7 @@ return array(
'OCA\\DAV\\Command\\ListCalendars' => $baseDir . '/../lib/Command/ListCalendars.php',
'OCA\\DAV\\Command\\MoveCalendar' => $baseDir . '/../lib/Command/MoveCalendar.php',
'OCA\\DAV\\Command\\RemoveInvalidShares' => $baseDir . '/../lib/Command/RemoveInvalidShares.php',
+ 'OCA\\DAV\\Command\\SendEventReminders' => $baseDir . '/../lib/Command/SendEventReminders.php',
'OCA\\DAV\\Command\\SyncBirthdayCalendar' => $baseDir . '/../lib/Command/SyncBirthdayCalendar.php',
'OCA\\DAV\\Command\\SyncSystemAddressBook' => $baseDir . '/../lib/Command/SyncSystemAddressBook.php',
'OCA\\DAV\\Comments\\CommentNode' => $baseDir . '/../lib/Comments/CommentNode.php',
diff --git a/apps/dav/composer/composer/autoload_static.php b/apps/dav/composer/composer/autoload_static.php
index 8ca83bb8b7..a28743ce43 100644
--- a/apps/dav/composer/composer/autoload_static.php
+++ b/apps/dav/composer/composer/autoload_static.php
@@ -111,6 +111,7 @@ class ComposerStaticInitDAV
'OCA\\DAV\\Command\\ListCalendars' => __DIR__ . '/..' . '/../lib/Command/ListCalendars.php',
'OCA\\DAV\\Command\\MoveCalendar' => __DIR__ . '/..' . '/../lib/Command/MoveCalendar.php',
'OCA\\DAV\\Command\\RemoveInvalidShares' => __DIR__ . '/..' . '/../lib/Command/RemoveInvalidShares.php',
+ 'OCA\\DAV\\Command\\SendEventReminders' => __DIR__ . '/..' . '/../lib/Command/SendEventReminders.php',
'OCA\\DAV\\Command\\SyncBirthdayCalendar' => __DIR__ . '/..' . '/../lib/Command/SyncBirthdayCalendar.php',
'OCA\\DAV\\Command\\SyncSystemAddressBook' => __DIR__ . '/..' . '/../lib/Command/SyncSystemAddressBook.php',
'OCA\\DAV\\Comments\\CommentNode' => __DIR__ . '/..' . '/../lib/Comments/CommentNode.php',
diff --git a/apps/dav/lib/Command/SendEventReminders.php b/apps/dav/lib/Command/SendEventReminders.php
new file mode 100644
index 0000000000..02d8a28726
--- /dev/null
+++ b/apps/dav/lib/Command/SendEventReminders.php
@@ -0,0 +1,74 @@
+
+ *
+ * @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\DAV\Command;
+
+use OCA\DAV\CalDAV\Reminder\ReminderService;
+use OCP\IConfig;
+use Symfony\Component\Console\Command\Command;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Output\OutputInterface;
+
+class SendEventReminders extends Command {
+
+ /** @var ReminderService */
+ protected $reminderService;
+
+ /** @var IConfig */
+ protected $config;
+
+ public function __construct(string $name = null,
+ ReminderService $reminderService,
+ IConfig $config) {
+ parent::__construct($name);
+ $this->reminderService = $reminderService;
+ $this->config = $config;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ protected function configure():void {
+ $this
+ ->setName('dav:send-event-reminders')
+ ->setDescription('Sends event reminders');
+ }
+
+ /**
+ * @param InputInterface $input
+ * @param OutputInterface $output
+ */
+ protected function execute(InputInterface $input, OutputInterface $output):void {
+ if ($this->config->getAppValue('dav', 'sendEventReminders', 'yes') !== 'yes') {
+ $output->writeln('Sending event reminders disabled!');
+ $output->writeln('Please run "php occ config:app:set dav sendEventReminders --value yes"');
+ return;
+ }
+
+ if ($this->config->getAppValue('dav', 'sendEventRemindersMode', 'backgroundjob') !== 'occ') {
+ $output->writeln('Sending event reminders mode set to background-job!');
+ $output->writeln('Please run "php occ config:app:set dav sendEventRemindersMode --value occ"');
+ return;
+ }
+
+ $this->reminderService->processReminders();
+ }
+}
From 36b9b51297165afcb9071d72f83cf431b01b4edd Mon Sep 17 00:00:00 2001
From: Georg Ehrke
Date: Fri, 2 Aug 2019 16:26:51 +0200
Subject: [PATCH 03/10] run send reminders background-job only when mode is not
set to occ
Signed-off-by: Georg Ehrke
---
apps/dav/lib/BackgroundJob/EventReminderJob.php | 13 +++++++++----
1 file changed, 9 insertions(+), 4 deletions(-)
diff --git a/apps/dav/lib/BackgroundJob/EventReminderJob.php b/apps/dav/lib/BackgroundJob/EventReminderJob.php
index e0a147e420..dfa76ffe16 100644
--- a/apps/dav/lib/BackgroundJob/EventReminderJob.php
+++ b/apps/dav/lib/BackgroundJob/EventReminderJob.php
@@ -50,10 +50,15 @@ class EventReminderJob extends TimedJob {
* @throws \OCA\DAV\CalDAV\Reminder\NotificationTypeDoesNotExistException
* @throws \OC\User\NoUserException
*/
- public function run($arg): void
- {
- if ($this->config->getAppValue('dav', 'sendEventReminders', 'yes') === 'yes') {
- $this->reminderService->processReminders();
+ public function run($arg):void {
+ if ($this->config->getAppValue('dav', 'sendEventReminders', 'yes') !== 'yes') {
+ return;
}
+
+ if ($this->config->getAppValue('dav', 'sendEventRemindersMode', 'backgroundjob') !== 'backgroundjob') {
+ return;
+ }
+
+ $this->reminderService->processReminders();
}
}
From 11fa45196ec5229e640a4e27569f94929510d7da Mon Sep 17 00:00:00 2001
From: Georg Ehrke
Date: Fri, 2 Aug 2019 16:34:39 +0200
Subject: [PATCH 04/10] smaller syntax changes to CalDAV reminder classes
Signed-off-by: Georg Ehrke
---
.../Reminder/AbstractNotificationProvider.php | 15 ++++++---------
apps/dav/lib/CalDAV/Reminder/Backend.php | 15 +++++----------
.../NotificationProvider/EmailProvider.php | 17 +++++++----------
.../NotificationProvider/PushProvider.php | 8 +++-----
.../Reminder/NotificationProviderManager.php | 6 ++----
apps/dav/lib/CalDAV/Reminder/Notifier.php | 17 +++++++----------
.../dav/lib/CalDAV/Reminder/ReminderService.php | 16 ++++++++--------
7 files changed, 38 insertions(+), 56 deletions(-)
diff --git a/apps/dav/lib/CalDAV/Reminder/AbstractNotificationProvider.php b/apps/dav/lib/CalDAV/Reminder/AbstractNotificationProvider.php
index ba928fac80..1d858627de 100644
--- a/apps/dav/lib/CalDAV/Reminder/AbstractNotificationProvider.php
+++ b/apps/dav/lib/CalDAV/Reminder/AbstractNotificationProvider.php
@@ -37,9 +37,9 @@ use Sabre\VObject\DateTimeParser;
use Sabre\VObject\Parameter;
use Sabre\VObject\Property;
-abstract class AbstractNotificationProvider
-{
+abstract class AbstractNotificationProvider {
+ /** @var string */
public const NOTIFICATION_TYPE = '';
/** @var ILogger */
@@ -78,7 +78,7 @@ abstract class AbstractNotificationProvider
* @param IUser $user
* @return void
*/
- public function send(VCalendar $vcalendar, string $calendarDisplayName, IUser $user): void {}
+ abstract function send(VCalendar $vcalendar, string $calendarDisplayName, IUser $user): void;
/**
* @var VCalendar $vcalendar
@@ -86,8 +86,7 @@ abstract class AbstractNotificationProvider
* @return array
* @throws \Exception
*/
- protected function extractEventDetails(VCalendar $vcalendar, $defaultValue = ''): array
- {
+ protected function extractEventDetails(VCalendar $vcalendar, $defaultValue = ''):array {
/** @var VEvent $vevent */
$vevent = $vcalendar->VEVENT;
@@ -129,8 +128,7 @@ abstract class AbstractNotificationProvider
* @return string
* @throws \Exception
*/
- private function generateWhenString(Property $dtstart, Property $dtend): string
- {
+ private function generateWhenString(Property $dtstart, Property $dtend):string {
$isAllDay = $dtstart instanceof Property\ICalendar\Date;
/** @var Property\ICalendar\Date | Property\ICalendar\DateTime $dtstart */
@@ -201,8 +199,7 @@ abstract class AbstractNotificationProvider
* @param DateTime $dtEnd
* @return bool
*/
- private function isDayEqual(DateTime $dtStart, DateTime $dtEnd): bool
- {
+ private function isDayEqual(DateTime $dtStart, DateTime $dtEnd):bool {
return $dtStart->format('Y-m-d') === $dtEnd->format('Y-m-d');
}
}
diff --git a/apps/dav/lib/CalDAV/Reminder/Backend.php b/apps/dav/lib/CalDAV/Reminder/Backend.php
index c85e7c365d..087a5785f3 100644
--- a/apps/dav/lib/CalDAV/Reminder/Backend.php
+++ b/apps/dav/lib/CalDAV/Reminder/Backend.php
@@ -56,8 +56,7 @@ class Backend {
* @param int $notificationDate
* @param int $eventStartDate
*/
- public function insertReminder(string $uid, string $calendarId, string $uri, string $type, int $notificationDate, int $eventStartDate): void
- {
+ public function insertReminder(string $uid, string $calendarId, string $uri, string $type, int $notificationDate, int $eventStartDate):void {
$query = $this->db->getQueryBuilder();
$query->insert('calendar_reminders')
->values([
@@ -76,8 +75,7 @@ class Backend {
* @param int $calendarId
* @param string $objectUri
*/
- public function cleanRemindersForEvent(int $calendarId, string $objectUri): void
- {
+ public function cleanRemindersForEvent(int $calendarId, string $objectUri):void {
$query = $this->db->getQueryBuilder();
$query->delete('calendar_reminders')
@@ -92,8 +90,7 @@ class Backend {
* @param integer $calendarId
* @return void
*/
- public function cleanRemindersForCalendar(int $calendarId): void
- {
+ public function cleanRemindersForCalendar(int $calendarId):void {
$query = $this->db->getQueryBuilder();
$query->delete('calendar_reminders')
@@ -107,8 +104,7 @@ class Backend {
* @param integer $reminderId
* @return void
*/
- public function removeReminder(int $reminderId): void
- {
+ public function removeReminder(int $reminderId):void {
$query = $this->db->getQueryBuilder();
$query->delete('calendar_reminders')
@@ -122,8 +118,7 @@ class Backend {
* @return array
* @throws \Exception
*/
- public function getRemindersToProcess(): array
- {
+ public function getRemindersToProcess():array {
$query = $this->db->getQueryBuilder();
$fields = ['cr.id', 'cr.calendarid', 'cr.objecturi', 'cr.type', 'cr.notificationdate', 'cr.uid', 'co.calendardata', 'c.displayname'];
$stmt = $query->select($fields)
diff --git a/apps/dav/lib/CalDAV/Reminder/NotificationProvider/EmailProvider.php b/apps/dav/lib/CalDAV/Reminder/NotificationProvider/EmailProvider.php
index 81d4474011..f05439932b 100644
--- a/apps/dav/lib/CalDAV/Reminder/NotificationProvider/EmailProvider.php
+++ b/apps/dav/lib/CalDAV/Reminder/NotificationProvider/EmailProvider.php
@@ -33,11 +33,12 @@ use OCP\Mail\IMailer;
use OCP\IUser;
use Sabre\VObject\Component\VCalendar;
-class EmailProvider extends AbstractNotificationProvider
-{
+class EmailProvider extends AbstractNotificationProvider {
+
/** @var IMailer */
private $mailer;
+ /** @var string */
public const NOTIFICATION_TYPE = 'EMAIL';
/**
@@ -63,8 +64,7 @@ class EmailProvider extends AbstractNotificationProvider
* @return void
* @throws \Exception
*/
- public function send(VCalendar $vcalendar, string $calendarDisplayName, IUser $user): void
- {
+ public function send(VCalendar $vcalendar, string $calendarDisplayName, IUser $user):void {
if ($user->getEMailAddress() === null) {
return;
}
@@ -111,8 +111,7 @@ class EmailProvider extends AbstractNotificationProvider
* @param IEMailTemplate $template
* @param string $summary
*/
- private function addSubjectAndHeading(IEMailTemplate $template, string $summary): void
- {
+ private function addSubjectAndHeading(IEMailTemplate $template, string $summary):void {
$template->setSubject('Notification: ' . $summary);
$template->addHeading($summary);
}
@@ -122,8 +121,7 @@ class EmailProvider extends AbstractNotificationProvider
* @param array $eventData
* @param string $calendarDisplayName
*/
- private function addBulletList(IEMailTemplate $template, array $eventData, string $calendarDisplayName): void
- {
+ private function addBulletList(IEMailTemplate $template, array $eventData, string $calendarDisplayName):void {
$template->addBodyListItem($calendarDisplayName, $this->l10n->t('Calendar:'),
$this->getAbsoluteImagePath('actions/info.svg'));
@@ -148,8 +146,7 @@ class EmailProvider extends AbstractNotificationProvider
* @param string $path
* @return string
*/
- private function getAbsoluteImagePath($path): string
- {
+ private function getAbsoluteImagePath(string $path):string {
return $this->urlGenerator->getAbsoluteURL(
$this->urlGenerator->imagePath('core', $path)
);
diff --git a/apps/dav/lib/CalDAV/Reminder/NotificationProvider/PushProvider.php b/apps/dav/lib/CalDAV/Reminder/NotificationProvider/PushProvider.php
index 1bb0e5c68b..f04b8e4c45 100644
--- a/apps/dav/lib/CalDAV/Reminder/NotificationProvider/PushProvider.php
+++ b/apps/dav/lib/CalDAV/Reminder/NotificationProvider/PushProvider.php
@@ -35,9 +35,9 @@ use OCP\Notification\INotification;
use Sabre\VObject\Component\VCalendar;
use OCP\AppFramework\Utility\ITimeFactory;
-class PushProvider extends AbstractNotificationProvider
-{
+class PushProvider extends AbstractNotificationProvider {
+ /** @var string */
public const NOTIFICATION_TYPE = 'DISPLAY';
/**
@@ -75,9 +75,7 @@ class PushProvider extends AbstractNotificationProvider
* @return void
* @throws \Exception
*/
- public function send(VCalendar $vcalendar, string $calendarDisplayName, IUser $user): void
- {
-
+ public function send(VCalendar $vcalendar, string $calendarDisplayName, IUser $user):void {
$lang = $this->config->getUserValue($user->getUID(), 'core', 'lang', $this->l10nFactory->findLanguage());
$this->l10n = $this->l10nFactory->get('dav', $lang);
diff --git a/apps/dav/lib/CalDAV/Reminder/NotificationProviderManager.php b/apps/dav/lib/CalDAV/Reminder/NotificationProviderManager.php
index 389cbbd2bf..0a2579aedf 100644
--- a/apps/dav/lib/CalDAV/Reminder/NotificationProviderManager.php
+++ b/apps/dav/lib/CalDAV/Reminder/NotificationProviderManager.php
@@ -31,8 +31,7 @@ class NotificationProviderManager {
* @throws ProviderNotAvailableException
* @throws NotificationTypeDoesNotExistException
*/
- public function getProvider(string $type): AbstractNotificationProvider
- {
+ public function getProvider(string $type):AbstractNotificationProvider {
if (in_array($type, ReminderService::REMINDER_TYPES, true)) {
if (isset($this->providers[$type])) {
return $this->providers[$type];
@@ -46,8 +45,7 @@ class NotificationProviderManager {
* @param string $providerClassName
* @throws \OCP\AppFramework\QueryException
*/
- public function registerProvider(string $providerClassName): void
- {
+ public function registerProvider(string $providerClassName):void {
$provider = \OC::$server->query($providerClassName);
if (!$provider instanceof AbstractNotificationProvider) {
diff --git a/apps/dav/lib/CalDAV/Reminder/Notifier.php b/apps/dav/lib/CalDAV/Reminder/Notifier.php
index d95774e019..ff34208a79 100644
--- a/apps/dav/lib/CalDAV/Reminder/Notifier.php
+++ b/apps/dav/lib/CalDAV/Reminder/Notifier.php
@@ -31,14 +31,15 @@ use OCP\IURLGenerator;
class Notifier implements INotifier {
- public static $units = array(
+ /** @var array */
+ public static $units = [
'y' => 'year',
'm' => 'month',
'd' => 'day',
'h' => 'hour',
'i' => 'minute',
's' => 'second',
- );
+ ];
/** @var IFactory */
protected $factory;
@@ -60,8 +61,7 @@ class Notifier implements INotifier {
* @return INotification
* @throws \Exception
*/
- public function prepare(INotification $notification, $languageCode): INotification
- {
+ public function prepare(INotification $notification, string $languageCode):INotification {
if ($notification->getApp() !== Application::APP_ID) {
throw new \InvalidArgumentException('Notification not from this app');
}
@@ -87,8 +87,7 @@ class Notifier implements INotifier {
* @return string
* @throws \Exception
*/
- private function processEventTitle(array $event): string
- {
+ private function processEventTitle(array $event):string {
$event_datetime = new \DateTime();
$event_datetime->setTimestamp($event['start']);
$now = new \DateTime();
@@ -112,8 +111,7 @@ class Notifier implements INotifier {
* @param string $title
* @return string
*/
- private function getPluralizedTitle(int $count, int $invert, string $unit, string $title): string
- {
+ private function getPluralizedTitle(int $count, int $invert, string $unit, string $title):string {
if ($invert) {
return $this->l->n('%s (in one %s)', '%s (in %n %ss)', $count, [$title, $unit]);
}
@@ -125,8 +123,7 @@ class Notifier implements INotifier {
* @param array $event
* @return string
*/
- private function processEventDescription(array $event): string
- {
+ private function processEventDescription(array $event):string {
$description = [
$this->l->t('Calendar: %s', $event['calendar']),
$this->l->t('Date: %s', $event['when']),
diff --git a/apps/dav/lib/CalDAV/Reminder/ReminderService.php b/apps/dav/lib/CalDAV/Reminder/ReminderService.php
index 87c2ce1067..ce6c846061 100644
--- a/apps/dav/lib/CalDAV/Reminder/ReminderService.php
+++ b/apps/dav/lib/CalDAV/Reminder/ReminderService.php
@@ -49,7 +49,11 @@ class ReminderService {
public const REMINDER_TYPE_DISPLAY = 'DISPLAY';
public const REMINDER_TYPE_AUDIO = 'AUDIO';
- public const REMINDER_TYPES = [self::REMINDER_TYPE_EMAIL, self::REMINDER_TYPE_DISPLAY, self::REMINDER_TYPE_AUDIO];
+ public const REMINDER_TYPES = [
+ self::REMINDER_TYPE_EMAIL,
+ self::REMINDER_TYPE_DISPLAY,
+ self::REMINDER_TYPE_AUDIO
+ ];
public function __construct(Backend $backend,
NotificationProviderManager $notificationProviderManager,
@@ -70,9 +74,7 @@ class ReminderService {
* @throws NotificationProvider\ProviderNotAvailableException
* @throws NotificationTypeDoesNotExistException
*/
- public function processReminders(): void
- {
-
+ public function processReminders():void {
$reminders = $this->backend->getRemindersToProcess();
foreach ($reminders as $reminder) {
@@ -101,8 +103,7 @@ class ReminderService {
* @throws VObject\InvalidDataException
* @throws NoUserException
*/
- public function onTouchCalendarObject(string $action, array $calendarData, array $shares, array $objectData): void
- {
+ public function onTouchCalendarObject(string $action, array $calendarData, array $shares, array $objectData):void {
if (!isset($calendarData['principaluri'])) {
return;
}
@@ -157,8 +158,7 @@ class ReminderService {
* @param array $shares
* @return string[]
*/
- private function getUsersForShares(array $shares): array
- {
+ private function getUsersForShares(array $shares):array {
$users = $groups = [];
foreach ($shares as $share) {
$principal = explode('/', $share['{http://owncloud.org/ns}principal']);
From a6f792616c37bb63fa826bb6d897738ad5227d6d Mon Sep 17 00:00:00 2001
From: Georg Ehrke
Date: Fri, 2 Aug 2019 16:36:50 +0200
Subject: [PATCH 05/10] Implement getID and getName in Reminder/Notifier as
required since Nextcloud 17
Signed-off-by: Georg Ehrke
---
apps/dav/lib/CalDAV/Reminder/Notifier.php | 20 ++++++++++++++++++++
1 file changed, 20 insertions(+)
diff --git a/apps/dav/lib/CalDAV/Reminder/Notifier.php b/apps/dav/lib/CalDAV/Reminder/Notifier.php
index ff34208a79..3718d5b29a 100644
--- a/apps/dav/lib/CalDAV/Reminder/Notifier.php
+++ b/apps/dav/lib/CalDAV/Reminder/Notifier.php
@@ -55,6 +55,26 @@ class Notifier implements INotifier {
$this->urlGenerator = $urlGenerator;
}
+ /**
+ * Identifier of the notifier, only use [a-z0-9_]
+ *
+ * @return string
+ * @since 17.0.0
+ */
+ public function getID():string {
+ return 'dav';
+ }
+
+ /**
+ * Human readable name describing the notifier
+ *
+ * @return string
+ * @since 17.0.0
+ */
+ public function getName():string {
+ return $this->factory->get('dav')->t('Calendar');
+ }
+
/**
* @param INotification $notification
* @param string $languageCode The code of the language that should be used to prepare the notification
From c5147529af4fac3eb8ac7bc4db3d10867600edaf Mon Sep 17 00:00:00 2001
From: Georg Ehrke
Date: Fri, 2 Aug 2019 16:40:35 +0200
Subject: [PATCH 06/10] Stop using deprecated registerNotifier, use
registerNotifierService instead
Signed-off-by: Georg Ehrke
---
apps/dav/lib/AppInfo/Application.php | 18 ++++++------------
1 file changed, 6 insertions(+), 12 deletions(-)
diff --git a/apps/dav/lib/AppInfo/Application.php b/apps/dav/lib/AppInfo/Application.php
index 2e3b95d8bf..1c5505d058 100644
--- a/apps/dav/lib/AppInfo/Application.php
+++ b/apps/dav/lib/AppInfo/Application.php
@@ -248,20 +248,14 @@ class Application extends App {
return $this->getContainer()->query(SyncService::class);
}
- public function registerNotifier() {
- $this->getContainer()->getServer()->getNotificationManager()->registerNotifier(function() {
- return $this->getContainer()->query(Notifier::class);
- }, function() {
- $l = $this->getContainer()->getServer()->getL10NFactory()->get(self::APP_ID);
- return [
- 'id' => self::APP_ID,
- 'name' => $l->t('Calendars and Contacts'),
- ];
- });
+ public function registerNotifier():void {
+ $this->getContainer()
+ ->getServer()
+ ->getNotificationManager()
+ ->registerNotifierService(Notifier::class);
}
- public function registerCalendarReminders(): void
- {
+ public function registerCalendarReminders():void {
try {
/** @var NotificationProviderManager $notificationProviderManager */
$notificationProviderManager = $this->getContainer()->query(NotificationProviderManager::class);
From 7c4a8a3bdf6d8c0c12cf7cab93cc465c83c4ec8f Mon Sep 17 00:00:00 2001
From: Georg Ehrke
Date: Fri, 2 Aug 2019 16:49:53 +0200
Subject: [PATCH 07/10] Provide a fake AudioProvider that is basically the same
as PushProvider, better then not showing reminders at all for now
Signed-off-by: Georg Ehrke
---
.../composer/composer/autoload_classmap.php | 1 +
.../dav/composer/composer/autoload_static.php | 1 +
apps/dav/lib/AppInfo/Application.php | 2 ++
.../NotificationProvider/AudioProvider.php | 36 +++++++++++++++++++
4 files changed, 40 insertions(+)
create mode 100644 apps/dav/lib/CalDAV/Reminder/NotificationProvider/AudioProvider.php
diff --git a/apps/dav/composer/composer/autoload_classmap.php b/apps/dav/composer/composer/autoload_classmap.php
index 59bcf7caf4..bd26290cc9 100644
--- a/apps/dav/composer/composer/autoload_classmap.php
+++ b/apps/dav/composer/composer/autoload_classmap.php
@@ -55,6 +55,7 @@ return array(
'OCA\\DAV\\CalDAV\\Reminder\\AbstractNotificationProvider' => $baseDir . '/../lib/CalDAV/Reminder/AbstractNotificationProvider.php',
'OCA\\DAV\\CalDAV\\Reminder\\Backend' => $baseDir . '/../lib/CalDAV/Reminder/Backend.php',
'OCA\\DAV\\CalDAV\\Reminder\\NotificationProviderManager' => $baseDir . '/../lib/CalDAV/Reminder/NotificationProviderManager.php',
+ 'OCA\\DAV\\CalDAV\\Reminder\\NotificationProvider\\AudioProvider' => $baseDir . '/../lib/CalDAV/Reminder/NotificationProvider/AudioProvider.php',
'OCA\\DAV\\CalDAV\\Reminder\\NotificationProvider\\EmailProvider' => $baseDir . '/../lib/CalDAV/Reminder/NotificationProvider/EmailProvider.php',
'OCA\\DAV\\CalDAV\\Reminder\\NotificationProvider\\ProviderNotAvailableException' => $baseDir . '/../lib/CalDAV/Reminder/NotificationProvider/ProviderNotAvailableException.php',
'OCA\\DAV\\CalDAV\\Reminder\\NotificationProvider\\PushProvider' => $baseDir . '/../lib/CalDAV/Reminder/NotificationProvider/PushProvider.php',
diff --git a/apps/dav/composer/composer/autoload_static.php b/apps/dav/composer/composer/autoload_static.php
index a28743ce43..1a0ce9d02f 100644
--- a/apps/dav/composer/composer/autoload_static.php
+++ b/apps/dav/composer/composer/autoload_static.php
@@ -70,6 +70,7 @@ class ComposerStaticInitDAV
'OCA\\DAV\\CalDAV\\Reminder\\AbstractNotificationProvider' => __DIR__ . '/..' . '/../lib/CalDAV/Reminder/AbstractNotificationProvider.php',
'OCA\\DAV\\CalDAV\\Reminder\\Backend' => __DIR__ . '/..' . '/../lib/CalDAV/Reminder/Backend.php',
'OCA\\DAV\\CalDAV\\Reminder\\NotificationProviderManager' => __DIR__ . '/..' . '/../lib/CalDAV/Reminder/NotificationProviderManager.php',
+ 'OCA\\DAV\\CalDAV\\Reminder\\NotificationProvider\\AudioProvider' => __DIR__ . '/..' . '/../lib/CalDAV/Reminder/NotificationProvider/AudioProvider.php',
'OCA\\DAV\\CalDAV\\Reminder\\NotificationProvider\\EmailProvider' => __DIR__ . '/..' . '/../lib/CalDAV/Reminder/NotificationProvider/EmailProvider.php',
'OCA\\DAV\\CalDAV\\Reminder\\NotificationProvider\\ProviderNotAvailableException' => __DIR__ . '/..' . '/../lib/CalDAV/Reminder/NotificationProvider/ProviderNotAvailableException.php',
'OCA\\DAV\\CalDAV\\Reminder\\NotificationProvider\\PushProvider' => __DIR__ . '/..' . '/../lib/CalDAV/Reminder/NotificationProvider/PushProvider.php',
diff --git a/apps/dav/lib/AppInfo/Application.php b/apps/dav/lib/AppInfo/Application.php
index 1c5505d058..5d17dc5a24 100644
--- a/apps/dav/lib/AppInfo/Application.php
+++ b/apps/dav/lib/AppInfo/Application.php
@@ -31,6 +31,7 @@ use OCA\DAV\CalDAV\Activity\Provider\Event;
use OCA\DAV\CalDAV\BirthdayService;
use OCA\DAV\CalDAV\CalendarManager;
use OCA\DAV\CalDAV\Reminder\Backend as ReminderBackend;
+use OCA\DAV\CalDAV\Reminder\NotificationProvider\AudioProvider;
use OCA\DAV\CalDAV\Reminder\NotificationProvider\EmailProvider;
use OCA\DAV\CalDAV\Reminder\NotificationProvider\PushProvider;
use OCA\DAV\CalDAV\Reminder\NotificationProviderManager;
@@ -259,6 +260,7 @@ class Application extends App {
try {
/** @var NotificationProviderManager $notificationProviderManager */
$notificationProviderManager = $this->getContainer()->query(NotificationProviderManager::class);
+ $notificationProviderManager->registerProvider(AudioProvider::class);
$notificationProviderManager->registerProvider(EmailProvider::class);
$notificationProviderManager->registerProvider(PushProvider::class);
} catch(\Exception $ex) {
diff --git a/apps/dav/lib/CalDAV/Reminder/NotificationProvider/AudioProvider.php b/apps/dav/lib/CalDAV/Reminder/NotificationProvider/AudioProvider.php
new file mode 100644
index 0000000000..6e702bcaca
--- /dev/null
+++ b/apps/dav/lib/CalDAV/Reminder/NotificationProvider/AudioProvider.php
@@ -0,0 +1,36 @@
+
+ *
+ * @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\DAV\CalDAV\Reminder\NotificationProvider;
+
+/**
+ * Class AudioProvider
+ *
+ * This class only extends PushProvider at the moment. It does not provide true
+ * audio-alarms yet, but it's better than no alarm at all right now.
+ *
+ * @package OCA\DAV\CalDAV\Reminder\NotificationProvider
+ */
+class AudioProvider extends PushProvider {
+
+ /** @var string */
+ public const NOTIFICATION_TYPE = 'AUDIO';
+}
From bcce568c6d892ccacc598c5d5bfa6b24c3284741 Mon Sep 17 00:00:00 2001
From: Georg Ehrke
Date: Fri, 9 Aug 2019 20:25:21 +0200
Subject: [PATCH 08/10] Support recurring events + repeating alarms
Signed-off-by: Georg Ehrke
---
apps/dav/appinfo/info.xml | 2 +-
.../composer/composer/autoload_classmap.php | 5 +-
.../dav/composer/composer/autoload_static.php | 5 +-
apps/dav/lib/AppInfo/Application.php | 4 +-
.../Reminder/AbstractNotificationProvider.php | 205 -----
apps/dav/lib/CalDAV/Reminder/Backend.php | 183 +++--
.../CalDAV/Reminder/INotificationProvider.php | 47 ++
.../NotificationProvider/AbstractProvider.php | 191 +++++
.../NotificationProvider/AudioProvider.php | 1 +
.../NotificationProvider/EmailProvider.php | 503 +++++++++++--
.../ProviderNotAvailableException.php | 2 +-
.../NotificationProvider/PushProvider.php | 108 ++-
.../Reminder/NotificationProviderManager.php | 42 +-
.../NotificationTypeDoesNotExistException.php | 3 +-
apps/dav/lib/CalDAV/Reminder/Notifier.php | 294 ++++++--
.../lib/CalDAV/Reminder/ReminderService.php | 710 ++++++++++++++++--
apps/dav/lib/Command/SendEventReminders.php | 14 +-
.../Version1004Date20170825134824.php | 3 +-
.../Version1007Date20181005133326.php | 82 --
.../Version1012Date20190808122342.php | 116 +++
20 files changed, 1926 insertions(+), 594 deletions(-)
delete mode 100644 apps/dav/lib/CalDAV/Reminder/AbstractNotificationProvider.php
create mode 100644 apps/dav/lib/CalDAV/Reminder/INotificationProvider.php
create mode 100644 apps/dav/lib/CalDAV/Reminder/NotificationProvider/AbstractProvider.php
delete mode 100644 apps/dav/lib/Migration/Version1007Date20181005133326.php
create mode 100644 apps/dav/lib/Migration/Version1012Date20190808122342.php
diff --git a/apps/dav/appinfo/info.xml b/apps/dav/appinfo/info.xml
index 6e0219e91d..81b93de055 100644
--- a/apps/dav/appinfo/info.xml
+++ b/apps/dav/appinfo/info.xml
@@ -5,7 +5,7 @@
WebDAVWebDAV endpointWebDAV endpoint
- 1.11.1
+ 1.13.0agplowncloud.orgDAV
diff --git a/apps/dav/composer/composer/autoload_classmap.php b/apps/dav/composer/composer/autoload_classmap.php
index bd26290cc9..a1c2d671b8 100644
--- a/apps/dav/composer/composer/autoload_classmap.php
+++ b/apps/dav/composer/composer/autoload_classmap.php
@@ -52,9 +52,10 @@ return array(
'OCA\\DAV\\CalDAV\\PublicCalendarRoot' => $baseDir . '/../lib/CalDAV/PublicCalendarRoot.php',
'OCA\\DAV\\CalDAV\\Publishing\\PublishPlugin' => $baseDir . '/../lib/CalDAV/Publishing/PublishPlugin.php',
'OCA\\DAV\\CalDAV\\Publishing\\Xml\\Publisher' => $baseDir . '/../lib/CalDAV/Publishing/Xml/Publisher.php',
- 'OCA\\DAV\\CalDAV\\Reminder\\AbstractNotificationProvider' => $baseDir . '/../lib/CalDAV/Reminder/AbstractNotificationProvider.php',
'OCA\\DAV\\CalDAV\\Reminder\\Backend' => $baseDir . '/../lib/CalDAV/Reminder/Backend.php',
+ 'OCA\\DAV\\CalDAV\\Reminder\\INotificationProvider' => $baseDir . '/../lib/CalDAV/Reminder/INotificationProvider.php',
'OCA\\DAV\\CalDAV\\Reminder\\NotificationProviderManager' => $baseDir . '/../lib/CalDAV/Reminder/NotificationProviderManager.php',
+ 'OCA\\DAV\\CalDAV\\Reminder\\NotificationProvider\\AbstractProvider' => $baseDir . '/../lib/CalDAV/Reminder/NotificationProvider/AbstractProvider.php',
'OCA\\DAV\\CalDAV\\Reminder\\NotificationProvider\\AudioProvider' => $baseDir . '/../lib/CalDAV/Reminder/NotificationProvider/AudioProvider.php',
'OCA\\DAV\\CalDAV\\Reminder\\NotificationProvider\\EmailProvider' => $baseDir . '/../lib/CalDAV/Reminder/NotificationProvider/EmailProvider.php',
'OCA\\DAV\\CalDAV\\Reminder\\NotificationProvider\\ProviderNotAvailableException' => $baseDir . '/../lib/CalDAV/Reminder/NotificationProvider/ProviderNotAvailableException.php',
@@ -188,7 +189,6 @@ return array(
'OCA\\DAV\\Migration\\Version1005Date20180530124431' => $baseDir . '/../lib/Migration/Version1005Date20180530124431.php',
'OCA\\DAV\\Migration\\Version1006Date20180619154313' => $baseDir . '/../lib/Migration/Version1006Date20180619154313.php',
'OCA\\DAV\\Migration\\Version1006Date20180628111625' => $baseDir . '/../lib/Migration/Version1006Date20180628111625.php',
- 'OCA\\DAV\\Migration\\Version1007Date20181005133326' => $baseDir . '/../lib/Migration/Version1007Date20181005133326.php',
'OCA\\DAV\\Migration\\Version1008Date20181030113700' => $baseDir . '/../lib/Migration/Version1008Date20181030113700.php',
'OCA\\DAV\\Migration\\Version1008Date20181105104826' => $baseDir . '/../lib/Migration/Version1008Date20181105104826.php',
'OCA\\DAV\\Migration\\Version1008Date20181105104833' => $baseDir . '/../lib/Migration/Version1008Date20181105104833.php',
@@ -197,6 +197,7 @@ return array(
'OCA\\DAV\\Migration\\Version1008Date20181114084440' => $baseDir . '/../lib/Migration/Version1008Date20181114084440.php',
'OCA\\DAV\\Migration\\Version1011Date20190725113607' => $baseDir . '/../lib/Migration/Version1011Date20190725113607.php',
'OCA\\DAV\\Migration\\Version1011Date20190806104428' => $baseDir . '/../lib/Migration/Version1011Date20190806104428.php',
+ 'OCA\\DAV\\Migration\\Version1012Date20190808122342' => $baseDir . '/../lib/Migration/Version1012Date20190808122342.php',
'OCA\\DAV\\Provisioning\\Apple\\AppleProvisioningNode' => $baseDir . '/../lib/Provisioning/Apple/AppleProvisioningNode.php',
'OCA\\DAV\\Provisioning\\Apple\\AppleProvisioningPlugin' => $baseDir . '/../lib/Provisioning/Apple/AppleProvisioningPlugin.php',
'OCA\\DAV\\RootCollection' => $baseDir . '/../lib/RootCollection.php',
diff --git a/apps/dav/composer/composer/autoload_static.php b/apps/dav/composer/composer/autoload_static.php
index 1a0ce9d02f..39488419f8 100644
--- a/apps/dav/composer/composer/autoload_static.php
+++ b/apps/dav/composer/composer/autoload_static.php
@@ -67,9 +67,10 @@ class ComposerStaticInitDAV
'OCA\\DAV\\CalDAV\\PublicCalendarRoot' => __DIR__ . '/..' . '/../lib/CalDAV/PublicCalendarRoot.php',
'OCA\\DAV\\CalDAV\\Publishing\\PublishPlugin' => __DIR__ . '/..' . '/../lib/CalDAV/Publishing/PublishPlugin.php',
'OCA\\DAV\\CalDAV\\Publishing\\Xml\\Publisher' => __DIR__ . '/..' . '/../lib/CalDAV/Publishing/Xml/Publisher.php',
- 'OCA\\DAV\\CalDAV\\Reminder\\AbstractNotificationProvider' => __DIR__ . '/..' . '/../lib/CalDAV/Reminder/AbstractNotificationProvider.php',
'OCA\\DAV\\CalDAV\\Reminder\\Backend' => __DIR__ . '/..' . '/../lib/CalDAV/Reminder/Backend.php',
+ 'OCA\\DAV\\CalDAV\\Reminder\\INotificationProvider' => __DIR__ . '/..' . '/../lib/CalDAV/Reminder/INotificationProvider.php',
'OCA\\DAV\\CalDAV\\Reminder\\NotificationProviderManager' => __DIR__ . '/..' . '/../lib/CalDAV/Reminder/NotificationProviderManager.php',
+ 'OCA\\DAV\\CalDAV\\Reminder\\NotificationProvider\\AbstractProvider' => __DIR__ . '/..' . '/../lib/CalDAV/Reminder/NotificationProvider/AbstractProvider.php',
'OCA\\DAV\\CalDAV\\Reminder\\NotificationProvider\\AudioProvider' => __DIR__ . '/..' . '/../lib/CalDAV/Reminder/NotificationProvider/AudioProvider.php',
'OCA\\DAV\\CalDAV\\Reminder\\NotificationProvider\\EmailProvider' => __DIR__ . '/..' . '/../lib/CalDAV/Reminder/NotificationProvider/EmailProvider.php',
'OCA\\DAV\\CalDAV\\Reminder\\NotificationProvider\\ProviderNotAvailableException' => __DIR__ . '/..' . '/../lib/CalDAV/Reminder/NotificationProvider/ProviderNotAvailableException.php',
@@ -203,7 +204,6 @@ class ComposerStaticInitDAV
'OCA\\DAV\\Migration\\Version1005Date20180530124431' => __DIR__ . '/..' . '/../lib/Migration/Version1005Date20180530124431.php',
'OCA\\DAV\\Migration\\Version1006Date20180619154313' => __DIR__ . '/..' . '/../lib/Migration/Version1006Date20180619154313.php',
'OCA\\DAV\\Migration\\Version1006Date20180628111625' => __DIR__ . '/..' . '/../lib/Migration/Version1006Date20180628111625.php',
- 'OCA\\DAV\\Migration\\Version1007Date20181005133326' => __DIR__ . '/..' . '/../lib/Migration/Version1007Date20181005133326.php',
'OCA\\DAV\\Migration\\Version1008Date20181030113700' => __DIR__ . '/..' . '/../lib/Migration/Version1008Date20181030113700.php',
'OCA\\DAV\\Migration\\Version1008Date20181105104826' => __DIR__ . '/..' . '/../lib/Migration/Version1008Date20181105104826.php',
'OCA\\DAV\\Migration\\Version1008Date20181105104833' => __DIR__ . '/..' . '/../lib/Migration/Version1008Date20181105104833.php',
@@ -212,6 +212,7 @@ class ComposerStaticInitDAV
'OCA\\DAV\\Migration\\Version1008Date20181114084440' => __DIR__ . '/..' . '/../lib/Migration/Version1008Date20181114084440.php',
'OCA\\DAV\\Migration\\Version1011Date20190725113607' => __DIR__ . '/..' . '/../lib/Migration/Version1011Date20190725113607.php',
'OCA\\DAV\\Migration\\Version1011Date20190806104428' => __DIR__ . '/..' . '/../lib/Migration/Version1011Date20190806104428.php',
+ 'OCA\\DAV\\Migration\\Version1012Date20190808122342' => __DIR__ . '/..' . '/../lib/Migration/Version1012Date20190808122342.php',
'OCA\\DAV\\Provisioning\\Apple\\AppleProvisioningNode' => __DIR__ . '/..' . '/../lib/Provisioning/Apple/AppleProvisioningNode.php',
'OCA\\DAV\\Provisioning\\Apple\\AppleProvisioningPlugin' => __DIR__ . '/..' . '/../lib/Provisioning/Apple/AppleProvisioningPlugin.php',
'OCA\\DAV\\RootCollection' => __DIR__ . '/..' . '/../lib/RootCollection.php',
diff --git a/apps/dav/lib/AppInfo/Application.php b/apps/dav/lib/AppInfo/Application.php
index 5d17dc5a24..80e9dea882 100644
--- a/apps/dav/lib/AppInfo/Application.php
+++ b/apps/dav/lib/AppInfo/Application.php
@@ -231,12 +231,10 @@ class Application extends App {
);
/** @var ReminderService $reminderBackend */
- $reminderService= $this->getContainer()->query(ReminderService::class);
+ $reminderService = $this->getContainer()->query(ReminderService::class);
$reminderService->onTouchCalendarObject(
$eventName,
- $event->getArgument('calendarData'),
- $event->getArgument('shares'),
$event->getArgument('objectData')
);
};
diff --git a/apps/dav/lib/CalDAV/Reminder/AbstractNotificationProvider.php b/apps/dav/lib/CalDAV/Reminder/AbstractNotificationProvider.php
deleted file mode 100644
index 1d858627de..0000000000
--- a/apps/dav/lib/CalDAV/Reminder/AbstractNotificationProvider.php
+++ /dev/null
@@ -1,205 +0,0 @@
-
- *
- * @author Thomas Citharel
- *
- * @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\DAV\CalDAV\Reminder;
-
-use \DateTime;
-use \DateTimeImmutable;
-use OCP\IConfig;
-use OCP\IL10N;
-use OCP\ILogger;
-use OCP\IURLGenerator;
-use OCP\L10N\IFactory as L10NFactory;
-use OCP\IUser;
-use Sabre\VObject\Component\VCalendar;
-use Sabre\VObject\Component\VEvent;
-use Sabre\VObject\DateTimeParser;
-use Sabre\VObject\Parameter;
-use Sabre\VObject\Property;
-
-abstract class AbstractNotificationProvider {
-
- /** @var string */
- public const NOTIFICATION_TYPE = '';
-
- /** @var ILogger */
- protected $logger;
-
- /** @var L10NFactory */
- protected $l10nFactory;
-
- /** @var IL10N */
- protected $l10n;
-
- /** @var IURLGenerator */
- protected $urlGenerator;
-
- /** @var IConfig */
- protected $config;
-
- /**
- * @param ILogger $logger
- * @param L10NFactory $l10nFactory
- * @param IConfig $config
- * @param IUrlGenerator $urlGenerator
- */
- public function __construct(ILogger $logger, L10NFactory $l10nFactory, IURLGenerator $urlGenerator, IConfig $config) {
- $this->logger = $logger;
- $this->l10nFactory = $l10nFactory;
- $this->urlGenerator = $urlGenerator;
- $this->config = $config;
- }
-
- /**
- * Send notification
- *
- * @param VCalendar $vcalendar
- * @param string $calendarDisplayName
- * @param IUser $user
- * @return void
- */
- abstract function send(VCalendar $vcalendar, string $calendarDisplayName, IUser $user): void;
-
- /**
- * @var VCalendar $vcalendar
- * @var string $defaultValue
- * @return array
- * @throws \Exception
- */
- protected function extractEventDetails(VCalendar $vcalendar, $defaultValue = ''):array {
- /** @var VEvent $vevent */
- $vevent = $vcalendar->VEVENT;
-
- /** @var Property $start */
- $start = $vevent->DTSTART;
- if (isset($vevent->DTEND)) {
- $end = $vevent->DTEND;
- } elseif (isset($vevent->DURATION)) {
- $isFloating = $vevent->DTSTART->isFloating();
- $end = clone $vevent->DTSTART;
- $endDateTime = $end->getDateTime();
- $endDateTime = $endDateTime->add(DateTimeParser::parse($vevent->DURATION->getValue()));
- $end->setDateTime($endDateTime, $isFloating);
- } elseif (!$vevent->DTSTART->hasTime()) {
- $isFloating = $vevent->DTSTART->isFloating();
- $end = clone $vevent->DTSTART;
- $endDateTime = $end->getDateTime();
- $endDateTime = $endDateTime->modify('+1 day');
- $end->setDateTime($endDateTime, $isFloating);
- } else {
- $end = clone $vevent->DTSTART;
- }
-
- return [
- 'title' => (string) $vevent->SUMMARY ?: $defaultValue,
- 'description' => (string) $vevent->DESCRIPTION ?: $defaultValue,
- 'start'=> $start->getDateTime(),
- 'end' => $end->getDateTime(),
- 'when' => $this->generateWhenString($start, $end),
- 'url' => (string) $vevent->URL ?: $defaultValue,
- 'location' => (string) $vevent->LOCATION ?: $defaultValue,
- 'uid' => (string) $vevent->UID,
- ];
- }
-
- /**
- * @param Property $dtstart
- * @param Property $dtend
- * @return string
- * @throws \Exception
- */
- private function generateWhenString(Property $dtstart, Property $dtend):string {
- $isAllDay = $dtstart instanceof Property\ICalendar\Date;
-
- /** @var Property\ICalendar\Date | Property\ICalendar\DateTime $dtstart */
- /** @var Property\ICalendar\Date | Property\ICalendar\DateTime $dtend */
- /** @var DateTimeImmutable $dtstartDt */
- $dtstartDt = $dtstart->getDateTime();
- /** @var DateTimeImmutable $dtendDt */
- $dtendDt = $dtend->getDateTime();
-
- $diff = $dtstartDt->diff($dtendDt);
-
- $dtstartDt = new DateTime($dtstartDt->format(DateTime::ATOM));
- $dtendDt = new DateTime($dtendDt->format(DateTime::ATOM));
-
- if ($isAllDay) {
- // One day event
- if ($diff->days === 1) {
- return $this->l10n->l('date', $dtstartDt, ['width' => 'medium']);
- }
-
- //event that spans over multiple days
- $localeStart = $this->l10n->l('date', $dtstartDt, ['width' => 'medium']);
- $localeEnd = $this->l10n->l('date', $dtendDt, ['width' => 'medium']);
-
- return $localeStart . ' - ' . $localeEnd;
- }
-
- /** @var Property\ICalendar\DateTime $dtstart */
- /** @var Property\ICalendar\DateTime $dtend */
- $isFloating = $dtstart->isFloating();
- $startTimezone = $endTimezone = null;
- if (!$isFloating) {
- $prop = $dtstart->offsetGet('TZID');
- if ($prop instanceof Parameter) {
- $startTimezone = $prop->getValue();
- }
-
- $prop = $dtend->offsetGet('TZID');
- if ($prop instanceof Parameter) {
- $endTimezone = $prop->getValue();
- }
- }
-
- $localeStart = $this->l10n->l('weekdayName', $dtstartDt, ['width' => 'abbreviated']) . ', ' .
- $this->l10n->l('datetime', $dtstartDt, ['width' => 'medium|short']);
-
- // always show full date with timezone if timezones are different
- if ($startTimezone !== $endTimezone) {
- $localeEnd = $this->l10n->l('datetime', $dtendDt, ['width' => 'medium|short']);
-
- return $localeStart . ' (' . $startTimezone . ') - ' .
- $localeEnd . ' (' . $endTimezone . ')';
- }
-
- // show only end time if date is the same
- if ($this->isDayEqual($dtstartDt, $dtendDt)) {
- $localeEnd = $this->l10n->l('time', $dtendDt, ['width' => 'short']);
- } else {
- $localeEnd = $this->l10n->l('weekdayName', $dtendDt, ['width' => 'abbreviated']) . ', ' .
- $this->l10n->l('datetime', $dtendDt, ['width' => 'medium|short']);
- }
-
- return $localeStart . ' - ' . $localeEnd . ' (' . $startTimezone . ')';
- }
-
- /**
- * @param DateTime $dtStart
- * @param DateTime $dtEnd
- * @return bool
- */
- private function isDayEqual(DateTime $dtStart, DateTime $dtEnd):bool {
- return $dtStart->format('Y-m-d') === $dtEnd->format('Y-m-d');
- }
-}
diff --git a/apps/dav/lib/CalDAV/Reminder/Backend.php b/apps/dav/lib/CalDAV/Reminder/Backend.php
index 087a5785f3..be65c35da0 100644
--- a/apps/dav/lib/CalDAV/Reminder/Backend.php
+++ b/apps/dav/lib/CalDAV/Reminder/Backend.php
@@ -1,8 +1,11 @@
+ * @copyright Copyright (c) 2019, Thomas Citharel
+ * @copyright Copyright (c) 2019, Georg Ehrke
*
* @author Thomas Citharel
+ * @author Georg Ehrke
*
* @license GNU AGPL version 3 or any later version
*
@@ -20,7 +23,6 @@
* along with this program. If not, see .
*
*/
-
namespace OCA\DAV\CalDAV\Reminder;
use OCP\IDBConnection;
@@ -40,61 +42,119 @@ class Backend {
private $timeFactory;
/**
+ * Backend constructor.
+ *
* @param IDBConnection $db
* @param ITimeFactory $timeFactory
*/
- public function __construct(IDBConnection $db, ITimeFactory $timeFactory) {
+ public function __construct(IDBConnection $db,
+ ITimeFactory $timeFactory) {
$this->db = $db;
$this->timeFactory = $timeFactory;
}
/**
- * @param string $uid
- * @param string $calendarId
- * @param string $uri
- * @param string $type
- * @param int $notificationDate
- * @param int $eventStartDate
+ * Get all reminders with a notification date before now
+ *
+ * @return array
+ * @throws \Exception
*/
- public function insertReminder(string $uid, string $calendarId, string $uri, string $type, int $notificationDate, int $eventStartDate):void {
+ public function getRemindersToProcess():array {
+ $query = $this->db->getQueryBuilder();
+ $query->select(['cr.*', 'co.calendardata', 'c.displayname', 'c.principaluri'])
+ ->from('calendar_reminders', 'cr')
+// ->where($query->expr()->lte('cr.notification_date', $query->createNamedParameter($this->timeFactory->getTime())))
+ ->leftJoin('cr', 'calendarobjects', 'co', $query->expr()->eq('cr.object_id', 'co.id'))
+ ->leftJoin('cr', 'calendars', 'c', $query->expr()->eq('cr.calendar_id', 'c.id'));
+ $stmt = $query->execute();
+
+ return array_map(
+ [$this, 'fixRowTyping'],
+ $stmt->fetchAll()
+ );
+ }
+
+ /**
+ * Get all scheduled reminders for an event
+ *
+ * @param int $objectId
+ * @return array
+ */
+ public function getAllScheduledRemindersForEvent(int $objectId):array {
+ $query = $this->db->getQueryBuilder();
+ $query->select('*')
+ ->from('calendar_reminders')
+ ->where($query->expr()->eq('object_id', $query->createNamedParameter($objectId)));
+ $stmt = $query->execute();
+
+ return array_map(
+ [$this, 'fixRowTyping'],
+ $stmt->fetchAll()
+ );
+ }
+
+ /**
+ * Insert a new reminder into the database
+ *
+ * @param int $calendarId
+ * @param int $objectId
+ * @param string $uid
+ * @param bool $isRecurring
+ * @param int $recurrenceId
+ * @param bool $isRecurrenceException
+ * @param string $eventHash
+ * @param string $alarmHash
+ * @param string $type
+ * @param bool $isRelative
+ * @param int $notificationDate
+ * @param bool $isRepeatBased
+ * @return int The insert id
+ */
+ public function insertReminder(int $calendarId,
+ int $objectId,
+ string $uid,
+ bool $isRecurring,
+ int $recurrenceId,
+ bool $isRecurrenceException,
+ string $eventHash,
+ string $alarmHash,
+ string $type,
+ bool $isRelative,
+ int $notificationDate,
+ bool $isRepeatBased):int {
$query = $this->db->getQueryBuilder();
$query->insert('calendar_reminders')
->values([
+ 'calendar_id' => $query->createNamedParameter($calendarId),
+ 'object_id' => $query->createNamedParameter($objectId),
'uid' => $query->createNamedParameter($uid),
- 'calendarid' => $query->createNamedParameter($calendarId),
- 'objecturi' => $query->createNamedParameter($uri),
+ 'is_recurring' => $query->createNamedParameter($isRecurring ? 1 : 0),
+ 'recurrence_id' => $query->createNamedParameter($recurrenceId),
+ 'is_recurrence_exception' => $query->createNamedParameter($isRecurrenceException ? 1 : 0),
+ 'event_hash' => $query->createNamedParameter($eventHash),
+ 'alarm_hash' => $query->createNamedParameter($alarmHash),
'type' => $query->createNamedParameter($type),
- 'notificationdate' => $query->createNamedParameter($notificationDate),
- 'eventstartdate' => $query->createNamedParameter($eventStartDate),
- ])->execute();
- }
-
- /**
- * Cleans reminders in database
- *
- * @param int $calendarId
- * @param string $objectUri
- */
- public function cleanRemindersForEvent(int $calendarId, string $objectUri):void {
- $query = $this->db->getQueryBuilder();
-
- $query->delete('calendar_reminders')
- ->where($query->expr()->eq('calendarid', $query->createNamedParameter($calendarId)))
- ->andWhere($query->expr()->eq('objecturi', $query->createNamedParameter($objectUri)))
+ 'is_relative' => $query->createNamedParameter($isRelative ? 1 : 0),
+ 'notification_date' => $query->createNamedParameter($notificationDate),
+ 'is_repeat_based' => $query->createNamedParameter($isRepeatBased ? 1 : 0),
+ ])
->execute();
+
+ return $query->getLastInsertId();
}
/**
- * Remove all reminders for a calendar
+ * Sets a new notificationDate on an existing reminder
*
- * @param integer $calendarId
- * @return void
+ * @param int $reminderId
+ * @param int $newNotificationDate
*/
- public function cleanRemindersForCalendar(int $calendarId):void {
+ public function updateReminder(int $reminderId,
+ int $newNotificationDate):void {
$query = $this->db->getQueryBuilder();
-
- $query->delete('calendar_reminders')
- ->where($query->expr()->eq('calendarid', $query->createNamedParameter($calendarId)))
+ $query->update('calendar_reminders')
+ ->set('notification_date', $query->createNamedParameter($newNotificationDate))
+ ->where($query->expr()->eq('id', $query->createNamedParameter($reminderId)))
->execute();
}
@@ -113,22 +173,47 @@ class Backend {
}
/**
- * Get all reminders with a notification date before now
+ * Cleans reminders in database
*
- * @return array
- * @throws \Exception
+ * @param int $objectId
*/
- public function getRemindersToProcess():array {
+ public function cleanRemindersForEvent(int $objectId):void {
$query = $this->db->getQueryBuilder();
- $fields = ['cr.id', 'cr.calendarid', 'cr.objecturi', 'cr.type', 'cr.notificationdate', 'cr.uid', 'co.calendardata', 'c.displayname'];
- $stmt = $query->select($fields)
- ->from('calendar_reminders', 'cr')
- ->where($query->expr()->lte('cr.notificationdate', $query->createNamedParameter($this->timeFactory->getTime())))
- ->andWhere($query->expr()->gte('cr.eventstartdate', $query->createNamedParameter($this->timeFactory->getTime()))) # We check that DTSTART isn't before
- ->leftJoin('cr', 'calendars', 'c', $query->expr()->eq('cr.calendarid', 'c.id'))
- ->leftJoin('cr', 'calendarobjects', 'co', $query->expr()->andX($query->expr()->eq('cr.calendarid', 'c.id'), $query->expr()->eq('co.uri', 'cr.objecturi')))
- ->execute();
- return $stmt->fetchAll();
+ $query->delete('calendar_reminders')
+ ->where($query->expr()->eq('object_id', $query->createNamedParameter($objectId)))
+ ->execute();
+ }
+
+ /**
+ * Remove all reminders for a calendar
+ *
+ * @param int $calendarId
+ * @return void
+ */
+ public function cleanRemindersForCalendar(int $calendarId):void {
+ $query = $this->db->getQueryBuilder();
+
+ $query->delete('calendar_reminders')
+ ->where($query->expr()->eq('calendar_id', $query->createNamedParameter($calendarId)))
+ ->execute();
+ }
+
+ /**
+ * @param array $row
+ * @return array
+ */
+ private function fixRowTyping(array $row): array {
+ $row['id'] = (int) $row['id'];
+ $row['calendar_id'] = (int) $row['calendar_id'];
+ $row['object_id'] = (int) $row['object_id'];
+ $row['is_recurring'] = (bool) $row['is_recurring'];
+ $row['recurrence_id'] = (int) $row['recurrence_id'];
+ $row['is_recurrence_exception'] = (bool) $row['is_recurrence_exception'];
+ $row['is_relative'] = (bool) $row['is_relative'];
+ $row['notification_date'] = (int) $row['notification_date'];
+ $row['is_repeat_based'] = (bool) $row['is_repeat_based'];
+
+ return $row;
}
}
diff --git a/apps/dav/lib/CalDAV/Reminder/INotificationProvider.php b/apps/dav/lib/CalDAV/Reminder/INotificationProvider.php
new file mode 100644
index 0000000000..d0e526eb0e
--- /dev/null
+++ b/apps/dav/lib/CalDAV/Reminder/INotificationProvider.php
@@ -0,0 +1,47 @@
+
+ *
+ * @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\DAV\CalDAV\Reminder;
+
+use OCP\IUser;
+use Sabre\VObject\Component\VEvent;
+
+/**
+ * Interface INotificationProvider
+ *
+ * @package OCA\DAV\CalDAV\Reminder
+ */
+interface INotificationProvider {
+
+ /**
+ * Send notification
+ *
+ * @param VEvent $vevent
+ * @param string $calendarDisplayName
+ * @param IUser[] $users
+ * @return void
+ */
+ public function send(VEvent $vevent,
+ string $calendarDisplayName,
+ array $users=[]): void;
+}
\ No newline at end of file
diff --git a/apps/dav/lib/CalDAV/Reminder/NotificationProvider/AbstractProvider.php b/apps/dav/lib/CalDAV/Reminder/NotificationProvider/AbstractProvider.php
new file mode 100644
index 0000000000..6b2364c802
--- /dev/null
+++ b/apps/dav/lib/CalDAV/Reminder/NotificationProvider/AbstractProvider.php
@@ -0,0 +1,191 @@
+
+ * @author Georg Ehrke
+ *
+ * @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\DAV\CalDAV\Reminder\NotificationProvider;
+
+use \DateTime;
+use \DateTimeImmutable;
+use OCA\DAV\CalDAV\Reminder\INotificationProvider;
+use OCP\IConfig;
+use OCP\IL10N;
+use OCP\ILogger;
+use OCP\IURLGenerator;
+use OCP\L10N\IFactory as L10NFactory;
+use OCP\IUser;
+use Sabre\VObject\Component\VEvent;
+use Sabre\VObject\DateTimeParser;
+use Sabre\VObject\Parameter;
+use Sabre\VObject\Property;
+
+/**
+ * Class AbstractProvider
+ *
+ * @package OCA\DAV\CalDAV\Reminder\NotificationProvider
+ */
+abstract class AbstractProvider implements INotificationProvider {
+
+ /** @var string */
+ public const NOTIFICATION_TYPE = '';
+
+ /** @var ILogger */
+ protected $logger;
+
+ /** @var L10NFactory */
+ private $l10nFactory;
+
+ /** @var IL10N[] */
+ private $l10ns;
+
+ /** @var string */
+ private $fallbackLanguage;
+
+ /** @var IURLGenerator */
+ protected $urlGenerator;
+
+ /** @var IConfig */
+ protected $config;
+
+ /**
+ * @param ILogger $logger
+ * @param L10NFactory $l10nFactory
+ * @param IConfig $config
+ * @param IUrlGenerator $urlGenerator
+ */
+ public function __construct(ILogger $logger,
+ L10NFactory $l10nFactory,
+ IURLGenerator $urlGenerator,
+ IConfig $config) {
+ $this->logger = $logger;
+ $this->l10nFactory = $l10nFactory;
+ $this->urlGenerator = $urlGenerator;
+ $this->config = $config;
+ }
+
+ /**
+ * Send notification
+ *
+ * @param VEvent $vevent
+ * @param string $calendarDisplayName
+ * @param IUser[] $users
+ * @return void
+ */
+ abstract public function send(VEvent $vevent,
+ string $calendarDisplayName,
+ array $users=[]): void;
+
+ /**
+ * @return string
+ */
+ protected function getFallbackLanguage():string {
+ if ($this->fallbackLanguage) {
+ return $this->fallbackLanguage;
+ }
+
+ $fallbackLanguage = $this->l10nFactory->findLanguage();
+ $this->fallbackLanguage = $fallbackLanguage;
+
+ return $fallbackLanguage;
+ }
+
+ /**
+ * @param string $lang
+ * @return bool
+ */
+ protected function hasL10NForLang(string $lang):bool {
+ return $this->l10nFactory->languageExists('dav', $lang);
+ }
+
+ /**
+ * @param string $lang
+ * @return IL10N
+ */
+ protected function getL10NForLang(string $lang):IL10N {
+ if (isset($this->l10ns[$lang])) {
+ return $this->l10ns[$lang];
+ }
+
+ $l10n = $this->l10nFactory->get('dav', $lang);
+ $this->l10ns[$lang] = $l10n;
+
+ return $l10n;
+ }
+
+ /**
+ * @param VEvent $vevent
+ * @return string
+ */
+ private function getStatusOfEvent(VEvent $vevent):string {
+ if ($vevent->STATUS) {
+ return (string) $vevent->STATUS;
+ }
+
+ // Doesn't say so in the standard,
+ // but we consider events without a status
+ // to be confirmed
+ return 'CONFIRMED';
+ }
+
+ /**
+ * @param VEvent $vevent
+ * @return bool
+ */
+ protected function isEventTentative(VEvent $vevent):bool {
+ return $this->getStatusOfEvent($vevent) === 'TENTATIVE';
+ }
+
+ /**
+ * @param VEvent $vevent
+ * @return Property\ICalendar\DateTime
+ */
+ protected function getDTEndFromEvent(VEvent $vevent):Property\ICalendar\DateTime {
+ if (isset($vevent->DTEND)) {
+ return $vevent->DTEND;
+ }
+
+ if (isset($vevent->DURATION)) {
+ $isFloating = $vevent->DTSTART->isFloating();
+ /** @var Property\ICalendar\DateTime $end */
+ $end = clone $vevent->DTSTART;
+ $endDateTime = $end->getDateTime();
+ $endDateTime = $endDateTime->add(DateTimeParser::parse($vevent->DURATION->getValue()));
+ $end->setDateTime($endDateTime, $isFloating);
+
+ return $end;
+ }
+
+ if (!$vevent->DTSTART->hasTime()) {
+ $isFloating = $vevent->DTSTART->isFloating();
+ /** @var Property\ICalendar\DateTime $end */
+ $end = clone $vevent->DTSTART;
+ $endDateTime = $end->getDateTime();
+ $endDateTime = $endDateTime->modify('+1 day');
+ $end->setDateTime($endDateTime, $isFloating);
+
+ return $end;
+ }
+
+ return clone $vevent->DTSTART;
+ }
+}
diff --git a/apps/dav/lib/CalDAV/Reminder/NotificationProvider/AudioProvider.php b/apps/dav/lib/CalDAV/Reminder/NotificationProvider/AudioProvider.php
index 6e702bcaca..ad4ac342f6 100644
--- a/apps/dav/lib/CalDAV/Reminder/NotificationProvider/AudioProvider.php
+++ b/apps/dav/lib/CalDAV/Reminder/NotificationProvider/AudioProvider.php
@@ -1,4 +1,5 @@
+ * @copyright Copyright (c) 2019, Thomas Citharel
+ * @copyright Copyright (c) 2019, Georg Ehrke
*
* @author Thomas Citharel
+ * @author Georg Ehrke
*
* @license GNU AGPL version 3 or any later version
*
@@ -20,27 +23,36 @@
* along with this program. If not, see .
*
*/
-
namespace OCA\DAV\CalDAV\Reminder\NotificationProvider;
-use OCA\DAV\CalDAV\Reminder\AbstractNotificationProvider;
+use DateTime;
+use DateTimeImmutable;
use OCP\IConfig;
+use OCP\IL10N;
use OCP\ILogger;
use OCP\IURLGenerator;
use OCP\L10N\IFactory as L10NFactory;
use OCP\Mail\IEMailTemplate;
use OCP\Mail\IMailer;
use OCP\IUser;
-use Sabre\VObject\Component\VCalendar;
+use Sabre\VObject\Component\VEvent;
+use Sabre\VObject;
+use Sabre\VObject\Parameter;
+use Sabre\VObject\Property;
-class EmailProvider extends AbstractNotificationProvider {
-
- /** @var IMailer */
- private $mailer;
+/**
+ * Class EmailProvider
+ *
+ * @package OCA\DAV\CalDAV\Reminder\NotificationProvider
+ */
+class EmailProvider extends AbstractProvider {
/** @var string */
public const NOTIFICATION_TYPE = 'EMAIL';
+ /** @var IMailer */
+ private $mailer;
+
/**
* @param IConfig $config
* @param IMailer $mailer
@@ -48,7 +60,9 @@ class EmailProvider extends AbstractNotificationProvider {
* @param L10NFactory $l10nFactory
* @param IUrlGenerator $urlGenerator
*/
- public function __construct(IConfig $config, IMailer $mailer, ILogger $logger,
+ public function __construct(IConfig $config,
+ IMailer $mailer,
+ ILogger $logger,
L10NFactory $l10nFactory,
IURLGenerator $urlGenerator) {
parent::__construct($logger, $l10nFactory, $urlGenerator, $config);
@@ -56,90 +70,100 @@ class EmailProvider extends AbstractNotificationProvider {
}
/**
- * Send notification
+ * Send out notification via email
*
- * @param VCalendar $vcalendar
+ * @param VEvent $vevent
* @param string $calendarDisplayName
- * @param IUser $user
- * @return void
+ * @param array $users
* @throws \Exception
*/
- public function send(VCalendar $vcalendar, string $calendarDisplayName, IUser $user):void {
- if ($user->getEMailAddress() === null) {
- return;
- }
+ public function send(VEvent $vevent,
+ string $calendarDisplayName,
+ array $users=[]):void {
+ $fallbackLanguage = $this->getFallbackLanguage();
- $lang = $this->config->getUserValue($user->getUID(), 'core', 'lang', $this->l10nFactory->findLanguage());
- $this->l10n = $this->l10nFactory->get('dav', $lang);
+ $emailAddressesOfSharees = $this->getEMailAddressesOfAllUsersWithWriteAccessToCalendar($users);
+ $emailAddressesOfAttendees = $this->getAllEMailAddressesFromEvent($vevent);
- $event = $this->extractEventDetails($vcalendar);
- $fromEMail = \OCP\Util::getDefaultEmailAddress('invitations-noreply');
-
- $message = $this->mailer->createMessage()
- ->setFrom([$fromEMail => 'Nextcloud'])
- // TODO: Set reply to from event creator
- // ->setReplyTo([$sender => $senderName])
- ->setTo([$user->getEMailAddress() => $user->getDisplayName()]);
-
- $template = $this->mailer->createEMailTemplate('dav.calendarReminder', $event);
- $template->addHeader();
-
- $this->addSubjectAndHeading($template, $event['title']);
- $this->addBulletList($template, $event, $calendarDisplayName);
-
- $template->addFooter();
- $message->useTemplate($template);
-
- $attachment = $this->mailer->createAttachment(
- $vcalendar->serialize(),
- $event['uid'].'.ics',// TODO(leon): Make file name unique, e.g. add event id
- 'text/calendar'
+ // Quote from php.net:
+ // If the input arrays have the same string keys, then the later value for that key will overwrite the previous one.
+ // => if there are duplicate email addresses, it will always take the system value
+ $emailAddresses = array_merge(
+ $emailAddressesOfAttendees,
+ $emailAddressesOfSharees
);
- $message->attach($attachment);
- try {
- $failed = $this->mailer->send($message);
- if ($failed) {
- $this->logger->error('Unable to deliver message to {failed}', ['app' => 'dav', 'failed' => implode(', ', $failed)]);
+ $sortedByLanguage = $this->sortEMailAddressesByLanguage($emailAddresses, $fallbackLanguage);
+ $organizer = $this->getOrganizerEMailAndNameFromEvent($vevent);
+
+ foreach($sortedByLanguage as $lang => $emailAddresses) {
+ if ($this->hasL10NForLang($lang)) {
+ $lang = $fallbackLanguage;
+ }
+ $l10n = $this->getL10NForLang($lang);
+ $fromEMail = \OCP\Util::getDefaultEmailAddress('reminders-noreply');
+
+ $message = $this->mailer->createMessage();
+ $message->setFrom([$fromEMail]);
+ if ($organizer) {
+ $message->setReplyTo($organizer);
+ }
+ $message->setBcc($emailAddresses);
+
+ $template = $this->mailer->createEMailTemplate('dav.calendarReminder');
+ $template->addHeader();
+
+ $this->addSubjectAndHeading($template, $l10n, $vevent);
+ $this->addBulletList($template, $l10n, $calendarDisplayName, $vevent);
+
+ $template->addFooter();
+ $message->useTemplate($template);
+
+ try {
+ $failed = $this->mailer->send($message);
+ if ($failed) {
+ $this->logger->error('Unable to deliver message to {failed}', ['app' => 'dav', 'failed' => implode(', ', $failed)]);
+ }
+ } catch (\Exception $ex) {
+ $this->logger->logException($ex, ['app' => 'dav']);
}
- } catch(\Exception $ex) {
- $this->logger->logException($ex, ['app' => 'dav']);
}
}
/**
* @param IEMailTemplate $template
- * @param string $summary
+ * @param IL10N $l10n
+ * @param VEvent $vevent
*/
- private function addSubjectAndHeading(IEMailTemplate $template, string $summary):void {
- $template->setSubject('Notification: ' . $summary);
- $template->addHeading($summary);
+ private function addSubjectAndHeading(IEMailTemplate $template, IL10N $l10n, VEvent $vevent):void {
+ $template->setSubject('Notification: ' . $this->getTitleFromVEvent($vevent, $l10n));
+ $template->addHeading($this->getTitleFromVEvent($vevent, $l10n));
}
/**
* @param IEMailTemplate $template
- * @param array $eventData
+ * @param IL10N $l10n
* @param string $calendarDisplayName
+ * @param array $eventData
*/
- private function addBulletList(IEMailTemplate $template, array $eventData, string $calendarDisplayName):void {
- $template->addBodyListItem($calendarDisplayName, $this->l10n->t('Calendar:'),
+ private function addBulletList(IEMailTemplate $template,
+ IL10N $l10n,
+ string $calendarDisplayName,
+ VEvent $vevent):void {
+ $template->addBodyListItem($calendarDisplayName, $l10n->t('Calendar:'),
$this->getAbsoluteImagePath('actions/info.svg'));
- $template->addBodyListItem($eventData['when'], $this->l10n->t('Date:'),
+ $template->addBodyListItem($this->generateDateString($l10n, $vevent), $l10n->t('Date:'),
$this->getAbsoluteImagePath('places/calendar.svg'));
- if ($eventData['location']) {
- $template->addBodyListItem((string) $eventData['location'], $this->l10n->t('Where:'),
+ if (isset($vevent->LOCATION)) {
+ $template->addBodyListItem((string) $vevent->LOCATION, $l10n->t('Where:'),
$this->getAbsoluteImagePath('actions/address.svg'));
}
- if ($eventData['description']) {
- $template->addBodyListItem((string) $eventData['description'], $this->l10n->t('Description:'),
+ if (isset($vevent->DESCRIPTION)) {
+ $template->addBodyListItem((string) $vevent->DESCRIPTION, $l10n->t('Description:'),
$this->getAbsoluteImagePath('actions/more.svg'));
}
- if ($eventData['url']) {
- $template->addBodyListItem((string) $eventData['url'], $this->l10n->t('Link:'),
- $this->getAbsoluteImagePath('places/link.svg'));
- }
}
/**
@@ -151,4 +175,355 @@ class EmailProvider extends AbstractNotificationProvider {
$this->urlGenerator->imagePath('core', $path)
);
}
+
+ /**
+ * @param VEvent $vevent
+ * @return array|null
+ */
+ private function getOrganizerEMailAndNameFromEvent(VEvent $vevent):?array {
+ if (!$vevent->ORGANIZER) {
+ return null;
+ }
+
+ $organizer = $vevent->ORGANZIER;
+ if (strcasecmp($organizer->getValue(), 'mailto:') !== 0) {
+ return null;
+ }
+
+ $organizerEMail = substr($organizer->getValue(), 7);
+
+ $name = $organizer->offsetGet('CN');
+ if ($name instanceof Parameter) {
+ return [$organizerEMail => $name];
+ }
+
+ return [$organizerEMail];
+ }
+
+ /**
+ * @param array $sortedByLanguage
+ * @param IUser[] $users
+ * @param string $defaultLanguage
+ */
+ private function sortUsersByLanguage(array &$sortedByLanguage,
+ array $users,
+ string $defaultLanguage):void {
+ /**
+ * @var array $sortedByLanguage
+ * [
+ * 'de' => ['a@b.com', 'c@d.com'],
+ * ...
+ * ]
+ */
+ foreach($users as $user) {
+ /** @var IUser $user */
+ $emailAddress = $user->getEMailAddress();
+ $lang = $this->config->getUserValue($user->getUID(),
+ 'core', 'lang', $defaultLanguage);
+
+ if (!isset($sortedByLanguage[$lang])) {
+ $sortedByLanguage[$lang] = [];
+ }
+
+ $sortedByLanguage[$lang][] = $emailAddress;
+ }
+ }
+
+ /**
+ * @param array $emails
+ * @param string $defaultLanguage
+ * @return array
+ */
+ private function sortEMailAddressesByLanguage(array $emails,
+ string $defaultLanguage):array {
+ $sortedByLanguage = [];
+
+ foreach($emails as $emailAddress => $parameters) {
+ if (isset($parameters['LANG'])) {
+ $lang = $parameters['LANG'];
+ } else {
+ $lang = $defaultLanguage;
+ }
+
+ if (!isset($sortedByLanguage[$lang])) {
+ $sortedByLanguage[$lang] = [];
+ }
+
+ $sortedByLanguage[$lang][] = $emailAddress;
+ }
+
+ return $sortedByLanguage;
+ }
+
+ /**
+ * @param VEvent $vevent
+ * @return array
+ */
+ private function getAllEMailAddressesFromEvent(VEvent $vevent):array {
+ $emailAddresses = [];
+
+ if (isset($vevent->ATTENDEE)) {
+ foreach ($vevent->ATTENDEE as $attendee) {
+ if (!($attendee instanceof VObject\Property)) {
+ continue;
+ }
+
+ $cuType = $this->getCUTypeOfAttendee($attendee);
+ if (\in_array($cuType, ['RESOURCE', 'ROOM', 'UNKNOWN'])) {
+ // Don't send emails to things
+ continue;
+ }
+
+ $partstat = $this->getPartstatOfAttendee($attendee);
+ if ($partstat === 'DECLINED') {
+ // Don't send out emails to people who declined
+ continue;
+ }
+ if ($partstat === 'DELEGATED') {
+ $delegates = $attendee->offsetGet('DELEGATED-TO');
+ if (!($delegates instanceof VObject\Parameter)) {
+ continue;
+ }
+
+ $emailAddressesOfDelegates = $delegates->getParts();
+ foreach($emailAddressesOfDelegates as $addressesOfDelegate) {
+ if (strcasecmp($addressesOfDelegate, 'mailto:') === 0) {
+ $emailAddresses[substr($addressesOfDelegate, 7)] = [];
+ }
+ }
+
+ continue;
+ }
+
+ $emailAddressOfAttendee = $this->getEMailAddressOfAttendee($attendee);
+ if ($emailAddressOfAttendee !== null) {
+ $properties = [];
+
+ $langProp = $attendee->offsetGet('LANG');
+ if ($langProp instanceof VObject\Parameter) {
+ $properties['LANG'] = $langProp->getValue();
+ }
+
+ $emailAddresses[$emailAddressOfAttendee] = $properties;
+ }
+ }
+ }
+
+ if (isset($vevent->ORGANIZER)) {
+ $emailAddresses[$this->getEMailAddressOfAttendee($vevent->ORGANIZER)] = [];
+ }
+
+ return $emailAddresses;
+ }
+
+
+
+ /**
+ * @param VObject\Property $attendee
+ * @return string
+ */
+ private function getCUTypeOfAttendee(VObject\Property $attendee):string {
+ $cuType = $attendee->offsetGet('CUTYPE');
+ if ($cuType instanceof VObject\Parameter) {
+ return strtoupper($cuType->getValue());
+ }
+
+ return 'INDIVIDUAL';
+ }
+
+ /**
+ * @param VObject\Property $attendee
+ * @return string
+ */
+ private function getPartstatOfAttendee(VObject\Property $attendee):string {
+ $partstat = $attendee->offsetGet('PARTSTAT');
+ if ($partstat instanceof VObject\Parameter) {
+ return strtoupper($partstat->getValue());
+ }
+
+ return 'NEEDS-ACTION';
+ }
+
+ /**
+ * @param VObject\Property $attendee
+ * @return bool
+ */
+ private function hasAttendeeMailURI(VObject\Property $attendee):bool {
+ return strcasecmp($attendee->getValue(), 'mailto:') === 0;
+ }
+
+ /**
+ * @param VObject\Property $attendee
+ * @return string|null
+ */
+ private function getEMailAddressOfAttendee(VObject\Property $attendee):?string {
+ if (!$this->hasAttendeeMailURI($attendee)) {
+ return null;
+ }
+
+ return substr($attendee->getValue(), 7);
+ }
+
+ /**
+ * @param array $users
+ * @return array
+ */
+ private function getEMailAddressesOfAllUsersWithWriteAccessToCalendar(array $users):array {
+ $emailAddresses = [];
+
+ foreach($users as $user) {
+ $emailAddress = $user->getEMailAddress();
+ if ($emailAddress) {
+ $lang = $this->getLangForUser($user);
+ if ($lang) {
+ $emailAddresses[$emailAddress] = [
+ 'LANG' => $lang,
+ ];
+ } else {
+ $emailAddresses[$emailAddress] = [];
+ }
+ }
+ }
+
+ return array_unique($emailAddresses);
+ }
+
+ /**
+ * @param IUser $user
+ * @return string
+ */
+ private function getLangForUser(IUser $user): ?string {
+ return $this->config->getUserValue($user->getUID(), 'core', 'lang', null);
+ }
+
+ /**
+ * @param IL10N $l10n
+ * @param VEvent $vevent
+ * @return string
+ * @throws \Exception
+ */
+ private function generateDateString(IL10N $l10n, VEvent $vevent):string {
+ $isAllDay = $vevent->DTSTART instanceof Property\ICalendar\Date;
+
+ /** @var Property\ICalendar\Date | Property\ICalendar\DateTime $dtstart */
+ /** @var Property\ICalendar\Date | Property\ICalendar\DateTime $dtend */
+ /** @var \DateTimeImmutable $dtstartDt */
+ $dtstartDt = $vevent->DTSTART->getDateTime();
+ /** @var \DateTimeImmutable $dtendDt */
+ $dtendDt = $this->getDTEndFromEvent($vevent)->getDateTime();
+
+ $diff = $dtstartDt->diff($dtendDt);
+
+ $dtstartDt = new \DateTime($dtstartDt->format(\DateTime::ATOM));
+ $dtendDt = new \DateTime($dtendDt->format(\DateTime::ATOM));
+
+ if ($isAllDay) {
+ // One day event
+ if ($diff->days === 1) {
+ return $this->getDateString($l10n, $dtstartDt);
+ }
+
+ return implode(' - ', [
+ $this->getDateString($l10n, $dtstartDt),
+ $this->getDateString($l10n, $dtendDt),
+ ]);
+ }
+
+ $startTimezone = $endTimezone = null;
+ if (!$vevent->DTSTART->isFloating()) {
+ $startTimezone = $vevent->DTSTART->getDateTime()->getTimezone()->getName();
+ $endTimezone = $this->getDTEndFromEvent($vevent)->getDateTime()->getTimezone()->getName();
+ }
+
+ $localeStart = implode(', ', [
+ $this->getWeekDayName($l10n, $dtstartDt),
+ $this->getDateTimeString($l10n, $dtstartDt)
+ ]);
+
+ // always show full date with timezone if timezones are different
+ if ($startTimezone !== $endTimezone) {
+ $localeEnd = implode(', ', [
+ $this->getWeekDayName($l10n, $dtendDt),
+ $this->getDateTimeString($l10n, $dtendDt)
+ ]);
+
+ return $localeStart
+ . ' (' . $startTimezone . ') '
+ . ' - '
+ . $localeEnd
+ . ' (' . $endTimezone . ')';
+ }
+
+ // Show only the time if the day is the same
+ $localeEnd = $this->isDayEqual($dtstartDt, $dtendDt)
+ ? $this->getTimeString($l10n, $dtendDt)
+ : implode(', ', [
+ $this->getWeekDayName($l10n, $dtendDt),
+ $this->getDateTimeString($l10n, $dtendDt)
+ ]);
+
+ return $localeStart
+ . ' - '
+ . $localeEnd
+ . ' (' . $startTimezone . ')';
+ }
+
+ /**
+ * @param DateTime $dtStart
+ * @param DateTime $dtEnd
+ * @return bool
+ */
+ private function isDayEqual(DateTime $dtStart,
+ DateTime $dtEnd):bool {
+ return $dtStart->format('Y-m-d') === $dtEnd->format('Y-m-d');
+ }
+
+ /**
+ * @param IL10N $l10n
+ * @param DateTime $dt
+ * @return string
+ */
+ private function getWeekDayName(IL10N $l10n, DateTime $dt):string {
+ return $l10n->l('weekdayName', $dt, ['width' => 'abbreviated']);
+ }
+
+ /**
+ * @param IL10N $l10n
+ * @param DateTime $dt
+ * @return string
+ */
+ private function getDateString(IL10N $l10n, DateTime $dt):string {
+ return $l10n->l('date', $dt, ['width' => 'medium']);
+ }
+
+ /**
+ * @param IL10N $l10n
+ * @param DateTime $dt
+ * @return string
+ */
+ private function getDateTimeString(IL10N $l10n, DateTime $dt):string {
+ return $l10n->l('datetime', $dt, ['width' => 'medium|short']);
+ }
+
+ /**
+ * @param IL10N $l10n
+ * @param DateTime $dt
+ * @return string
+ */
+ private function getTimeString(IL10N $l10n, DateTime $dt):string {
+ return $l10n->l('time', $dt, ['width' => 'short']);
+ }
+
+ /**
+ * @param VEvent $vevent
+ * @param IL10N $l10n
+ * @return string
+ */
+ private function getTitleFromVEvent(VEvent $vevent, IL10N $l10n):string {
+ if (isset($vevent->SUMMARY)) {
+ return (string)$vevent->SUMMARY;
+ }
+
+ return $l10n->t('Untitled event');
+ }
}
diff --git a/apps/dav/lib/CalDAV/Reminder/NotificationProvider/ProviderNotAvailableException.php b/apps/dav/lib/CalDAV/Reminder/NotificationProvider/ProviderNotAvailableException.php
index bf736db8a3..bfa6db9585 100644
--- a/apps/dav/lib/CalDAV/Reminder/NotificationProvider/ProviderNotAvailableException.php
+++ b/apps/dav/lib/CalDAV/Reminder/NotificationProvider/ProviderNotAvailableException.php
@@ -1,4 +1,5 @@
*
@@ -20,7 +21,6 @@
* along with this program. If not, see .
*
*/
-
namespace OCA\DAV\CalDAV\Reminder\NotificationProvider;
class ProviderNotAvailableException extends \Exception {
diff --git a/apps/dav/lib/CalDAV/Reminder/NotificationProvider/PushProvider.php b/apps/dav/lib/CalDAV/Reminder/NotificationProvider/PushProvider.php
index f04b8e4c45..2e580fd78a 100644
--- a/apps/dav/lib/CalDAV/Reminder/NotificationProvider/PushProvider.php
+++ b/apps/dav/lib/CalDAV/Reminder/NotificationProvider/PushProvider.php
@@ -1,8 +1,11 @@
+ * @copyright Copyright (c) 2019, Thomas Citharel
+ * @copyright Copyright (c) 2019, Georg Ehrke
*
* @author Thomas Citharel
+ * @author Georg Ehrke
*
* @license GNU AGPL version 3 or any later version
*
@@ -20,11 +23,9 @@
* along with this program. If not, see .
*
*/
-
namespace OCA\DAV\CalDAV\Reminder\NotificationProvider;
use OCA\DAV\AppInfo\Application;
-use OCA\DAV\CalDAV\Reminder\AbstractNotificationProvider;
use OCP\IConfig;
use OCP\ILogger;
use OCP\IURLGenerator;
@@ -32,22 +33,24 @@ use OCP\L10N\IFactory as L10NFactory;
use OCP\Notification\IManager;
use OCP\IUser;
use OCP\Notification\INotification;
-use Sabre\VObject\Component\VCalendar;
use OCP\AppFramework\Utility\ITimeFactory;
+use Sabre\VObject\Component\VEvent;
+use Sabre\VObject\Property;
-class PushProvider extends AbstractNotificationProvider {
+/**
+ * Class PushProvider
+ *
+ * @package OCA\DAV\CalDAV\Reminder\NotificationProvider
+ */
+class PushProvider extends AbstractProvider {
/** @var string */
public const NOTIFICATION_TYPE = 'DISPLAY';
- /**
- * @var IManager
- */
+ /** @var IManager */
private $manager;
- /**
- * @var ITimeFactory
- */
+ /** @var ITimeFactory */
private $timeFactory;
/**
@@ -58,42 +61,75 @@ class PushProvider extends AbstractNotificationProvider {
* @param IUrlGenerator $urlGenerator
* @param ITimeFactory $timeFactory
*/
- public function __construct(IConfig $config, IManager $manager, ILogger $logger,
+ public function __construct(IConfig $config,
+ IManager $manager,
+ ILogger $logger,
L10NFactory $l10nFactory,
- IURLGenerator $urlGenerator, ITimeFactory $timeFactory) {
+ IURLGenerator $urlGenerator,
+ ITimeFactory $timeFactory) {
parent::__construct($logger, $l10nFactory, $urlGenerator, $config);
$this->manager = $manager;
$this->timeFactory = $timeFactory;
}
/**
- * Send notification
+ * Send push notification to all users.
*
- * @param VCalendar $vcalendar
+ * @param VEvent $vevent
* @param string $calendarDisplayName
- * @param IUser $user
- * @return void
+ * @param IUser[] $users
* @throws \Exception
*/
- public function send(VCalendar $vcalendar, string $calendarDisplayName, IUser $user):void {
- $lang = $this->config->getUserValue($user->getUID(), 'core', 'lang', $this->l10nFactory->findLanguage());
- $this->l10n = $this->l10nFactory->get('dav', $lang);
+ public function send(VEvent $vevent,
+ string $calendarDisplayName=null,
+ array $users=[]):void {
+ $eventDetails = $this->extractEventDetails($vevent);
+ $eventDetails['calendar_displayname'] = $calendarDisplayName;
- $event = $this->extractEventDetails($vcalendar);
- /** @var INotification $notification */
- $notification = $this->manager->createNotification();
- $notification->setApp(Application::APP_ID)
- ->setUser($user->getUID())
- ->setDateTime($this->timeFactory->getDateTime())
- ->setObject(Application::APP_ID, $event['uid']) // $type and $id
- ->setSubject('calendar_reminder', ['title' => $event['title'], 'start' => $event['start']->getTimestamp()]) // $subject and $parameters
- ->setMessage('calendar_reminder', [
- 'when' => $event['when'],
- 'description' => $event['description'],
- 'location' => $event['location'],
- 'calendar' => $calendarDisplayName
- ])
- ;
- $this->manager->notify($notification);
+ foreach($users as $user) {
+ /** @var INotification $notification */
+ $notification = $this->manager->createNotification();
+ $notification->setApp(Application::APP_ID)
+ ->setUser($user->getUID())
+ ->setDateTime($this->timeFactory->getDateTime())
+ ->setObject(Application::APP_ID, (string) $vevent->UID)
+ ->setSubject('calendar_reminder', [
+ 'title' => $eventDetails['title'],
+ 'start_atom' => $eventDetails['start_atom']
+ ])
+ ->setMessage('calendar_reminder', $eventDetails);
+
+ $this->manager->notify($notification);
+ }
}
+
+ /**
+ * @var VEvent $vevent
+ * @return array
+ * @throws \Exception
+ */
+ protected function extractEventDetails(VEvent $vevent):array {
+ /** @var Property\ICalendar\DateTime $start */
+ $start = $vevent->DTSTART;
+ $end = $this->getDTEndFromEvent($vevent);
+
+ return [
+ 'title' => isset($vevent->SUMMARY)
+ ? ((string) $vevent->SUMMARY)
+ : null,
+ 'description' => isset($vevent->DESCRIPTION)
+ ? ((string) $vevent->DESCRIPTION)
+ : null,
+ 'location' => isset($vevent->LOCATION)
+ ? ((string) $vevent->LOCATION)
+ : null,
+ 'all_day' => $start instanceof Property\ICalendar\Date,
+ 'start_atom' => $start->getDateTime()->format(\DateTime::ATOM),
+ 'start_is_floating' => $start->isFloating(),
+ 'start_timezone' => $start->getDateTime()->getTimezone()->getName(),
+ 'end_atom' => $end->getDateTime()->format(\DateTime::ATOM),
+ 'end_is_floating' => $end->isFloating(),
+ 'end_timezone' => $end->getDateTime()->getTimezone()->getName(),
+ ];
+ }
}
diff --git a/apps/dav/lib/CalDAV/Reminder/NotificationProviderManager.php b/apps/dav/lib/CalDAV/Reminder/NotificationProviderManager.php
index 0a2579aedf..3d54970562 100644
--- a/apps/dav/lib/CalDAV/Reminder/NotificationProviderManager.php
+++ b/apps/dav/lib/CalDAV/Reminder/NotificationProviderManager.php
@@ -1,6 +1,11 @@
+ * @author Georg Ehrke
*
* @license AGPL-3.0
*
@@ -19,36 +24,55 @@
*/
namespace OCA\DAV\CalDAV\Reminder;
-use OCA\DAV\CalDAV\Reminder\NotificationProvider\ProviderNotAvailableException;
-
+/**
+ * Class NotificationProviderManager
+ *
+ * @package OCA\DAV\CalDAV\Reminder
+ */
class NotificationProviderManager {
- /** @var array */
+ /** @var INotificationProvider[] */
private $providers = [];
+
+ /**
+ * Checks whether a provider for a given ACTION exists
+ *
+ * @param string $type
+ * @return bool
+ */
+ public function hasProvider(string $type):bool {
+ return (\in_array($type, ReminderService::REMINDER_TYPES, true)
+ && isset($this->providers[$type]));
+ }
+
/**
- * @var string $type
- * @return AbstractNotificationProvider
- * @throws ProviderNotAvailableException
+ * Get provider for a given ACTION
+ *
+ * @param string $type
+ * @return INotificationProvider
+ * @throws NotificationProvider\ProviderNotAvailableException
* @throws NotificationTypeDoesNotExistException
*/
- public function getProvider(string $type):AbstractNotificationProvider {
+ public function getProvider(string $type):INotificationProvider {
if (in_array($type, ReminderService::REMINDER_TYPES, true)) {
if (isset($this->providers[$type])) {
return $this->providers[$type];
}
- throw new ProviderNotAvailableException($type);
+ throw new NotificationProvider\ProviderNotAvailableException($type);
}
throw new NotificationTypeDoesNotExistException($type);
}
/**
+ * Registers a new provider
+ *
* @param string $providerClassName
* @throws \OCP\AppFramework\QueryException
*/
public function registerProvider(string $providerClassName):void {
$provider = \OC::$server->query($providerClassName);
- if (!$provider instanceof AbstractNotificationProvider) {
+ if (!$provider instanceof INotificationProvider) {
throw new \InvalidArgumentException('Invalid notification provider registered');
}
diff --git a/apps/dav/lib/CalDAV/Reminder/NotificationTypeDoesNotExistException.php b/apps/dav/lib/CalDAV/Reminder/NotificationTypeDoesNotExistException.php
index ae4ec3bd3b..c060089785 100644
--- a/apps/dav/lib/CalDAV/Reminder/NotificationTypeDoesNotExistException.php
+++ b/apps/dav/lib/CalDAV/Reminder/NotificationTypeDoesNotExistException.php
@@ -1,6 +1,7 @@
+ * @copyright Copyright (c) 2019, Thomas Citharel
*
* @author Thomas Citharel
*
diff --git a/apps/dav/lib/CalDAV/Reminder/Notifier.php b/apps/dav/lib/CalDAV/Reminder/Notifier.php
index 3718d5b29a..4bad984178 100644
--- a/apps/dav/lib/CalDAV/Reminder/Notifier.php
+++ b/apps/dav/lib/CalDAV/Reminder/Notifier.php
@@ -1,8 +1,11 @@
+ * @copyright Copyright (c) 2019, Thomas Citharel
+ * @copyright Copyright (c) 2019, Georg Ehrke
*
* @author Thomas Citharel
+ * @author Georg Ehrke
*
* @license AGPL-3.0
*
@@ -22,37 +25,47 @@
namespace OCA\DAV\CalDAV\Reminder;
+use DateTime;
use OCA\DAV\AppInfo\Application;
+use OCP\AppFramework\Utility\ITimeFactory;
use OCP\IL10N;
use OCP\L10N\IFactory;
use OCP\Notification\INotification;
use OCP\Notification\INotifier;
use OCP\IURLGenerator;
+/**
+ * Class Notifier
+ *
+ * @package OCA\DAV\CalDAV\Reminder
+ */
class Notifier implements INotifier {
- /** @var array */
- public static $units = [
- 'y' => 'year',
- 'm' => 'month',
- 'd' => 'day',
- 'h' => 'hour',
- 'i' => 'minute',
- 's' => 'second',
- ];
-
/** @var IFactory */
- protected $factory;
+ private $l10nFactory;
/** @var IURLGenerator */
- protected $urlGenerator;
+ private $urlGenerator;
/** @var IL10N */
- protected $l;
+ private $l10n;
- public function __construct(IFactory $factory, IURLGenerator $urlGenerator) {
- $this->factory = $factory;
+ /** @var ITimeFactory */
+ private $timeFactory;
+
+ /**
+ * Notifier constructor.
+ *
+ * @param IFactory $factory
+ * @param IURLGenerator $urlGenerator
+ * @param ITimeFactory $timeFactory
+ */
+ public function __construct(IFactory $factory,
+ IURLGenerator $urlGenerator,
+ ITimeFactory $timeFactory) {
+ $this->l10nFactory = $factory;
$this->urlGenerator = $urlGenerator;
+ $this->timeFactory = $timeFactory;
}
/**
@@ -62,7 +75,7 @@ class Notifier implements INotifier {
* @since 17.0.0
*/
public function getID():string {
- return 'dav';
+ return Application::APP_ID;
}
/**
@@ -72,89 +85,236 @@ class Notifier implements INotifier {
* @since 17.0.0
*/
public function getName():string {
- return $this->factory->get('dav')->t('Calendar');
+ if ($this->l10n) {
+ return $this->l10n->t('Calendar');
+ }
+
+ return $this->l10nFactory->get('dav')->t('Calendar');
}
/**
+ * Prepare sending the notification
+ *
* @param INotification $notification
* @param string $languageCode The code of the language that should be used to prepare the notification
* @return INotification
* @throws \Exception
*/
- public function prepare(INotification $notification, string $languageCode):INotification {
+ public function prepare(INotification $notification,
+ string $languageCode):INotification {
if ($notification->getApp() !== Application::APP_ID) {
throw new \InvalidArgumentException('Notification not from this app');
}
// Read the language from the notification
- $this->l = $this->factory->get('dav', $languageCode);
+ $this->l10n = $this->l10nFactory->get('dav', $languageCode);
- if ($notification->getSubject() === 'calendar_reminder') {
- $subjectParameters = $notification->getSubjectParameters();
- $notification->setParsedSubject($this->processEventTitle($subjectParameters));
+ // Handle notifier subjects
+ switch($notification->getSubject()) {
+ case 'calendar_reminder':
+ return $this->prepareReminderNotification($notification);
+
+ default:
+ throw new \InvalidArgumentException('Unknown subject');
- $messageParameters = $notification->getMessageParameters();
- $notification->setParsedMessage($this->processEventDescription($messageParameters));
- $notification->setIcon($this->urlGenerator->getAbsoluteURL($this->urlGenerator->imagePath('core', 'places/calendar.svg')));
- return $notification;
}
- // Unknown subject => Unknown notification => throw
- throw new \InvalidArgumentException('Unknown subject');
}
/**
- * @param array $event
+ * @param INotification $notification
+ * @return INotification
+ */
+ private function prepareReminderNotification(INotification $notification):INotification {
+ $imagePath = $this->urlGenerator->imagePath('core', 'places/calendar.svg');
+ $iconUrl = $this->urlGenerator->getAbsoluteURL($imagePath);
+ $notification->setIcon($iconUrl);
+
+ $this->prepareNotificationSubject($notification);
+ $this->prepareNotificationMessage($notification);
+
+ return $notification;
+ }
+
+ /**
+ * Sets the notification subject based on the parameters set in PushProvider
+ *
+ * @param INotification $notification
+ */
+ private function prepareNotificationSubject(INotification $notification): void {
+ $parameters = $notification->getSubjectParameters();
+
+ $startTime = \DateTime::createFromFormat(\DateTimeInterface::ATOM, $parameters['start_atom']);
+ $now = $this->timeFactory->getDateTime();
+ $title = $this->getTitleFromParameters($parameters);
+
+ $diff = $startTime->diff($now);
+ if ($diff === false) {
+ return;
+ }
+
+ $components = [];
+ if ($diff->y) {
+ $components[] = $this->l10n->n('%n year', '%n years', $diff->y);
+ }
+ if ($diff->m) {
+ $components[] = $this->l10n->n('%n month', '%n months', $diff->m);
+ }
+ if ($diff->d) {
+ $components[] = $this->l10n->n('%n day', '%n days', $diff->d);
+ }
+ if ($diff->h) {
+ $components[] = $this->l10n->n('%n hour', '%n hours', $diff->h);
+ }
+ if ($diff->i) {
+ $components[] = $this->l10n->n('%n minute', '%n minutes', $diff->i);
+ }
+
+ // Limiting to the first three components to prevent
+ // the string from getting too long
+ $firstThreeComponents = array_slice($components, 0, 2);
+ $diffLabel = implode(', ', $firstThreeComponents);
+
+ if ($diff->invert) {
+ $title = $this->l10n->t('%s (in %s)', [$title, $diffLabel]);
+ } else {
+ $title = $this->l10n->t('%s (%s ago)', [$title, $diffLabel]);
+ }
+
+ $notification->setParsedSubject($title);
+ }
+
+ /**
+ * Sets the notification message based on the parameters set in PushProvider
+ *
+ * @param INotification $notification
+ */
+ private function prepareNotificationMessage(INotification $notification): void {
+ $parameters = $notification->getMessageParameters();
+
+ $description = [
+ $this->l10n->t('Calendar: %s', $parameters['calendar_displayname']),
+ $this->l10n->t('Date: %s', $this->generateDateString($parameters)),
+ ];
+ if ($parameters['description']) {
+ $description[] = $this->l10n->t('Description: %s', $parameters['description']);
+ }
+ if ($parameters['location']) {
+ $description[] = $this->l10n->t('Where: %s', $parameters['location']);
+ }
+
+ $message = implode("\r\n", $description);
+ $notification->setParsedMessage($message);
+ }
+
+ /**
+ * @param array $parameters
+ * @return string
+ */
+ private function getTitleFromParameters(array $parameters):string {
+ return $parameters['title'] ?? $this->l10n->t('Untitled event');
+ }
+
+ /**
+ * @param array $parameters
* @return string
* @throws \Exception
*/
- private function processEventTitle(array $event):string {
- $event_datetime = new \DateTime();
- $event_datetime->setTimestamp($event['start']);
- $now = new \DateTime();
+ private function generateDateString(array $parameters):string {
+ $startDateTime = DateTime::createFromFormat(DATE_ATOM, $parameters['start_atom']);
+ $endDateTime = DateTime::createFromFormat(DATE_ATOM, $parameters['end_atom']);
+ $isAllDay = $parameters['all_day'];
+ $diff = $startDateTime->diff($endDateTime);
- $diff = $event_datetime->diff($now);
+ if ($isAllDay) {
+ // One day event
+ if ($diff->days === 1) {
+ return $this->getDateString($startDateTime);
+ }
- foreach (self::$units as $attribute => $unit) {
- $count = $diff->$attribute;
- if (0 !== $count) {
- return $this->getPluralizedTitle($count, $diff->invert, $unit, $event['title']);
- }
- }
- return '';
+ return implode(' - ', [
+ $this->getDateString($startDateTime),
+ $this->getDateString($endDateTime),
+ ]);
+ }
+
+ $startTimezone = $endTimezone = null;
+ if (!$parameters['start_is_floating']) {
+ $startTimezone = $parameters['start_timezone'];
+ $endTimezone = $parameters['end_timezone'];
+ }
+
+ $localeStart = implode(', ', [
+ $this->getWeekDayName($startDateTime),
+ $this->getDateTimeString($startDateTime)
+ ]);
+
+ // always show full date with timezone if timezones are different
+ if ($startTimezone !== $endTimezone) {
+ $localeEnd = implode(', ', [
+ $this->getWeekDayName($endDateTime),
+ $this->getDateTimeString($endDateTime)
+ ]);
+
+ return $localeStart
+ . ' (' . $startTimezone . ') '
+ . ' - '
+ . $localeEnd
+ . ' (' . $endTimezone . ')';
+ }
+
+ // Show only the time if the day is the same
+ $localeEnd = $this->isDayEqual($startDateTime, $endDateTime)
+ ? $this->getTimeString($endDateTime)
+ : implode(', ', [
+ $this->getWeekDayName($endDateTime),
+ $this->getDateTimeString($endDateTime)
+ ]);
+
+ return $localeStart
+ . ' - '
+ . $localeEnd
+ . ' (' . $startTimezone . ')';
}
/**
- *
- * @param int $count
- * @param int $invert
- * @param string $unit
- * @param string $title
- * @return string
+ * @param DateTime $dtStart
+ * @param DateTime $dtEnd
+ * @return bool
*/
- private function getPluralizedTitle(int $count, int $invert, string $unit, string $title):string {
- if ($invert) {
- return $this->l->n('%s (in one %s)', '%s (in %n %ss)', $count, [$title, $unit]);
- }
- // This should probably not show up
- return $this->l->n('%s (one %s ago)', '%s (%n %ss ago)', $count, [$title, $unit]);
+ private function isDayEqual(DateTime $dtStart,
+ DateTime $dtEnd):bool {
+ return $dtStart->format('Y-m-d') === $dtEnd->format('Y-m-d');
}
/**
- * @param array $event
+ * @param DateTime $dt
* @return string
*/
- private function processEventDescription(array $event):string {
- $description = [
- $this->l->t('Calendar: %s', $event['calendar']),
- $this->l->t('Date: %s', $event['when']),
- ];
+ private function getWeekDayName(DateTime $dt):string {
+ return $this->l10n->l('weekdayName', $dt, ['width' => 'abbreviated']);
+ }
- if ($event['description']) {
- $description[] = $this->l->t('Description: %s', $event['description']);
- }
- if ($event['location']) {
- $description[] = $this->l->t('Where: %s', $event['location']);
- }
- return implode(' ', $description);
+ /**
+ * @param DateTime $dt
+ * @return string
+ */
+ private function getDateString(DateTime $dt):string {
+ return $this->l10n->l('date', $dt, ['width' => 'medium']);
+ }
+
+ /**
+ * @param DateTime $dt
+ * @return string
+ */
+ private function getDateTimeString(DateTime $dt):string {
+ return $this->l10n->l('datetime', $dt, ['width' => 'medium|short']);
+ }
+
+ /**
+ * @param DateTime $dt
+ * @return string
+ */
+ private function getTimeString(DateTime $dt):string {
+ return $this->l10n->l('time', $dt, ['width' => 'short']);
}
}
diff --git a/apps/dav/lib/CalDAV/Reminder/ReminderService.php b/apps/dav/lib/CalDAV/Reminder/ReminderService.php
index ce6c846061..ad428eef74 100644
--- a/apps/dav/lib/CalDAV/Reminder/ReminderService.php
+++ b/apps/dav/lib/CalDAV/Reminder/ReminderService.php
@@ -1,6 +1,11 @@
+ * @author Georg Ehrke
*
* @license AGPL-3.0
*
@@ -19,14 +24,19 @@
*/
namespace OCA\DAV\CalDAV\Reminder;
-use OC\User\NoUserException;
+use DateTimeImmutable;
+use OCA\DAV\CalDAV\CalDavBackend;
+use OCP\AppFramework\Utility\ITimeFactory;
use OCP\IGroup;
use OCP\IGroupManager;
+use OCP\IUser;
use OCP\IUserManager;
-use OCP\IUserSession;
use Sabre\VObject;
use Sabre\VObject\Component\VAlarm;
-use Sabre\VObject\Reader;
+use Sabre\VObject\Component\VEvent;
+use Sabre\VObject\ParseException;
+use Sabre\VObject\Recur\EventIterator;
+use Sabre\VObject\Recur\NoInstancesException;
class ReminderService {
@@ -42,144 +52,710 @@ class ReminderService {
/** @var IGroupManager */
private $groupManager;
- /** @var IUserSession */
- private $userSession;
+ /** @var CalDavBackend */
+ private $caldavBackend;
+
+ /** @var ITimeFactory */
+ private $timeFactory;
public const REMINDER_TYPE_EMAIL = 'EMAIL';
public const REMINDER_TYPE_DISPLAY = 'DISPLAY';
public const REMINDER_TYPE_AUDIO = 'AUDIO';
+ /**
+ * @var String[]
+ *
+ * Official RFC5545 reminder types
+ */
public const REMINDER_TYPES = [
self::REMINDER_TYPE_EMAIL,
self::REMINDER_TYPE_DISPLAY,
self::REMINDER_TYPE_AUDIO
];
+ /**
+ * ReminderService constructor.
+ *
+ * @param Backend $backend
+ * @param NotificationProviderManager $notificationProviderManager
+ * @param IUserManager $userManager
+ * @param IGroupManager $groupManager
+ * @param CalDavBackend $caldavBackend
+ * @param ITimeFactory $timeFactory
+ */
public function __construct(Backend $backend,
NotificationProviderManager $notificationProviderManager,
IUserManager $userManager,
IGroupManager $groupManager,
- IUserSession $userSession) {
+ CalDavBackend $caldavBackend,
+ ITimeFactory $timeFactory) {
$this->backend = $backend;
$this->notificationProviderManager = $notificationProviderManager;
$this->userManager = $userManager;
$this->groupManager = $groupManager;
- $this->userSession = $userSession;
+ $this->caldavBackend = $caldavBackend;
+ $this->timeFactory = $timeFactory;
}
/**
* Process reminders to activate
*
- * @throws NoUserException
* @throws NotificationProvider\ProviderNotAvailableException
* @throws NotificationTypeDoesNotExistException
*/
public function processReminders():void {
$reminders = $this->backend->getRemindersToProcess();
- foreach ($reminders as $reminder) {
- $calendarData = Reader::read($reminder['calendardata']);
+ foreach($reminders as $reminder) {
+ $vcalendar = $this->parseCalendarData($reminder['calendardata']);
+ if (!$vcalendar) {
+ $this->backend->removeReminder($reminder['id']);
+ continue;
+ }
- $user = $this->userManager->get($reminder['uid']);
+ $vevent = $this->getVEventByRecurrenceId($vcalendar, $reminder['recurrence_id'], $reminder['is_recurrence_exception']);
+ if (!$vevent) {
+ $this->backend->removeReminder($reminder['id']);
+ continue;
+ }
- if ($user === null) {
- throw new NoUserException('User not found for calendar');
+ if ($this->wasEventCancelled($vevent)) {
+ $this->deleteOrProcessNext($reminder, $vevent);
+ continue;
+ }
+
+ if (!$this->notificationProviderManager->hasProvider($reminder['type'])) {
+ $this->deleteOrProcessNext($reminder, $vevent);
+ continue;
+ }
+
+ $users = $this->getAllUsersWithWriteAccessToCalendar($reminder['calendar_id']);
+ $user = $this->getUserFromPrincipalURI($reminder['principaluri']);
+ if ($user) {
+ $users[] = $user;
}
$notificationProvider = $this->notificationProviderManager->getProvider($reminder['type']);
- $notificationProvider->send($calendarData, $reminder['displayname'], $user);
- $this->backend->removeReminder($reminder['id']);
+ $notificationProvider->send($vevent, $reminder['displayname'], $users);
+
+ $this->deleteOrProcessNext($reminder, $vevent);
}
}
/**
- * Saves reminders when a calendar object with some alarms was created/updated/deleted
- *
* @param string $action
- * @param array $calendarData
- * @param array $shares
* @param array $objectData
- * @return void
* @throws VObject\InvalidDataException
- * @throws NoUserException
*/
- public function onTouchCalendarObject(string $action, array $calendarData, array $shares, array $objectData):void {
- if (!isset($calendarData['principaluri'])) {
+ public function onTouchCalendarObject(string $action,
+ array $objectData):void {
+ // We only support VEvents for now
+ if (strcasecmp($objectData['component'], 'vevent') !== 0) {
return;
}
- // Always remove existing reminders for this event
- $this->backend->cleanRemindersForEvent($objectData['calendarid'], $objectData['uri']);
+ switch($action) {
+ case '\OCA\DAV\CalDAV\CalDavBackend::createCalendarObject':
+ $this->onCalendarObjectCreate($objectData);
+ break;
- /**
- * If we are deleting the event, no need to go further
- */
- if ($action === '\OCA\DAV\CalDAV\CalDavBackend::deleteCalendarObject') {
+ case '\OCA\DAV\CalDAV\CalDavBackend::updateCalendarObject':
+ $this->onCalendarObjectEdit($objectData);
+ break;
+
+ case '\OCA\DAV\CalDAV\CalDavBackend::deleteCalendarObject':
+ $this->onCalendarObjectDelete($objectData);
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ /**
+ * @param array $objectData
+ */
+ private function onCalendarObjectCreate(array $objectData):void {
+ /** @var VObject\Component\VCalendar $vcalendar */
+ $vcalendar = $this->parseCalendarData($objectData['calendardata']);
+ if (!$vcalendar) {
return;
}
- $user = $this->userSession->getUser();
-
- if ($user === null) {
- throw new NoUserException('No user in session');
+ $vevents = $this->getAllVEventsFromVCalendar($vcalendar);
+ if (count($vevents) === 0) {
+ return;
}
- $users = $this->getUsersForShares($shares);
+ $uid = (string) $vevents[0]->UID;
+ $recurrenceExceptions = $this->getRecurrenceExceptionFromListOfVEvents($vevents);
+ $masterItem = $this->getMasterItemFromListOfVEvents($vevents);
+ $now = $this->timeFactory->getDateTime();
+ $isRecurring = $masterItem ? $this->isRecurring($masterItem) : false;
- $users[] = $user->getUID();
+ foreach($recurrenceExceptions as $recurrenceException) {
+ $eventHash = $this->getEventHash($recurrenceException);
- $vobject = VObject\Reader::read($objectData['calendardata']);
-
- foreach ($vobject->VEVENT->VALARM as $alarm) {
- if ($alarm instanceof VAlarm) {
- $type = strtoupper($alarm->ACTION->getValue());
- if (in_array($type, self::REMINDER_TYPES, true)) {
- $time = $alarm->getEffectiveTriggerTime();
-
- foreach ($users as $uid) {
- $this->backend->insertReminder(
- $uid,
- $objectData['calendarid'],
- $objectData['uri'],
- $type,
- $time->getTimestamp(),
- $vobject->VEVENT->DTSTART->getDateTime()->getTimestamp());
-
- }
+ foreach($recurrenceException->VALARM as $valarm) {
+ /** @var VAlarm $valarm */
+ $alarmHash = $this->getAlarmHash($valarm);
+ $triggerTime = $valarm->getEffectiveTriggerTime();
+ $diff = $now->diff($triggerTime);
+ if ($diff->invert === 1) {
+ continue;
}
+
+ $alarms = $this->getRemindersForVAlarm($valarm, $objectData,
+ $eventHash, $alarmHash, true, true);
+ $this->writeRemindersToDatabase($alarms);
+ }
+ }
+
+ if ($masterItem) {
+ $processedAlarms = [];
+ $masterAlarms = [];
+ $masterHash = $this->getEventHash($masterItem);
+
+ foreach($masterItem->VALARM as $valarm) {
+ $masterAlarms[] = $this->getAlarmHash($valarm);
+ }
+
+ try {
+ $iterator = new EventIterator($vevents, $uid);
+ } catch (NoInstancesException $e) {
+ // This event is recurring, but it doesn't have a single
+ // instance. We are skipping this event from the output
+ // entirely.
+ return;
+ }
+
+ while($iterator->valid() && count($processedAlarms) < count($masterAlarms)) {
+ $event = $iterator->getEventObject();
+
+ // Recurrence-exceptions are handled separately, so just ignore them here
+ if (\in_array($event, $recurrenceExceptions, true)) {
+ $iterator->next();
+ continue;
+ }
+
+ foreach($event->VALARM as $valarm) {
+ /** @var VAlarm $valarm */
+ $alarmHash = $this->getAlarmHash($valarm);
+ if (\in_array($alarmHash, $processedAlarms, true)) {
+ continue;
+ }
+
+ if (!\in_array((string) $valarm->ACTION, self::REMINDER_TYPES, true)) {
+ // Action allows x-name, we don't insert reminders
+ // into the database if they are not standard
+ $processedAlarms[] = $alarmHash;
+ continue;
+ }
+
+ $triggerTime = $valarm->getEffectiveTriggerTime();
+
+ // If effective trigger time is in the past
+ // just skip and generate for next event
+ $diff = $now->diff($triggerTime);
+ if ($diff->invert === 1) {
+ // If an absolute alarm is in the past,
+ // just add it to processedAlarms, so
+ // we don't extend till eternity
+ if (!$this->isAlarmRelative($valarm)) {
+ $processedAlarms[] = $alarmHash;
+ }
+
+ continue;
+ }
+
+ $alarms = $this->getRemindersForVAlarm($valarm, $objectData, $masterHash, $alarmHash, $isRecurring, false);
+ $this->writeRemindersToDatabase($alarms);
+ $processedAlarms[] = $alarmHash;
+ }
+
+ $iterator->next();
}
}
}
+ /**
+ * @param array $objectData
+ */
+ private function onCalendarObjectEdit(array $objectData):void {
+ // TODO - this can be vastly improved
+ // - get cached reminders
+ // - ...
+
+ $this->onCalendarObjectDelete($objectData);
+ $this->onCalendarObjectCreate($objectData);
+ }
/**
- * Get all users that have access to a given calendar
- *
- * @param array $shares
- * @return string[]
+ * @param array $objectData
*/
- private function getUsersForShares(array $shares):array {
- $users = $groups = [];
+ private function onCalendarObjectDelete(array $objectData):void {
+ $this->backend->cleanRemindersForEvent((int) $objectData['id']);
+ }
+
+ /**
+ * @param VAlarm $valarm
+ * @param array $objectData
+ * @param string|null $eventHash
+ * @param string|null $alarmHash
+ * @param bool $isRecurring
+ * @param bool $isRecurrenceException
+ * @return array
+ */
+ private function getRemindersForVAlarm(VAlarm $valarm,
+ array $objectData,
+ string $eventHash=null,
+ string $alarmHash=null,
+ bool $isRecurring=false,
+ bool $isRecurrenceException=false):array {
+ if ($eventHash === null) {
+ $eventHash = $this->getEventHash($valarm->parent);
+ }
+ if ($alarmHash === null) {
+ $alarmHash = $this->getAlarmHash($valarm);
+ }
+
+ $recurrenceId = $this->getEffectiveRecurrenceIdOfVEvent($valarm->parent);
+ $isRelative = $this->isAlarmRelative($valarm);
+ /** @var DateTimeImmutable $notificationDate */
+ $notificationDate = $valarm->getEffectiveTriggerTime();
+ $clonedNotificationDate = new \DateTime('now', $notificationDate->getTimezone());
+ $clonedNotificationDate->setTimestamp($notificationDate->getTimestamp());
+
+ $alarms = [];
+
+ $alarms[] = [
+ 'calendar_id' => $objectData['calendarid'],
+ 'object_id' => $objectData['id'],
+ 'uid' => (string) $valarm->parent->UID,
+ 'is_recurring' => $isRecurring,
+ 'recurrence_id' => $recurrenceId,
+ 'is_recurrence_exception' => $isRecurrenceException,
+ 'event_hash' => $eventHash,
+ 'alarm_hash' => $alarmHash,
+ 'type' => (string) $valarm->ACTION,
+ 'is_relative' => $isRelative,
+ 'notification_date' => $notificationDate->getTimestamp(),
+ 'is_repeat_based' => false,
+ ];
+
+ $repeat = $valarm->REPEAT ? (int) $valarm->REPEAT : 0;
+ for($i = 0; $i < $repeat; $i++) {
+ if ($valarm->DURATION === null) {
+ continue;
+ }
+
+ $clonedNotificationDate->add($valarm->DURATION->getDateInterval());
+ $alarms[] = [
+ 'calendar_id' => $objectData['calendarid'],
+ 'object_id' => $objectData['id'],
+ 'uid' => (string) $valarm->parent->UID,
+ 'is_recurring' => $isRecurring,
+ 'recurrence_id' => $recurrenceId,
+ 'is_recurrence_exception' => $isRecurrenceException,
+ 'event_hash' => $eventHash,
+ 'alarm_hash' => $alarmHash,
+ 'type' => (string) $valarm->ACTION,
+ 'is_relative' => $isRelative,
+ 'notification_date' => $clonedNotificationDate->getTimestamp(),
+ 'is_repeat_based' => true,
+ ];
+ }
+
+ return $alarms;
+ }
+
+ /**
+ * @param array $reminders
+ */
+ private function writeRemindersToDatabase(array $reminders): void {
+ foreach($reminders as $reminder) {
+ $this->backend->insertReminder(
+ (int) $reminder['calendar_id'],
+ (int) $reminder['object_id'],
+ $reminder['uid'],
+ $reminder['is_recurring'],
+ (int) $reminder['recurrence_id'],
+ $reminder['is_recurrence_exception'],
+ $reminder['event_hash'],
+ $reminder['alarm_hash'],
+ $reminder['type'],
+ $reminder['is_relative'],
+ (int) $reminder['notification_date'],
+ $reminder['is_repeat_based']
+ );
+ }
+ }
+
+ /**
+ * @param array $reminder
+ * @param VEvent $vevent
+ */
+ private function deleteOrProcessNext(array $reminder,
+ VObject\Component\VEvent $vevent):void {
+ if ($reminder['is_repeat_based'] ||
+ !$reminder['is_recurring'] ||
+ !$reminder['is_relative'] ||
+ $reminder['is_recurrence_exception']) {
+
+ $this->backend->removeReminder($reminder['id']);
+ return;
+ }
+
+ $vevents = $this->getAllVEventsFromVCalendar($vevent->parent);
+ $recurrenceExceptions = $this->getRecurrenceExceptionFromListOfVEvents($vevents);
+ $now = $this->timeFactory->getDateTime();
+
+ try {
+ $iterator = new EventIterator($vevents, $reminder['uid']);
+ } catch (NoInstancesException $e) {
+ // This event is recurring, but it doesn't have a single
+ // instance. We are skipping this event from the output
+ // entirely.
+ return;
+ }
+
+ while($iterator->valid()) {
+ $event = $iterator->getEventObject();
+
+ // Recurrence-exceptions are handled separately, so just ignore them here
+ if (\in_array($event, $recurrenceExceptions, true)) {
+ $iterator->next();
+ continue;
+ }
+
+ $recurrenceId = $this->getEffectiveRecurrenceIdOfVEvent($event);
+ if ($reminder['recurrence_id'] >= $recurrenceId) {
+ $iterator->next();
+ continue;
+ }
+
+ foreach($event->VALARM as $valarm) {
+ /** @var VAlarm $valarm */
+ $alarmHash = $this->getAlarmHash($valarm);
+ if ($alarmHash !== $reminder['alarm_hash']) {
+ continue;
+ }
+
+ $triggerTime = $valarm->getEffectiveTriggerTime();
+
+ // If effective trigger time is in the past
+ // just skip and generate for next event
+ $diff = $now->diff($triggerTime);
+ if ($diff->invert === 1) {
+ continue;
+ }
+
+ $this->backend->removeReminder($reminder['id']);
+ $alarms = $this->getRemindersForVAlarm($valarm, [
+ 'calendarid' => $reminder['calendar_id'],
+ 'id' => $reminder['object_id'],
+ ], $reminder['event_hash'], $alarmHash, true, false);
+ $this->writeRemindersToDatabase($alarms);
+
+ // Abort generating reminders after creating one successfully
+ return;
+ }
+
+ $iterator->next();
+ }
+
+ $this->backend->removeReminder($reminder['id']);
+ }
+
+ /**
+ * @param int $calendarId
+ * @return IUser[]
+ */
+ private function getAllUsersWithWriteAccessToCalendar(int $calendarId):array {
+ $shares = $this->caldavBackend->getShares($calendarId);
+
+ $users = [];
+ $userIds = [];
+ $groups = [];
foreach ($shares as $share) {
+ // Only consider writable shares
+ if ($share['readOnly']) {
+ continue;
+ }
+
$principal = explode('/', $share['{http://owncloud.org/ns}principal']);
if ($principal[1] === 'users') {
- $users[] = $principal[2];
+ $user = $this->userManager->get($principal[2]);
+ if ($user) {
+ $users[] = $user;
+ $userIds[] = $principal[2];
+ }
} else if ($principal[1] === 'groups') {
$groups[] = $principal[2];
}
}
- if (!empty($groups)) {
- foreach ($groups as $gid) {
- $group = $this->groupManager->get($gid);
- if ($group instanceof IGroup) {
- foreach ($group->getUsers() as $user) {
- $users[] = $user->getUID();
+ foreach ($groups as $gid) {
+ $group = $this->groupManager->get($gid);
+ if ($group instanceof IGroup) {
+ foreach ($group->getUsers() as $user) {
+ if (!\in_array($user->getUID(), $userIds, true)) {
+ $users[] = $user;
+ $userIds[] = $user->getUID();
}
}
}
}
- return array_unique($users);
+ return $users;
+ }
+
+ /**
+ * Gets a hash of the event.
+ * If the hash changes, we have to update all relative alarms.
+ *
+ * @param VEvent $vevent
+ * @return string
+ */
+ private function getEventHash(VEvent $vevent):string {
+ $properties = [
+ (string) $vevent->DTSTART->serialize(),
+ ];
+
+ if ($vevent->DTEND) {
+ $properties[] = (string) $vevent->DTEND->serialize();
+ }
+ if ($vevent->DURATION) {
+ $properties[] = (string) $vevent->DURATION->serialize();
+ }
+ if ($vevent->{'RECURRENCE-ID'}) {
+ $properties[] = (string) $vevent->{'RECURRENCE-ID'}->serialize();
+ }
+ if ($vevent->RRULE) {
+ $properties[] = (string) $vevent->RRULE->serialize();
+ }
+ if ($vevent->EXDATE) {
+ $properties[] = (string) $vevent->EXDATE->serialize();
+ }
+ if ($vevent->RDATE) {
+ $properties[] = (string) $vevent->RDATE->serialize();
+ }
+
+ return md5(implode('::', $properties));
+ }
+
+ /**
+ * Gets a hash of the alarm.
+ * If the hash changes, we have to update oc_dav_reminders.
+ *
+ * @param VAlarm $valarm
+ * @return string
+ */
+ private function getAlarmHash(VAlarm $valarm):string {
+ $properties = [
+ (string) $valarm->ACTION->serialize(),
+ (string) $valarm->TRIGGER->serialize(),
+ ];
+
+ if ($valarm->DURATION) {
+ $properties[] = (string) $valarm->DURATION->serialize();
+ }
+ if ($valarm->REPEAT) {
+ $properties[] = (string) $valarm->REPEAT->serialize();
+ }
+
+ return md5(implode('::', $properties));
+ }
+
+ /**
+ * @param VObject\Component\VCalendar $vcalendar
+ * @param int $recurrenceId
+ * @param bool $isRecurrenceException
+ * @return VEvent|null
+ */
+ private function getVEventByRecurrenceId(VObject\Component\VCalendar $vcalendar,
+ int $recurrenceId,
+ bool $isRecurrenceException):?VEvent {
+ $vevents = $this->getAllVEventsFromVCalendar($vcalendar);
+ if (count($vevents) === 0) {
+ return null;
+ }
+
+ $uid = (string) $vevents[0]->UID;
+ $recurrenceExceptions = $this->getRecurrenceExceptionFromListOfVEvents($vevents);
+ $masterItem = $this->getMasterItemFromListOfVEvents($vevents);
+
+ // Handle recurrence-exceptions first, because recurrence-expansion is expensive
+ if ($isRecurrenceException) {
+ foreach($recurrenceExceptions as $recurrenceException) {
+ if ($this->getEffectiveRecurrenceIdOfVEvent($recurrenceException) === $recurrenceId) {
+ return $recurrenceException;
+ }
+ }
+
+ return null;
+ }
+
+ if ($masterItem) {
+ try {
+ $iterator = new EventIterator($vevents, $uid);
+ } catch (NoInstancesException $e) {
+ // This event is recurring, but it doesn't have a single
+ // instance. We are skipping this event from the output
+ // entirely.
+ return null;
+ }
+
+ while ($iterator->valid()) {
+ $event = $iterator->getEventObject();
+
+ // Recurrence-exceptions are handled separately, so just ignore them here
+ if (\in_array($event, $recurrenceExceptions, true)) {
+ $iterator->next();
+ continue;
+ }
+
+ if ($this->getEffectiveRecurrenceIdOfVEvent($event) === $recurrenceId) {
+ return $event;
+ }
+
+ $iterator->next();
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * @param VEvent $vevent
+ * @return string
+ */
+ private function getStatusOfEvent(VEvent $vevent):string {
+ if ($vevent->STATUS) {
+ return (string) $vevent->STATUS;
+ }
+
+ // Doesn't say so in the standard,
+ // but we consider events without a status
+ // to be confirmed
+ return 'CONFIRMED';
+ }
+
+ /**
+ * @param VObject\Component\VEvent $vevent
+ * @return bool
+ */
+ private function wasEventCancelled(VObject\Component\VEvent $vevent):bool {
+ return $this->getStatusOfEvent($vevent) === 'CANCELLED';
+ }
+
+ /**
+ * @param string $calendarData
+ * @return VObject\Component\VCalendar|null
+ */
+ private function parseCalendarData(string $calendarData):?VObject\Component\VCalendar {
+ try {
+ return VObject\Reader::read($calendarData,
+ VObject\Reader::OPTION_FORGIVING);
+ } catch(ParseException $ex) {
+ return null;
+ }
+ }
+
+ /**
+ * @param string $principalUri
+ * @return IUser|null
+ */
+ private function getUserFromPrincipalURI(string $principalUri):?IUser {
+ if (!$principalUri) {
+ return null;
+ }
+
+ if (strcasecmp($principalUri, 'principals/users/') !== 0) {
+ return null;
+ }
+
+ $userId = substr($principalUri, 17);
+ return $this->userManager->get($userId);
+ }
+
+ /**
+ * @param VObject\Component\VCalendar $vcalendar
+ * @return VObject\Component\VEvent[]
+ */
+ private function getAllVEventsFromVCalendar(VObject\Component\VCalendar $vcalendar):array {
+ $vevents = [];
+
+ foreach($vcalendar->children() as $child) {
+ if (!($child instanceof VObject\Component)) {
+ continue;
+ }
+
+ if ($child->name !== 'VEVENT') {
+ continue;
+ }
+
+ $vevents[] = $child;
+ }
+
+ return $vevents;
+ }
+
+ /**
+ * @param array $vevents
+ * @return VObject\Component\VEvent[]
+ */
+ private function getRecurrenceExceptionFromListOfVEvents(array $vevents):array {
+ return array_values(array_filter($vevents, function(VEvent $vevent) {
+ return $vevent->{'RECURRENCE-ID'} !== null;
+ }));
+ }
+
+ /**
+ * @param array $vevents
+ * @return VEvent|null
+ */
+ private function getMasterItemFromListOfVEvents(array $vevents):?VEvent {
+ $elements = array_values(array_filter($vevents, function(VEvent $vevent) {
+ return $vevent->{'RECURRENCE-ID'} === null;
+ }));
+
+ if (count($elements) === 0) {
+ return null;
+ }
+ if (count($elements) > 1) {
+ throw new \TypeError('Multiple master objects');
+ }
+
+ return $elements[0];
+ }
+
+ /**
+ * @param VAlarm $valarm
+ * @return bool
+ */
+ private function isAlarmRelative(VAlarm $valarm):bool {
+ $trigger = $valarm->TRIGGER;
+ return $trigger instanceof VObject\Property\ICalendar\Duration;
+ }
+
+ /**
+ * @param VEvent $vevent
+ * @return int
+ */
+ private function getEffectiveRecurrenceIdOfVEvent(VEvent $vevent):int {
+ if (isset($vevent->{'RECURRENCE-ID'})) {
+ return $vevent->{'RECURRENCE-ID'}->getDateTime()->getTimestamp();
+ }
+
+ return $vevent->DTSTART->getDateTime()->getTimestamp();
+ }
+
+ /**
+ * @param VEvent $vevent
+ * @return bool
+ */
+ private function isRecurring(VEvent $vevent):bool {
+ return isset($vevent->RRULE) || isset($vevent->RDATE);
}
}
diff --git a/apps/dav/lib/Command/SendEventReminders.php b/apps/dav/lib/Command/SendEventReminders.php
index 02d8a28726..93477cb0f7 100644
--- a/apps/dav/lib/Command/SendEventReminders.php
+++ b/apps/dav/lib/Command/SendEventReminders.php
@@ -27,6 +27,11 @@ use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
+/**
+ * Class SendEventReminders
+ *
+ * @package OCA\DAV\Command
+ */
class SendEventReminders extends Command {
/** @var ReminderService */
@@ -35,10 +40,13 @@ class SendEventReminders extends Command {
/** @var IConfig */
protected $config;
- public function __construct(string $name = null,
- ReminderService $reminderService,
+ /**
+ * @param ReminderService $reminderService
+ * @param IConfig $config
+ */
+ public function __construct(ReminderService $reminderService,
IConfig $config) {
- parent::__construct($name);
+ parent::__construct();
$this->reminderService = $reminderService;
$this->config = $config;
}
diff --git a/apps/dav/lib/Migration/Version1004Date20170825134824.php b/apps/dav/lib/Migration/Version1004Date20170825134824.php
index 26855c2e23..f3165a0fe3 100644
--- a/apps/dav/lib/Migration/Version1004Date20170825134824.php
+++ b/apps/dav/lib/Migration/Version1004Date20170825134824.php
@@ -324,8 +324,7 @@ class Version1004Date20170825134824 extends SimpleMigrationStep {
'length' => 1,
]);
$table->addColumn('stripattachments', 'smallint', [
- 'notnull' => false,
- 'length' => 1,
+
]);
$table->addColumn('lastmodified', 'integer', [
'notnull' => false,
diff --git a/apps/dav/lib/Migration/Version1007Date20181005133326.php b/apps/dav/lib/Migration/Version1007Date20181005133326.php
deleted file mode 100644
index 1e4cce950a..0000000000
--- a/apps/dav/lib/Migration/Version1007Date20181005133326.php
+++ /dev/null
@@ -1,82 +0,0 @@
-hasTable('calendar_reminders')) {
- $table = $schema->createTable('calendar_reminders');
-
- $table->addColumn('id', Type::BIGINT, [
- 'autoincrement' => true,
- 'notnull' => true,
- 'length' => 11,
- 'unsigned' => true,
- ]);
- $table->addColumn('uid', Type::STRING, [
- 'notnull' => true,
- 'length' => 255,
- ]);
- $table->addColumn('calendarid', Type::BIGINT, [
- 'notnull' => false,
- 'length' => 11,
- ]);
- $table->addColumn('objecturi', Type::STRING, [
- 'notnull' => true,
- 'length' => 255,
- ]);
- $table->addColumn('type', Type::STRING, [
- 'notnull' => true,
- 'length' => 255,
- ]);
- $table->addColumn('notificationdate', Type::DATETIME, [
- 'notnull' => false,
- ]);
- $table->addColumn('eventstartdate', Type::DATETIME, [
- 'notnull' => false,
- ]);
-
- $table->setPrimaryKey(['id']);
- $table->addIndex(['calendarid'], 'calendar_reminder_calendars');
-
- return $schema;
- }
- }
-
- /**
- * @param IOutput $output
- * @param Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper`
- * @param array $options
- */
- public function postSchemaChange(IOutput $output, Closure $schemaClosure, array $options) {
- }
-}
diff --git a/apps/dav/lib/Migration/Version1012Date20190808122342.php b/apps/dav/lib/Migration/Version1012Date20190808122342.php
new file mode 100644
index 0000000000..4aa768e705
--- /dev/null
+++ b/apps/dav/lib/Migration/Version1012Date20190808122342.php
@@ -0,0 +1,116 @@
+
+ *
+ * @author Georg Ehrke
+ *
+ * @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\DAV\Migration;
+
+use Doctrine\DBAL\Types\Type;
+use OCP\DB\ISchemaWrapper;
+use OCP\Migration\SimpleMigrationStep;
+use OCP\Migration\IOutput;
+
+/**
+ * Auto-generated migration step: Please modify to your needs!
+ */
+class Version1012Date20190808122342 extends SimpleMigrationStep {
+
+ /**
+ * @param IOutput $output
+ * @param \Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper`
+ * @param array $options
+ * @return null|ISchemaWrapper
+ * @since 13.0.0
+ */
+ public function changeSchema(IOutput $output,
+ \Closure $schemaClosure,
+ array $options):?ISchemaWrapper {
+ /** @var ISchemaWrapper $schema */
+ $schema = $schemaClosure();
+
+ if (!$schema->hasTable('calendar_reminders')) {
+ $table = $schema->createTable('calendar_reminders');
+
+ $table->addColumn('id', Type::BIGINT, [
+ 'autoincrement' => true,
+ 'notnull' => true,
+ 'length' => 11,
+ 'unsigned' => true,
+ ]);
+ $table->addColumn('calendar_id', Type::BIGINT, [
+ 'notnull' => true,
+ 'length' => 11,
+ ]);
+ $table->addColumn('object_id', Type::BIGINT, [
+ 'notnull' => true,
+ 'length' => 11,
+ ]);
+ $table->addColumn('is_recurring', Type::SMALLINT, [
+ 'notnull' => true,
+ 'length' => 1,
+ ]);
+ $table->addColumn('uid', Type::STRING, [
+ 'notnull' => true,
+ 'length' => 255,
+ ]);
+ $table->addColumn('recurrence_id', Type::BIGINT, [
+ 'notnull' => false,
+ 'length' => 11,
+ 'unsigned' => true,
+ ]);
+ $table->addColumn('is_recurrence_exception', Type::SMALLINT, [
+ 'notnull' => true,
+ 'length' => 1,
+ ]);
+ $table->addColumn('event_hash', Type::STRING, [
+ 'notnull' => true,
+ 'length' => 255,
+ ]);
+ $table->addColumn('alarm_hash', Type::STRING, [
+ 'notnull' => true,
+ 'length' => 255,
+ ]);
+ $table->addColumn('type', Type::STRING, [
+ 'notnull' => true,
+ 'length' => 255,
+ ]);
+ $table->addColumn('is_relative', Type::SMALLINT, [
+ 'notnull' => true,
+ 'length' => 1,
+ ]);
+ $table->addColumn('notification_date', Type::BIGINT, [
+ 'notnull' => true,
+ 'length' => 11,
+ 'unsigned' => true,
+ ]);
+ $table->addColumn('is_repeat_based', Type::SMALLINT, [
+ 'notnull' => true,
+ 'length' => 1,
+ ]);
+
+ $table->setPrimaryKey(['id']);
+ $table->addIndex(['object_id'], 'calendar_reminder_objid');
+ $table->addIndex(['uid', 'recurrence_id'], 'calendar_reminder_uidrec');
+
+ return $schema;
+ }
+ }
+}
From 2d6473e79b7d9e2e7435020084061d79d707067c Mon Sep 17 00:00:00 2001
From: Georg Ehrke
Date: Sun, 11 Aug 2019 17:54:14 +0200
Subject: [PATCH 09/10] Add repair step to register reminder index for existing
events
Signed-off-by: Georg Ehrke
---
apps/dav/appinfo/info.xml | 1 +
.../composer/composer/autoload_classmap.php | 2 +
.../dav/composer/composer/autoload_static.php | 2 +
.../BuildReminderIndexBackgroundJob.php | 134 ++++++++++++++++++
.../NotificationProvider/EmailProvider.php | 9 +-
...egisterBuildReminderIndexBackgroundJob.php | 96 +++++++++++++
6 files changed, 240 insertions(+), 4 deletions(-)
create mode 100644 apps/dav/lib/BackgroundJob/BuildReminderIndexBackgroundJob.php
create mode 100644 apps/dav/lib/Migration/RegisterBuildReminderIndexBackgroundJob.php
diff --git a/apps/dav/appinfo/info.xml b/apps/dav/appinfo/info.xml
index 81b93de055..6b593b97b7 100644
--- a/apps/dav/appinfo/info.xml
+++ b/apps/dav/appinfo/info.xml
@@ -33,6 +33,7 @@
OCA\DAV\Migration\CalDAVRemoveEmptyValueOCA\DAV\Migration\BuildCalendarSearchIndexOCA\DAV\Migration\RefreshWebcalJobRegistrar
+ OCA\DAV\Migration\RegisterBuildReminderIndexBackgroundJobOCA\DAV\Migration\RemoveOrphanEventsAndContactsOCA\DAV\Migration\RemoveClassifiedEventActivity
diff --git a/apps/dav/composer/composer/autoload_classmap.php b/apps/dav/composer/composer/autoload_classmap.php
index a1c2d671b8..1084430719 100644
--- a/apps/dav/composer/composer/autoload_classmap.php
+++ b/apps/dav/composer/composer/autoload_classmap.php
@@ -11,6 +11,7 @@ return array(
'OCA\\DAV\\Avatars\\AvatarHome' => $baseDir . '/../lib/Avatars/AvatarHome.php',
'OCA\\DAV\\Avatars\\AvatarNode' => $baseDir . '/../lib/Avatars/AvatarNode.php',
'OCA\\DAV\\Avatars\\RootCollection' => $baseDir . '/../lib/Avatars/RootCollection.php',
+ 'OCA\\DAV\\BackgroundJob\\BuildReminderIndexBackgroundJob' => $baseDir . '/../lib/BackgroundJob/BuildReminderIndexBackgroundJob.php',
'OCA\\DAV\\BackgroundJob\\CleanupDirectLinksJob' => $baseDir . '/../lib/BackgroundJob/CleanupDirectLinksJob.php',
'OCA\\DAV\\BackgroundJob\\CleanupInvitationTokenJob' => $baseDir . '/../lib/BackgroundJob/CleanupInvitationTokenJob.php',
'OCA\\DAV\\BackgroundJob\\EventReminderJob' => $baseDir . '/../lib/BackgroundJob/EventReminderJob.php',
@@ -179,6 +180,7 @@ return array(
'OCA\\DAV\\Migration\\FixBirthdayCalendarComponent' => $baseDir . '/../lib/Migration/FixBirthdayCalendarComponent.php',
'OCA\\DAV\\Migration\\RefreshWebcalJobRegistrar' => $baseDir . '/../lib/Migration/RefreshWebcalJobRegistrar.php',
'OCA\\DAV\\Migration\\RegenerateBirthdayCalendars' => $baseDir . '/../lib/Migration/RegenerateBirthdayCalendars.php',
+ 'OCA\\DAV\\Migration\\RegisterBuildReminderIndexBackgroundJob' => $baseDir . '/../lib/Migration/RegisterBuildReminderIndexBackgroundJob.php',
'OCA\\DAV\\Migration\\RemoveClassifiedEventActivity' => $baseDir . '/../lib/Migration/RemoveClassifiedEventActivity.php',
'OCA\\DAV\\Migration\\RemoveOrphanEventsAndContacts' => $baseDir . '/../lib/Migration/RemoveOrphanEventsAndContacts.php',
'OCA\\DAV\\Migration\\Version1004Date20170825134824' => $baseDir . '/../lib/Migration/Version1004Date20170825134824.php',
diff --git a/apps/dav/composer/composer/autoload_static.php b/apps/dav/composer/composer/autoload_static.php
index 39488419f8..b3f5688166 100644
--- a/apps/dav/composer/composer/autoload_static.php
+++ b/apps/dav/composer/composer/autoload_static.php
@@ -26,6 +26,7 @@ class ComposerStaticInitDAV
'OCA\\DAV\\Avatars\\AvatarHome' => __DIR__ . '/..' . '/../lib/Avatars/AvatarHome.php',
'OCA\\DAV\\Avatars\\AvatarNode' => __DIR__ . '/..' . '/../lib/Avatars/AvatarNode.php',
'OCA\\DAV\\Avatars\\RootCollection' => __DIR__ . '/..' . '/../lib/Avatars/RootCollection.php',
+ 'OCA\\DAV\\BackgroundJob\\BuildReminderIndexBackgroundJob' => __DIR__ . '/..' . '/../lib/BackgroundJob/BuildReminderIndexBackgroundJob.php',
'OCA\\DAV\\BackgroundJob\\CleanupDirectLinksJob' => __DIR__ . '/..' . '/../lib/BackgroundJob/CleanupDirectLinksJob.php',
'OCA\\DAV\\BackgroundJob\\CleanupInvitationTokenJob' => __DIR__ . '/..' . '/../lib/BackgroundJob/CleanupInvitationTokenJob.php',
'OCA\\DAV\\BackgroundJob\\EventReminderJob' => __DIR__ . '/..' . '/../lib/BackgroundJob/EventReminderJob.php',
@@ -194,6 +195,7 @@ class ComposerStaticInitDAV
'OCA\\DAV\\Migration\\FixBirthdayCalendarComponent' => __DIR__ . '/..' . '/../lib/Migration/FixBirthdayCalendarComponent.php',
'OCA\\DAV\\Migration\\RefreshWebcalJobRegistrar' => __DIR__ . '/..' . '/../lib/Migration/RefreshWebcalJobRegistrar.php',
'OCA\\DAV\\Migration\\RegenerateBirthdayCalendars' => __DIR__ . '/..' . '/../lib/Migration/RegenerateBirthdayCalendars.php',
+ 'OCA\\DAV\\Migration\\RegisterBuildReminderIndexBackgroundJob' => __DIR__ . '/..' . '/../lib/Migration/RegisterBuildReminderIndexBackgroundJob.php',
'OCA\\DAV\\Migration\\RemoveClassifiedEventActivity' => __DIR__ . '/..' . '/../lib/Migration/RemoveClassifiedEventActivity.php',
'OCA\\DAV\\Migration\\RemoveOrphanEventsAndContacts' => __DIR__ . '/..' . '/../lib/Migration/RemoveOrphanEventsAndContacts.php',
'OCA\\DAV\\Migration\\Version1004Date20170825134824' => __DIR__ . '/..' . '/../lib/Migration/Version1004Date20170825134824.php',
diff --git a/apps/dav/lib/BackgroundJob/BuildReminderIndexBackgroundJob.php b/apps/dav/lib/BackgroundJob/BuildReminderIndexBackgroundJob.php
new file mode 100644
index 0000000000..cf55f42440
--- /dev/null
+++ b/apps/dav/lib/BackgroundJob/BuildReminderIndexBackgroundJob.php
@@ -0,0 +1,134 @@
+
+ *
+ * @author Georg Ehrke
+ *
+ * @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\DAV\BackgroundJob;
+
+use OC\BackgroundJob\QueuedJob;
+use OCA\DAV\CalDAV\Reminder\ReminderService;
+use OCP\AppFramework\Utility\ITimeFactory;
+use OCP\BackgroundJob\IJobList;
+use OCP\IDBConnection;
+use OCP\ILogger;
+
+/**
+ * Class BuildReminderIndexBackgroundJob
+ *
+ * @package OCA\DAV\BackgroundJob
+ */
+class BuildReminderIndexBackgroundJob extends QueuedJob {
+
+ /** @var IDBConnection */
+ private $db;
+
+ /** @var ReminderService */
+ private $reminderService;
+
+ /** @var ILogger */
+ private $logger;
+
+ /** @var IJobList */
+ private $jobList;
+
+ /** @var ITimeFactory */
+ private $timeFactory;
+
+ /**
+ * BuildReminderIndexBackgroundJob constructor.
+ *
+ * @param IDBConnection $db
+ * @param ReminderService $reminderService
+ * @param ILogger $logger
+ * @param IJobList $jobList
+ * @param ITimeFactory $timeFactory
+ */
+ public function __construct(IDBConnection $db,
+ ReminderService $reminderService,
+ ILogger $logger,
+ IJobList $jobList,
+ ITimeFactory $timeFactory) {
+ $this->db = $db;
+ $this->reminderService = $reminderService;
+ $this->logger = $logger;
+ $this->jobList = $jobList;
+ $this->timeFactory = $timeFactory;
+ }
+
+ /**
+ * @param $arguments
+ */
+ public function run($arguments) {
+ $offset = (int) $arguments['offset'];
+ $stopAt = (int) $arguments['stopAt'];
+
+ $this->logger->info('Building calendar reminder index (' . $offset .'/' . $stopAt . ')');
+
+ $offset = $this->buildIndex($offset, $stopAt);
+
+ if ($offset >= $stopAt) {
+ $this->logger->info('Building calendar reminder index done');
+ } else {
+ $this->jobList->add(self::class, [
+ 'offset' => $offset,
+ 'stopAt' => $stopAt
+ ]);
+ $this->logger->info('Scheduled a new BuildReminderIndexBackgroundJob with offset ' . $offset);
+ }
+ }
+
+ /**
+ * @param int $offset
+ * @param int $stopAt
+ * @return int
+ */
+ private function buildIndex(int $offset, int $stopAt):int {
+ $startTime = $this->timeFactory->getTime();
+
+ $query = $this->db->getQueryBuilder();
+ $query->select('*')
+ ->from('calendarobjects')
+ ->where($query->expr()->lte('id', $query->createNamedParameter($stopAt)))
+ ->andWhere($query->expr()->gt('id', $query->createNamedParameter($offset)))
+ ->orderBy('id', 'ASC');
+
+ $stmt = $query->execute();
+ while($row = $stmt->fetch(\PDO::FETCH_ASSOC)) {
+ $offset = $row['id'];
+ if (is_resource($row['calendardata'])) {
+ $row['calendardata'] = stream_get_contents($row['calendardata']);
+ }
+ $row['component'] = $row['componenttype'];
+
+ try {
+ $this->reminderService->onTouchCalendarObject('\OCA\DAV\CalDAV\CalDavBackend::createCalendarObject', $row);
+ } catch(\Exception $ex) {
+ $this->logger->logException($ex);
+ }
+
+ if (($this->timeFactory->getTime() - $startTime) > 15) {
+ return $offset;
+ }
+ }
+
+ return $stopAt;
+ }
+}
\ No newline at end of file
diff --git a/apps/dav/lib/CalDAV/Reminder/NotificationProvider/EmailProvider.php b/apps/dav/lib/CalDAV/Reminder/NotificationProvider/EmailProvider.php
index 2a7eb2a403..cf79fad7ab 100644
--- a/apps/dav/lib/CalDAV/Reminder/NotificationProvider/EmailProvider.php
+++ b/apps/dav/lib/CalDAV/Reminder/NotificationProvider/EmailProvider.php
@@ -108,7 +108,8 @@ class EmailProvider extends AbstractProvider {
if ($organizer) {
$message->setReplyTo($organizer);
}
- $message->setBcc($emailAddresses);
+ $message->setTo([])
+ ->setBcc($emailAddresses);
$template = $this->mailer->createEMailTemplate('dav.calendarReminder');
$template->addHeader();
@@ -185,7 +186,7 @@ class EmailProvider extends AbstractProvider {
return null;
}
- $organizer = $vevent->ORGANZIER;
+ $organizer = $vevent->ORGANIZER;
if (strcasecmp($organizer->getValue(), 'mailto:') !== 0) {
return null;
}
@@ -309,7 +310,7 @@ class EmailProvider extends AbstractProvider {
}
}
- if (isset($vevent->ORGANIZER)) {
+ if (isset($vevent->ORGANIZER) && $this->hasAttendeeMailURI($vevent->ORGANIZER)) {
$emailAddresses[$this->getEMailAddressOfAttendee($vevent->ORGANIZER)] = [];
}
@@ -349,7 +350,7 @@ class EmailProvider extends AbstractProvider {
* @return bool
*/
private function hasAttendeeMailURI(VObject\Property $attendee):bool {
- return strcasecmp($attendee->getValue(), 'mailto:') === 0;
+ return stripos($attendee->getValue(), 'mailto:') === 0;
}
/**
diff --git a/apps/dav/lib/Migration/RegisterBuildReminderIndexBackgroundJob.php b/apps/dav/lib/Migration/RegisterBuildReminderIndexBackgroundJob.php
new file mode 100644
index 0000000000..c7a1cf63ab
--- /dev/null
+++ b/apps/dav/lib/Migration/RegisterBuildReminderIndexBackgroundJob.php
@@ -0,0 +1,96 @@
+
+ *
+ * @author Georg Ehrke
+ *
+ * @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\DAV\Migration;
+
+use OCA\DAV\BackgroundJob\BuildReminderIndexBackgroundJob;
+use OCP\BackgroundJob\IJobList;
+use OCP\IConfig;
+use OCP\IDBConnection;
+use OCP\Migration\IOutput;
+use OCP\Migration\IRepairStep;
+
+/**
+ * Class RegisterBuildReminderIndexBackgroundJob
+ *
+ * @package OCA\DAV\Migration
+ */
+class RegisterBuildReminderIndexBackgroundJob implements IRepairStep {
+
+ /** @var IDBConnection */
+ private $db;
+
+ /** @var IJobList */
+ private $jobList;
+
+ /** @var IConfig */
+ private $config;
+
+ /** @var string */
+ private const CONFIG_KEY = 'buildCalendarReminderIndex';
+
+ /**
+ * @param IDBConnection $db
+ * @param IJobList $jobList
+ * @param IConfig $config
+ */
+ public function __construct(IDBConnection $db,
+ IJobList $jobList,
+ IConfig $config) {
+ $this->db = $db;
+ $this->jobList = $jobList;
+ $this->config = $config;
+ }
+
+ /**
+ * @return string
+ */
+ public function getName() {
+ return 'Registering building of calendar reminder index as background job';
+ }
+
+ /**
+ * @param IOutput $output
+ */
+ public function run(IOutput $output) {
+ // only run once
+ if ($this->config->getAppValue('dav', self::CONFIG_KEY) === 'yes') {
+ $output->info('Repair step already executed');
+ return;
+ }
+
+ $query = $this->db->getQueryBuilder();
+ $query->select($query->createFunction('MAX(' . $query->getColumnName('id') . ')'))
+ ->from('calendarobjects');
+ $maxId = (int)$query->execute()->fetchColumn();
+
+ $output->info('Add background job');
+ $this->jobList->add(BuildReminderIndexBackgroundJob::class, [
+ 'offset' => 0,
+ 'stopAt' => $maxId
+ ]);
+
+ // if all were done, no need to redo the repair during next upgrade
+ $this->config->setAppValue('dav', self::CONFIG_KEY, 'yes');
+ }
+}
\ No newline at end of file
From 4d28a4544e1f85046e139146a1be9933e9ff6ae3 Mon Sep 17 00:00:00 2001
From: Georg Ehrke
Date: Mon, 12 Aug 2019 13:20:03 +0200
Subject: [PATCH 10/10] Final fixes
Signed-off-by: Georg Ehrke
---
3rdparty | 2 +-
.../BuildReminderIndexBackgroundJob.php | 2 +-
apps/dav/lib/CalDAV/Reminder/Backend.php | 2 +-
.../NotificationProvider/AbstractProvider.php | 39 +-
.../NotificationProvider/EmailProvider.php | 77 +--
.../NotificationProvider/PushProvider.php | 14 +-
.../Reminder/NotificationProviderManager.php | 36 +-
apps/dav/lib/CalDAV/Reminder/Notifier.php | 12 +-
.../lib/CalDAV/Reminder/ReminderService.php | 38 +-
.../Version1004Date20170825134824.php | 3 +-
.../BackgroundJob/EventReminderJobTest.php | 35 +-
.../unit/CalDAV/Reminder/BackendTest.php | 590 +++++++++-------
.../AbstractNotificationProviderTest.php | 50 +-
.../AudioProviderTest.php | 33 +
.../EmailProviderTest.php | 606 +++++++++++++----
.../NotificationProvider/PushProviderTest.php | 161 +++--
.../NotificationProviderManagerTest.php | 56 +-
.../unit/CalDAV/Reminder/NotifierTest.php | 64 +-
.../CalDAV/Reminder/ReminderServiceTest.php | 639 +++++++++++++-----
19 files changed, 1625 insertions(+), 834 deletions(-)
rename apps/dav/tests/unit/CalDAV/Reminder/{ => NotificationProvider}/AbstractNotificationProviderTest.php (54%)
create mode 100644 apps/dav/tests/unit/CalDAV/Reminder/NotificationProvider/AudioProviderTest.php
diff --git a/3rdparty b/3rdparty
index ef289bc27e..49ccfbb286 160000
--- a/3rdparty
+++ b/3rdparty
@@ -1 +1 @@
-Subproject commit ef289bc27eae0cdfc3f74f419ace8dda8dd84ef0
+Subproject commit 49ccfbb28661b9ef7743c1725cd2571259215929
diff --git a/apps/dav/lib/BackgroundJob/BuildReminderIndexBackgroundJob.php b/apps/dav/lib/BackgroundJob/BuildReminderIndexBackgroundJob.php
index cf55f42440..15c52cd852 100644
--- a/apps/dav/lib/BackgroundJob/BuildReminderIndexBackgroundJob.php
+++ b/apps/dav/lib/BackgroundJob/BuildReminderIndexBackgroundJob.php
@@ -23,7 +23,7 @@ declare(strict_types=1);
*/
namespace OCA\DAV\BackgroundJob;
-use OC\BackgroundJob\QueuedJob;
+use OCP\BackgroundJob\QueuedJob;
use OCA\DAV\CalDAV\Reminder\ReminderService;
use OCP\AppFramework\Utility\ITimeFactory;
use OCP\BackgroundJob\IJobList;
diff --git a/apps/dav/lib/CalDAV/Reminder/Backend.php b/apps/dav/lib/CalDAV/Reminder/Backend.php
index be65c35da0..b3cc013fb3 100644
--- a/apps/dav/lib/CalDAV/Reminder/Backend.php
+++ b/apps/dav/lib/CalDAV/Reminder/Backend.php
@@ -63,7 +63,7 @@ class Backend {
$query = $this->db->getQueryBuilder();
$query->select(['cr.*', 'co.calendardata', 'c.displayname', 'c.principaluri'])
->from('calendar_reminders', 'cr')
-// ->where($query->expr()->lte('cr.notification_date', $query->createNamedParameter($this->timeFactory->getTime())))
+ ->where($query->expr()->lte('cr.notification_date', $query->createNamedParameter($this->timeFactory->getTime())))
->leftJoin('cr', 'calendarobjects', 'co', $query->expr()->eq('cr.object_id', 'co.id'))
->leftJoin('cr', 'calendars', 'c', $query->expr()->eq('cr.calendar_id', 'c.id'));
$stmt = $query->execute();
diff --git a/apps/dav/lib/CalDAV/Reminder/NotificationProvider/AbstractProvider.php b/apps/dav/lib/CalDAV/Reminder/NotificationProvider/AbstractProvider.php
index 6b2364c802..6e3a8eadde 100644
--- a/apps/dav/lib/CalDAV/Reminder/NotificationProvider/AbstractProvider.php
+++ b/apps/dav/lib/CalDAV/Reminder/NotificationProvider/AbstractProvider.php
@@ -25,8 +25,6 @@ declare(strict_types=1);
*/
namespace OCA\DAV\CalDAV\Reminder\NotificationProvider;
-use \DateTime;
-use \DateTimeImmutable;
use OCA\DAV\CalDAV\Reminder\INotificationProvider;
use OCP\IConfig;
use OCP\IL10N;
@@ -36,7 +34,6 @@ use OCP\L10N\IFactory as L10NFactory;
use OCP\IUser;
use Sabre\VObject\Component\VEvent;
use Sabre\VObject\DateTimeParser;
-use Sabre\VObject\Parameter;
use Sabre\VObject\Property;
/**
@@ -49,22 +46,22 @@ abstract class AbstractProvider implements INotificationProvider {
/** @var string */
public const NOTIFICATION_TYPE = '';
- /** @var ILogger */
- protected $logger;
+ /** @var ILogger */
+ protected $logger;
- /** @var L10NFactory */
- private $l10nFactory;
+ /** @var L10NFactory */
+ private $l10nFactory;
- /** @var IL10N[] */
+ /** @var IL10N[] */
private $l10ns;
/** @var string */
private $fallbackLanguage;
- /** @var IURLGenerator */
- protected $urlGenerator;
+ /** @var IURLGenerator */
+ protected $urlGenerator;
- /** @var IConfig */
+ /** @var IConfig */
protected $config;
/**
@@ -79,9 +76,9 @@ abstract class AbstractProvider implements INotificationProvider {
IConfig $config) {
$this->logger = $logger;
$this->l10nFactory = $l10nFactory;
- $this->urlGenerator = $urlGenerator;
+ $this->urlGenerator = $urlGenerator;
$this->config = $config;
- }
+ }
/**
* Send notification
@@ -91,22 +88,22 @@ abstract class AbstractProvider implements INotificationProvider {
* @param IUser[] $users
* @return void
*/
- abstract public function send(VEvent $vevent,
+ abstract public function send(VEvent $vevent,
string $calendarDisplayName,
array $users=[]): void;
/**
* @return string
*/
- protected function getFallbackLanguage():string {
- if ($this->fallbackLanguage) {
- return $this->fallbackLanguage;
+ protected function getFallbackLanguage():string {
+ if ($this->fallbackLanguage) {
+ return $this->fallbackLanguage;
}
- $fallbackLanguage = $this->l10nFactory->findLanguage();
- $this->fallbackLanguage = $fallbackLanguage;
+ $fallbackLanguage = $this->l10nFactory->findLanguage();
+ $this->fallbackLanguage = $fallbackLanguage;
- return $fallbackLanguage;
+ return $fallbackLanguage;
}
/**
@@ -114,7 +111,7 @@ abstract class AbstractProvider implements INotificationProvider {
* @return bool
*/
protected function hasL10NForLang(string $lang):bool {
- return $this->l10nFactory->languageExists('dav', $lang);
+ return $this->l10nFactory->languageExists('dav', $lang);
}
/**
diff --git a/apps/dav/lib/CalDAV/Reminder/NotificationProvider/EmailProvider.php b/apps/dav/lib/CalDAV/Reminder/NotificationProvider/EmailProvider.php
index cf79fad7ab..f5932a87b3 100644
--- a/apps/dav/lib/CalDAV/Reminder/NotificationProvider/EmailProvider.php
+++ b/apps/dav/lib/CalDAV/Reminder/NotificationProvider/EmailProvider.php
@@ -25,8 +25,7 @@ declare(strict_types=1);
*/
namespace OCA\DAV\CalDAV\Reminder\NotificationProvider;
-use DateTime;
-use DateTimeImmutable;
+use \DateTime;
use OCP\IConfig;
use OCP\IL10N;
use OCP\ILogger;
@@ -97,36 +96,35 @@ class EmailProvider extends AbstractProvider {
$organizer = $this->getOrganizerEMailAndNameFromEvent($vevent);
foreach($sortedByLanguage as $lang => $emailAddresses) {
- if ($this->hasL10NForLang($lang)) {
+ if (!$this->hasL10NForLang($lang)) {
$lang = $fallbackLanguage;
}
$l10n = $this->getL10NForLang($lang);
$fromEMail = \OCP\Util::getDefaultEmailAddress('reminders-noreply');
- $message = $this->mailer->createMessage();
- $message->setFrom([$fromEMail]);
- if ($organizer) {
- $message->setReplyTo($organizer);
- }
- $message->setTo([])
- ->setBcc($emailAddresses);
-
$template = $this->mailer->createEMailTemplate('dav.calendarReminder');
$template->addHeader();
-
$this->addSubjectAndHeading($template, $l10n, $vevent);
$this->addBulletList($template, $l10n, $calendarDisplayName, $vevent);
-
$template->addFooter();
- $message->useTemplate($template);
- try {
- $failed = $this->mailer->send($message);
- if ($failed) {
- $this->logger->error('Unable to deliver message to {failed}', ['app' => 'dav', 'failed' => implode(', ', $failed)]);
+ foreach ($emailAddresses as $emailAddress) {
+ $message = $this->mailer->createMessage();
+ $message->setFrom([$fromEMail]);
+ if ($organizer) {
+ $message->setReplyTo($organizer);
+ }
+ $message->setTo([$emailAddress]);
+ $message->useTemplate($template);
+
+ try {
+ $failed = $this->mailer->send($message);
+ if ($failed) {
+ $this->logger->error('Unable to deliver message to {failed}', ['app' => 'dav', 'failed' => implode(', ', $failed)]);
+ }
+ } catch (\Exception $ex) {
+ $this->logger->logException($ex, ['app' => 'dav']);
}
- } catch (\Exception $ex) {
- $this->logger->logException($ex, ['app' => 'dav']);
}
}
}
@@ -165,9 +163,9 @@ class EmailProvider extends AbstractProvider {
$template->addBodyListItem((string) $vevent->DESCRIPTION, $l10n->t('Description:'),
$this->getAbsoluteImagePath('actions/more.svg'));
}
- }
+ }
- /**
+ /**
* @param string $path
* @return string
*/
@@ -201,35 +199,6 @@ class EmailProvider extends AbstractProvider {
return [$organizerEMail];
}
- /**
- * @param array $sortedByLanguage
- * @param IUser[] $users
- * @param string $defaultLanguage
- */
- private function sortUsersByLanguage(array &$sortedByLanguage,
- array $users,
- string $defaultLanguage):void {
- /**
- * @var array $sortedByLanguage
- * [
- * 'de' => ['a@b.com', 'c@d.com'],
- * ...
- * ]
- */
- foreach($users as $user) {
- /** @var IUser $user */
- $emailAddress = $user->getEMailAddress();
- $lang = $this->config->getUserValue($user->getUID(),
- 'core', 'lang', $defaultLanguage);
-
- if (!isset($sortedByLanguage[$lang])) {
- $sortedByLanguage[$lang] = [];
- }
-
- $sortedByLanguage[$lang][] = $emailAddress;
- }
- }
-
/**
* @param array $emails
* @param string $defaultLanguage
@@ -386,7 +355,7 @@ class EmailProvider extends AbstractProvider {
}
}
- return array_unique($emailAddresses);
+ return $emailAddresses;
}
/**
@@ -415,7 +384,9 @@ class EmailProvider extends AbstractProvider {
$diff = $dtstartDt->diff($dtendDt);
+ /** @phan-suppress-next-line PhanUndeclaredClassMethod */
$dtstartDt = new \DateTime($dtstartDt->format(\DateTime::ATOM));
+ /** @phan-suppress-next-line PhanUndeclaredClassMethod */
$dtendDt = new \DateTime($dtendDt->format(\DateTime::ATOM));
if ($isAllDay) {
@@ -432,7 +403,9 @@ class EmailProvider extends AbstractProvider {
$startTimezone = $endTimezone = null;
if (!$vevent->DTSTART->isFloating()) {
+ /** @phan-suppress-next-line PhanUndeclaredClassMethod */
$startTimezone = $vevent->DTSTART->getDateTime()->getTimezone()->getName();
+ /** @phan-suppress-next-line PhanUndeclaredClassMethod */
$endTimezone = $this->getDTEndFromEvent($vevent)->getDateTime()->getTimezone()->getName();
}
diff --git a/apps/dav/lib/CalDAV/Reminder/NotificationProvider/PushProvider.php b/apps/dav/lib/CalDAV/Reminder/NotificationProvider/PushProvider.php
index 2e580fd78a..3872b67e59 100644
--- a/apps/dav/lib/CalDAV/Reminder/NotificationProvider/PushProvider.php
+++ b/apps/dav/lib/CalDAV/Reminder/NotificationProvider/PushProvider.php
@@ -47,7 +47,7 @@ class PushProvider extends AbstractProvider {
/** @var string */
public const NOTIFICATION_TYPE = 'DISPLAY';
- /** @var IManager */
+ /** @var IManager */
private $manager;
/** @var ITimeFactory */
@@ -70,7 +70,7 @@ class PushProvider extends AbstractProvider {
parent::__construct($logger, $l10nFactory, $urlGenerator, $config);
$this->manager = $manager;
$this->timeFactory = $timeFactory;
- }
+ }
/**
* Send push notification to all users.
@@ -80,13 +80,13 @@ class PushProvider extends AbstractProvider {
* @param IUser[] $users
* @throws \Exception
*/
- public function send(VEvent $vevent,
+ public function send(VEvent $vevent,
string $calendarDisplayName=null,
array $users=[]):void {
$eventDetails = $this->extractEventDetails($vevent);
$eventDetails['calendar_displayname'] = $calendarDisplayName;
- foreach($users as $user) {
+ foreach($users as $user) {
/** @var INotification $notification */
$notification = $this->manager->createNotification();
$notification->setApp(Application::APP_ID)
@@ -101,7 +101,7 @@ class PushProvider extends AbstractProvider {
$this->manager->notify($notification);
}
- }
+ }
/**
* @var VEvent $vevent
@@ -124,11 +124,15 @@ class PushProvider extends AbstractProvider {
? ((string) $vevent->LOCATION)
: null,
'all_day' => $start instanceof Property\ICalendar\Date,
+ /** @phan-suppress-next-line PhanUndeclaredClassMethod */
'start_atom' => $start->getDateTime()->format(\DateTime::ATOM),
'start_is_floating' => $start->isFloating(),
+ /** @phan-suppress-next-line PhanUndeclaredClassMethod */
'start_timezone' => $start->getDateTime()->getTimezone()->getName(),
+ /** @phan-suppress-next-line PhanUndeclaredClassMethod */
'end_atom' => $end->getDateTime()->format(\DateTime::ATOM),
'end_is_floating' => $end->isFloating(),
+ /** @phan-suppress-next-line PhanUndeclaredClassMethod */
'end_timezone' => $end->getDateTime()->getTimezone()->getName(),
];
}
diff --git a/apps/dav/lib/CalDAV/Reminder/NotificationProviderManager.php b/apps/dav/lib/CalDAV/Reminder/NotificationProviderManager.php
index 3d54970562..c9bcf2dd06 100644
--- a/apps/dav/lib/CalDAV/Reminder/NotificationProviderManager.php
+++ b/apps/dav/lib/CalDAV/Reminder/NotificationProviderManager.php
@@ -31,8 +31,8 @@ namespace OCA\DAV\CalDAV\Reminder;
*/
class NotificationProviderManager {
- /** @var INotificationProvider[] */
- private $providers = [];
+ /** @var INotificationProvider[] */
+ private $providers = [];
/**
* Checks whether a provider for a given ACTION exists
@@ -45,23 +45,23 @@ class NotificationProviderManager {
&& isset($this->providers[$type]));
}
- /**
+ /**
* Get provider for a given ACTION
*
- * @param string $type
- * @return INotificationProvider
- * @throws NotificationProvider\ProviderNotAvailableException
- * @throws NotificationTypeDoesNotExistException
- */
- public function getProvider(string $type):INotificationProvider {
- if (in_array($type, ReminderService::REMINDER_TYPES, true)) {
- if (isset($this->providers[$type])) {
- return $this->providers[$type];
- }
- throw new NotificationProvider\ProviderNotAvailableException($type);
- }
- throw new NotificationTypeDoesNotExistException($type);
- }
+ * @param string $type
+ * @return INotificationProvider
+ * @throws NotificationProvider\ProviderNotAvailableException
+ * @throws NotificationTypeDoesNotExistException
+ */
+ public function getProvider(string $type):INotificationProvider {
+ if (in_array($type, ReminderService::REMINDER_TYPES, true)) {
+ if (isset($this->providers[$type])) {
+ return $this->providers[$type];
+ }
+ throw new NotificationProvider\ProviderNotAvailableException($type);
+ }
+ throw new NotificationTypeDoesNotExistException($type);
+ }
/**
* Registers a new provider
@@ -69,7 +69,7 @@ class NotificationProviderManager {
* @param string $providerClassName
* @throws \OCP\AppFramework\QueryException
*/
- public function registerProvider(string $providerClassName):void {
+ public function registerProvider(string $providerClassName):void {
$provider = \OC::$server->query($providerClassName);
if (!$provider instanceof INotificationProvider) {
diff --git a/apps/dav/lib/CalDAV/Reminder/Notifier.php b/apps/dav/lib/CalDAV/Reminder/Notifier.php
index 4bad984178..2c5f05f62a 100644
--- a/apps/dav/lib/CalDAV/Reminder/Notifier.php
+++ b/apps/dav/lib/CalDAV/Reminder/Notifier.php
@@ -25,7 +25,7 @@ declare(strict_types=1);
namespace OCA\DAV\CalDAV\Reminder;
-use DateTime;
+use \DateTime;
use OCA\DAV\AppInfo\Application;
use OCP\AppFramework\Utility\ITimeFactory;
use OCP\IL10N;
@@ -85,10 +85,6 @@ class Notifier implements INotifier {
* @since 17.0.0
*/
public function getName():string {
- if ($this->l10n) {
- return $this->l10n->t('Calendar');
- }
-
return $this->l10nFactory->get('dav')->t('Calendar');
}
@@ -143,7 +139,7 @@ class Notifier implements INotifier {
private function prepareNotificationSubject(INotification $notification): void {
$parameters = $notification->getSubjectParameters();
- $startTime = \DateTime::createFromFormat(\DateTimeInterface::ATOM, $parameters['start_atom']);
+ $startTime = \DateTime::createFromFormat(\DateTime::ATOM, $parameters['start_atom']);
$now = $this->timeFactory->getDateTime();
$title = $this->getTitleFromParameters($parameters);
@@ -220,8 +216,8 @@ class Notifier implements INotifier {
* @throws \Exception
*/
private function generateDateString(array $parameters):string {
- $startDateTime = DateTime::createFromFormat(DATE_ATOM, $parameters['start_atom']);
- $endDateTime = DateTime::createFromFormat(DATE_ATOM, $parameters['end_atom']);
+ $startDateTime = DateTime::createFromFormat(\DateTime::ATOM, $parameters['start_atom']);
+ $endDateTime = DateTime::createFromFormat(\DateTime::ATOM, $parameters['end_atom']);
$isAllDay = $parameters['all_day'];
$diff = $startDateTime->diff($endDateTime);
diff --git a/apps/dav/lib/CalDAV/Reminder/ReminderService.php b/apps/dav/lib/CalDAV/Reminder/ReminderService.php
index ad428eef74..f36ddd157c 100644
--- a/apps/dav/lib/CalDAV/Reminder/ReminderService.php
+++ b/apps/dav/lib/CalDAV/Reminder/ReminderService.php
@@ -24,7 +24,7 @@ declare(strict_types=1);
*/
namespace OCA\DAV\CalDAV\Reminder;
-use DateTimeImmutable;
+use \DateTimeImmutable;
use OCA\DAV\CalDAV\CalDavBackend;
use OCP\AppFramework\Utility\ITimeFactory;
use OCP\IGroup;
@@ -40,11 +40,11 @@ use Sabre\VObject\Recur\NoInstancesException;
class ReminderService {
- /** @var Backend */
- private $backend;
+ /** @var Backend */
+ private $backend;
- /** @var NotificationProviderManager */
- private $notificationProviderManager;
+ /** @var NotificationProviderManager */
+ private $notificationProviderManager;
/** @var IUserManager */
private $userManager;
@@ -83,19 +83,19 @@ class ReminderService {
* @param CalDavBackend $caldavBackend
* @param ITimeFactory $timeFactory
*/
- public function __construct(Backend $backend,
- NotificationProviderManager $notificationProviderManager,
+ public function __construct(Backend $backend,
+ NotificationProviderManager $notificationProviderManager,
IUserManager $userManager,
IGroupManager $groupManager,
CalDavBackend $caldavBackend,
ITimeFactory $timeFactory) {
- $this->backend = $backend;
- $this->notificationProviderManager = $notificationProviderManager;
+ $this->backend = $backend;
+ $this->notificationProviderManager = $notificationProviderManager;
$this->userManager = $userManager;
$this->groupManager = $groupManager;
$this->caldavBackend = $caldavBackend;
$this->timeFactory = $timeFactory;
- }
+ }
/**
* Process reminders to activate
@@ -103,12 +103,12 @@ class ReminderService {
* @throws NotificationProvider\ProviderNotAvailableException
* @throws NotificationTypeDoesNotExistException
*/
- public function processReminders():void {
- $reminders = $this->backend->getRemindersToProcess();
+ public function processReminders():void {
+ $reminders = $this->backend->getRemindersToProcess();
- foreach($reminders as $reminder) {
- $vcalendar = $this->parseCalendarData($reminder['calendardata']);
- if (!$vcalendar) {
+ foreach($reminders as $reminder) {
+ $vcalendar = $this->parseCalendarData($reminder['calendardata']);
+ if (!$vcalendar) {
$this->backend->removeReminder($reminder['id']);
continue;
}
@@ -140,7 +140,7 @@ class ReminderService {
$this->deleteOrProcessNext($reminder, $vevent);
}
- }
+ }
/**
* @param string $action
@@ -175,7 +175,7 @@ class ReminderService {
/**
* @param array $objectData
*/
- private function onCalendarObjectCreate(array $objectData):void {
+ private function onCalendarObjectCreate(array $objectData):void {
/** @var VObject\Component\VCalendar $vcalendar */
$vcalendar = $this->parseCalendarData($objectData['calendardata']);
if (!$vcalendar) {
@@ -343,7 +343,7 @@ class ReminderService {
'is_repeat_based' => false,
];
- $repeat = $valarm->REPEAT ? (int) $valarm->REPEAT : 0;
+ $repeat = isset($valarm->REPEAT) ? (int) $valarm->REPEAT->getValue() : 0;
for($i = 0; $i < $repeat; $i++) {
if ($valarm->DURATION === null) {
continue;
@@ -671,7 +671,7 @@ class ReminderService {
return null;
}
- if (strcasecmp($principalUri, 'principals/users/') !== 0) {
+ if (stripos($principalUri, 'principals/users/') !== 0) {
return null;
}
diff --git a/apps/dav/lib/Migration/Version1004Date20170825134824.php b/apps/dav/lib/Migration/Version1004Date20170825134824.php
index f3165a0fe3..26855c2e23 100644
--- a/apps/dav/lib/Migration/Version1004Date20170825134824.php
+++ b/apps/dav/lib/Migration/Version1004Date20170825134824.php
@@ -324,7 +324,8 @@ class Version1004Date20170825134824 extends SimpleMigrationStep {
'length' => 1,
]);
$table->addColumn('stripattachments', 'smallint', [
-
+ 'notnull' => false,
+ 'length' => 1,
]);
$table->addColumn('lastmodified', 'integer', [
'notnull' => false,
diff --git a/apps/dav/tests/unit/BackgroundJob/EventReminderJobTest.php b/apps/dav/tests/unit/BackgroundJob/EventReminderJobTest.php
index 960dd481dd..c3b4b7e54c 100644
--- a/apps/dav/tests/unit/BackgroundJob/EventReminderJobTest.php
+++ b/apps/dav/tests/unit/BackgroundJob/EventReminderJobTest.php
@@ -51,17 +51,42 @@ class EventReminderJobTest extends TestCase {
public function data(): array
{
- return [[true], [false]];
+ return [
+ [true, true, true],
+ [true, false, false],
+ [false, true, false],
+ [false, false, false],
+ ];
}
/**
* @dataProvider data
+ *
* @param bool $sendEventReminders
+ * @param bool $sendEventRemindersMode
+ * @param bool $expectCall
*/
- public function testRun(bool $sendEventReminders): void
- {
- $this->config->expects($this->once())->method('getAppValue')->with('dav', 'sendEventReminders', 'yes')->willReturn($sendEventReminders ? 'yes' : 'no');
- $this->reminderService->expects($this->exactly($sendEventReminders ? 1 : 0))->method('processReminders');
+ public function testRun(bool $sendEventReminders, bool $sendEventRemindersMode, bool $expectCall): void {
+ $this->config->expects($this->at(0))
+ ->method('getAppValue')
+ ->with('dav', 'sendEventReminders', 'yes')
+ ->willReturn($sendEventReminders ? 'yes' : 'no');
+
+ if ($sendEventReminders) {
+ $this->config->expects($this->at(1))
+ ->method('getAppValue')
+ ->with('dav', 'sendEventRemindersMode', 'backgroundjob')
+ ->willReturn($sendEventRemindersMode ? 'backgroundjob' : 'cron');
+
+ }
+
+ if ($expectCall) {
+ $this->reminderService->expects($this->once())
+ ->method('processReminders');
+ } else {
+ $this->reminderService->expects($this->never())
+ ->method('processReminders');
+ }
$this->backgroundJob->run([]);
}
diff --git a/apps/dav/tests/unit/CalDAV/Reminder/BackendTest.php b/apps/dav/tests/unit/CalDAV/Reminder/BackendTest.php
index 1cd979dc5d..71453ac73a 100644
--- a/apps/dav/tests/unit/CalDAV/Reminder/BackendTest.php
+++ b/apps/dav/tests/unit/CalDAV/Reminder/BackendTest.php
@@ -1,8 +1,11 @@
+ * @author Georg Ehrke
*
* @license AGPL-3.0
*
@@ -21,7 +24,6 @@
*/
namespace OCA\DAV\Tests\unit\CalDAV\Reminder;
-use OCP\IDBConnection;
use OCP\DB\QueryBuilder\IQueryBuilder;
use OCP\AppFramework\Utility\ITimeFactory;
use OCA\DAV\CalDAV\Reminder\Backend as ReminderBackend;
@@ -29,285 +31,367 @@ use Test\TestCase;
class BackendTest extends TestCase {
- /**
- * Reminder Backend
- *
- * @var ReminderBackend|\PHPUnit\Framework\MockObject\MockObject
- */
- private $reminderBackend;
-
- /** @var IDBConnection|\PHPUnit\Framework\MockObject\MockObject */
- private $dbConnection;
+ /**
+ * Reminder Backend
+ *
+ * @var ReminderBackend|\PHPUnit\Framework\MockObject\MockObject
+ */
+ private $reminderBackend;
/** @var ITimeFactory|\PHPUnit\Framework\MockObject\MockObject */
- private $timeFactory;
+ private $timeFactory;
- public function setUp() {
+ public function setUp() {
parent::setUp();
- $this->dbConnection = $this->createMock(IDBConnection::class);
+ $query = self::$realDatabase->getQueryBuilder();
+ $query->delete('calendar_reminders')->execute();
+ $query->delete('calendarobjects')->execute();
+ $query->delete('calendars')->execute();
+
$this->timeFactory = $this->createMock(ITimeFactory::class);
- $this->reminderBackend = new ReminderBackend($this->dbConnection, $this->timeFactory);
- }
+ $this->reminderBackend = new ReminderBackend(self::$realDatabase, $this->timeFactory);
- public function testCleanRemindersForEvent(): void
- {
- /** @var IQueryBuilder|\PHPUnit\Framework\MockObject\MockObject $queryBuilder */
- $queryBuilder = $this->createMock(IQueryBuilder::class);
- $stmt = $this->createMock(\Doctrine\DBAL\Driver\Statement::class);
- $expr = $this->createMock(\OCP\DB\QueryBuilder\IExpressionBuilder::class);
-
- $this->dbConnection->expects($this->once())
- ->method('getQueryBuilder')
- ->with()
- ->will($this->returnValue($queryBuilder));
- $queryBuilder->method('expr')
- ->will($this->returnValue($expr));
-
- $expr->method('eq')
- ->will($this->returnValueMap([
- ['calendarid', 'createNamedParameter-1', null, 'WHERE_CLAUSE_1'],
- ['objecturi', 'createNamedParameter-2', null, 'WHERE_CLAUSE_2'],
- ]));
- $queryBuilder->method('createNamedParameter')
- ->will($this->returnValueMap([
- [1, \PDO::PARAM_STR, null, 'createNamedParameter-1'],
- ['object.ics', \PDO::PARAM_STR, null, 'createNamedParameter-2'],
- ]));
-
- $queryBuilder->expects($this->at(0))
- ->method('delete')
- ->with('calendar_reminders')
- ->willReturn($queryBuilder);
- $queryBuilder->expects($this->at(3))
- ->method('where')
- ->with('WHERE_CLAUSE_1')
- ->will($this->returnValue($queryBuilder));
- $queryBuilder->expects($this->at(6))
- ->method('andWhere')
- ->with('WHERE_CLAUSE_2')
- ->will($this->returnValue($queryBuilder));
- $queryBuilder->expects($this->at(7))
- ->method('execute')
- ->with()
- ->willReturn($stmt);
-
- $this->reminderBackend->cleanRemindersForEvent(1, 'object.ics');
+ $this->createRemindersTestSet();
}
- public function testCleanRemindersForCalendar(): void
- {
- /** @var IQueryBuilder|\PHPUnit\Framework\MockObject\MockObject $queryBuilder */
- $queryBuilder = $this->createMock(IQueryBuilder::class);
- $stmt = $this->createMock(\Doctrine\DBAL\Driver\Statement::class);
- $expr = $this->createMock(\OCP\DB\QueryBuilder\IExpressionBuilder::class);
-
- $this->dbConnection->expects($this->once())
- ->method('getQueryBuilder')
- ->with()
- ->will($this->returnValue($queryBuilder));
- $queryBuilder->method('expr')
- ->will($this->returnValue($expr));
-
- $expr->method('eq')
- ->will($this->returnValueMap([
- ['calendarid', 'createNamedParameter-1', null, 'WHERE_CLAUSE_1'],
- ]));
- $queryBuilder->method('createNamedParameter')
- ->will($this->returnValueMap([
- [1337, \PDO::PARAM_STR, null, 'createNamedParameter-1'],
- ]));
-
- $queryBuilder->expects($this->at(0))
- ->method('delete')
- ->with('calendar_reminders')
- ->willReturn($queryBuilder);
- $queryBuilder->expects($this->at(3))
- ->method('where')
- ->with('WHERE_CLAUSE_1')
- ->will($this->returnValue($queryBuilder));
- $queryBuilder->expects($this->at(4))
- ->method('execute')
- ->with()
- ->willReturn($stmt);
-
- $this->reminderBackend->cleanRemindersForCalendar(1337);
+ protected function tearDown() {
+ $query = self::$realDatabase->getQueryBuilder();
+ $query->delete('calendar_reminders')->execute();
+ $query->delete('calendarobjects')->execute();
+ $query->delete('calendars')->execute();
}
- public function testRemoveReminder(): void
- {
- /** @var IQueryBuilder|\PHPUnit\Framework\MockObject\MockObject $queryBuilder */
- $queryBuilder = $this->createMock(IQueryBuilder::class);
- $stmt = $this->createMock(\Doctrine\DBAL\Driver\Statement::class);
- $expr = $this->createMock(\OCP\DB\QueryBuilder\IExpressionBuilder::class);
- $this->dbConnection->expects($this->once())
- ->method('getQueryBuilder')
- ->with()
- ->will($this->returnValue($queryBuilder));
- $queryBuilder->method('expr')
- ->will($this->returnValue($expr));
+ public function testCleanRemindersForEvent(): void {
+ $query = self::$realDatabase->getQueryBuilder();
+ $rows = $query->select('*')
+ ->from('calendar_reminders')
+ ->execute()
+ ->fetchAll();
- $expr->method('eq')
- ->will($this->returnValueMap([
- ['id', 'createNamedParameter-1', null, 'WHERE_CLAUSE_1'],
- ]));
- $queryBuilder->method('createNamedParameter')
- ->will($this->returnValueMap([
- [16, \PDO::PARAM_STR, null, 'createNamedParameter-1'],
- ]));
+ $this->assertCount(4, $rows);
- $queryBuilder->expects($this->at(0))
- ->method('delete')
- ->with('calendar_reminders')
- ->willReturn($queryBuilder);
- $queryBuilder->expects($this->at(3))
- ->method('where')
- ->with('WHERE_CLAUSE_1')
- ->will($this->returnValue($queryBuilder));
- $queryBuilder->expects($this->at(4))
- ->method('execute')
- ->with()
- ->willReturn($stmt);
+ $this->reminderBackend->cleanRemindersForEvent(1);
- $this->reminderBackend->removeReminder(16);
+ $query = self::$realDatabase->getQueryBuilder();
+ $rows = $query->select('*')
+ ->from('calendar_reminders')
+ ->execute()
+ ->fetchAll();
+
+ $this->assertCount(2, $rows);
}
- public function testGetRemindersToProcess(): void
- {
- $dbData = [[
- 'cr.id' => 30,
- 'cr.calendarid' => 3,
- 'cr.objecturi' => 'object.ics',
- 'cr.type' => 'EMAIL',
- 'cr.notificationdate' => 1337,
- 'cr.uid' => 'user1',
- 'co.calendardata' => 'BEGIN:VCALENDAR',
- 'c.displayname' => 'My Calendar'
- ]];
+ public function testCleanRemindersForCalendar(): void {
+ $query = self::$realDatabase->getQueryBuilder();
+ $rows = $query->select('*')
+ ->from('calendar_reminders')
+ ->execute()
+ ->fetchAll();
- $this->timeFactory->expects($this->exactly(2))
+ $this->assertCount(4, $rows);
+
+ $this->reminderBackend->cleanRemindersForCalendar(1);
+
+ $query = self::$realDatabase->getQueryBuilder();
+ $rows = $query->select('*')
+ ->from('calendar_reminders')
+ ->execute()
+ ->fetchAll();
+
+ $this->assertCount(1, $rows);
+ }
+
+ public function testRemoveReminder(): void {
+ $query = self::$realDatabase->getQueryBuilder();
+ $rows = $query->select('*')
+ ->from('calendar_reminders')
+ ->execute()
+ ->fetchAll();
+
+ $this->assertCount(4, $rows);
+
+ $this->reminderBackend->removeReminder((int) $rows[3]['id']);
+
+ $query = self::$realDatabase->getQueryBuilder();
+ $rows = $query->select('*')
+ ->from('calendar_reminders')
+ ->execute()
+ ->fetchAll();
+
+ $this->assertCount(3, $rows);
+ }
+
+ public function testGetRemindersToProcess(): void {
+ $this->timeFactory->expects($this->exactly(1))
->method('getTime')
->with()
- ->willReturn(1337);
+ ->willReturn(123457);
- /** @var IQueryBuilder|\PHPUnit\Framework\MockObject\MockObject $queryBuilder */
- $queryBuilder = $this->createMock(IQueryBuilder::class);
- $stmt = $this->createMock(\Doctrine\DBAL\Driver\Statement::class);
- $expr = $this->createMock(\OCP\DB\QueryBuilder\IExpressionBuilder::class);
+ $rows = $this->reminderBackend->getRemindersToProcess();
- $this->dbConnection->expects($this->once())
- ->method('getQueryBuilder')
- ->with()
- ->willReturn($queryBuilder);
- $queryBuilder->method('expr')
- ->willReturn($expr);
+ $this->assertCount(2, $rows);
+ unset($rows[0]['id']);
+ unset($rows[1]['id']);
- $expr->method('eq')
- ->willReturnMap([
- ['cr.calendarid', 'c.id', null, 'EQ_CLAUSE_1'],
- ['co.uri', 'cr.objecturi', null, 'EQ_CLAUSE_2'],
- ]);
- $expr->method('andX')
- ->willReturnMap([
- ['EQ_CLAUSE_1', 'EQ_CLAUSE_2', 'ANDX_CLAUSE'],
- ]);
-
- $expr->method('lte')
- ->with('cr.notificationdate', 'createNamedParameter-1', null)
- ->willReturn('LTE_CLAUSE_1');
-
- $expr->method('gte')
- ->with('cr.eventstartdate', 'createNamedParameter-1', null)
- ->willReturn('GTE_CLAUSE_2');
-
- $queryBuilder->method('createNamedParameter')
- ->willReturnMap([
- [1337, \PDO::PARAM_STR, null, 'createNamedParameter-1'],
- ]);
-
- $queryBuilder->expects($this->at(0))
- ->method('select')
- ->with(['cr.id', 'cr.calendarid', 'cr.objecturi', 'cr.type', 'cr.notificationdate', 'cr.uid', 'co.calendardata', 'c.displayname'])
- ->willReturn($queryBuilder);
- $queryBuilder->expects($this->at(1))
- ->method('from')
- ->with('calendar_reminders', 'cr')
- ->willReturn($queryBuilder);
- $queryBuilder->expects($this->at(4))
- ->method('where')
- ->with('LTE_CLAUSE_1')
- ->willReturn($queryBuilder);
- $queryBuilder->expects($this->at(7))
- ->method('andWhere')
- ->with('GTE_CLAUSE_2')
- ->willReturn($queryBuilder);
- $queryBuilder->expects($this->at(9))
- ->method('leftJoin')
- ->with('cr', 'calendars', 'c', 'EQ_CLAUSE_1')
- ->willReturn($queryBuilder);
- $queryBuilder->expects($this->at(13))
- ->method('leftJoin')
- ->with('cr', 'calendarobjects', 'co', 'ANDX_CLAUSE')
- ->willReturn($queryBuilder);
- $queryBuilder->expects($this->at(14))
- ->method('execute')
- ->with()
- ->willReturn($stmt);
-
- $stmt->expects($this->once())
- ->method('fetchAll')
- ->with()
- ->willReturn($dbData);
-
- $actual = $this->reminderBackend->getRemindersToProcess();
- $this->assertEquals($dbData, $actual);
+ $this->assertEquals($rows[0], [
+ 'calendar_id' => 1,
+ 'object_id' => 1,
+ 'uid' => 'asd',
+ 'is_recurring' => false,
+ 'recurrence_id' => 123458,
+ 'is_recurrence_exception' => false,
+ 'event_hash' => 'asd123',
+ 'alarm_hash' => 'asd567',
+ 'type' => 'EMAIL',
+ 'is_relative' => true,
+ 'notification_date' => 123456,
+ 'is_repeat_based' => false,
+ 'calendardata' => 'Calendar data 123',
+ 'displayname' => 'Displayname 123',
+ 'principaluri' => 'principals/users/user001',
+ ]);
+ $this->assertEquals($rows[1], [
+ 'calendar_id' => 1,
+ 'object_id' => 1,
+ 'uid' => 'asd',
+ 'is_recurring' => false,
+ 'recurrence_id' => 123458,
+ 'is_recurrence_exception' => false,
+ 'event_hash' => 'asd123',
+ 'alarm_hash' => 'asd567',
+ 'type' => 'AUDIO',
+ 'is_relative' => true,
+ 'notification_date' => 123456,
+ 'is_repeat_based' => false,
+ 'calendardata' => 'Calendar data 123',
+ 'displayname' => 'Displayname 123',
+ 'principaluri' => 'principals/users/user001',
+ ]);
}
- public function testInsertReminder(): void
- {
- /** @var IQueryBuilder|\PHPUnit\Framework\MockObject\MockObject $queryBuilder */
- $queryBuilder = $this->createMock(IQueryBuilder::class);
- $stmt = $this->createMock(\Doctrine\DBAL\Driver\Statement::class);
- $expr = $this->createMock(\OCP\DB\QueryBuilder\IExpressionBuilder::class);
+ public function testGetAllScheduledRemindersForEvent(): void {
+ $rows = $this->reminderBackend->getAllScheduledRemindersForEvent(1);
- $this->dbConnection->expects($this->once())
- ->method('getQueryBuilder')
- ->with()
- ->will($this->returnValue($queryBuilder));
- $queryBuilder->method('expr')
- ->will($this->returnValue($expr));
+ $this->assertCount(2, $rows);
+ unset($rows[0]['id']);
+ unset($rows[1]['id']);
- $queryBuilder->method('createNamedParameter')
- ->will($this->returnValueMap([
- ['user1', \PDO::PARAM_STR, null, 'createNamedParameter-1'],
- ['1', \PDO::PARAM_STR, null, 'createNamedParameter-2'],
- ['object.ics', \PDO::PARAM_STR, null, 'createNamedParameter-3'],
- ['EMAIL', \PDO::PARAM_STR, null, 'createNamedParameter-4'],
- [1227, \PDO::PARAM_STR, null, 'createNamedParameter-5'],
- [1337, \PDO::PARAM_STR, null, 'createNamedParameter-6'],
- ]));
+ $this->assertEquals($rows[0], [
+ 'calendar_id' => 1,
+ 'object_id' => 1,
+ 'uid' => 'asd',
+ 'is_recurring' => false,
+ 'recurrence_id' => 123458,
+ 'is_recurrence_exception' => false,
+ 'event_hash' => 'asd123',
+ 'alarm_hash' => 'asd567',
+ 'type' => 'EMAIL',
+ 'is_relative' => true,
+ 'notification_date' => 123456,
+ 'is_repeat_based' => false,
+ ]);
+ $this->assertEquals($rows[1], [
+ 'calendar_id' => 1,
+ 'object_id' => 1,
+ 'uid' => 'asd',
+ 'is_recurring' => false,
+ 'recurrence_id' => 123458,
+ 'is_recurrence_exception' => false,
+ 'event_hash' => 'asd123',
+ 'alarm_hash' => 'asd567',
+ 'type' => 'AUDIO',
+ 'is_relative' => true,
+ 'notification_date' => 123456,
+ 'is_repeat_based' => false,
+ ]);
+ }
- $queryBuilder->expects($this->at(0))
- ->method('insert')
- ->with('calendar_reminders')
- ->willReturn($queryBuilder);
- $queryBuilder->expects($this->at(7))
- ->method('values')
- ->with([
- 'uid' => 'createNamedParameter-1',
- 'calendarid' => 'createNamedParameter-2',
- 'objecturi' => 'createNamedParameter-3',
- 'type' => 'createNamedParameter-4',
- 'notificationdate' => 'createNamedParameter-5',
- 'eventstartdate' => 'createNamedParameter-6',
+ public function testInsertReminder(): void {
+ $query = self::$realDatabase->getQueryBuilder();
+ $rows = $query->select('*')
+ ->from('calendar_reminders')
+ ->execute()
+ ->fetchAll();
+
+ $this->assertCount(4, $rows);
+
+ $this->reminderBackend->insertReminder(42, 1337, 'uid99', true, 12345678,
+ true, 'hash99', 'hash42', 'AUDIO', false, 12345670, false);
+
+ $query = self::$realDatabase->getQueryBuilder();
+ $rows = $query->select('*')
+ ->from('calendar_reminders')
+ ->execute()
+ ->fetchAll();
+
+ $this->assertCount(5, $rows);
+
+ unset($rows[4]['id']);
+
+ $this->assertEquals($rows[4], [
+ 'calendar_id' => '42',
+ 'object_id' => '1337',
+ 'is_recurring' => '1',
+ 'uid' => 'uid99',
+ 'recurrence_id' => '12345678',
+ 'is_recurrence_exception' => '1',
+ 'event_hash' => 'hash99',
+ 'alarm_hash' => 'hash42',
+ 'type' => 'AUDIO',
+ 'is_relative' => '0',
+ 'notification_date' => '12345670',
+ 'is_repeat_based' => '0',
+ ]);
+ }
+
+ public function testUpdateReminder() {
+ $query = self::$realDatabase->getQueryBuilder();
+ $rows = $query->select('*')
+ ->from('calendar_reminders')
+ ->execute()
+ ->fetchAll();
+
+ $this->assertCount(4, $rows);
+
+ $this->assertEquals($rows[3]['notification_date'], 123600);
+
+ $reminderId = (int) $rows[3]['id'];
+ $newNotificationDate = 123700;
+
+ $this->reminderBackend->updateReminder($reminderId, $newNotificationDate);
+
+ $query = self::$realDatabase->getQueryBuilder();
+ $row = $query->select('notification_date')
+ ->from('calendar_reminders')
+ ->where($query->expr()->eq('id', $query->createNamedParameter($reminderId)))
+ ->execute()
+ ->fetch();
+
+ $this->assertEquals((int) $row['notification_date'], 123700);
+ }
+
+
+ private function createRemindersTestSet(): void {
+ $query = self::$realDatabase->getQueryBuilder();
+ $query->insert('calendars')
+ ->values([
+ 'id' => $query->createNamedParameter(1),
+ 'principaluri' => $query->createNamedParameter('principals/users/user001'),
+ 'displayname' => $query->createNamedParameter('Displayname 123'),
])
- ->willReturn($queryBuilder);
- $queryBuilder->expects($this->at(8))
- ->method('execute')
- ->with()
- ->willReturn($stmt);
+ ->execute();
- $actual = $this->reminderBackend->insertReminder('user1', '1', 'object.ics', 'EMAIL', 1227, 1337);
- }
+ $query = self::$realDatabase->getQueryBuilder();
+ $query->insert('calendars')
+ ->values([
+ 'id' => $query->createNamedParameter(99),
+ 'principaluri' => $query->createNamedParameter('principals/users/user002'),
+ 'displayname' => $query->createNamedParameter('Displayname 99'),
+ ])
+ ->execute();
+
+ $query = self::$realDatabase->getQueryBuilder();
+ $query->insert('calendarobjects')
+ ->values([
+ 'id' => $query->createNamedParameter(1),
+ 'calendardata' => $query->createNamedParameter('Calendar data 123'),
+ 'calendarid' => $query->createNamedParameter(1),
+ 'size' => $query->createNamedParameter(42),
+ ])
+ ->execute();
+
+ $query = self::$realDatabase->getQueryBuilder();
+ $query->insert('calendarobjects')
+ ->values([
+ 'id' => $query->createNamedParameter(2),
+ 'calendardata' => $query->createNamedParameter('Calendar data 456'),
+ 'calendarid' => $query->createNamedParameter(1),
+ 'size' => $query->createNamedParameter(42),
+ ])
+ ->execute();
+
+ $query = self::$realDatabase->getQueryBuilder();
+ $query->insert('calendarobjects')
+ ->values([
+ 'id' => $query->createNamedParameter(10),
+ 'calendardata' => $query->createNamedParameter('Calendar data 789'),
+ 'calendarid' => $query->createNamedParameter(99),
+ 'size' => $query->createNamedParameter(42),
+ ])
+ ->execute();
+
+ $query = self::$realDatabase->getQueryBuilder();
+ $query->insert('calendar_reminders')
+ ->values([
+ 'calendar_id' => $query->createNamedParameter(1),
+ 'object_id' => $query->createNamedParameter(1),
+ 'uid' => $query->createNamedParameter('asd'),
+ 'is_recurring' => $query->createNamedParameter(0),
+ 'recurrence_id' => $query->createNamedParameter(123458),
+ 'is_recurrence_exception' => $query->createNamedParameter(0),
+ 'event_hash' => $query->createNamedParameter('asd123'),
+ 'alarm_hash' => $query->createNamedParameter('asd567'),
+ 'type' => $query->createNamedParameter('EMAIL'),
+ 'is_relative' => $query->createNamedParameter(1),
+ 'notification_date' => $query->createNamedParameter(123456),
+ 'is_repeat_based' => $query->createNamedParameter(0),
+ ])
+ ->execute();
+
+ $query = self::$realDatabase->getQueryBuilder();
+ $query->insert('calendar_reminders')
+ ->values([
+ 'calendar_id' => $query->createNamedParameter(1),
+ 'object_id' => $query->createNamedParameter(1),
+ 'uid' => $query->createNamedParameter('asd'),
+ 'is_recurring' => $query->createNamedParameter(0),
+ 'recurrence_id' => $query->createNamedParameter(123458),
+ 'is_recurrence_exception' => $query->createNamedParameter(0),
+ 'event_hash' => $query->createNamedParameter('asd123'),
+ 'alarm_hash' => $query->createNamedParameter('asd567'),
+ 'type' => $query->createNamedParameter('AUDIO'),
+ 'is_relative' => $query->createNamedParameter(1),
+ 'notification_date' => $query->createNamedParameter(123456),
+ 'is_repeat_based' => $query->createNamedParameter(0),
+ ])
+ ->execute();
+
+ $query = self::$realDatabase->getQueryBuilder();
+ $query->insert('calendar_reminders')
+ ->values([
+ 'calendar_id' => $query->createNamedParameter(1),
+ 'object_id' => $query->createNamedParameter(2),
+ 'uid' => $query->createNamedParameter('asd'),
+ 'is_recurring' => $query->createNamedParameter(0),
+ 'recurrence_id' => $query->createNamedParameter(123900),
+ 'is_recurrence_exception' => $query->createNamedParameter(0),
+ 'event_hash' => $query->createNamedParameter('asd123'),
+ 'alarm_hash' => $query->createNamedParameter('asd567'),
+ 'type' => $query->createNamedParameter('EMAIL'),
+ 'is_relative' => $query->createNamedParameter(1),
+ 'notification_date' => $query->createNamedParameter(123499),
+ 'is_repeat_based' => $query->createNamedParameter(0),
+ ])
+ ->execute();
+
+ $query = self::$realDatabase->getQueryBuilder();
+ $query->insert('calendar_reminders')
+ ->values([
+ 'calendar_id' => $query->createNamedParameter(99),
+ 'object_id' => $query->createNamedParameter(10),
+ 'uid' => $query->createNamedParameter('asd'),
+ 'is_recurring' => $query->createNamedParameter(0),
+ 'recurrence_id' => $query->createNamedParameter(123900),
+ 'is_recurrence_exception' => $query->createNamedParameter(0),
+ 'event_hash' => $query->createNamedParameter('asd123'),
+ 'alarm_hash' => $query->createNamedParameter('asd567'),
+ 'type' => $query->createNamedParameter('DISPLAY'),
+ 'is_relative' => $query->createNamedParameter(1),
+ 'notification_date' => $query->createNamedParameter(123600),
+ 'is_repeat_based' => $query->createNamedParameter(0),
+ ])
+ ->execute();
+ }
}
diff --git a/apps/dav/tests/unit/CalDAV/Reminder/AbstractNotificationProviderTest.php b/apps/dav/tests/unit/CalDAV/Reminder/NotificationProvider/AbstractNotificationProviderTest.php
similarity index 54%
rename from apps/dav/tests/unit/CalDAV/Reminder/AbstractNotificationProviderTest.php
rename to apps/dav/tests/unit/CalDAV/Reminder/NotificationProvider/AbstractNotificationProviderTest.php
index ba2e54af33..5a8d328ef8 100644
--- a/apps/dav/tests/unit/CalDAV/Reminder/AbstractNotificationProviderTest.php
+++ b/apps/dav/tests/unit/CalDAV/Reminder/NotificationProvider/AbstractNotificationProviderTest.php
@@ -1,4 +1,5 @@
*
*/
-namespace OCA\DAV\Tests\unit\CalDAV\Reminder;
+namespace OCA\DAV\Tests\unit\CalDAV\Reminder\NotificationProvider;
-use OCA\DAV\CalDAV\Reminder\AbstractNotificationProvider;
+use OCA\DAV\CalDAV\Reminder\NotificationProvider\AbstractProvider;
use OCP\IConfig;
use OCP\IL10N;
use OCP\ILogger;
@@ -33,23 +34,23 @@ use Sabre\VObject\Component\VCalendar;
abstract class AbstractNotificationProviderTest extends TestCase {
- /** @var ILogger|\PHPUnit\Framework\MockObject\MockObject */
- protected $logger;
+ /** @var ILogger|\PHPUnit\Framework\MockObject\MockObject */
+ protected $logger;
- /** @var L10NFactory|\PHPUnit\Framework\MockObject\MockObject */
- protected $l10nFactory;
+ /** @var L10NFactory|\PHPUnit\Framework\MockObject\MockObject */
+ protected $l10nFactory;
- /** @var IL10N|\PHPUnit\Framework\MockObject\MockObject */
- protected $l10n;
+ /** @var IL10N|\PHPUnit\Framework\MockObject\MockObject */
+ protected $l10n;
- /** @var IURLGenerator|\PHPUnit\Framework\MockObject\MockObject */
- protected $urlGenerator;
+ /** @var IURLGenerator|\PHPUnit\Framework\MockObject\MockObject */
+ protected $urlGenerator;
- /** @var IConfig|\PHPUnit\Framework\MockObject\MockObject */
- protected $config;
+ /** @var IConfig|\PHPUnit\Framework\MockObject\MockObject */
+ protected $config;
- /** @var AbstractNotificationProvider|\PHPUnit\Framework\MockObject\MockObject */
- protected $provider;
+ /** @var AbstractProvider|\PHPUnit\Framework\MockObject\MockObject */
+ protected $provider;
/**
* @var VCalendar
@@ -69,19 +70,20 @@ abstract class AbstractNotificationProviderTest extends TestCase {
public function setUp() {
parent::setUp();
- $this->logger = $this->createMock(ILogger::class);
- $this->l10nFactory = $this->createMock(L10NFactory::class);
- $this->l10n = $this->createMock(IL10N::class);
- $this->urlGenerator = $this->createMock(IURLGenerator::class);
- $this->config = $this->createMock(IConfig::class);
+ $this->logger = $this->createMock(ILogger::class);
+ $this->l10nFactory = $this->createMock(L10NFactory::class);
+ $this->l10n = $this->createMock(IL10N::class);
+ $this->urlGenerator = $this->createMock(IURLGenerator::class);
+ $this->config = $this->createMock(IConfig::class);
- $this->vcalendar = new VCalendar();
+ $this->vcalendar = new VCalendar();
$this->vcalendar->add('VEVENT', [
'SUMMARY' => 'Fellowship meeting',
- 'DTSTART' => new \DateTime('2017-01-01 00:00:00') // 1483228800
- ]);
+ 'DTSTART' => new \DateTime('2017-01-01 00:00:00+00:00'), // 1483228800,
+ 'UID' => 'uid1234',
+ ]);
$this->calendarDisplayName = 'Personal';
- $this->user = $this->createMock(IUser::class);
- }
+ $this->user = $this->createMock(IUser::class);
+ }
}
diff --git a/apps/dav/tests/unit/CalDAV/Reminder/NotificationProvider/AudioProviderTest.php b/apps/dav/tests/unit/CalDAV/Reminder/NotificationProvider/AudioProviderTest.php
new file mode 100644
index 0000000000..9938b2f732
--- /dev/null
+++ b/apps/dav/tests/unit/CalDAV/Reminder/NotificationProvider/AudioProviderTest.php
@@ -0,0 +1,33 @@
+
+ *
+ * @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\DAV\Tests\unit\CalDAV\Reminder\NotificationProvider;
+
+use OCA\DAV\CalDAV\Reminder\NotificationProvider\AudioProvider;
+
+class AudioProviderTest extends PushProviderTest {
+
+ public function testNotificationType():void {
+ $this->assertEquals(AudioProvider::NOTIFICATION_TYPE, 'AUDIO');
+ }
+
+}
\ No newline at end of file
diff --git a/apps/dav/tests/unit/CalDAV/Reminder/NotificationProvider/EmailProviderTest.php b/apps/dav/tests/unit/CalDAV/Reminder/NotificationProvider/EmailProviderTest.php
index 34a61b34fc..9bf2957e9f 100644
--- a/apps/dav/tests/unit/CalDAV/Reminder/NotificationProvider/EmailProviderTest.php
+++ b/apps/dav/tests/unit/CalDAV/Reminder/NotificationProvider/EmailProviderTest.php
@@ -1,8 +1,11 @@
+ * @author Georg Ehrke
*
* @license AGPL-3.0
*
@@ -22,7 +25,6 @@
namespace OCA\DAV\Tests\unit\CalDAV\Reminder\NotificationProvider;
use OCA\DAV\CalDAV\Reminder\NotificationProvider\EmailProvider;
-use OCA\DAV\CalDAV\Reminder\AbstractNotificationProvider;
use OCP\IConfig;
use OCP\IL10N;
use OCP\ILogger;
@@ -33,194 +35,512 @@ use OCP\Mail\IEMailTemplate;
use OCP\Mail\IMailer;
use OCP\Mail\IAttachment;
use OCP\Mail\IMessage;
+use Sabre\VObject\Component\VCalendar;
use Test\TestCase;
-use OCA\DAV\Tests\unit\CalDAV\Reminder\AbstractNotificationProviderTest;
class EmailProviderTest extends AbstractNotificationProviderTest {
- const USER_EMAIL = 'frodo@hobb.it';
+ const USER_EMAIL = 'frodo@hobb.it';
- /** @var ILogger|\PHPUnit\Framework\MockObject\MockObject */
- protected $logger;
+ /** @var ILogger|\PHPUnit\Framework\MockObject\MockObject */
+ protected $logger;
- /** @var L10NFactory|\PHPUnit\Framework\MockObject\MockObject */
- protected $l10nFactory;
+ /** @var L10NFactory|\PHPUnit\Framework\MockObject\MockObject */
+ protected $l10nFactory;
- /** @var IL10N|\PHPUnit\Framework\MockObject\MockObject */
- protected $l10n;
+ /** @var IL10N|\PHPUnit\Framework\MockObject\MockObject */
+ protected $l10n;
- /** @var IURLGenerator|\PHPUnit\Framework\MockObject\MockObject */
- protected $urlGenerator;
+ /** @var IURLGenerator|\PHPUnit\Framework\MockObject\MockObject */
+ protected $urlGenerator;
- /** @var IConfig|\PHPUnit\Framework\MockObject\MockObject */
- protected $config;
+ /** @var IConfig|\PHPUnit\Framework\MockObject\MockObject */
+ protected $config;
- /** @var IMailer|\PHPUnit\Framework\MockObject\MockObject */
+ /** @var IMailer|\PHPUnit\Framework\MockObject\MockObject */
private $mailer;
- public function setUp() {
- parent::setUp();
+ public function setUp() {
+ parent::setUp();
- $this->mailer = $this->createMock(IMailer::class);
+ $this->mailer = $this->createMock(IMailer::class);
- $this->provider = new EmailProvider(
- $this->config,
- $this->mailer,
- $this->logger,
- $this->l10nFactory,
- $this->urlGenerator
- );
- }
+ $this->provider = new EmailProvider(
+ $this->config,
+ $this->mailer,
+ $this->logger,
+ $this->l10nFactory,
+ $this->urlGenerator
+ );
+ }
- public function testSendWithNoUserEmail(): void
- {
- $this->user->expects($this->once())
- ->method('getEMailAddress')
- ->with()
- ->willReturn(null);
+ public function testSendWithoutAttendees():void {
+ $user1 = $this->createMock(IUser::class);
+ $user1->method('getUID')
+ ->willReturn('uid1');
+ $user1->method('getEMailAddress')
+ ->willReturn('uid1@example.com');
+ $user2 = $this->createMock(IUser::class);
+ $user2->method('getUID')
+ ->willReturn('uid2');
+ $user2->method('getEMailAddress')
+ ->willReturn('uid2@example.com');
+ $user3 = $this->createMock(IUser::class);
+ $user3->method('getUID')
+ ->willReturn('uid3');
+ $user3->method('getEMailAddress')
+ ->willReturn('uid3@example.com');
+ $user4 = $this->createMock(IUser::class);
+ $user4->method('getUID')
+ ->willReturn('uid4');
+ $user4->method('getEMailAddress')
+ ->willReturn(null);
- $this->mailer
- ->expects($this->never())
- ->method('send');
+ $users = [$user1, $user2, $user3, $user4];
- $this->provider->send($this->vcalendar, $this->calendarDisplayName, $this->user);
- }
+ $this->config->expects($this->at(0))
+ ->method('getUserValue')
+ ->with('uid1', 'core', 'lang', null)
+ ->willReturn(null);
+ $this->config->expects($this->at(1))
+ ->method('getUserValue')
+ ->with('uid2', 'core', 'lang', null)
+ ->willReturn('de');
+ $this->config->expects($this->at(2))
+ ->method('getUserValue')
+ ->with('uid3', 'core', 'lang', null)
+ ->willReturn('de');
- public function testSendWithFailedRecipients(): void
- {
- $this->user->expects($this->exactly(2))
- ->method('getEMailAddress')
- ->with()
- ->willReturn(self::USER_EMAIL);
+ $enL10N = $this->createMock(IL10N::class);
+ $enL10N->method('t')
+ ->will($this->returnArgument(0));
+ $enL10N->method('l')
+ ->will($this->returnArgument(0));
- $this->mailer
- ->expects($this->once())
- ->method('send')
- ->willReturn([self::USER_EMAIL])
- ;
+ $deL10N = $this->createMock(IL10N::class);
+ $deL10N->method('t')
+ ->will($this->returnArgument(0));
+ $deL10N->method('l')
+ ->will($this->returnArgument(0));
- $this->logger
- ->expects($this->once())
- ->method('error');
+ $this->l10nFactory->expects($this->at(0))
+ ->method('findLanguage')
+ ->with()
+ ->willReturn('en');
- $l10n = $this->createMock(IL10N::class);
- $this->l10nFactory
- ->method('get')
- ->willReturn($l10n);
+ $this->l10nFactory->expects($this->at(1))
+ ->method('languageExists')
+ ->with('dav', 'en')
+ ->willReturn(true);
- $this->provider->send($this->vcalendar, $this->calendarDisplayName, $this->user);
- }
+ $this->l10nFactory->expects($this->at(2))
+ ->method('get')
+ ->with('dav', 'en')
+ ->willReturn($enL10N);
- public function testSendWithMailerFailure(): void
- {
- $this->user->expects($this->exactly(2))
- ->method('getEMailAddress')
- ->with()
- ->willReturn(self::USER_EMAIL);
+ $this->l10nFactory->expects($this->at(3))
+ ->method('languageExists')
+ ->with('dav', 'de')
+ ->willReturn(true);
- $ex = new \Exception();
+ $this->l10nFactory->expects($this->at(4))
+ ->method('get')
+ ->with('dav', 'de')
+ ->willReturn($deL10N);
- $this->mailer
- ->expects($this->once())
- ->method('send')
- ->will($this->throwException($ex))
- ;
+ $template1 = $this->getTemplateMock();
+ $message11 = $this->getMessageMock('uid1@example.com', $template1);
+ $template2 = $this->getTemplateMock();
+ $message21 = $this->getMessageMock('uid2@example.com', $template2);
+ $message22 = $this->getMessageMock('uid3@example.com', $template2);
- $this->logger
- ->expects($this->once())
- ->method('logException')
- ->with($ex, ['app' => 'dav']);
+ $this->mailer->expects($this->at(0))
+ ->method('createEMailTemplate')
+ ->with('dav.calendarReminder')
+ ->willReturn($template1);
- $l10n = $this->createMock(IL10N::class);
- $this->l10nFactory
- ->method('get')
- ->willReturn($l10n);
+ $this->mailer->expects($this->at(1))
+ ->method('createMessage')
+ ->with()
+ ->willReturn($message11);
+ $this->mailer->expects($this->at(2))
+ ->method('send')
+ ->with($message11)
+ ->willReturn([]);
- $this->provider->send($this->vcalendar, $this->calendarDisplayName, $this->user);
- }
+ $this->mailer->expects($this->at(3))
+ ->method('createEMailTemplate')
+ ->with('dav.calendarReminder')
+ ->willReturn($template2);
- public function testSend(): void
- {
- $this->user->expects($this->exactly(2))
- ->method('getEMailAddress')
- ->with()
- ->willReturn(self::USER_EMAIL);
+ $this->mailer->expects($this->at(4))
+ ->method('createMessage')
+ ->with()
+ ->willReturn($message21);
+ $this->mailer->expects($this->at(5))
+ ->method('send')
+ ->with($message21)
+ ->willReturn([]);
+ $this->mailer->expects($this->at(6))
+ ->method('createMessage')
+ ->with()
+ ->willReturn($message22);
+ $this->mailer->expects($this->at(7))
+ ->method('send')
+ ->with($message22)
+ ->willReturn([]);
- $this->user->expects($this->once())
- ->method('getDisplayName')
- ->with()
- ->willReturn('Frodo');
+ $this->setupURLGeneratorMock(2);
- $this->urlGenerator
- ->expects($this->exactly(2))
- ->method('getAbsoluteURL');
+ $vcalendar = $this->getNoAttendeeVCalendar();
+ $this->provider->send($vcalendar->VEVENT, $this->calendarDisplayName, $users);
+ }
- $this->urlGenerator
- ->expects($this->exactly(2))
- ->method('imagePath');
+ public function testSendWithAttendees(): void {
+ $user1 = $this->createMock(IUser::class);
+ $user1->method('getUID')
+ ->willReturn('uid1');
+ $user1->method('getEMailAddress')
+ ->willReturn('uid1@example.com');
+ $user2 = $this->createMock(IUser::class);
+ $user2->method('getUID')
+ ->willReturn('uid2');
+ $user2->method('getEMailAddress')
+ ->willReturn('uid2@example.com');
+ $user3 = $this->createMock(IUser::class);
+ $user3->method('getUID')
+ ->willReturn('uid3');
+ $user3->method('getEMailAddress')
+ ->willReturn('uid3@example.com');
+ $user4 = $this->createMock(IUser::class);
+ $user4->method('getUID')
+ ->willReturn('uid4');
+ $user4->method('getEMailAddress')
+ ->willReturn(null);
- $mailMessage = $this->createMock(IMessage::class);
- $mailMessage->expects($this->once())
- ->method('setFrom')
- ->with([\OCP\Util::getDefaultEmailAddress('invitations-noreply') => 'Nextcloud'])
- ->willReturn($mailMessage);
+ $users = [$user1, $user2, $user3, $user4];
- $mailMessage->expects($this->once())
- ->method('setTo')
- ->with([self::USER_EMAIL => 'Frodo'])
- ->willReturn($mailMessage);
+ $this->config->expects($this->at(0))
+ ->method('getUserValue')
+ ->with('uid1', 'core', 'lang', null)
+ ->willReturn(null);
+ $this->config->expects($this->at(1))
+ ->method('getUserValue')
+ ->with('uid2', 'core', 'lang', null)
+ ->willReturn('de');
+ $this->config->expects($this->at(2))
+ ->method('getUserValue')
+ ->with('uid3', 'core', 'lang', null)
+ ->willReturn('de');
- $mailMessage
- ->expects($this->never())
- ->method('setReplyTo')
- ->willReturn($mailMessage);
+ $enL10N = $this->createMock(IL10N::class);
+ $enL10N->method('t')
+ ->will($this->returnArgument(0));
+ $enL10N->method('l')
+ ->will($this->returnArgument(0));
- $emailTemplate = $this->createMock(IEMailTemplate::class);
- $this->mailer
- ->expects($this->once())
- ->method('createEMailTemplate')
- ->willReturn($emailTemplate);
+ $deL10N = $this->createMock(IL10N::class);
+ $deL10N->method('t')
+ ->will($this->returnArgument(0));
+ $deL10N->method('l')
+ ->will($this->returnArgument(0));
- $emailTemplate->expects($this->once())
+ $this->l10nFactory->expects($this->at(0))
+ ->method('findLanguage')
+ ->with()
+ ->willReturn('en');
+
+ $this->l10nFactory->expects($this->at(1))
+ ->method('languageExists')
+ ->with('dav', 'de')
+ ->willReturn(true);
+
+ $this->l10nFactory->expects($this->at(2))
+ ->method('get')
+ ->with('dav', 'de')
+ ->willReturn($enL10N);
+
+ $this->l10nFactory->expects($this->at(3))
+ ->method('languageExists')
+ ->with('dav', 'en')
+ ->willReturn(true);
+
+ $this->l10nFactory->expects($this->at(4))
+ ->method('get')
+ ->with('dav', 'en')
+ ->willReturn($deL10N);
+
+ $template1 = $this->getTemplateMock();
+ $message11 = $this->getMessageMock('foo1@example.org', $template1);
+ $message12 = $this->getMessageMock('uid2@example.com', $template1);
+ $message13 = $this->getMessageMock('uid3@example.com', $template1);
+ $template2 = $this->getTemplateMock();
+ $message21 = $this->getMessageMock('foo3@example.org', $template2);
+ $message22 = $this->getMessageMock('foo4@example.org', $template2);
+ $message23 = $this->getMessageMock('uid1@example.com', $template2);
+
+ $this->mailer->expects($this->at(0))
+ ->method('createEMailTemplate')
+ ->with('dav.calendarReminder')
+ ->willReturn($template1);
+
+ $this->mailer->expects($this->at(1))
+ ->method('createMessage')
+ ->with()
+ ->willReturn($message11);
+ $this->mailer->expects($this->at(2))
+ ->method('send')
+ ->with($message11)
+ ->willReturn([]);
+ $this->mailer->expects($this->at(3))
+ ->method('createMessage')
+ ->with()
+ ->willReturn($message12);
+ $this->mailer->expects($this->at(4))
+ ->method('send')
+ ->with($message12)
+ ->willReturn([]);
+ $this->mailer->expects($this->at(5))
+ ->method('createMessage')
+ ->with()
+ ->willReturn($message13);
+ $this->mailer->expects($this->at(6))
+ ->method('send')
+ ->with($message13)
+ ->willReturn([]);
+
+ $this->mailer->expects($this->at(7))
+ ->method('createEMailTemplate')
+ ->with('dav.calendarReminder')
+ ->willReturn($template2);
+
+ $this->mailer->expects($this->at(8))
+ ->method('createMessage')
+ ->with()
+ ->willReturn($message21);
+ $this->mailer->expects($this->at(9))
+ ->method('send')
+ ->with($message21)
+ ->willReturn([]);
+ $this->mailer->expects($this->at(10))
+ ->method('createMessage')
+ ->with()
+ ->willReturn($message22);
+ $this->mailer->expects($this->at(11))
+ ->method('send')
+ ->with($message22)
+ ->willReturn([]);
+ $this->mailer->expects($this->at(12))
+ ->method('createMessage')
+ ->with()
+ ->willReturn($message23);
+ $this->mailer->expects($this->at(13))
+ ->method('send')
+ ->with($message23)
+ ->willReturn([]);
+
+ $this->setupURLGeneratorMock(2);
+
+ $vcalendar = $this->getAttendeeVCalendar();
+ $this->provider->send($vcalendar->VEVENT, $this->calendarDisplayName, $users);
+ }
+
+ /**
+ * @return IEMailTemplate
+ */
+ private function getTemplateMock():IEMailTemplate {
+ $template = $this->createMock(IEMailTemplate::class);
+
+ $template->expects($this->at(0))
+ ->method('addHeader')
+ ->with()
+ ->willReturn($template);
+
+ $template->expects($this->at(1))
->method('setSubject')
- ->with('Notification: Fellowship meeting');
+ ->with()
+ ->willReturn($template);
- $emailTemplate->expects($this->once())
- ->method('addHeader');
+ $template->expects($this->at(2))
+ ->method('addHeading')
+ ->with()
+ ->willReturn($template);
- $emailTemplate->expects($this->once())
- ->method('addHeading');
+ $template->expects($this->at(3))
+ ->method('addBodyListItem')
+ ->with()
+ ->willReturn($template);
- $emailTemplate->expects($this->exactly(2))
- ->method('addBodyListItem');
+ $template->expects($this->at(4))
+ ->method('addBodyListItem')
+ ->with()
+ ->willReturn($template);
- $emailTemplate->expects($this->once())
- ->method('addFooter');
+ $template->expects($this->at(5))
+ ->method('addBodyListItem')
+ ->with()
+ ->willReturn($template);
- $mailMessage->expects($this->once())
- ->method('useTemplate')
- ->with($emailTemplate);
+ $template->expects($this->at(6))
+ ->method('addBodyListItem')
+ ->with()
+ ->willReturn($template);
- $this->mailer
- ->expects($this->once())
- ->method('createMessage')
- ->willReturn($mailMessage);
+ $template->expects($this->at(7))
+ ->method('addFooter')
+ ->with()
+ ->willReturn($template);
- $emailAttachment = $this->createMock(IAttachment::class);
- $this->mailer
- ->expects($this->once())
- ->method('createAttachment')
- ->willReturn($emailAttachment);
+ return $template;
+ }
- $this->mailer
- ->expects($this->once())
- ->method('send');
+ /**
+ * @param array $toMail
+ * @param IEMailTemplate $templateMock
+ * @param array $replyTo
+ * @return IMessage
+ */
+ private function getMessageMock(string $toMail, IEMailTemplate $templateMock, array $replyTo=null):IMessage {
+ $message = $this->createMock(IMessage::class);
+ $i = 0;
- $l10n = $this->createMock(IL10N::class);
- $this->l10nFactory
- ->method('get')
- ->willReturn($l10n);
+ $message->expects($this->at($i++))
+ ->method('setFrom')
+ ->with([\OCP\Util::getDefaultEmailAddress('reminders-noreply')])
+ ->willReturn($message);
- $this->provider->send($this->vcalendar, $this->calendarDisplayName, $this->user);
- }
+ if ($replyTo) {
+ $message->expects($this->at($i++))
+ ->method('setReplyTo')
+ ->with($replyTo)
+ ->willReturn($message);
+ }
+
+ $message->expects($this->at($i++))
+ ->method('setTo')
+ ->with([$toMail])
+ ->willReturn($message);
+
+ $message->expects($this->at($i++))
+ ->method('useTemplate')
+ ->with($templateMock)
+ ->willReturn($message);
+
+ return $message;
+ }
+
+ private function getNoAttendeeVCalendar():VCalendar {
+ $vcalendar = new VCalendar();
+ $vcalendar->add('VEVENT', [
+ 'SUMMARY' => 'Fellowship meeting',
+ 'DTSTART' => new \DateTime('2017-01-01 00:00:00+00:00'), // 1483228800,
+ 'UID' => 'uid1234',
+ 'LOCATION' => 'Location 123',
+ 'DESCRIPTION' => 'DESCRIPTION 456',
+ ]);
+
+ return $vcalendar;
+ }
+
+ private function getAttendeeVCalendar():VCalendar {
+ $vcalendar = new VCalendar();
+ $vcalendar->add('VEVENT', [
+ 'SUMMARY' => 'Fellowship meeting',
+ 'DTSTART' => new \DateTime('2017-01-01 00:00:00+00:00'), // 1483228800,
+ 'UID' => 'uid1234',
+ 'LOCATION' => 'Location 123',
+ 'DESCRIPTION' => 'DESCRIPTION 456',
+ ]);
+
+ $vcalendar->VEVENT->add(
+ 'ATTENDEE',
+ 'mailto:foo1@example.org',
+ [
+ 'LANG' => 'de',
+ 'PARTSTAT' => 'NEEDS-ACTION',
+ ]
+ );
+
+ $vcalendar->VEVENT->add(
+ 'ATTENDEE',
+ 'mailto:foo2@example.org',
+ [
+ 'LANG' => 'de',
+ 'PARTSTAT' => 'DECLINED',
+ ]
+ );
+
+ $vcalendar->VEVENT->add(
+ 'ATTENDEE',
+ 'mailto:foo3@example.org',
+ [
+ 'LANG' => 'en',
+ 'PARTSTAT' => 'CONFIRMED',
+ ]
+ );
+
+ $vcalendar->VEVENT->add(
+ 'ATTENDEE',
+ 'mailto:foo4@example.org'
+ );
+
+ $vcalendar->VEVENT->add(
+ 'ATTENDEE',
+ 'tomail:foo5@example.org'
+ );
+
+ return $vcalendar;
+ }
+
+ private function setupURLGeneratorMock(int $times=1):void {
+ for ($i = 0; $i < $times; $i++) {
+ $this->urlGenerator
+ ->expects($this->at(8 * $i))
+ ->method('imagePath')
+ ->with('core', 'actions/info.svg')
+ ->willReturn('imagePath1');
+
+ $this->urlGenerator
+ ->expects($this->at(8 * $i + 1))
+ ->method('getAbsoluteURL')
+ ->with('imagePath1')
+ ->willReturn('AbsURL1');
+
+ $this->urlGenerator
+ ->expects($this->at(8 * $i + 2))
+ ->method('imagePath')
+ ->with('core', 'places/calendar.svg')
+ ->willReturn('imagePath2');
+
+ $this->urlGenerator
+ ->expects($this->at(8 * $i + 3))
+ ->method('getAbsoluteURL')
+ ->with('imagePath2')
+ ->willReturn('AbsURL2');
+
+ $this->urlGenerator
+ ->expects($this->at(8 * $i + 4))
+ ->method('imagePath')
+ ->with('core', 'actions/address.svg')
+ ->willReturn('imagePath3');
+
+ $this->urlGenerator
+ ->expects($this->at(8 * $i + 5))
+ ->method('getAbsoluteURL')
+ ->with('imagePath3')
+ ->willReturn('AbsURL3');
+
+ $this->urlGenerator
+ ->expects($this->at(8 * $i + 6))
+ ->method('imagePath')
+ ->with('core', 'actions/more.svg')
+ ->willReturn('imagePath4');
+
+ $this->urlGenerator
+ ->expects($this->at(8 * $i + 7))
+ ->method('getAbsoluteURL')
+ ->with('imagePath4')
+ ->willReturn('AbsURL4');
+ }
+ }
}
diff --git a/apps/dav/tests/unit/CalDAV/Reminder/NotificationProvider/PushProviderTest.php b/apps/dav/tests/unit/CalDAV/Reminder/NotificationProvider/PushProviderTest.php
index e10afb44d2..bbf71837b0 100644
--- a/apps/dav/tests/unit/CalDAV/Reminder/NotificationProvider/PushProviderTest.php
+++ b/apps/dav/tests/unit/CalDAV/Reminder/NotificationProvider/PushProviderTest.php
@@ -1,8 +1,11 @@
+ * @author Georg Ehrke
*
* @license AGPL-3.0
*
@@ -23,7 +26,6 @@ namespace OCA\DAV\Tests\unit\CalDAV\Reminder\NotificationProvider;
use OCA\DAV\AppInfo\Application;
use OCA\DAV\CalDAV\Reminder\NotificationProvider\PushProvider;
-use OCA\DAV\CalDAV\Reminder\AbstractNotificationProvider;
use OCP\IConfig;
use OCP\IL10N;
use OCP\ILogger;
@@ -34,7 +36,6 @@ use OCP\Notification\IManager;
use OCP\Notification\INotification;
use OCP\AppFramework\Utility\ITimeFactory;
use Test\TestCase;
-use OCA\DAV\Tests\unit\CalDAV\Reminder\AbstractNotificationProviderTest;
class PushProviderTest extends AbstractNotificationProviderTest {
@@ -75,65 +76,111 @@ class PushProviderTest extends AbstractNotificationProviderTest {
);
}
- public function testSend(): void
- {
- $notification = $this->createMock(INotification::class);
- $notification
- ->expects($this->once())
- ->method('setApp')
- ->with(Application::APP_ID)
- ->willReturn($notification);
+ public function testNotificationType():void {
+ $this->assertEquals(PushProvider::NOTIFICATION_TYPE, 'DISPLAY');
+ }
- $notification
- ->expects($this->once())
- ->method('setUser')
- ->willReturn($notification)
- ;
+ public function testSend(): void {
+ $user1 = $this->createMock(IUser::class);
+ $user1->method('getUID')
+ ->willReturn('uid1');
+ $user2 = $this->createMock(IUser::class);
+ $user2->method('getUID')
+ ->willReturn('uid2');
+ $user3 = $this->createMock(IUser::class);
+ $user3->method('getUID')
+ ->willReturn('uid3');
- $notification
- ->expects($this->once())
- ->method('setDateTime')
- ->willReturn($notification)
- ;
+ $users = [$user1, $user2, $user3];
- $notification
- ->expects($this->once())
- ->method('setObject')
- ->willReturn($notification)
- ;
-
- $notification
- ->expects($this->once())
- ->method('setSubject')
- ->willReturn($notification)
- ;
-
- $notification
- ->expects($this->once())
- ->method('setMessage')
- ->willReturn($notification)
- ;
-
- $this->manager
- ->expects($this->once())
- ->method('createNotification')
- ->willReturn($notification);
-
- $this->manager
- ->expects($this->once())
- ->method('notify')
- ->with($notification);
-
- $l10n = $this->createMock(IL10N::class);
- $this->l10nFactory
- ->method('get')
- ->willReturn($l10n);
-
- $this->timeFactory->expects($this->once())
- ->method('getDateTime')
+ $dateTime = new \DateTime('@946684800');
+ $this->timeFactory->method('getDateTime')
->with()
- ->willReturn(new \DateTime());
+ ->willReturn($dateTime);
- $this->provider->send($this->vcalendar, $this->calendarDisplayName, $this->user);
+ $notification1 = $this->createNotificationMock('uid1', $dateTime);
+ $notification2 = $this->createNotificationMock('uid2', $dateTime);
+ $notification3 = $this->createNotificationMock('uid3', $dateTime);
+
+ $this->manager->expects($this->at(0))
+ ->method('createNotification')
+ ->with()
+ ->willReturn($notification1);
+ $this->manager->expects($this->at(2))
+ ->method('createNotification')
+ ->with()
+ ->willReturn($notification2);
+ $this->manager->expects($this->at(4))
+ ->method('createNotification')
+ ->with()
+ ->willReturn($notification3);
+
+ $this->manager->expects($this->at(1))
+ ->method('notify')
+ ->with($notification1);
+ $this->manager->expects($this->at(3))
+ ->method('notify')
+ ->with($notification2);
+ $this->manager->expects($this->at(5))
+ ->method('notify')
+ ->with($notification3);
+
+ $this->provider->send($this->vcalendar->VEVENT, $this->calendarDisplayName, $users);
}
+
+ /**
+ * @param string $uid
+ * @param \DateTime $dt
+ */
+ private function createNotificationMock(string $uid, \DateTime $dt):INotification {
+ $notification = $this->createMock(INotification::class);
+ $notification
+ ->expects($this->once())
+ ->method('setApp')
+ ->with('dav')
+ ->willReturn($notification);
+
+ $notification->expects($this->once())
+ ->method('setUser')
+ ->with($uid)
+ ->willReturn($notification);
+
+ $notification->expects($this->once())
+ ->method('setDateTime')
+ ->with($dt)
+ ->willReturn($notification);
+
+ $notification->expects($this->once())
+ ->method('setObject')
+ ->with('dav', 'uid1234')
+ ->willReturn($notification);
+
+ $notification->expects($this->once())
+ ->method('setSubject')
+ ->with('calendar_reminder', [
+ 'title' => 'Fellowship meeting',
+ 'start_atom' => '2017-01-01T00:00:00+00:00',
+ ])
+ ->willReturn($notification);
+
+ $notification
+ ->expects($this->once())
+ ->method('setMessage')
+ ->with('calendar_reminder', [
+ 'title' => 'Fellowship meeting',
+ 'start_atom' => '2017-01-01T00:00:00+00:00',
+ 'description' => null,
+ 'location' => null,
+ 'all_day' => false,
+ 'start_is_floating' => false,
+ 'start_timezone' => 'UTC',
+ 'end_atom' => '2017-01-01T00:00:00+00:00',
+ 'end_is_floating' => false,
+ 'end_timezone' => 'UTC',
+ 'calendar_displayname' => 'Personal',
+ ])
+ ->willReturn($notification);
+
+ return $notification;
+ }
}
diff --git a/apps/dav/tests/unit/CalDAV/Reminder/NotificationProviderManagerTest.php b/apps/dav/tests/unit/CalDAV/Reminder/NotificationProviderManagerTest.php
index d962b631c1..b4c62eacd7 100644
--- a/apps/dav/tests/unit/CalDAV/Reminder/NotificationProviderManagerTest.php
+++ b/apps/dav/tests/unit/CalDAV/Reminder/NotificationProviderManagerTest.php
@@ -1,8 +1,11 @@
+ * @author Georg Ehrke
*
* @license AGPL-3.0
*
@@ -31,8 +34,8 @@ use Test\TestCase;
class NotificationProviderManagerTest extends TestCase {
- /** @var NotificationProviderManager|\PHPUnit\Framework\MockObject\MockObject */
- private $providerManager;
+ /** @var NotificationProviderManager|\PHPUnit\Framework\MockObject\MockObject */
+ private $providerManager;
/**
* @throws \OCP\AppFramework\QueryException
@@ -40,9 +43,9 @@ class NotificationProviderManagerTest extends TestCase {
public function setUp() {
parent::setUp();
- $this->providerManager = new NotificationProviderManager();
- $this->providerManager->registerProvider(EmailProvider::class);
- }
+ $this->providerManager = new NotificationProviderManager();
+ $this->providerManager->registerProvider(EmailProvider::class);
+ }
/**
* @expectedException OCA\DAV\CalDAV\Reminder\NotificationTypeDoesNotExistException
@@ -50,10 +53,9 @@ class NotificationProviderManagerTest extends TestCase {
* @throws ProviderNotAvailableException
* @throws NotificationTypeDoesNotExistException
*/
- public function testGetProviderForUnknownType(): void
- {
- $this->providerManager->getProvider('NOT EXISTENT');
- }
+ public function testGetProviderForUnknownType(): void{
+ $this->providerManager->getProvider('NOT EXISTENT');
+ }
/**
* @expectedException OCA\DAV\CalDAV\Reminder\NotificationProvider\ProviderNotAvailableException
@@ -61,28 +63,16 @@ class NotificationProviderManagerTest extends TestCase {
* @throws NotificationTypeDoesNotExistException
* @throws ProviderNotAvailableException
*/
- public function testGetProviderForUnRegisteredType(): void
- {
- $this->providerManager->getProvider('AUDIO');
- }
+ public function testGetProviderForUnRegisteredType(): void{
+ $this->providerManager->getProvider('AUDIO');
+ }
- /**
- * @throws NotificationTypeDoesNotExistException
- * @throws ProviderNotAvailableException
- */
- public function testGetProvider(): void
- {
- $provider = $this->providerManager->getProvider('EMAIL');
- $this->assertInstanceOf(EmailProvider::class, $provider);
- }
+ public function testGetProvider(): void{
+ $provider = $this->providerManager->getProvider('EMAIL');
+ $this->assertInstanceOf(EmailProvider::class, $provider);
+ }
- /**
- * @throws NotificationTypeDoesNotExistException
- * @throws ProviderNotAvailableException
- * @throws \OCP\AppFramework\QueryException
- */
- public function testRegisterProvider(): void
- {
+ public function testRegisterProvider(): void{
$this->providerManager->registerProvider(PushProvider::class);
$provider = $this->providerManager->getProvider('DISPLAY');
$this->assertInstanceOf(PushProvider::class, $provider);
@@ -93,8 +83,12 @@ class NotificationProviderManagerTest extends TestCase {
* @expectedException \InvalidArgumentException
* @throws \OCP\AppFramework\QueryException
*/
- public function testRegisterBadProvider(): void
- {
+ public function testRegisterBadProvider(): void{
$this->providerManager->registerProvider(Capabilities::class);
}
+
+ public function testHasProvider(): void {
+ $this->assertTrue($this->providerManager->hasProvider('EMAIL'));
+ $this->assertFalse($this->providerManager->hasProvider('EMAIL123'));
+ }
}
diff --git a/apps/dav/tests/unit/CalDAV/Reminder/NotifierTest.php b/apps/dav/tests/unit/CalDAV/Reminder/NotifierTest.php
index b9695b33fd..8d38617ad2 100644
--- a/apps/dav/tests/unit/CalDAV/Reminder/NotifierTest.php
+++ b/apps/dav/tests/unit/CalDAV/Reminder/NotifierTest.php
@@ -1,8 +1,11 @@
+ * @copyright Copyright (c) 2019, Thomas Citharel
+ * @copyright Copyright (c) 2019, Georg Ehrke
*
* @author Thomas Citharel
+ * @author Georg Ehrke
*
* @license AGPL-3.0
*
@@ -24,6 +27,7 @@ namespace OCA\DAV\Tests\unit\CalDAV\Reminder;
use OCA\DAV\AppInfo\Application;
use OCA\DAV\CalDAV\Reminder\Notifier;
+use OCP\AppFramework\Utility\ITimeFactory;
use OCP\IL10N;
use OCP\IURLGenerator;
use OCP\L10N\IFactory;
@@ -36,22 +40,33 @@ class NotifierTest extends TestCase {
/** @var IFactory|\PHPUnit\Framework\MockObject\MockObject */
protected $factory;
+
/** @var IURLGenerator|\PHPUnit\Framework\MockObject\MockObject */
protected $urlGenerator;
+
/** @var IL10N|\PHPUnit\Framework\MockObject\MockObject */
- protected $l;
+ protected $l10n;
+
+ /** @var ITimeFactory|\PHPUnit\Framework\MockObject\MockObject */
+ protected $timeFactory;
protected function setUp() {
parent::setUp();
$this->urlGenerator = $this->createMock(IURLGenerator::class);
- $this->l = $this->createMock(IL10N::class);
- $this->l->expects($this->any())
+ $this->l10n = $this->createMock(IL10N::class);
+ $this->l10n->expects($this->any())
->method('t')
->willReturnCallback(function($string, $args) {
return vsprintf($string, $args);
});
- $this->l->expects($this->any())
+ $this->l10n->expects($this->any())
+ ->method('l')
+ ->willReturnCallback(function($string, $args) {
+ /** \DateTime $args */
+ return $args->format(\DateTime::ATOM);
+ });
+ $this->l10n->expects($this->any())
->method('n')
->willReturnCallback(function($textSingular, $textPlural, $count, $args) {
$text = $count === 1 ? $textSingular : $textPlural;
@@ -61,14 +76,28 @@ class NotifierTest extends TestCase {
$this->factory = $this->createMock(IFactory::class);
$this->factory->expects($this->any())
->method('get')
- ->willReturn($this->l);
+ ->willReturn($this->l10n);
+
+ $this->timeFactory = $this->createMock(ITimeFactory::class);
+ $this->timeFactory
+ ->method('getDateTime')
+ ->willReturn(\DateTime::createFromFormat(\DateTime::ATOM, '2005-08-15T14:00:00+02:00'));
$this->notifier = new Notifier(
$this->factory,
- $this->urlGenerator
+ $this->urlGenerator,
+ $this->timeFactory
);
}
+ public function testGetId():void {
+ $this->assertEquals($this->notifier->getID(), 'dav');
+ }
+
+ public function testGetName():void {
+ $this->assertEquals($this->notifier->getName(), 'Calendar');
+ }
+
/**
* @expectedException \InvalidArgumentException
* @expectedExceptionMessage Notification not from this app
@@ -111,17 +140,24 @@ class NotifierTest extends TestCase {
[
'calendar_reminder',
[
- 'title' => 'foo',
- 'start' => time() - 60 * 60 * 24
+ 'title' => 'Title of this event',
+ 'start_atom' => '2005-08-15T15:52:01+02:00'
],
- 'foo (one day ago)',
+ 'Title of this event (in 1 hour, 52 minutes)',
[
- 'when' => 'foo',
- 'description' => 'bar',
+ 'title' => 'Title of this event',
+ 'description' => null,
'location' => 'NC Headquarters',
- 'calendar' => 'Personal'
+ 'all_day' => false,
+ 'start_atom' => '2005-08-15T15:52:01+02:00',
+ 'start_is_floating' => false,
+ 'start_timezone' => 'Europe/Berlin',
+ 'end_atom' => '2005-08-15T17:52:01+02:00',
+ 'end_is_floating' => false,
+ 'end_timezone' => 'Europe/Berlin',
+ 'calendar_displayname' => 'Personal',
],
- 'Calendar: Personal Date: foo Description: bar Where: NC Headquarters'
+ "Calendar: Personal\r\nDate: 2005-08-15T15:52:01+02:00, 2005-08-15T15:52:01+02:00 - 2005-08-15T17:52:01+02:00 (Europe/Berlin)\r\nWhere: NC Headquarters"
],
];
}
diff --git a/apps/dav/tests/unit/CalDAV/Reminder/ReminderServiceTest.php b/apps/dav/tests/unit/CalDAV/Reminder/ReminderServiceTest.php
index 061c3f16e3..a9acca66a8 100644
--- a/apps/dav/tests/unit/CalDAV/Reminder/ReminderServiceTest.php
+++ b/apps/dav/tests/unit/CalDAV/Reminder/ReminderServiceTest.php
@@ -1,4 +1,5 @@
notificationProviderManager = $this->createMock(NotificationProviderManager::class);
$this->userManager = $this->createMock(IUserManager::class);
$this->groupManager = $this->createMock(IGroupManager::class);
- $this->userSession = $this->createMock(IUserSession::class);
+ $this->caldavBackend = $this->createMock(CalDavBackend::class);
+ $this->timeFactory = $this->createMock(ITimeFactory::class);
+
+ $this->caldavBackend->method('getShares')->willReturn([]);
+
+ $this->reminderService = new ReminderService($this->backend,
+ $this->notificationProviderManager,
+ $this->userManager,
+ $this->groupManager,
+ $this->caldavBackend,
+ $this->timeFactory);
}
- public function dataTestProcessReminders(): array
- {
- return [
- [
- [], null
- ],
- [
- [
- [
- 'calendardata' => self::CALENDAR_DATA,
- 'displayname' => 'Personal',
- 'type' => 'EMAIL',
- 'uid' => 1,
- 'id' => 1,
- ],
- ],
- $this->createMock(EmailProvider::class),
- ],
- [
- [
- [
- 'calendardata' => self::CALENDAR_DATA,
- 'displayname' => 'Personal',
- 'type' => 'DISPLAY',
- 'uid' => 1,
- 'id' => 1,
- ],
- ],
- $this->createMock(PushProvider::class),
- ]
- ];
- }
+ public function testOnCalendarObjectDelete():void {
+ $this->backend->expects($this->once())
+ ->method('cleanRemindersForEvent')
+ ->with(44);
- /**
- * @dataProvider dataTestProcessReminders
- * @param array $reminders
- * @param AbstractNotificationProvider|null $notificationProvider
- * @throws \OCA\DAV\CalDAV\Reminder\NotificationProvider\ProviderNotAvailableException
- * @throws \OCA\DAV\CalDAV\Reminder\NotificationTypeDoesNotExistException
- * @throws \OC\User\NoUserException
- */
- public function testProcessReminders(array $reminders, ?AbstractNotificationProvider $notificationProvider): void
- {
- $user = $this->createMock(IUser::class);
-
- $this->backend->expects($this->once())->method('getRemindersToProcess')->willReturn($reminders);
- if (count($reminders) > 0) {
- $this->userManager->expects($this->exactly(count($reminders)))->method('get')->willReturn($user);
- $this->backend->expects($this->exactly(count($reminders)))->method('removeReminder');
- $this->notificationProviderManager->expects($this->exactly(count($reminders)))->method('getProvider')->willReturn($notificationProvider);
- }
-
- $reminderService = new ReminderService($this->backend, $this->notificationProviderManager, $this->userManager, $this->groupManager, $this->userSession);
- $reminderService->processReminders();
- }
-
- /**
- * @expectedException OC\User\NoUserException
- */
- public function testProcessReminderWithBadUser(): void
- {
- $this->backend->expects($this->once())->method('getRemindersToProcess')->willReturn([
- [
- 'calendardata' => self::CALENDAR_DATA,
- 'type' => 'DISPLAY',
- 'uid' => 1,
- 'id' => 1,
- ]
- ]);
- $this->userManager->expects($this->once())->method('get')->with(1)->willReturn(null);
- $reminderService = new ReminderService($this->backend, $this->notificationProviderManager, $this->userManager, $this->groupManager, $this->userSession);
- $reminderService->processReminders();
- }
-
- public function providesTouchCalendarObject(): array
- {
- return [
- [
- '\OCA\DAV\CalDAV\CalDavBackend::deleteCalendarObject',
- [
- 'principaluri' => 'principals/users/personal'
- ],
- [],
- [
- 'calendarid' => 1,
- 'uri' => 'something.ics',
- ],
- 0
- ],
- [
- '\OCA\DAV\CalDAV\CalDavBackend::createCalendarObject',
- [
- 'principaluri' => 'principals/users/personal'
- ],
- [],
- [
- 'calendarid' => 1,
- 'uri' => 'something.ics',
- 'calendardata' => self::CALENDAR_DATA
- ],
- 0
- ],
- [
- '\OCA\DAV\CalDAV\CalDavBackend::updateCalendarObject',
- [
- 'principaluri' => 'principals/users/someone',
- 'uri' => 'personal'
- ],
- [
- [
- '{http://owncloud.org/ns}principal' => 'principals/users/someone'
- ]
- ],
- [
- 'calendarid' => 1,
- 'uri' => 'something.ics',
- 'calendardata' => self::CALENDAR_DATA
- ],
- 0
- ],
- [
- '\OCA\DAV\CalDAV\CalDavBackend::createCalendarObject',
- [
- 'principaluri' => 'principals/users/someone',
- 'uri' => 'personal'
- ],
- [
- [
- '{http://owncloud.org/ns}principal' => 'principals/groups/somegroup'
- ]
- ],
- [
- 'calendarid' => 1,
- 'uri' => 'something.ics',
- 'calendardata' => self::CALENDAR_DATA
- ],
- 1
- ]
+ $action = '\OCA\DAV\CalDAV\CalDavBackend::deleteCalendarObject';
+ $objectData = [
+ 'id' => '44',
+ 'component' => 'vevent',
];
+
+ $this->reminderService->onTouchCalendarObject($action, $objectData);
}
- /**
- * @dataProvider providesTouchCalendarObject
- * @param string $action
- * @param array $calendarData
- * @param array $shares
- * @param array $objectData
- * @param int $numberOfGroups
- * @throws \OC\User\NoUserException
- * @throws \Sabre\VObject\InvalidDataException
- */
- public function testOnTouchCalendarObject(string $action, array $calendarData, array $shares, array $objectData, int $numberOfGroups): void
- {
- $this->backend->expects($this->once())->method('cleanRemindersForEvent')->with($objectData['calendarid'], $objectData['uri']);
+ public function testOnCalendarObjectCreateSingleEntry():void {
+ $action = '\OCA\DAV\CalDAV\CalDavBackend::createCalendarObject';
+ $objectData = [
+ 'calendardata' => self::CALENDAR_DATA,
+ 'id' => '42',
+ 'calendarid' => '1337',
+ 'component' => 'vevent',
+ ];
- if ($action !== '\OCA\DAV\CalDAV\CalDavBackend::deleteCalendarObject') {
- $user = $this->createMock(IUser::class);
- $user->expects($this->once())->method('getUID')->willReturn('user');
+ $this->backend->expects($this->exactly(2))
+ ->method('insertReminder')
+ ->withConsecutive(
+ [1337, 42, 'wej2z68l9h', false, 1465430400, false, '5c70531aab15c92b52518ae10a2f78a4', 'de919af7429d3b5c11e8b9d289b411a6', 'EMAIL', true, 1465429500, false],
+ [1337, 42, 'wej2z68l9h', false, 1465430400, false, '5c70531aab15c92b52518ae10a2f78a4', '35b3eae8e792aa2209f0b4e1a302f105', 'DISPLAY', false, 1465344000, false]
+ )
+ ->willReturn(1);
- $this->userSession->expects($this->once())->method('getUser')->willReturn($user);
- if ($numberOfGroups === 0) {
- $this->backend->expects($this->exactly(count($shares) + 1))->method('insertReminder');
- } else {
- $group = $this->createMock(IGroup::class);
- $groupUser = $this->createMock(IUser::class);
- $groupUser->expects($this->once())->method('getUID')->willReturn('groupuser');
- $group->expects($this->once())->method('getUsers')->willReturn([$groupUser]);
- $this->groupManager->expects($this->exactly($numberOfGroups))->method('get')->willReturn($group);
- }
- }
- $reminderService = new ReminderService($this->backend, $this->notificationProviderManager, $this->userManager, $this->groupManager, $this->userSession);
- $reminderService->onTouchCalendarObject($action, $calendarData, $shares, $objectData);
+ $this->timeFactory->expects($this->once())
+ ->method('getDateTime')
+ ->with()
+ ->willReturn(\DateTime::createFromFormat(\DateTime::ATOM, '2016-06-08T00:00:00+00:00'));
+
+ $this->reminderService->onTouchCalendarObject($action, $objectData);
}
- /**
- * @expectedException OC\User\NoUserException
- */
- public function testOnTouchCalendarObjectWithNoSession(): void
- {
- $this->backend->expects($this->once())->method('cleanRemindersForEvent');
- $this->userSession->expects($this->once())->method('getUser')->willReturn(null);
+ public function testOnCalendarObjectCreateSingleEntryWithRepeat(): void {
+ $action = '\OCA\DAV\CalDAV\CalDavBackend::createCalendarObject';
+ $objectData = [
+ 'calendardata' => self::CALENDAR_DATA_REPEAT,
+ 'id' => '42',
+ 'calendarid' => '1337',
+ 'component' => 'vevent',
+ ];
- $reminderService = new ReminderService($this->backend, $this->notificationProviderManager, $this->userManager, $this->groupManager, $this->userSession);
- $reminderService->onTouchCalendarObject('', ['principaluri' => 'foo'], [], ['calendarid' => 1, 'uri' => 'bar']);
+ $this->backend->expects($this->exactly(5))
+ ->method('insertReminder')
+ ->withConsecutive(
+ [1337, 42, 'wej2z68l9h', false, 1465430400, false, '5c70531aab15c92b52518ae10a2f78a4', 'ecacbf07d413c3c78d1ac7ad8c469602', 'EMAIL', true, 1465429500, false],
+ [1337, 42, 'wej2z68l9h', false, 1465430400, false, '5c70531aab15c92b52518ae10a2f78a4', 'ecacbf07d413c3c78d1ac7ad8c469602', 'EMAIL', true, 1465429620, true],
+ [1337, 42, 'wej2z68l9h', false, 1465430400, false, '5c70531aab15c92b52518ae10a2f78a4', 'ecacbf07d413c3c78d1ac7ad8c469602', 'EMAIL', true, 1465429740, true],
+ [1337, 42, 'wej2z68l9h', false, 1465430400, false, '5c70531aab15c92b52518ae10a2f78a4', 'ecacbf07d413c3c78d1ac7ad8c469602', 'EMAIL', true, 1465429860, true],
+ [1337, 42, 'wej2z68l9h', false, 1465430400, false, '5c70531aab15c92b52518ae10a2f78a4', 'ecacbf07d413c3c78d1ac7ad8c469602', 'EMAIL', true, 1465429980, true]
+ )
+ ->willReturn(1);
+
+ $this->timeFactory->expects($this->once())
+ ->method('getDateTime')
+ ->with()
+ ->willReturn(\DateTime::createFromFormat(\DateTime::ATOM, '2016-06-08T00:00:00+00:00'));
+
+ $this->reminderService->onTouchCalendarObject($action, $objectData);
}
- public function testOnTouchCalendarObjectWithNoCalendarURI(): void
- {
- $reminderService = new ReminderService($this->backend, $this->notificationProviderManager, $this->userManager, $this->groupManager, $this->userSession);
- $this->assertNull($reminderService->onTouchCalendarObject('', [], [], []));
+ public function testOnCalendarObjectCreateRecurringEntry(): void {
+ $action = '\OCA\DAV\CalDAV\CalDavBackend::createCalendarObject';
+ $objectData = [
+ 'calendardata' => self::CALENDAR_DATA_RECURRING,
+ 'id' => '42',
+ 'calendarid' => '1337',
+ 'component' => 'vevent',
+ ];
+
+ $this->backend->expects($this->exactly(2))
+ ->method('insertReminder')
+ ->withConsecutive(
+ [1337, 42, 'wej2z68l9h', true, 1467244800, false, 'fbdb2726bc0f7dfacac1d881c1453e20', 'de919af7429d3b5c11e8b9d289b411a6', 'EMAIL', true, 1467243900, false],
+ [1337, 42, 'wej2z68l9h', true, 1467849600, false, 'fbdb2726bc0f7dfacac1d881c1453e20', '8996992118817f9f311ac5cc56d1cc97', 'EMAIL', true, 1467158400, false]
+ )
+ ->willReturn(1);
+
+ $this->timeFactory->expects($this->once())
+ ->method('getDateTime')
+ ->with()
+ ->willReturn(\DateTime::createFromFormat(\DateTime::ATOM, '2016-06-29T00:00:00+00:00'));
+
+ $this->reminderService->onTouchCalendarObject($action, $objectData);
+ }
+
+ public function testOnCalendarObjectCreateRecurringEntryWithRepeat():void {
+ $action = '\OCA\DAV\CalDAV\CalDavBackend::createCalendarObject';
+ $objectData = [
+ 'calendardata' => self::CALENDAR_DATA_RECURRING_REPEAT,
+ 'id' => '42',
+ 'calendarid' => '1337',
+ 'component' => 'vevent',
+ ];
+
+ $this->backend->expects($this->exactly(6))
+ ->method('insertReminder')
+ ->withConsecutive(
+ [1337, 42, 'wej2z68l9h', true, 1467244800, false, 'fbdb2726bc0f7dfacac1d881c1453e20', 'ecacbf07d413c3c78d1ac7ad8c469602', 'EMAIL', true, 1467243900, false],
+ [1337, 42, 'wej2z68l9h', true, 1467244800, false, 'fbdb2726bc0f7dfacac1d881c1453e20', 'ecacbf07d413c3c78d1ac7ad8c469602', 'EMAIL', true, 1467244020, true],
+ [1337, 42, 'wej2z68l9h', true, 1467244800, false, 'fbdb2726bc0f7dfacac1d881c1453e20', 'ecacbf07d413c3c78d1ac7ad8c469602', 'EMAIL', true, 1467244140, true],
+ [1337, 42, 'wej2z68l9h', true, 1467244800, false, 'fbdb2726bc0f7dfacac1d881c1453e20', 'ecacbf07d413c3c78d1ac7ad8c469602', 'EMAIL', true, 1467244260, true],
+ [1337, 42, 'wej2z68l9h', true, 1467244800, false, 'fbdb2726bc0f7dfacac1d881c1453e20', 'ecacbf07d413c3c78d1ac7ad8c469602', 'EMAIL', true, 1467244380, true],
+ [1337, 42, 'wej2z68l9h', true, 1467849600, false, 'fbdb2726bc0f7dfacac1d881c1453e20', '8996992118817f9f311ac5cc56d1cc97', 'EMAIL', true, 1467158400, false]
+ )
+ ->willReturn(1);
+
+ $this->timeFactory->expects($this->once())
+ ->method('getDateTime')
+ ->with()
+ ->willReturn(\DateTime::createFromFormat(\DateTime::ATOM, '2016-06-29T00:00:00+00:00'));
+
+ $this->reminderService->onTouchCalendarObject($action, $objectData);
+ }
+
+ public function testProcessReminders():void {
+ $this->backend->expects($this->at(0))
+ ->method('getRemindersToProcess')
+ ->with()
+ ->willReturn([
+ [
+ 'id' => 1,
+ 'calendar_id' => 1337,
+ 'object_id' => 42,
+ 'uid' => 'wej2z68l9h',
+ 'is_recurring' => false,
+ 'recurrence_id' => 1465430400,
+ 'is_recurrence_exception' => false,
+ 'event_hash' => '5c70531aab15c92b52518ae10a2f78a4',
+ 'alarm_hash' => 'de919af7429d3b5c11e8b9d289b411a6',
+ 'type' => 'EMAIL',
+ 'is_relative' => true,
+ 'notification_date' => 1465429500,
+ 'is_repeat_based' => false,
+ 'calendardata' => self::CALENDAR_DATA,
+ 'displayname' => 'Displayname 123',
+ 'principaluri' => 'principals/users/user001',
+ ],
+ [
+ 'id' => 2,
+ 'calendar_id' => 1337,
+ 'object_id' => 42,
+ 'uid' => 'wej2z68l9h',
+ 'is_recurring' => false,
+ 'recurrence_id' => 1465430400,
+ 'is_recurrence_exception' => false,
+ 'event_hash' => '5c70531aab15c92b52518ae10a2f78a4',
+ 'alarm_hash' => 'ecacbf07d413c3c78d1ac7ad8c469602',
+ 'type' => 'EMAIL',
+ 'is_relative' => true,
+ 'notification_date' => 1465429740,
+ 'is_repeat_based' => true,
+ 'calendardata' => self::CALENDAR_DATA_REPEAT,
+ 'displayname' => 'Displayname 123',
+ 'principaluri' => 'principals/users/user001',
+ ],
+ [
+ 'id' => 3,
+ 'calendar_id' => 1337,
+ 'object_id' => 42,
+ 'uid' => 'wej2z68l9h',
+ 'is_recurring' => false,
+ 'recurrence_id' => 1465430400,
+ 'is_recurrence_exception' => false,
+ 'event_hash' => '5c70531aab15c92b52518ae10a2f78a4',
+ 'alarm_hash' => '35b3eae8e792aa2209f0b4e1a302f105',
+ 'type' => 'DISPLAY',
+ 'is_relative' => false,
+ 'notification_date' => 1465344000,
+ 'is_repeat_based' => false,
+ 'calendardata' => self::CALENDAR_DATA,
+ 'displayname' => 'Displayname 123',
+ 'principaluri' => 'principals/users/user001',
+ ],
+ [
+ 'id' => 4,
+ 'calendar_id' => 1337,
+ 'object_id' => 42,
+ 'uid' => 'wej2z68l9h',
+ 'is_recurring' => true,
+ 'recurrence_id' => 1467244800,
+ 'is_recurrence_exception' => false,
+ 'event_hash' => 'fbdb2726bc0f7dfacac1d881c1453e20',
+ 'alarm_hash' => 'ecacbf07d413c3c78d1ac7ad8c469602',
+ 'type' => 'EMAIL',
+ 'is_relative' => true,
+ 'notification_date' => 1467243900,
+ 'is_repeat_based' => false,
+ 'calendardata' => self::CALENDAR_DATA_RECURRING_REPEAT,
+ 'displayname' => 'Displayname 123',
+ 'principaluri' => 'principals/users/user001',
+ ],
+ [
+ 'id' => 5,
+ 'calendar_id' => 1337,
+ 'object_id' => 42,
+ 'uid' => 'wej2z68l9h',
+ 'is_recurring' => true,
+ 'recurrence_id' => 1467849600,
+ 'is_recurrence_exception' => false,
+ 'event_hash' => 'fbdb2726bc0f7dfacac1d881c1453e20',
+ 'alarm_hash' => '8996992118817f9f311ac5cc56d1cc97',
+ 'type' => 'EMAIL',
+ 'is_relative' => true,
+ 'notification_date' => 1467158400,
+ 'is_repeat_based' => false,
+ 'calendardata' => self::CALENDAR_DATA_RECURRING,
+ 'displayname' => 'Displayname 123',
+ 'principaluri' => 'principals/users/user001',
+ ]
+ ]);
+
+ $this->notificationProviderManager->expects($this->at(0))
+ ->method('hasProvider')
+ ->with('EMAIL')
+ ->willReturn(true);
+
+ $provider1 = $this->createMock(INotificationProvider::class);
+ $this->notificationProviderManager->expects($this->at(1))
+ ->method('getProvider')
+ ->with('EMAIL')
+ ->willReturn($provider1);
+
+ $this->notificationProviderManager->expects($this->at(2))
+ ->method('hasProvider')
+ ->with('EMAIL')
+ ->willReturn(true);
+
+ $provider2 = $this->createMock(INotificationProvider::class);
+ $this->notificationProviderManager->expects($this->at(3))
+ ->method('getProvider')
+ ->with('EMAIL')
+ ->willReturn($provider2);
+
+ $this->notificationProviderManager->expects($this->at(4))
+ ->method('hasProvider')
+ ->with('DISPLAY')
+ ->willReturn(true);
+
+ $provider3 = $this->createMock(INotificationProvider::class);
+ $this->notificationProviderManager->expects($this->at(5))
+ ->method('getProvider')
+ ->with('DISPLAY')
+ ->willReturn($provider3);
+
+ $this->notificationProviderManager->expects($this->at(6))
+ ->method('hasProvider')
+ ->with('EMAIL')
+ ->willReturn(true);
+
+ $provider4 = $this->createMock(INotificationProvider::class);
+ $this->notificationProviderManager->expects($this->at(7))
+ ->method('getProvider')
+ ->with('EMAIL')
+ ->willReturn($provider4);
+
+ $this->notificationProviderManager->expects($this->at(8))
+ ->method('hasProvider')
+ ->with('EMAIL')
+ ->willReturn(true);
+
+ $provider5 = $this->createMock(INotificationProvider::class);
+ $this->notificationProviderManager->expects($this->at(9))
+ ->method('getProvider')
+ ->with('EMAIL')
+ ->willReturn($provider5);
+
+ $user = $this->createMock(IUser::class);
+ $this->userManager->expects($this->exactly(5))
+ ->method('get')
+ ->with('user001')
+ ->willReturn($user);
+
+ $provider1->expects($this->once())
+ ->method('send')
+ ->with($this->callback(function($vevent) {
+ if ($vevent->DTSTART->getDateTime()->format(\DateTime::ATOM) !== '2016-06-09T00:00:00+00:00') {
+ return false;
+ }
+ return true;
+ }, 'Displayname 123', $user));
+ $provider2->expects($this->once())
+ ->method('send')
+ ->with($this->callback(function($vevent) {
+ if ($vevent->DTSTART->getDateTime()->format(\DateTime::ATOM) !== '2016-06-09T00:00:00+00:00') {
+ return false;
+ }
+ return true;
+ }, 'Displayname 123', $user));
+ $provider3->expects($this->once())
+ ->method('send')
+ ->with($this->callback(function($vevent) {
+ if ($vevent->DTSTART->getDateTime()->format(\DateTime::ATOM) !== '2016-06-09T00:00:00+00:00') {
+ return false;
+ }
+ return true;
+ }, 'Displayname 123', $user));
+ $provider4->expects($this->once())
+ ->method('send')
+ ->with($this->callback(function($vevent) {
+ if ($vevent->DTSTART->getDateTime()->format(\DateTime::ATOM) !== '2016-06-30T00:00:00+00:00') {
+ return false;
+ }
+ return true;
+ }, 'Displayname 123', $user));
+ $provider5->expects($this->once())
+ ->method('send')
+ ->with($this->callback(function($vevent) {
+ if ($vevent->DTSTART->getDateTime()->format(\DateTime::ATOM) !== '2016-07-07T00:00:00+00:00') {
+ return false;
+ }
+ return true;
+ }, 'Displayname 123', $user));
+
+ $this->backend->expects($this->at(1))
+ ->method('removeReminder')
+ ->with(1);
+ $this->backend->expects($this->at(2))
+ ->method('removeReminder')
+ ->with(2);
+ $this->backend->expects($this->at(3))
+ ->method('removeReminder')
+ ->with(3);
+ $this->backend->expects($this->at(4))
+ ->method('removeReminder')
+ ->with(4);
+ $this->backend->expects($this->at(5))
+ ->method('insertReminder')
+ ->with(1337, 42, 'wej2z68l9h', true, 1467849600, false, 'fbdb2726bc0f7dfacac1d881c1453e20', 'ecacbf07d413c3c78d1ac7ad8c469602', 'EMAIL', true, 1467848700, false)
+ ->willReturn(99);
+
+ $this->backend->expects($this->at(6))
+ ->method('insertReminder')
+ ->with(1337, 42, 'wej2z68l9h', true, 1467849600, false, 'fbdb2726bc0f7dfacac1d881c1453e20', 'ecacbf07d413c3c78d1ac7ad8c469602', 'EMAIL', true, 1467848820, true)
+ ->willReturn(99);
+ $this->backend->expects($this->at(7))
+ ->method('insertReminder')
+ ->with(1337, 42, 'wej2z68l9h', true, 1467849600, false, 'fbdb2726bc0f7dfacac1d881c1453e20', 'ecacbf07d413c3c78d1ac7ad8c469602', 'EMAIL', true, 1467848940, true)
+ ->willReturn(99);
+ $this->backend->expects($this->at(8))
+ ->method('insertReminder')
+ ->with(1337, 42, 'wej2z68l9h', true, 1467849600, false, 'fbdb2726bc0f7dfacac1d881c1453e20', 'ecacbf07d413c3c78d1ac7ad8c469602', 'EMAIL', true, 1467849060, true)
+ ->willReturn(99);
+ $this->backend->expects($this->at(9))
+ ->method('insertReminder')
+ ->with(1337, 42, 'wej2z68l9h', true, 1467849600, false, 'fbdb2726bc0f7dfacac1d881c1453e20', 'ecacbf07d413c3c78d1ac7ad8c469602', 'EMAIL', true, 1467849180, true)
+ ->willReturn(99);
+ $this->backend->expects($this->at(10))
+ ->method('removeReminder')
+ ->with(5);
+ $this->backend->expects($this->at(11))
+ ->method('insertReminder')
+ ->with(1337, 42, 'wej2z68l9h', true, 1468454400, false, 'fbdb2726bc0f7dfacac1d881c1453e20', '8996992118817f9f311ac5cc56d1cc97', 'EMAIL', true, 1467763200, false)
+ ->willReturn(99);
+
+ $this->timeFactory->method('getDateTime')
+ ->willReturn(\DateTime::createFromFormat(\DateTime::ATOM, '2016-06-08T00:00:00+00:00'));
+
+ $this->reminderService->processReminders();
}
}