summaryrefslogtreecommitdiffstats
path: root/apps/dav/lib/CalDAV/Reminder/NotificationProvider
diff options
context:
space:
mode:
Diffstat (limited to 'apps/dav/lib/CalDAV/Reminder/NotificationProvider')
-rw-r--r--apps/dav/lib/CalDAV/Reminder/NotificationProvider/AbstractProvider.php191
-rw-r--r--apps/dav/lib/CalDAV/Reminder/NotificationProvider/AudioProvider.php1
-rw-r--r--apps/dav/lib/CalDAV/Reminder/NotificationProvider/EmailProvider.php495
-rw-r--r--apps/dav/lib/CalDAV/Reminder/NotificationProvider/ProviderNotAvailableException.php2
-rw-r--r--apps/dav/lib/CalDAV/Reminder/NotificationProvider/PushProvider.php108
5 files changed, 700 insertions, 97 deletions
diff --git a/apps/dav/lib/CalDAV/Reminder/NotificationProvider/AbstractProvider.php b/apps/dav/lib/CalDAV/Reminder/NotificationProvider/AbstractProvider.php
new file mode 100644
index 00000000000..6b2364c8022
--- /dev/null
+++ b/apps/dav/lib/CalDAV/Reminder/NotificationProvider/AbstractProvider.php
@@ -0,0 +1,191 @@
+<?php
+declare(strict_types=1);
+/**
+ * @copyright Copyright (c) 2019, Thomas Citharel
+ * @copyright Copyright (c) 2019, Georg Ehrke
+ *
+ * @author Thomas Citharel <tcit@tcit.fr>
+ * @author Georg Ehrke <oc.list@georgehrke.com>
+ *
+ * @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 \DateTime;
+use \DateTimeImmutable;
+use OCA\DAV\CalDAV\Reminder\INotificationProvider;
+use OCP\IConfig;
+use OCP\IL10N;
+use OCP\ILogger;
+use OCP\IURLGenerator;
+use OCP\L10N\IFactory as L10NFactory;
+use OCP\IUser;
+use Sabre\VObject\Component\VEvent;
+use Sabre\VObject\DateTimeParser;
+use Sabre\VObject\Parameter;
+use Sabre\VObject\Property;
+
+/**
+ * Class AbstractProvider
+ *
+ * @package OCA\DAV\CalDAV\Reminder\NotificationProvider
+ */
+abstract class AbstractProvider implements INotificationProvider {
+
+ /** @var string */
+ public const NOTIFICATION_TYPE = '';
+
+ /** @var ILogger */
+ protected $logger;
+
+ /** @var L10NFactory */
+ private $l10nFactory;
+
+ /** @var IL10N[] */
+ private $l10ns;
+
+ /** @var string */
+ private $fallbackLanguage;
+
+ /** @var IURLGenerator */
+ protected $urlGenerator;
+
+ /** @var IConfig */
+ protected $config;
+
+ /**
+ * @param ILogger $logger
+ * @param L10NFactory $l10nFactory
+ * @param IConfig $config
+ * @param IUrlGenerator $urlGenerator
+ */
+ public function __construct(ILogger $logger,
+ L10NFactory $l10nFactory,
+ IURLGenerator $urlGenerator,
+ IConfig $config) {
+ $this->logger = $logger;
+ $this->l10nFactory = $l10nFactory;
+ $this->urlGenerator = $urlGenerator;
+ $this->config = $config;
+ }
+
+ /**
+ * Send notification
+ *
+ * @param VEvent $vevent
+ * @param string $calendarDisplayName
+ * @param IUser[] $users
+ * @return void
+ */
+ abstract public function send(VEvent $vevent,
+ string $calendarDisplayName,
+ array $users=[]): void;
+
+ /**
+ * @return string
+ */
+ protected function getFallbackLanguage():string {
+ if ($this->fallbackLanguage) {
+ return $this->fallbackLanguage;
+ }
+
+ $fallbackLanguage = $this->l10nFactory->findLanguage();
+ $this->fallbackLanguage = $fallbackLanguage;
+
+ return $fallbackLanguage;
+ }
+
+ /**
+ * @param string $lang
+ * @return bool
+ */
+ protected function hasL10NForLang(string $lang):bool {
+ return $this->l10nFactory->languageExists('dav', $lang);
+ }
+
+ /**
+ * @param string $lang
+ * @return IL10N
+ */
+ protected function getL10NForLang(string $lang):IL10N {
+ if (isset($this->l10ns[$lang])) {
+ return $this->l10ns[$lang];
+ }
+
+ $l10n = $this->l10nFactory->get('dav', $lang);
+ $this->l10ns[$lang] = $l10n;
+
+ return $l10n;
+ }
+
+ /**
+ * @param VEvent $vevent
+ * @return string
+ */
+ private function getStatusOfEvent(VEvent $vevent):string {
+ if ($vevent->STATUS) {
+ return (string) $vevent->STATUS;
+ }
+
+ // Doesn't say so in the standard,
+ // but we consider events without a status
+ // to be confirmed
+ return 'CONFIRMED';
+ }
+
+ /**
+ * @param VEvent $vevent
+ * @return bool
+ */
+ protected function isEventTentative(VEvent $vevent):bool {
+ return $this->getStatusOfEvent($vevent) === 'TENTATIVE';
+ }
+
+ /**
+ * @param VEvent $vevent
+ * @return Property\ICalendar\DateTime
+ */
+ protected function getDTEndFromEvent(VEvent $vevent):Property\ICalendar\DateTime {
+ if (isset($vevent->DTEND)) {
+ return $vevent->DTEND;
+ }
+
+ if (isset($vevent->DURATION)) {
+ $isFloating = $vevent->DTSTART->isFloating();
+ /** @var Property\ICalendar\DateTime $end */
+ $end = clone $vevent->DTSTART;
+ $endDateTime = $end->getDateTime();
+ $endDateTime = $endDateTime->add(DateTimeParser::parse($vevent->DURATION->getValue()));
+ $end->setDateTime($endDateTime, $isFloating);
+
+ return $end;
+ }
+
+ if (!$vevent->DTSTART->hasTime()) {
+ $isFloating = $vevent->DTSTART->isFloating();
+ /** @var Property\ICalendar\DateTime $end */
+ $end = clone $vevent->DTSTART;
+ $endDateTime = $end->getDateTime();
+ $endDateTime = $endDateTime->modify('+1 day');
+ $end->setDateTime($endDateTime, $isFloating);
+
+ return $end;
+ }
+
+ return clone $vevent->DTSTART;
+ }
+}
diff --git a/apps/dav/lib/CalDAV/Reminder/NotificationProvider/AudioProvider.php b/apps/dav/lib/CalDAV/Reminder/NotificationProvider/AudioProvider.php
index 6e702bcacaa..ad4ac342f66 100644
--- a/apps/dav/lib/CalDAV/Reminder/NotificationProvider/AudioProvider.php
+++ b/apps/dav/lib/CalDAV/Reminder/NotificationProvider/AudioProvider.php
@@ -1,4 +1,5 @@
<?php
+declare(strict_types=1);
/**
* @copyright Copyright (c) 2019, Georg Ehrke
*
diff --git a/apps/dav/lib/CalDAV/Reminder/NotificationProvider/EmailProvider.php b/apps/dav/lib/CalDAV/Reminder/NotificationProvider/EmailProvider.php
index f05439932b6..2a7eb2a4032 100644
--- a/apps/dav/lib/CalDAV/Reminder/NotificationProvider/EmailProvider.php
+++ b/apps/dav/lib/CalDAV/Reminder/NotificationProvider/EmailProvider.php
@@ -1,8 +1,11 @@
<?php
+declare(strict_types=1);
/**
- * @copyright Copyright (c) 2018 Thomas Citharel <tcit@tcit.fr>
+ * @copyright Copyright (c) 2019, Thomas Citharel
+ * @copyright Copyright (c) 2019, Georg Ehrke
*
* @author Thomas Citharel <tcit@tcit.fr>
+ * @author Georg Ehrke <oc.list@georgehrke.com>
*
* @license GNU AGPL version 3 or any later version
*
@@ -20,27 +23,36 @@
* 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 DateTime;
+use DateTimeImmutable;
use OCP\IConfig;
+use OCP\IL10N;
use OCP\ILogger;
use OCP\IURLGenerator;
use OCP\L10N\IFactory as L10NFactory;
use OCP\Mail\IEMailTemplate;
use OCP\Mail\IMailer;
use OCP\IUser;
-use Sabre\VObject\Component\VCalendar;
-
-class EmailProvider extends AbstractNotificationProvider {
+use Sabre\VObject\Component\VEvent;
+use Sabre\VObject;
+use Sabre\VObject\Parameter;
+use Sabre\VObject\Property;
- /** @var IMailer */
- private $mailer;
+/**
+ * Class EmailProvider
+ *
+ * @package OCA\DAV\CalDAV\Reminder\NotificationProvider
+ */
+class EmailProvider extends AbstractProvider {
/** @var string */
public const NOTIFICATION_TYPE = 'EMAIL';
+ /** @var IMailer */
+ private $mailer;
+
/**
* @param IConfig $config
* @param IMailer $mailer
@@ -48,7 +60,9 @@ class EmailProvider extends AbstractNotificationProvider {
* @param L10NFactory $l10nFactory
* @param IUrlGenerator $urlGenerator
*/
- public function __construct(IConfig $config, IMailer $mailer, ILogger $logger,
+ public function __construct(IConfig $config,
+ IMailer $mailer,
+ ILogger $logger,
L10NFactory $l10nFactory,
IURLGenerator $urlGenerator) {
parent::__construct($logger, $l10nFactory, $urlGenerator, $config);
@@ -56,90 +70,100 @@ class EmailProvider extends AbstractNotificationProvider {
}
/**
- * Send notification
+ * Send out notification via email
*
- * @param VCalendar $vcalendar
+ * @param VEvent $vevent
* @param string $calendarDisplayName
- * @param IUser $user
- * @return void
+ * @param array $users
* @throws \Exception
*/
- public function send(VCalendar $vcalendar, string $calendarDisplayName, IUser $user):void {
- if ($user->getEMailAddress() === null) {
- return;
- }
+ public function send(VEvent $vevent,
+ string $calendarDisplayName,
+ array $users=[]):void {
+ $fallbackLanguage = $this->getFallbackLanguage();
- $lang = $this->config->getUserValue($user->getUID(), 'core', 'lang', $this->l10nFactory->findLanguage());
- $this->l10n = $this->l10nFactory->get('dav', $lang);
+ $emailAddressesOfSharees = $this->getEMailAddressesOfAllUsersWithWriteAccessToCalendar($users);
+ $emailAddressesOfAttendees = $this->getAllEMailAddressesFromEvent($vevent);
- $event = $this->extractEventDetails($vcalendar);
- $fromEMail = \OCP\Util::getDefaultEmailAddress('invitations-noreply');
+ // Quote from php.net:
+ // If the input arrays have the same string keys, then the later value for that key will overwrite the previous one.
+ // => if there are duplicate email addresses, it will always take the system value
+ $emailAddresses = array_merge(
+ $emailAddressesOfAttendees,
+ $emailAddressesOfSharees
+ );
- $message = $this->mailer->createMessage()
- ->setFrom([$fromEMail => 'Nextcloud'])
- // TODO: Set reply to from event creator
- // ->setReplyTo([$sender => $senderName])
- ->setTo([$user->getEMailAddress() => $user->getDisplayName()]);
+ $sortedByLanguage = $this->sortEMailAddressesByLanguage($emailAddresses, $fallbackLanguage);
+ $organizer = $this->getOrganizerEMailAndNameFromEvent($vevent);
+
+ foreach($sortedByLanguage as $lang => $emailAddresses) {
+ if ($this->hasL10NForLang($lang)) {
+ $lang = $fallbackLanguage;
+ }
+ $l10n = $this->getL10NForLang($lang);
+ $fromEMail = \OCP\Util::getDefaultEmailAddress('reminders-noreply');
- $template = $this->mailer->createEMailTemplate('dav.calendarReminder', $event);
- $template->addHeader();
+ $message = $this->mailer->createMessage();
+ $message->setFrom([$fromEMail]);
+ if ($organizer) {
+ $message->setReplyTo($organizer);
+ }
+ $message->setBcc($emailAddresses);
- $this->addSubjectAndHeading($template, $event['title']);
- $this->addBulletList($template, $event, $calendarDisplayName);
+ $template = $this->mailer->createEMailTemplate('dav.calendarReminder');
+ $template->addHeader();
- $template->addFooter();
- $message->useTemplate($template);
+ $this->addSubjectAndHeading($template, $l10n, $vevent);
+ $this->addBulletList($template, $l10n, $calendarDisplayName, $vevent);
- $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);
+ $template->addFooter();
+ $message->useTemplate($template);
- try {
- $failed = $this->mailer->send($message);
- if ($failed) {
- $this->logger->error('Unable to deliver message to {failed}', ['app' => 'dav', 'failed' => implode(', ', $failed)]);
+ try {
+ $failed = $this->mailer->send($message);
+ if ($failed) {
+ $this->logger->error('Unable to deliver message to {failed}', ['app' => 'dav', 'failed' => implode(', ', $failed)]);
+ }
+ } catch (\Exception $ex) {
+ $this->logger->logException($ex, ['app' => 'dav']);
}
- } catch(\Exception $ex) {
- $this->logger->logException($ex, ['app' => 'dav']);
}
}
/**
* @param IEMailTemplate $template
- * @param string $summary
+ * @param IL10N $l10n
+ * @param VEvent $vevent
*/
- private function addSubjectAndHeading(IEMailTemplate $template, string $summary):void {
- $template->setSubject('Notification: ' . $summary);
- $template->addHeading($summary);
+ private function addSubjectAndHeading(IEMailTemplate $template, IL10N $l10n, VEvent $vevent):void {
+ $template->setSubject('Notification: ' . $this->getTitleFromVEvent($vevent, $l10n));
+ $template->addHeading($this->getTitleFromVEvent($vevent, $l10n));
}
/**
* @param IEMailTemplate $template
- * @param array $eventData
+ * @param IL10N $l10n
* @param string $calendarDisplayName
+ * @param array $eventData
*/
- private function addBulletList(IEMailTemplate $template, array $eventData, string $calendarDisplayName):void {
- $template->addBodyListItem($calendarDisplayName, $this->l10n->t('Calendar:'),
+ private function addBulletList(IEMailTemplate $template,
+ IL10N $l10n,
+ string $calendarDisplayName,
+ VEvent $vevent):void {
+ $template->addBodyListItem($calendarDisplayName, $l10n->t('Calendar:'),
$this->getAbsoluteImagePath('actions/info.svg'));
- $template->addBodyListItem($eventData['when'], $this->l10n->t('Date:'),
+ $template->addBodyListItem($this->generateDateString($l10n, $vevent), $l10n->t('Date:'),
$this->getAbsoluteImagePath('places/calendar.svg'));
- if ($eventData['location']) {
- $template->addBodyListItem((string) $eventData['location'], $this->l10n->t('Where:'),
+ if (isset($vevent->LOCATION)) {
+ $template->addBodyListItem((string) $vevent->LOCATION, $l10n->t('Where:'),
$this->getAbsoluteImagePath('actions/address.svg'));
}
- if ($eventData['description']) {
- $template->addBodyListItem((string) $eventData['description'], $this->l10n->t('Description:'),
+ if (isset($vevent->DESCRIPTION)) {
+ $template->addBodyListItem((string) $vevent->DESCRIPTION, $l10n->t('Description:'),
$this->getAbsoluteImagePath('actions/more.svg'));
}
- if ($eventData['url']) {
- $template->addBodyListItem((string) $eventData['url'], $this->l10n->t('Link:'),
- $this->getAbsoluteImagePath('places/link.svg'));
- }
}
/**
@@ -151,4 +175,355 @@ class EmailProvider extends AbstractNotificationProvider {
$this->urlGenerator->imagePath('core', $path)
);
}
+
+ /**
+ * @param VEvent $vevent
+ * @return array|null
+ */
+ private function getOrganizerEMailAndNameFromEvent(VEvent $vevent):?array {
+ if (!$vevent->ORGANIZER) {
+ return null;
+ }
+
+ $organizer = $vevent->ORGANZIER;
+ if (strcasecmp($organizer->getValue(), 'mailto:') !== 0) {
+ return null;
+ }
+
+ $organizerEMail = substr($organizer->getValue(), 7);
+
+ $name = $organizer->offsetGet('CN');
+ if ($name instanceof Parameter) {
+ return [$organizerEMail => $name];
+ }
+
+ return [$organizerEMail];
+ }
+
+ /**
+ * @param array $sortedByLanguage
+ * @param IUser[] $users
+ * @param string $defaultLanguage
+ */
+ private function sortUsersByLanguage(array &$sortedByLanguage,
+ array $users,
+ string $defaultLanguage):void {
+ /**
+ * @var array $sortedByLanguage
+ * [
+ * 'de' => ['a@b.com', 'c@d.com'],
+ * ...
+ * ]
+ */
+ foreach($users as $user) {
+ /** @var IUser $user */
+ $emailAddress = $user->getEMailAddress();
+ $lang = $this->config->getUserValue($user->getUID(),
+ 'core', 'lang', $defaultLanguage);
+
+ if (!isset($sortedByLanguage[$lang])) {
+ $sortedByLanguage[$lang] = [];
+ }
+
+ $sortedByLanguage[$lang][] = $emailAddress;
+ }
+ }
+
+ /**
+ * @param array $emails
+ * @param string $defaultLanguage
+ * @return array
+ */
+ private function sortEMailAddressesByLanguage(array $emails,
+ string $defaultLanguage):array {
+ $sortedByLanguage = [];
+
+ foreach($emails as $emailAddress => $parameters) {
+ if (isset($parameters['LANG'])) {
+ $lang = $parameters['LANG'];
+ } else {
+ $lang = $defaultLanguage;
+ }
+
+ if (!isset($sortedByLanguage[$lang])) {
+ $sortedByLanguage[$lang] = [];
+ }
+
+ $sortedByLanguage[$lang][] = $emailAddress;
+ }
+
+ return $sortedByLanguage;
+ }
+
+ /**
+ * @param VEvent $vevent
+ * @return array
+ */
+ private function getAllEMailAddressesFromEvent(VEvent $vevent):array {
+ $emailAddresses = [];
+
+ if (isset($vevent->ATTENDEE)) {
+ foreach ($vevent->ATTENDEE as $attendee) {
+ if (!($attendee instanceof VObject\Property)) {
+ continue;
+ }
+
+ $cuType = $this->getCUTypeOfAttendee($attendee);
+ if (\in_array($cuType, ['RESOURCE', 'ROOM', 'UNKNOWN'])) {
+ // Don't send emails to things
+ continue;
+ }
+
+ $partstat = $this->getPartstatOfAttendee($attendee);
+ if ($partstat === 'DECLINED') {
+ // Don't send out emails to people who declined
+ continue;
+ }
+ if ($partstat === 'DELEGATED') {
+ $delegates = $attendee->offsetGet('DELEGATED-TO');
+ if (!($delegates instanceof VObject\Parameter)) {
+ continue;
+ }
+
+ $emailAddressesOfDelegates = $delegates->getParts();
+ foreach($emailAddressesOfDelegates as $addressesOfDelegate) {
+ if (strcasecmp($addressesOfDelegate, 'mailto:') === 0) {
+ $emailAddresses[substr($addressesOfDelegate, 7)] = [];
+ }
+ }
+
+ continue;
+ }
+
+ $emailAddressOfAttendee = $this->getEMailAddressOfAttendee($attendee);
+ if ($emailAddressOfAttendee !== null) {
+ $properties = [];
+
+ $langProp = $attendee->offsetGet('LANG');
+ if ($langProp instanceof VObject\Parameter) {
+ $properties['LANG'] = $langProp->getValue();
+ }
+
+ $emailAddresses[$emailAddressOfAttendee] = $properties;
+ }
+ }
+ }
+
+ if (isset($vevent->ORGANIZER)) {
+ $emailAddresses[$this->getEMailAddressOfAttendee($vevent->ORGANIZER)] = [];
+ }
+
+ return $emailAddresses;
+ }
+
+
+
+ /**
+ * @param VObject\Property $attendee
+ * @return string
+ */
+ private function getCUTypeOfAttendee(VObject\Property $attendee):string {
+ $cuType = $attendee->offsetGet('CUTYPE');
+ if ($cuType instanceof VObject\Parameter) {
+ return strtoupper($cuType->getValue());
+ }
+
+ return 'INDIVIDUAL';
+ }
+
+ /**
+ * @param VObject\Property $attendee
+ * @return string
+ */
+ private function getPartstatOfAttendee(VObject\Property $attendee):string {
+ $partstat = $attendee->offsetGet('PARTSTAT');
+ if ($partstat instanceof VObject\Parameter) {
+ return strtoupper($partstat->getValue());
+ }
+
+ return 'NEEDS-ACTION';
+ }
+
+ /**
+ * @param VObject\Property $attendee
+ * @return bool
+ */
+ private function hasAttendeeMailURI(VObject\Property $attendee):bool {
+ return strcasecmp($attendee->getValue(), 'mailto:') === 0;
+ }
+
+ /**
+ * @param VObject\Property $attendee
+ * @return string|null
+ */
+ private function getEMailAddressOfAttendee(VObject\Property $attendee):?string {
+ if (!$this->hasAttendeeMailURI($attendee)) {
+ return null;
+ }
+
+ return substr($attendee->getValue(), 7);
+ }
+
+ /**
+ * @param array $users
+ * @return array
+ */
+ private function getEMailAddressesOfAllUsersWithWriteAccessToCalendar(array $users):array {
+ $emailAddresses = [];
+
+ foreach($users as $user) {
+ $emailAddress = $user->getEMailAddress();
+ if ($emailAddress) {
+ $lang = $this->getLangForUser($user);
+ if ($lang) {
+ $emailAddresses[$emailAddress] = [
+ 'LANG' => $lang,
+ ];
+ } else {
+ $emailAddresses[$emailAddress] = [];
+ }
+ }
+ }
+
+ return array_unique($emailAddresses);
+ }
+
+ /**
+ * @param IUser $user
+ * @return string
+ */
+ private function getLangForUser(IUser $user): ?string {
+ return $this->config->getUserValue($user->getUID(), 'core', 'lang', null);
+ }
+
+ /**
+ * @param IL10N $l10n
+ * @param VEvent $vevent
+ * @return string
+ * @throws \Exception
+ */
+ private function generateDateString(IL10N $l10n, VEvent $vevent):string {
+ $isAllDay = $vevent->DTSTART instanceof Property\ICalendar\Date;
+
+ /** @var Property\ICalendar\Date | Property\ICalendar\DateTime $dtstart */
+ /** @var Property\ICalendar\Date | Property\ICalendar\DateTime $dtend */
+ /** @var \DateTimeImmutable $dtstartDt */
+ $dtstartDt = $vevent->DTSTART->getDateTime();
+ /** @var \DateTimeImmutable $dtendDt */
+ $dtendDt = $this->getDTEndFromEvent($vevent)->getDateTime();
+
+ $diff = $dtstartDt->diff($dtendDt);
+
+ $dtstartDt = new \DateTime($dtstartDt->format(\DateTime::ATOM));
+ $dtendDt = new \DateTime($dtendDt->format(\DateTime::ATOM));
+
+ if ($isAllDay) {
+ // One day event
+ if ($diff->days === 1) {
+ return $this->getDateString($l10n, $dtstartDt);
+ }
+
+ return implode(' - ', [
+ $this->getDateString($l10n, $dtstartDt),
+ $this->getDateString($l10n, $dtendDt),
+ ]);
+ }
+
+ $startTimezone = $endTimezone = null;
+ if (!$vevent->DTSTART->isFloating()) {
+ $startTimezone = $vevent->DTSTART->getDateTime()->getTimezone()->getName();
+ $endTimezone = $this->getDTEndFromEvent($vevent)->getDateTime()->getTimezone()->getName();
+ }
+
+ $localeStart = implode(', ', [
+ $this->getWeekDayName($l10n, $dtstartDt),
+ $this->getDateTimeString($l10n, $dtstartDt)
+ ]);
+
+ // always show full date with timezone if timezones are different
+ if ($startTimezone !== $endTimezone) {
+ $localeEnd = implode(', ', [
+ $this->getWeekDayName($l10n, $dtendDt),
+ $this->getDateTimeString($l10n, $dtendDt)
+ ]);
+
+ return $localeStart
+ . ' (' . $startTimezone . ') '
+ . ' - '
+ . $localeEnd
+ . ' (' . $endTimezone . ')';
+ }
+
+ // Show only the time if the day is the same
+ $localeEnd = $this->isDayEqual($dtstartDt, $dtendDt)
+ ? $this->getTimeString($l10n, $dtendDt)
+ : implode(', ', [
+ $this->getWeekDayName($l10n, $dtendDt),
+ $this->getDateTimeString($l10n, $dtendDt)
+ ]);
+
+ return $localeStart
+ . ' - '
+ . $localeEnd
+ . ' (' . $startTimezone . ')';
+ }
+
+ /**
+ * @param DateTime $dtStart
+ * @param DateTime $dtEnd
+ * @return bool
+ */
+ private function isDayEqual(DateTime $dtStart,
+ DateTime $dtEnd):bool {
+ return $dtStart->format('Y-m-d') === $dtEnd->format('Y-m-d');
+ }
+
+ /**
+ * @param IL10N $l10n
+ * @param DateTime $dt
+ * @return string
+ */
+ private function getWeekDayName(IL10N $l10n, DateTime $dt):string {
+ return $l10n->l('weekdayName', $dt, ['width' => 'abbreviated']);
+ }
+
+ /**
+ * @param IL10N $l10n
+ * @param DateTime $dt
+ * @return string
+ */
+ private function getDateString(IL10N $l10n, DateTime $dt):string {
+ return $l10n->l('date', $dt, ['width' => 'medium']);
+ }
+
+ /**
+ * @param IL10N $l10n
+ * @param DateTime $dt
+ * @return string
+ */
+ private function getDateTimeString(IL10N $l10n, DateTime $dt):string {
+ return $l10n->l('datetime', $dt, ['width' => 'medium|short']);
+ }
+
+ /**
+ * @param IL10N $l10n
+ * @param DateTime $dt
+ * @return string
+ */
+ private function getTimeString(IL10N $l10n, DateTime $dt):string {
+ return $l10n->l('time', $dt, ['width' => 'short']);
+ }
+
+ /**
+ * @param VEvent $vevent
+ * @param IL10N $l10n
+ * @return string
+ */
+ private function getTitleFromVEvent(VEvent $vevent, IL10N $l10n):string {
+ if (isset($vevent->SUMMARY)) {
+ return (string)$vevent->SUMMARY;
+ }
+
+ return $l10n->t('Untitled event');
+ }
}
diff --git a/apps/dav/lib/CalDAV/Reminder/NotificationProvider/ProviderNotAvailableException.php b/apps/dav/lib/CalDAV/Reminder/NotificationProvider/ProviderNotAvailableException.php
index bf736db8a34..bfa6db95852 100644
--- a/apps/dav/lib/CalDAV/Reminder/NotificationProvider/ProviderNotAvailableException.php
+++ b/apps/dav/lib/CalDAV/Reminder/NotificationProvider/ProviderNotAvailableException.php
@@ -1,4 +1,5 @@
<?php
+declare(strict_types=1);
/**
* @copyright Copyright (c) 2018 Thomas Citharel <tcit@tcit.fr>
*
@@ -20,7 +21,6 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
-
namespace OCA\DAV\CalDAV\Reminder\NotificationProvider;
class ProviderNotAvailableException extends \Exception {
diff --git a/apps/dav/lib/CalDAV/Reminder/NotificationProvider/PushProvider.php b/apps/dav/lib/CalDAV/Reminder/NotificationProvider/PushProvider.php
index f04b8e4c45a..2e580fd78a3 100644
--- a/apps/dav/lib/CalDAV/Reminder/NotificationProvider/PushProvider.php
+++ b/apps/dav/lib/CalDAV/Reminder/NotificationProvider/PushProvider.php
@@ -1,8 +1,11 @@
<?php
+declare(strict_types=1);
/**
- * @copyright Copyright (c) 2018 Thomas Citharel <tcit@tcit.fr>
+ * @copyright Copyright (c) 2019, Thomas Citharel
+ * @copyright Copyright (c) 2019, Georg Ehrke
*
* @author Thomas Citharel <tcit@tcit.fr>
+ * @author Georg Ehrke <oc.list@georgehrke.com>
*
* @license GNU AGPL version 3 or any later version
*
@@ -20,11 +23,9 @@
* 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;
@@ -32,22 +33,24 @@ use OCP\L10N\IFactory as L10NFactory;
use OCP\Notification\IManager;
use OCP\IUser;
use OCP\Notification\INotification;
-use Sabre\VObject\Component\VCalendar;
use OCP\AppFramework\Utility\ITimeFactory;
+use Sabre\VObject\Component\VEvent;
+use Sabre\VObject\Property;
-class PushProvider extends AbstractNotificationProvider {
+/**
+ * Class PushProvider
+ *
+ * @package OCA\DAV\CalDAV\Reminder\NotificationProvider
+ */
+class PushProvider extends AbstractProvider {
/** @var string */
public const NOTIFICATION_TYPE = 'DISPLAY';
- /**
- * @var IManager
- */
+ /** @var IManager */
private $manager;
- /**
- * @var ITimeFactory
- */
+ /** @var ITimeFactory */
private $timeFactory;
/**
@@ -58,42 +61,75 @@ class PushProvider extends AbstractNotificationProvider {
* @param IUrlGenerator $urlGenerator
* @param ITimeFactory $timeFactory
*/
- public function __construct(IConfig $config, IManager $manager, ILogger $logger,
+ public function __construct(IConfig $config,
+ IManager $manager,
+ ILogger $logger,
L10NFactory $l10nFactory,
- IURLGenerator $urlGenerator, ITimeFactory $timeFactory) {
+ IURLGenerator $urlGenerator,
+ ITimeFactory $timeFactory) {
parent::__construct($logger, $l10nFactory, $urlGenerator, $config);
$this->manager = $manager;
$this->timeFactory = $timeFactory;
}
/**
- * Send notification
+ * Send push notification to all users.
*
- * @param VCalendar $vcalendar
+ * @param VEvent $vevent
* @param string $calendarDisplayName
- * @param IUser $user
- * @return void
+ * @param IUser[] $users
* @throws \Exception
*/
- public function send(VCalendar $vcalendar, string $calendarDisplayName, IUser $user):void {
- $lang = $this->config->getUserValue($user->getUID(), 'core', 'lang', $this->l10nFactory->findLanguage());
- $this->l10n = $this->l10nFactory->get('dav', $lang);
+ public function send(VEvent $vevent,
+ string $calendarDisplayName=null,
+ array $users=[]):void {
+ $eventDetails = $this->extractEventDetails($vevent);
+ $eventDetails['calendar_displayname'] = $calendarDisplayName;
- $event = $this->extractEventDetails($vcalendar);
- /** @var INotification $notification */
- $notification = $this->manager->createNotification();
- $notification->setApp(Application::APP_ID)
- ->setUser($user->getUID())
- ->setDateTime($this->timeFactory->getDateTime())
- ->setObject(Application::APP_ID, $event['uid']) // $type and $id
- ->setSubject('calendar_reminder', ['title' => $event['title'], 'start' => $event['start']->getTimestamp()]) // $subject and $parameters
- ->setMessage('calendar_reminder', [
- 'when' => $event['when'],
- 'description' => $event['description'],
- 'location' => $event['location'],
- 'calendar' => $calendarDisplayName
- ])
- ;
- $this->manager->notify($notification);
+ foreach($users as $user) {
+ /** @var INotification $notification */
+ $notification = $this->manager->createNotification();
+ $notification->setApp(Application::APP_ID)
+ ->setUser($user->getUID())
+ ->setDateTime($this->timeFactory->getDateTime())
+ ->setObject(Application::APP_ID, (string) $vevent->UID)
+ ->setSubject('calendar_reminder', [
+ 'title' => $eventDetails['title'],
+ 'start_atom' => $eventDetails['start_atom']
+ ])
+ ->setMessage('calendar_reminder', $eventDetails);
+
+ $this->manager->notify($notification);
+ }
}
+
+ /**
+ * @var VEvent $vevent
+ * @return array
+ * @throws \Exception
+ */
+ protected function extractEventDetails(VEvent $vevent):array {
+ /** @var Property\ICalendar\DateTime $start */
+ $start = $vevent->DTSTART;
+ $end = $this->getDTEndFromEvent($vevent);
+
+ return [
+ 'title' => isset($vevent->SUMMARY)
+ ? ((string) $vevent->SUMMARY)
+ : null,
+ 'description' => isset($vevent->DESCRIPTION)
+ ? ((string) $vevent->DESCRIPTION)
+ : null,
+ 'location' => isset($vevent->LOCATION)
+ ? ((string) $vevent->LOCATION)
+ : null,
+ 'all_day' => $start instanceof Property\ICalendar\Date,
+ 'start_atom' => $start->getDateTime()->format(\DateTime::ATOM),
+ 'start_is_floating' => $start->isFloating(),
+ 'start_timezone' => $start->getDateTime()->getTimezone()->getName(),
+ 'end_atom' => $end->getDateTime()->format(\DateTime::ATOM),
+ 'end_is_floating' => $end->isFloating(),
+ 'end_timezone' => $end->getDateTime()->getTimezone()->getName(),
+ ];
+ }
}