summaryrefslogtreecommitdiffstats
path: root/apps/dav
diff options
context:
space:
mode:
Diffstat (limited to 'apps/dav')
-rw-r--r--apps/dav/appinfo/app.php3
-rw-r--r--apps/dav/appinfo/info.xml1
-rw-r--r--apps/dav/composer/composer/autoload_classmap.php11
-rw-r--r--apps/dav/composer/composer/autoload_static.php11
-rw-r--r--apps/dav/js/settings-admin-caldav.js6
-rw-r--r--apps/dav/lib/AppInfo/Application.php58
-rw-r--r--apps/dav/lib/BackgroundJob/EventReminderJob.php59
-rw-r--r--apps/dav/lib/CalDAV/CalDavBackend.php1
-rw-r--r--apps/dav/lib/CalDAV/Reminder/AbstractNotificationProvider.php208
-rw-r--r--apps/dav/lib/CalDAV/Reminder/Backend.php139
-rw-r--r--apps/dav/lib/CalDAV/Reminder/NotificationProvider/EmailProvider.php157
-rw-r--r--apps/dav/lib/CalDAV/Reminder/NotificationProvider/ProviderNotAvailableException.php39
-rw-r--r--apps/dav/lib/CalDAV/Reminder/NotificationProvider/PushProvider.php101
-rw-r--r--apps/dav/lib/CalDAV/Reminder/NotificationProviderManager.php59
-rw-r--r--apps/dav/lib/CalDAV/Reminder/NotificationTypeDoesNotExistException.php39
-rw-r--r--apps/dav/lib/CalDAV/Reminder/Notifier.php143
-rw-r--r--apps/dav/lib/CalDAV/Reminder/ReminderService.php185
-rw-r--r--apps/dav/lib/Migration/Version1007Date20181005133326.php82
-rw-r--r--apps/dav/lib/Settings/CalDAVSettings.php1
-rw-r--r--apps/dav/templates/settings-admin-caldav.php21
-rw-r--r--apps/dav/tests/unit/BackgroundJob/EventReminderJobTest.php68
-rw-r--r--apps/dav/tests/unit/CalDAV/Reminder/AbstractNotificationProviderTest.php87
-rw-r--r--apps/dav/tests/unit/CalDAV/Reminder/BackendTest.php313
-rw-r--r--apps/dav/tests/unit/CalDAV/Reminder/NotificationProvider/EmailProviderTest.php226
-rw-r--r--apps/dav/tests/unit/CalDAV/Reminder/NotificationProvider/PushProviderTest.php139
-rw-r--r--apps/dav/tests/unit/CalDAV/Reminder/NotificationProviderManagerTest.php100
-rw-r--r--apps/dav/tests/unit/CalDAV/Reminder/NotifierTest.php184
-rw-r--r--apps/dav/tests/unit/CalDAV/Reminder/ReminderServiceTest.php276
28 files changed, 2711 insertions, 6 deletions
diff --git a/apps/dav/appinfo/app.php b/apps/dav/appinfo/app.php
index 70d01088be2..dd9e0e9c096 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 dc90ac58188..91617e90024 100644
--- a/apps/dav/appinfo/info.xml
+++ b/apps/dav/appinfo/info.xml
@@ -23,6 +23,7 @@
<job>OCA\DAV\BackgroundJob\CleanupDirectLinksJob</job>
<job>OCA\DAV\BackgroundJob\UpdateCalendarResourcesRoomsBackgroundJob</job>
<job>OCA\DAV\BackgroundJob\CleanupInvitationTokenJob</job>
+ <job>OCA\DAV\BackgroundJob\EventReminderJob</job>
</background-jobs>
<repair-steps>
diff --git a/apps/dav/composer/composer/autoload_classmap.php b/apps/dav/composer/composer/autoload_classmap.php
index 694231eebda..33977f31668 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 6f104916077..8ca83bb8b75 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 1a40c208dfe..ad30ba6ad37 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 41570ee7442..2e3b95d8bfa 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 00000000000..e0a147e4203
--- /dev/null
+++ b/apps/dav/lib/BackgroundJob/EventReminderJob.php
@@ -0,0 +1,59 @@
+<?php
+/**
+ * @author Thomas Citharel <tcit@tcit.fr>
+ *
+ * @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 <http://www.gnu.org/licenses/>
+ *
+ */
+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 91281dc0cb3..62d3909ce39 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 00000000000..ba928fac80f
--- /dev/null
+++ b/apps/dav/lib/CalDAV/Reminder/AbstractNotificationProvider.php
@@ -0,0 +1,208 @@
+<?php
+/**
+ * @copyright Copyright (c) 2018 Thomas Citharel <tcit@tcit.fr>
+ *
+ * @author Thomas Citharel <tcit@tcit.fr>
+ *
+ * @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 <http://www.gnu.org/licenses/>.
+ *
+ */
+
+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 00000000000..c85e7c365dc
--- /dev/null
+++ b/apps/dav/lib/CalDAV/Reminder/Backend.php
@@ -0,0 +1,139 @@
+<?php
+/**
+ * @copyright Copyright (c) 2019 Thomas Citharel <tcit@tcit.fr>
+ *
+ * @author Thomas Citharel <tcit@tcit.fr>
+ *
+ * @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 <http://www.gnu.org/licenses/>.
+ *
+ */
+
+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 00000000000..81d4474011c
--- /dev/null
+++ b/apps/dav/lib/CalDAV/Reminder/NotificationProvider/EmailProvider.php
@@ -0,0 +1,157 @@
+<?php
+/**
+ * @copyright Copyright (c) 2018 Thomas Citharel <tcit@tcit.fr>
+ *
+ * @author Thomas Citharel <tcit@tcit.fr>
+ *
+ * @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 <http://www.gnu.org/licenses/>.
+ *
+ */
+
+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 00000000000..bf736db8a34
--- /dev/null
+++ b/apps/dav/lib/CalDAV/Reminder/NotificationProvider/ProviderNotAvailableException.php
@@ -0,0 +1,39 @@
+<?php
+/**
+ * @copyright Copyright (c) 2018 Thomas Citharel <tcit@tcit.fr>
+ *
+ * @author Thomas Citharel <tcit@tcit.fr>
+ *
+ * @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 <http://www.gnu.org/licenses/>.
+ *
+ */
+
+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 00000000000..1bb0e5c68b1
--- /dev/null
+++ b/apps/dav/lib/CalDAV/Reminder/NotificationProvider/PushProvider.php
@@ -0,0 +1,101 @@
+<?php
+/**
+ * @copyright Copyright (c) 2018 Thomas Citharel <tcit@tcit.fr>
+ *
+ * @author Thomas Citharel <tcit@tcit.fr>
+ *
+ * @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 <http://www.gnu.org/licenses/>.
+ *
+ */
+
+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 00000000000..389cbbd2bfa
--- /dev/null
+++ b/apps/dav/lib/CalDAV/Reminder/NotificationProviderManager.php
@@ -0,0 +1,59 @@
+<?php
+/**
+ * @author Thomas Citharel <tcit@tcit.fr>
+ *
+ * @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 <http://www.gnu.org/licenses/>
+ *
+ */
+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 00000000000..ae4ec3bd3b7
--- /dev/null
+++ b/apps/dav/lib/CalDAV/Reminder/NotificationTypeDoesNotExistException.php
@@ -0,0 +1,39 @@
+<?php
+/**
+ * @copyright Copyright (c) 2018 Thomas Citharel <tcit@tcit.fr>
+ *
+ * @author Thomas Citharel <tcit@tcit.fr>
+ *
+ * @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 <http://www.gnu.org/licenses/>.
+ *
+ */
+
+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 00000000000..d95774e019e
--- /dev/null
+++ b/apps/dav/lib/CalDAV/Reminder/Notifier.php
@@ -0,0 +1,143 @@
+<?php
+/**
+ * @copyright Copyright (c) 2019 Thomas Citharel <tcit@tcit.fr>
+ *
+ * @author Thomas Citharel <tcit@tcit.fr>
+ *
+ * @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 <http://www.gnu.org/licenses/>
+ *
+ */
+
+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('<br>', $description);
+ }
+}
diff --git a/apps/dav/lib/CalDAV/Reminder/ReminderService.php b/apps/dav/lib/CalDAV/Reminder/ReminderService.php
new file mode 100644
index 00000000000..87c2ce10673
--- /dev/null
+++ b/apps/dav/lib/CalDAV/Reminder/ReminderService.php
@@ -0,0 +1,185 @@
+<?php
+/**
+ * @author Thomas Citharel <tcit@tcit.fr>
+ *
+ * @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 <http://www.gnu.org/licenses/>
+ *
+ */
+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 00000000000..1e4cce950ac
--- /dev/null
+++ b/apps/dav/lib/Migration/Version1007Date20181005133326.php
@@ -0,0 +1,82 @@
+<?php
+
+declare(strict_types=1);
+
+namespace OCA\DAV\Migration;
+
+use Closure;
+use OCP\DB\ISchemaWrapper;
+use OCP\Migration\SimpleMigrationStep;
+use OCP\Migration\IOutput;
+use Doctrine\DBAL\Types\Type;
+
+/**
+ * Auto-generated migration step: Please modify to your needs!
+ */
+class Version1007Date20181005133326 extends SimpleMigrationStep {
+
+ /**
+ * @param IOutput $output
+ * @param Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper`
+ * @param array $options
+ */
+ public function preSchemaChange(IOutput $output, Closure $schemaClosure, array $options) {
+ }
+
+ /**
+ * @param IOutput $output
+ * @param Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper`
+ * @param array $options
+ * @return null|ISchemaWrapper
+ */
+ public function changeSchema(IOutput $output, Closure $schemaClosure, array $options) {
+ /** @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('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 f38143b5b4e..958c463b1d3 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 87b159923d2..ba55a884702 100644
--- a/apps/dav/templates/settings-admin-caldav.php
+++ b/apps/dav/templates/settings-admin-caldav.php
@@ -72,4 +72,25 @@ script('dav', [
<em><?php p($l->t('Birthday calendars will be generated by a background job.')); ?></em><br>
<em><?php p($l->t('Hence they will not be available immediately after enabling but will show up after some time.')); ?></em>
</p>
+ <p>
+ <input type="checkbox" name="caldav_send_reminders_notifications" id="caldavSendRemindersNotifications" class="checkbox"
+ <?php ($_['send_reminders_notifications'] === 'yes') ? print_unescaped('checked="checked"') : null ?>/>
+ <label for="caldavSendRemindersNotifications"><?php p($l->t('Send notifications for events')); ?></label>
+ <br>
+ <em>
+ <?php print_unescaped(str_replace(
+ [
+ '{emailopen}',
+ '{linkclose}',
+ ],
+ [
+ '<a href="../admin#mail_general_settings">',
+ '</a>',
+ ],
+ $l->t('Please make sure to properly set up {emailopen}the email server{linkclose}.')
+ )); ?>
+ </em>
+ <br>
+ <em><?php p($l->t('Notifications will be send through background jobs, so these need to happen often enough.')); ?></em>
+ </p>
</form>
diff --git a/apps/dav/tests/unit/BackgroundJob/EventReminderJobTest.php b/apps/dav/tests/unit/BackgroundJob/EventReminderJobTest.php
new file mode 100644
index 00000000000..960dd481dd2
--- /dev/null
+++ b/apps/dav/tests/unit/BackgroundJob/EventReminderJobTest.php
@@ -0,0 +1,68 @@
+<?php
+declare(strict_types=1);
+/**
+ * @copyright 2018, Thomas Citharel <tcit@tcit.fr>
+ *
+ * @author Thomas Citharel <tcit@tcit.fr>
+ *
+ * @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 <http://www.gnu.org/licenses/>.
+ *
+ */
+
+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 00000000000..ba2e54af33c
--- /dev/null
+++ b/apps/dav/tests/unit/CalDAV/Reminder/AbstractNotificationProviderTest.php
@@ -0,0 +1,87 @@
+<?php
+/**
+ * @copyright Copyright (c) 2019, Thomas Citharel
+ *
+ * @author Thomas Citharel <tcit@tcit.fr>
+ *
+ * @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 <http://www.gnu.org/licenses/>
+ *
+ */
+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 00000000000..1cd979dc5d9
--- /dev/null
+++ b/apps/dav/tests/unit/CalDAV/Reminder/BackendTest.php
@@ -0,0 +1,313 @@
+<?php
+/**
+ * @copyright Copyright (c) 2018, Thomas Citharel
+ *
+ * @author Thomas Citharel <tcit@tcit.fr>
+ *
+ * @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 <http://www.gnu.org/licenses/>
+ *
+ */
+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 00000000000..34a61b34fc5
--- /dev/null
+++ b/apps/dav/tests/unit/CalDAV/Reminder/NotificationProvider/EmailProviderTest.php
@@ -0,0 +1,226 @@
+<?php
+/**
+ * @copyright Copyright (c) 2019, Thomas Citharel
+ *
+ * @author Thomas Citharel <tcit@tcit.fr>
+ *
+ * @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 <http://www.gnu.org/licenses/>
+ *
+ */
+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 00000000000..e10afb44d27
--- /dev/null
+++ b/apps/dav/tests/unit/CalDAV/Reminder/NotificationProvider/PushProviderTest.php
@@ -0,0 +1,139 @@
+<?php
+/**
+ * @copyright Copyright (c) 2019, Thomas Citharel
+ *
+ * @author Thomas Citharel <tcit@tcit.fr>
+ *
+ * @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 <http://www.gnu.org/licenses/>
+ *
+ */
+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 00000000000..d962b631c10
--- /dev/null
+++ b/apps/dav/tests/unit/CalDAV/Reminder/NotificationProviderManagerTest.php
@@ -0,0 +1,100 @@
+<?php
+/**
+ * @copyright Copyright (c) 2019, Thomas Citharel
+ *
+ * @author Thomas Citharel <tcit@tcit.fr>
+ *
+ * @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 <http://www.gnu.org/licenses/>
+ *
+ */
+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 00000000000..b9695b33fdc
--- /dev/null
+++ b/apps/dav/tests/unit/CalDAV/Reminder/NotifierTest.php
@@ -0,0 +1,184 @@
+<?php
+/**
+ * @copyright Copyright (c) 2019 Thomas Citharel <tcit@tcit.fr>
+ *
+ * @author Thomas Citharel <tcit@tcit.fr>
+ *
+ * @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 <http://www.gnu.org/licenses/>
+ *
+ */
+
+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<br>Date: foo<br>Description: bar<br>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 00000000000..061c3f16e38
--- /dev/null
+++ b/apps/dav/tests/unit/CalDAV/Reminder/ReminderServiceTest.php
@@ -0,0 +1,276 @@
+<?php
+/**
+ * @copyright Copyright (c) 2019, Thomas Citharel
+ *
+ * @author Thomas Citharel <tcit@tcit.fr>
+ *
+ * @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 <http://www.gnu.org/licenses/>
+ *
+ */
+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 = <<<EOD
+BEGIN:VCALENDAR
+PRODID:-//Nextcloud calendar v1.6.4
+BEGIN:VEVENT
+CREATED:20160602T133732
+DTSTAMP:20160602T133732
+LAST-MODIFIED:20160602T133732
+UID:wej2z68l9h
+SUMMARY:Test Event
+LOCATION:Somewhere ...
+DESCRIPTION:maybe ....
+DTSTART;TZID=Europe/Berlin;VALUE=DATE:20160609
+DTEND;TZID=Europe/Berlin;VALUE=DATE:20160610
+BEGIN:VALARM
+ACTION:EMAIL
+TRIGGER:-PT15M
+END:VALARM
+END:VEVENT
+END:VCALENDAR
+EOD;
+
+ public function setUp() {
+ parent::setUp();
+
+ $this->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('', [], [], []));
+ }
+}