Signed-off-by: Thomas Citharel <tcit@tcit.fr>tags/v17.0.0beta1
@@ -108,3 +108,6 @@ $calendarManager->register(function() use ($calendarManager, $app) { | |||
$app->setupCalendarProvider($calendarManager, $user->getUID()); | |||
} | |||
}); | |||
$app->registerNotifier(); | |||
$app->registerCalendarReminders(); |
@@ -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> |
@@ -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', |
@@ -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', |
@@ -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'); | |||
}); |
@@ -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); | |||
} | |||
} | |||
} |
@@ -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(); | |||
} | |||
} | |||
} |
@@ -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)) |
@@ -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'); | |||
} | |||
} |
@@ -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(); | |||
} | |||
} |
@@ -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) | |||
); | |||
} | |||
} |
@@ -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"); | |||
} | |||
} |
@@ -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); | |||
} | |||
} |
@@ -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; | |||
} | |||
} |
@@ -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"); | |||
} | |||
} |
@@ -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); | |||
} | |||
} |
@@ -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); | |||
} | |||
} |
@@ -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) { | |||
} | |||
} |
@@ -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); |
@@ -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> |
@@ -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([]); | |||
} | |||
} |
@@ -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); | |||
} | |||
} |
@@ -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); | |||
} | |||
} |
@@ -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); | |||
} | |||
} |
@@ -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); | |||
} | |||
} |
@@ -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); | |||
} | |||
} |
@@ -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); | |||
} | |||
} |
@@ -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('', [], [], [])); | |||
} | |||
} |