Support recurring events + repeating alarms
Signed-off-by: Georg Ehrke <developer@georgehrke.com>
This commit is contained in:
parent
7c4a8a3bdf
commit
bcce568c6d
20 changed files with 1926 additions and 594 deletions
|
@ -5,7 +5,7 @@
|
|||
<name>WebDAV</name>
|
||||
<summary>WebDAV endpoint</summary>
|
||||
<description>WebDAV endpoint</description>
|
||||
<version>1.11.1</version>
|
||||
<version>1.13.0</version>
|
||||
<licence>agpl</licence>
|
||||
<author>owncloud.org</author>
|
||||
<namespace>DAV</namespace>
|
||||
|
|
|
@ -52,9 +52,10 @@ return array(
|
|||
'OCA\\DAV\\CalDAV\\PublicCalendarRoot' => $baseDir . '/../lib/CalDAV/PublicCalendarRoot.php',
|
||||
'OCA\\DAV\\CalDAV\\Publishing\\PublishPlugin' => $baseDir . '/../lib/CalDAV/Publishing/PublishPlugin.php',
|
||||
'OCA\\DAV\\CalDAV\\Publishing\\Xml\\Publisher' => $baseDir . '/../lib/CalDAV/Publishing/Xml/Publisher.php',
|
||||
'OCA\\DAV\\CalDAV\\Reminder\\AbstractNotificationProvider' => $baseDir . '/../lib/CalDAV/Reminder/AbstractNotificationProvider.php',
|
||||
'OCA\\DAV\\CalDAV\\Reminder\\Backend' => $baseDir . '/../lib/CalDAV/Reminder/Backend.php',
|
||||
'OCA\\DAV\\CalDAV\\Reminder\\INotificationProvider' => $baseDir . '/../lib/CalDAV/Reminder/INotificationProvider.php',
|
||||
'OCA\\DAV\\CalDAV\\Reminder\\NotificationProviderManager' => $baseDir . '/../lib/CalDAV/Reminder/NotificationProviderManager.php',
|
||||
'OCA\\DAV\\CalDAV\\Reminder\\NotificationProvider\\AbstractProvider' => $baseDir . '/../lib/CalDAV/Reminder/NotificationProvider/AbstractProvider.php',
|
||||
'OCA\\DAV\\CalDAV\\Reminder\\NotificationProvider\\AudioProvider' => $baseDir . '/../lib/CalDAV/Reminder/NotificationProvider/AudioProvider.php',
|
||||
'OCA\\DAV\\CalDAV\\Reminder\\NotificationProvider\\EmailProvider' => $baseDir . '/../lib/CalDAV/Reminder/NotificationProvider/EmailProvider.php',
|
||||
'OCA\\DAV\\CalDAV\\Reminder\\NotificationProvider\\ProviderNotAvailableException' => $baseDir . '/../lib/CalDAV/Reminder/NotificationProvider/ProviderNotAvailableException.php',
|
||||
|
@ -188,7 +189,6 @@ return array(
|
|||
'OCA\\DAV\\Migration\\Version1005Date20180530124431' => $baseDir . '/../lib/Migration/Version1005Date20180530124431.php',
|
||||
'OCA\\DAV\\Migration\\Version1006Date20180619154313' => $baseDir . '/../lib/Migration/Version1006Date20180619154313.php',
|
||||
'OCA\\DAV\\Migration\\Version1006Date20180628111625' => $baseDir . '/../lib/Migration/Version1006Date20180628111625.php',
|
||||
'OCA\\DAV\\Migration\\Version1007Date20181005133326' => $baseDir . '/../lib/Migration/Version1007Date20181005133326.php',
|
||||
'OCA\\DAV\\Migration\\Version1008Date20181030113700' => $baseDir . '/../lib/Migration/Version1008Date20181030113700.php',
|
||||
'OCA\\DAV\\Migration\\Version1008Date20181105104826' => $baseDir . '/../lib/Migration/Version1008Date20181105104826.php',
|
||||
'OCA\\DAV\\Migration\\Version1008Date20181105104833' => $baseDir . '/../lib/Migration/Version1008Date20181105104833.php',
|
||||
|
@ -197,6 +197,7 @@ return array(
|
|||
'OCA\\DAV\\Migration\\Version1008Date20181114084440' => $baseDir . '/../lib/Migration/Version1008Date20181114084440.php',
|
||||
'OCA\\DAV\\Migration\\Version1011Date20190725113607' => $baseDir . '/../lib/Migration/Version1011Date20190725113607.php',
|
||||
'OCA\\DAV\\Migration\\Version1011Date20190806104428' => $baseDir . '/../lib/Migration/Version1011Date20190806104428.php',
|
||||
'OCA\\DAV\\Migration\\Version1012Date20190808122342' => $baseDir . '/../lib/Migration/Version1012Date20190808122342.php',
|
||||
'OCA\\DAV\\Provisioning\\Apple\\AppleProvisioningNode' => $baseDir . '/../lib/Provisioning/Apple/AppleProvisioningNode.php',
|
||||
'OCA\\DAV\\Provisioning\\Apple\\AppleProvisioningPlugin' => $baseDir . '/../lib/Provisioning/Apple/AppleProvisioningPlugin.php',
|
||||
'OCA\\DAV\\RootCollection' => $baseDir . '/../lib/RootCollection.php',
|
||||
|
|
|
@ -67,9 +67,10 @@ class ComposerStaticInitDAV
|
|||
'OCA\\DAV\\CalDAV\\PublicCalendarRoot' => __DIR__ . '/..' . '/../lib/CalDAV/PublicCalendarRoot.php',
|
||||
'OCA\\DAV\\CalDAV\\Publishing\\PublishPlugin' => __DIR__ . '/..' . '/../lib/CalDAV/Publishing/PublishPlugin.php',
|
||||
'OCA\\DAV\\CalDAV\\Publishing\\Xml\\Publisher' => __DIR__ . '/..' . '/../lib/CalDAV/Publishing/Xml/Publisher.php',
|
||||
'OCA\\DAV\\CalDAV\\Reminder\\AbstractNotificationProvider' => __DIR__ . '/..' . '/../lib/CalDAV/Reminder/AbstractNotificationProvider.php',
|
||||
'OCA\\DAV\\CalDAV\\Reminder\\Backend' => __DIR__ . '/..' . '/../lib/CalDAV/Reminder/Backend.php',
|
||||
'OCA\\DAV\\CalDAV\\Reminder\\INotificationProvider' => __DIR__ . '/..' . '/../lib/CalDAV/Reminder/INotificationProvider.php',
|
||||
'OCA\\DAV\\CalDAV\\Reminder\\NotificationProviderManager' => __DIR__ . '/..' . '/../lib/CalDAV/Reminder/NotificationProviderManager.php',
|
||||
'OCA\\DAV\\CalDAV\\Reminder\\NotificationProvider\\AbstractProvider' => __DIR__ . '/..' . '/../lib/CalDAV/Reminder/NotificationProvider/AbstractProvider.php',
|
||||
'OCA\\DAV\\CalDAV\\Reminder\\NotificationProvider\\AudioProvider' => __DIR__ . '/..' . '/../lib/CalDAV/Reminder/NotificationProvider/AudioProvider.php',
|
||||
'OCA\\DAV\\CalDAV\\Reminder\\NotificationProvider\\EmailProvider' => __DIR__ . '/..' . '/../lib/CalDAV/Reminder/NotificationProvider/EmailProvider.php',
|
||||
'OCA\\DAV\\CalDAV\\Reminder\\NotificationProvider\\ProviderNotAvailableException' => __DIR__ . '/..' . '/../lib/CalDAV/Reminder/NotificationProvider/ProviderNotAvailableException.php',
|
||||
|
@ -203,7 +204,6 @@ class ComposerStaticInitDAV
|
|||
'OCA\\DAV\\Migration\\Version1005Date20180530124431' => __DIR__ . '/..' . '/../lib/Migration/Version1005Date20180530124431.php',
|
||||
'OCA\\DAV\\Migration\\Version1006Date20180619154313' => __DIR__ . '/..' . '/../lib/Migration/Version1006Date20180619154313.php',
|
||||
'OCA\\DAV\\Migration\\Version1006Date20180628111625' => __DIR__ . '/..' . '/../lib/Migration/Version1006Date20180628111625.php',
|
||||
'OCA\\DAV\\Migration\\Version1007Date20181005133326' => __DIR__ . '/..' . '/../lib/Migration/Version1007Date20181005133326.php',
|
||||
'OCA\\DAV\\Migration\\Version1008Date20181030113700' => __DIR__ . '/..' . '/../lib/Migration/Version1008Date20181030113700.php',
|
||||
'OCA\\DAV\\Migration\\Version1008Date20181105104826' => __DIR__ . '/..' . '/../lib/Migration/Version1008Date20181105104826.php',
|
||||
'OCA\\DAV\\Migration\\Version1008Date20181105104833' => __DIR__ . '/..' . '/../lib/Migration/Version1008Date20181105104833.php',
|
||||
|
@ -212,6 +212,7 @@ class ComposerStaticInitDAV
|
|||
'OCA\\DAV\\Migration\\Version1008Date20181114084440' => __DIR__ . '/..' . '/../lib/Migration/Version1008Date20181114084440.php',
|
||||
'OCA\\DAV\\Migration\\Version1011Date20190725113607' => __DIR__ . '/..' . '/../lib/Migration/Version1011Date20190725113607.php',
|
||||
'OCA\\DAV\\Migration\\Version1011Date20190806104428' => __DIR__ . '/..' . '/../lib/Migration/Version1011Date20190806104428.php',
|
||||
'OCA\\DAV\\Migration\\Version1012Date20190808122342' => __DIR__ . '/..' . '/../lib/Migration/Version1012Date20190808122342.php',
|
||||
'OCA\\DAV\\Provisioning\\Apple\\AppleProvisioningNode' => __DIR__ . '/..' . '/../lib/Provisioning/Apple/AppleProvisioningNode.php',
|
||||
'OCA\\DAV\\Provisioning\\Apple\\AppleProvisioningPlugin' => __DIR__ . '/..' . '/../lib/Provisioning/Apple/AppleProvisioningPlugin.php',
|
||||
'OCA\\DAV\\RootCollection' => __DIR__ . '/..' . '/../lib/RootCollection.php',
|
||||
|
|
|
@ -231,12 +231,10 @@ class Application extends App {
|
|||
);
|
||||
|
||||
/** @var ReminderService $reminderBackend */
|
||||
$reminderService= $this->getContainer()->query(ReminderService::class);
|
||||
$reminderService = $this->getContainer()->query(ReminderService::class);
|
||||
|
||||
$reminderService->onTouchCalendarObject(
|
||||
$eventName,
|
||||
$event->getArgument('calendarData'),
|
||||
$event->getArgument('shares'),
|
||||
$event->getArgument('objectData')
|
||||
);
|
||||
};
|
||||
|
|
|
@ -1,205 +0,0 @@
|
|||
<?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 {
|
||||
|
||||
/** @var string */
|
||||
public const NOTIFICATION_TYPE = '';
|
||||
|
||||
/** @var ILogger */
|
||||
protected $logger;
|
||||
|
||||
/** @var L10NFactory */
|
||||
protected $l10nFactory;
|
||||
|
||||
/** @var IL10N */
|
||||
protected $l10n;
|
||||
|
||||
/** @var IURLGenerator */
|
||||
protected $urlGenerator;
|
||||
|
||||
/** @var IConfig */
|
||||
protected $config;
|
||||
|
||||
/**
|
||||
* @param ILogger $logger
|
||||
* @param L10NFactory $l10nFactory
|
||||
* @param IConfig $config
|
||||
* @param IUrlGenerator $urlGenerator
|
||||
*/
|
||||
public function __construct(ILogger $logger, L10NFactory $l10nFactory, IURLGenerator $urlGenerator, IConfig $config) {
|
||||
$this->logger = $logger;
|
||||
$this->l10nFactory = $l10nFactory;
|
||||
$this->urlGenerator = $urlGenerator;
|
||||
$this->config = $config;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send notification
|
||||
*
|
||||
* @param VCalendar $vcalendar
|
||||
* @param string $calendarDisplayName
|
||||
* @param IUser $user
|
||||
* @return void
|
||||
*/
|
||||
abstract function send(VCalendar $vcalendar, string $calendarDisplayName, IUser $user): void;
|
||||
|
||||
/**
|
||||
* @var VCalendar $vcalendar
|
||||
* @var string $defaultValue
|
||||
* @return array
|
||||
* @throws \Exception
|
||||
*/
|
||||
protected function extractEventDetails(VCalendar $vcalendar, $defaultValue = ''):array {
|
||||
/** @var VEvent $vevent */
|
||||
$vevent = $vcalendar->VEVENT;
|
||||
|
||||
/** @var Property $start */
|
||||
$start = $vevent->DTSTART;
|
||||
if (isset($vevent->DTEND)) {
|
||||
$end = $vevent->DTEND;
|
||||
} elseif (isset($vevent->DURATION)) {
|
||||
$isFloating = $vevent->DTSTART->isFloating();
|
||||
$end = clone $vevent->DTSTART;
|
||||
$endDateTime = $end->getDateTime();
|
||||
$endDateTime = $endDateTime->add(DateTimeParser::parse($vevent->DURATION->getValue()));
|
||||
$end->setDateTime($endDateTime, $isFloating);
|
||||
} elseif (!$vevent->DTSTART->hasTime()) {
|
||||
$isFloating = $vevent->DTSTART->isFloating();
|
||||
$end = clone $vevent->DTSTART;
|
||||
$endDateTime = $end->getDateTime();
|
||||
$endDateTime = $endDateTime->modify('+1 day');
|
||||
$end->setDateTime($endDateTime, $isFloating);
|
||||
} else {
|
||||
$end = clone $vevent->DTSTART;
|
||||
}
|
||||
|
||||
return [
|
||||
'title' => (string) $vevent->SUMMARY ?: $defaultValue,
|
||||
'description' => (string) $vevent->DESCRIPTION ?: $defaultValue,
|
||||
'start'=> $start->getDateTime(),
|
||||
'end' => $end->getDateTime(),
|
||||
'when' => $this->generateWhenString($start, $end),
|
||||
'url' => (string) $vevent->URL ?: $defaultValue,
|
||||
'location' => (string) $vevent->LOCATION ?: $defaultValue,
|
||||
'uid' => (string) $vevent->UID,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Property $dtstart
|
||||
* @param Property $dtend
|
||||
* @return string
|
||||
* @throws \Exception
|
||||
*/
|
||||
private function generateWhenString(Property $dtstart, Property $dtend):string {
|
||||
$isAllDay = $dtstart instanceof Property\ICalendar\Date;
|
||||
|
||||
/** @var Property\ICalendar\Date | Property\ICalendar\DateTime $dtstart */
|
||||
/** @var Property\ICalendar\Date | Property\ICalendar\DateTime $dtend */
|
||||
/** @var DateTimeImmutable $dtstartDt */
|
||||
$dtstartDt = $dtstart->getDateTime();
|
||||
/** @var DateTimeImmutable $dtendDt */
|
||||
$dtendDt = $dtend->getDateTime();
|
||||
|
||||
$diff = $dtstartDt->diff($dtendDt);
|
||||
|
||||
$dtstartDt = new DateTime($dtstartDt->format(DateTime::ATOM));
|
||||
$dtendDt = new DateTime($dtendDt->format(DateTime::ATOM));
|
||||
|
||||
if ($isAllDay) {
|
||||
// One day event
|
||||
if ($diff->days === 1) {
|
||||
return $this->l10n->l('date', $dtstartDt, ['width' => 'medium']);
|
||||
}
|
||||
|
||||
//event that spans over multiple days
|
||||
$localeStart = $this->l10n->l('date', $dtstartDt, ['width' => 'medium']);
|
||||
$localeEnd = $this->l10n->l('date', $dtendDt, ['width' => 'medium']);
|
||||
|
||||
return $localeStart . ' - ' . $localeEnd;
|
||||
}
|
||||
|
||||
/** @var Property\ICalendar\DateTime $dtstart */
|
||||
/** @var Property\ICalendar\DateTime $dtend */
|
||||
$isFloating = $dtstart->isFloating();
|
||||
$startTimezone = $endTimezone = null;
|
||||
if (!$isFloating) {
|
||||
$prop = $dtstart->offsetGet('TZID');
|
||||
if ($prop instanceof Parameter) {
|
||||
$startTimezone = $prop->getValue();
|
||||
}
|
||||
|
||||
$prop = $dtend->offsetGet('TZID');
|
||||
if ($prop instanceof Parameter) {
|
||||
$endTimezone = $prop->getValue();
|
||||
}
|
||||
}
|
||||
|
||||
$localeStart = $this->l10n->l('weekdayName', $dtstartDt, ['width' => 'abbreviated']) . ', ' .
|
||||
$this->l10n->l('datetime', $dtstartDt, ['width' => 'medium|short']);
|
||||
|
||||
// always show full date with timezone if timezones are different
|
||||
if ($startTimezone !== $endTimezone) {
|
||||
$localeEnd = $this->l10n->l('datetime', $dtendDt, ['width' => 'medium|short']);
|
||||
|
||||
return $localeStart . ' (' . $startTimezone . ') - ' .
|
||||
$localeEnd . ' (' . $endTimezone . ')';
|
||||
}
|
||||
|
||||
// show only end time if date is the same
|
||||
if ($this->isDayEqual($dtstartDt, $dtendDt)) {
|
||||
$localeEnd = $this->l10n->l('time', $dtendDt, ['width' => 'short']);
|
||||
} else {
|
||||
$localeEnd = $this->l10n->l('weekdayName', $dtendDt, ['width' => 'abbreviated']) . ', ' .
|
||||
$this->l10n->l('datetime', $dtendDt, ['width' => 'medium|short']);
|
||||
}
|
||||
|
||||
return $localeStart . ' - ' . $localeEnd . ' (' . $startTimezone . ')';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param DateTime $dtStart
|
||||
* @param DateTime $dtEnd
|
||||
* @return bool
|
||||
*/
|
||||
private function isDayEqual(DateTime $dtStart, DateTime $dtEnd):bool {
|
||||
return $dtStart->format('Y-m-d') === $dtEnd->format('Y-m-d');
|
||||
}
|
||||
}
|
|
@ -1,8 +1,11 @@
|
|||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* @copyright Copyright (c) 2019 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,7 +23,6 @@
|
|||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace OCA\DAV\CalDAV\Reminder;
|
||||
|
||||
use OCP\IDBConnection;
|
||||
|
@ -40,61 +42,119 @@ class Backend {
|
|||
private $timeFactory;
|
||||
|
||||
/**
|
||||
* Backend constructor.
|
||||
*
|
||||
* @param IDBConnection $db
|
||||
* @param ITimeFactory $timeFactory
|
||||
*/
|
||||
public function __construct(IDBConnection $db, ITimeFactory $timeFactory) {
|
||||
public function __construct(IDBConnection $db,
|
||||
ITimeFactory $timeFactory) {
|
||||
$this->db = $db;
|
||||
$this->timeFactory = $timeFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $uid
|
||||
* @param string $calendarId
|
||||
* @param string $uri
|
||||
* @param string $type
|
||||
* @param int $notificationDate
|
||||
* @param int $eventStartDate
|
||||
* Get all reminders with a notification date before now
|
||||
*
|
||||
* @return array
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function insertReminder(string $uid, string $calendarId, string $uri, string $type, int $notificationDate, int $eventStartDate):void {
|
||||
public function getRemindersToProcess():array {
|
||||
$query = $this->db->getQueryBuilder();
|
||||
$query->select(['cr.*', 'co.calendardata', 'c.displayname', 'c.principaluri'])
|
||||
->from('calendar_reminders', 'cr')
|
||||
// ->where($query->expr()->lte('cr.notification_date', $query->createNamedParameter($this->timeFactory->getTime())))
|
||||
->leftJoin('cr', 'calendarobjects', 'co', $query->expr()->eq('cr.object_id', 'co.id'))
|
||||
->leftJoin('cr', 'calendars', 'c', $query->expr()->eq('cr.calendar_id', 'c.id'));
|
||||
$stmt = $query->execute();
|
||||
|
||||
return array_map(
|
||||
[$this, 'fixRowTyping'],
|
||||
$stmt->fetchAll()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all scheduled reminders for an event
|
||||
*
|
||||
* @param int $objectId
|
||||
* @return array
|
||||
*/
|
||||
public function getAllScheduledRemindersForEvent(int $objectId):array {
|
||||
$query = $this->db->getQueryBuilder();
|
||||
$query->select('*')
|
||||
->from('calendar_reminders')
|
||||
->where($query->expr()->eq('object_id', $query->createNamedParameter($objectId)));
|
||||
$stmt = $query->execute();
|
||||
|
||||
return array_map(
|
||||
[$this, 'fixRowTyping'],
|
||||
$stmt->fetchAll()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert a new reminder into the database
|
||||
*
|
||||
* @param int $calendarId
|
||||
* @param int $objectId
|
||||
* @param string $uid
|
||||
* @param bool $isRecurring
|
||||
* @param int $recurrenceId
|
||||
* @param bool $isRecurrenceException
|
||||
* @param string $eventHash
|
||||
* @param string $alarmHash
|
||||
* @param string $type
|
||||
* @param bool $isRelative
|
||||
* @param int $notificationDate
|
||||
* @param bool $isRepeatBased
|
||||
* @return int The insert id
|
||||
*/
|
||||
public function insertReminder(int $calendarId,
|
||||
int $objectId,
|
||||
string $uid,
|
||||
bool $isRecurring,
|
||||
int $recurrenceId,
|
||||
bool $isRecurrenceException,
|
||||
string $eventHash,
|
||||
string $alarmHash,
|
||||
string $type,
|
||||
bool $isRelative,
|
||||
int $notificationDate,
|
||||
bool $isRepeatBased):int {
|
||||
$query = $this->db->getQueryBuilder();
|
||||
$query->insert('calendar_reminders')
|
||||
->values([
|
||||
'calendar_id' => $query->createNamedParameter($calendarId),
|
||||
'object_id' => $query->createNamedParameter($objectId),
|
||||
'uid' => $query->createNamedParameter($uid),
|
||||
'calendarid' => $query->createNamedParameter($calendarId),
|
||||
'objecturi' => $query->createNamedParameter($uri),
|
||||
'is_recurring' => $query->createNamedParameter($isRecurring ? 1 : 0),
|
||||
'recurrence_id' => $query->createNamedParameter($recurrenceId),
|
||||
'is_recurrence_exception' => $query->createNamedParameter($isRecurrenceException ? 1 : 0),
|
||||
'event_hash' => $query->createNamedParameter($eventHash),
|
||||
'alarm_hash' => $query->createNamedParameter($alarmHash),
|
||||
'type' => $query->createNamedParameter($type),
|
||||
'notificationdate' => $query->createNamedParameter($notificationDate),
|
||||
'eventstartdate' => $query->createNamedParameter($eventStartDate),
|
||||
])->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleans reminders in database
|
||||
*
|
||||
* @param int $calendarId
|
||||
* @param string $objectUri
|
||||
*/
|
||||
public function cleanRemindersForEvent(int $calendarId, string $objectUri):void {
|
||||
$query = $this->db->getQueryBuilder();
|
||||
|
||||
$query->delete('calendar_reminders')
|
||||
->where($query->expr()->eq('calendarid', $query->createNamedParameter($calendarId)))
|
||||
->andWhere($query->expr()->eq('objecturi', $query->createNamedParameter($objectUri)))
|
||||
'is_relative' => $query->createNamedParameter($isRelative ? 1 : 0),
|
||||
'notification_date' => $query->createNamedParameter($notificationDate),
|
||||
'is_repeat_based' => $query->createNamedParameter($isRepeatBased ? 1 : 0),
|
||||
])
|
||||
->execute();
|
||||
|
||||
return $query->getLastInsertId();
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove all reminders for a calendar
|
||||
* Sets a new notificationDate on an existing reminder
|
||||
*
|
||||
* @param integer $calendarId
|
||||
* @return void
|
||||
* @param int $reminderId
|
||||
* @param int $newNotificationDate
|
||||
*/
|
||||
public function cleanRemindersForCalendar(int $calendarId):void {
|
||||
public function updateReminder(int $reminderId,
|
||||
int $newNotificationDate):void {
|
||||
$query = $this->db->getQueryBuilder();
|
||||
|
||||
$query->delete('calendar_reminders')
|
||||
->where($query->expr()->eq('calendarid', $query->createNamedParameter($calendarId)))
|
||||
$query->update('calendar_reminders')
|
||||
->set('notification_date', $query->createNamedParameter($newNotificationDate))
|
||||
->where($query->expr()->eq('id', $query->createNamedParameter($reminderId)))
|
||||
->execute();
|
||||
}
|
||||
|
||||
|
@ -113,22 +173,47 @@ class Backend {
|
|||
}
|
||||
|
||||
/**
|
||||
* Get all reminders with a notification date before now
|
||||
* Cleans reminders in database
|
||||
*
|
||||
* @return array
|
||||
* @throws \Exception
|
||||
* @param int $objectId
|
||||
*/
|
||||
public function getRemindersToProcess():array {
|
||||
public function cleanRemindersForEvent(int $objectId):void {
|
||||
$query = $this->db->getQueryBuilder();
|
||||
$fields = ['cr.id', 'cr.calendarid', 'cr.objecturi', 'cr.type', 'cr.notificationdate', 'cr.uid', 'co.calendardata', 'c.displayname'];
|
||||
$stmt = $query->select($fields)
|
||||
->from('calendar_reminders', 'cr')
|
||||
->where($query->expr()->lte('cr.notificationdate', $query->createNamedParameter($this->timeFactory->getTime())))
|
||||
->andWhere($query->expr()->gte('cr.eventstartdate', $query->createNamedParameter($this->timeFactory->getTime()))) # We check that DTSTART isn't before
|
||||
->leftJoin('cr', 'calendars', 'c', $query->expr()->eq('cr.calendarid', 'c.id'))
|
||||
->leftJoin('cr', 'calendarobjects', 'co', $query->expr()->andX($query->expr()->eq('cr.calendarid', 'c.id'), $query->expr()->eq('co.uri', 'cr.objecturi')))
|
||||
->execute();
|
||||
|
||||
return $stmt->fetchAll();
|
||||
$query->delete('calendar_reminders')
|
||||
->where($query->expr()->eq('object_id', $query->createNamedParameter($objectId)))
|
||||
->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove all reminders for a calendar
|
||||
*
|
||||
* @param int $calendarId
|
||||
* @return void
|
||||
*/
|
||||
public function cleanRemindersForCalendar(int $calendarId):void {
|
||||
$query = $this->db->getQueryBuilder();
|
||||
|
||||
$query->delete('calendar_reminders')
|
||||
->where($query->expr()->eq('calendar_id', $query->createNamedParameter($calendarId)))
|
||||
->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $row
|
||||
* @return array
|
||||
*/
|
||||
private function fixRowTyping(array $row): array {
|
||||
$row['id'] = (int) $row['id'];
|
||||
$row['calendar_id'] = (int) $row['calendar_id'];
|
||||
$row['object_id'] = (int) $row['object_id'];
|
||||
$row['is_recurring'] = (bool) $row['is_recurring'];
|
||||
$row['recurrence_id'] = (int) $row['recurrence_id'];
|
||||
$row['is_recurrence_exception'] = (bool) $row['is_recurrence_exception'];
|
||||
$row['is_relative'] = (bool) $row['is_relative'];
|
||||
$row['notification_date'] = (int) $row['notification_date'];
|
||||
$row['is_repeat_based'] = (bool) $row['is_repeat_based'];
|
||||
|
||||
return $row;
|
||||
}
|
||||
}
|
||||
|
|
47
apps/dav/lib/CalDAV/Reminder/INotificationProvider.php
Normal file
47
apps/dav/lib/CalDAV/Reminder/INotificationProvider.php
Normal file
|
@ -0,0 +1,47 @@
|
|||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* @copyright Copyright (c) 2019, Georg Ehrke
|
||||
*
|
||||
* @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;
|
||||
|
||||
use OCP\IUser;
|
||||
use Sabre\VObject\Component\VEvent;
|
||||
|
||||
/**
|
||||
* Interface INotificationProvider
|
||||
*
|
||||
* @package OCA\DAV\CalDAV\Reminder
|
||||
*/
|
||||
interface INotificationProvider {
|
||||
|
||||
/**
|
||||
* Send notification
|
||||
*
|
||||
* @param VEvent $vevent
|
||||
* @param string $calendarDisplayName
|
||||
* @param IUser[] $users
|
||||
* @return void
|
||||
*/
|
||||
public function send(VEvent $vevent,
|
||||
string $calendarDisplayName,
|
||||
array $users=[]): void;
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* @copyright Copyright (c) 2019, Georg Ehrke
|
||||
*
|
||||
|
|
|
@ -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;
|
||||
use Sabre\VObject\Component\VEvent;
|
||||
use Sabre\VObject;
|
||||
use Sabre\VObject\Parameter;
|
||||
use Sabre\VObject\Property;
|
||||
|
||||
class EmailProvider extends AbstractNotificationProvider {
|
||||
|
||||
/** @var IMailer */
|
||||
private $mailer;
|
||||
/**
|
||||
* Class EmailProvider
|
||||
*
|
||||
* @package OCA\DAV\CalDAV\Reminder\NotificationProvider
|
||||
*/
|
||||
class EmailProvider extends AbstractProvider {
|
||||
|
||||
/** @var string */
|
||||
public const NOTIFICATION_TYPE = 'EMAIL';
|
||||
|
||||
/** @var IMailer */
|
||||
private $mailer;
|
||||
|
||||
/**
|
||||
* @param IConfig $config
|
||||
* @param IMailer $mailer
|
||||
|
@ -48,7 +60,9 @@ class EmailProvider extends AbstractNotificationProvider {
|
|||
* @param L10NFactory $l10nFactory
|
||||
* @param IUrlGenerator $urlGenerator
|
||||
*/
|
||||
public function __construct(IConfig $config, IMailer $mailer, ILogger $logger,
|
||||
public function __construct(IConfig $config,
|
||||
IMailer $mailer,
|
||||
ILogger $logger,
|
||||
L10NFactory $l10nFactory,
|
||||
IURLGenerator $urlGenerator) {
|
||||
parent::__construct($logger, $l10nFactory, $urlGenerator, $config);
|
||||
|
@ -56,90 +70,100 @@ class EmailProvider extends AbstractNotificationProvider {
|
|||
}
|
||||
|
||||
/**
|
||||
* Send notification
|
||||
* Send out notification via email
|
||||
*
|
||||
* @param VCalendar $vcalendar
|
||||
* @param VEvent $vevent
|
||||
* @param string $calendarDisplayName
|
||||
* @param IUser $user
|
||||
* @return void
|
||||
* @param array $users
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function send(VCalendar $vcalendar, string $calendarDisplayName, IUser $user):void {
|
||||
if ($user->getEMailAddress() === null) {
|
||||
return;
|
||||
}
|
||||
public function send(VEvent $vevent,
|
||||
string $calendarDisplayName,
|
||||
array $users=[]):void {
|
||||
$fallbackLanguage = $this->getFallbackLanguage();
|
||||
|
||||
$lang = $this->config->getUserValue($user->getUID(), 'core', 'lang', $this->l10nFactory->findLanguage());
|
||||
$this->l10n = $this->l10nFactory->get('dav', $lang);
|
||||
$emailAddressesOfSharees = $this->getEMailAddressesOfAllUsersWithWriteAccessToCalendar($users);
|
||||
$emailAddressesOfAttendees = $this->getAllEMailAddressesFromEvent($vevent);
|
||||
|
||||
$event = $this->extractEventDetails($vcalendar);
|
||||
$fromEMail = \OCP\Util::getDefaultEmailAddress('invitations-noreply');
|
||||
|
||||
$message = $this->mailer->createMessage()
|
||||
->setFrom([$fromEMail => 'Nextcloud'])
|
||||
// TODO: Set reply to from event creator
|
||||
// ->setReplyTo([$sender => $senderName])
|
||||
->setTo([$user->getEMailAddress() => $user->getDisplayName()]);
|
||||
|
||||
$template = $this->mailer->createEMailTemplate('dav.calendarReminder', $event);
|
||||
$template->addHeader();
|
||||
|
||||
$this->addSubjectAndHeading($template, $event['title']);
|
||||
$this->addBulletList($template, $event, $calendarDisplayName);
|
||||
|
||||
$template->addFooter();
|
||||
$message->useTemplate($template);
|
||||
|
||||
$attachment = $this->mailer->createAttachment(
|
||||
$vcalendar->serialize(),
|
||||
$event['uid'].'.ics',// TODO(leon): Make file name unique, e.g. add event id
|
||||
'text/calendar'
|
||||
// Quote from php.net:
|
||||
// If the input arrays have the same string keys, then the later value for that key will overwrite the previous one.
|
||||
// => if there are duplicate email addresses, it will always take the system value
|
||||
$emailAddresses = array_merge(
|
||||
$emailAddressesOfAttendees,
|
||||
$emailAddressesOfSharees
|
||||
);
|
||||
$message->attach($attachment);
|
||||
|
||||
try {
|
||||
$failed = $this->mailer->send($message);
|
||||
if ($failed) {
|
||||
$this->logger->error('Unable to deliver message to {failed}', ['app' => 'dav', 'failed' => implode(', ', $failed)]);
|
||||
$sortedByLanguage = $this->sortEMailAddressesByLanguage($emailAddresses, $fallbackLanguage);
|
||||
$organizer = $this->getOrganizerEMailAndNameFromEvent($vevent);
|
||||
|
||||
foreach($sortedByLanguage as $lang => $emailAddresses) {
|
||||
if ($this->hasL10NForLang($lang)) {
|
||||
$lang = $fallbackLanguage;
|
||||
}
|
||||
$l10n = $this->getL10NForLang($lang);
|
||||
$fromEMail = \OCP\Util::getDefaultEmailAddress('reminders-noreply');
|
||||
|
||||
$message = $this->mailer->createMessage();
|
||||
$message->setFrom([$fromEMail]);
|
||||
if ($organizer) {
|
||||
$message->setReplyTo($organizer);
|
||||
}
|
||||
$message->setBcc($emailAddresses);
|
||||
|
||||
$template = $this->mailer->createEMailTemplate('dav.calendarReminder');
|
||||
$template->addHeader();
|
||||
|
||||
$this->addSubjectAndHeading($template, $l10n, $vevent);
|
||||
$this->addBulletList($template, $l10n, $calendarDisplayName, $vevent);
|
||||
|
||||
$template->addFooter();
|
||||
$message->useTemplate($template);
|
||||
|
||||
try {
|
||||
$failed = $this->mailer->send($message);
|
||||
if ($failed) {
|
||||
$this->logger->error('Unable to deliver message to {failed}', ['app' => 'dav', 'failed' => implode(', ', $failed)]);
|
||||
}
|
||||
} catch (\Exception $ex) {
|
||||
$this->logger->logException($ex, ['app' => 'dav']);
|
||||
}
|
||||
} catch(\Exception $ex) {
|
||||
$this->logger->logException($ex, ['app' => 'dav']);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param IEMailTemplate $template
|
||||
* @param string $summary
|
||||
* @param IL10N $l10n
|
||||
* @param VEvent $vevent
|
||||
*/
|
||||
private function addSubjectAndHeading(IEMailTemplate $template, string $summary):void {
|
||||
$template->setSubject('Notification: ' . $summary);
|
||||
$template->addHeading($summary);
|
||||
private function addSubjectAndHeading(IEMailTemplate $template, IL10N $l10n, VEvent $vevent):void {
|
||||
$template->setSubject('Notification: ' . $this->getTitleFromVEvent($vevent, $l10n));
|
||||
$template->addHeading($this->getTitleFromVEvent($vevent, $l10n));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param IEMailTemplate $template
|
||||
* @param array $eventData
|
||||
* @param IL10N $l10n
|
||||
* @param string $calendarDisplayName
|
||||
* @param array $eventData
|
||||
*/
|
||||
private function addBulletList(IEMailTemplate $template, array $eventData, string $calendarDisplayName):void {
|
||||
$template->addBodyListItem($calendarDisplayName, $this->l10n->t('Calendar:'),
|
||||
private function addBulletList(IEMailTemplate $template,
|
||||
IL10N $l10n,
|
||||
string $calendarDisplayName,
|
||||
VEvent $vevent):void {
|
||||
$template->addBodyListItem($calendarDisplayName, $l10n->t('Calendar:'),
|
||||
$this->getAbsoluteImagePath('actions/info.svg'));
|
||||
|
||||
$template->addBodyListItem($eventData['when'], $this->l10n->t('Date:'),
|
||||
$template->addBodyListItem($this->generateDateString($l10n, $vevent), $l10n->t('Date:'),
|
||||
$this->getAbsoluteImagePath('places/calendar.svg'));
|
||||
|
||||
if ($eventData['location']) {
|
||||
$template->addBodyListItem((string) $eventData['location'], $this->l10n->t('Where:'),
|
||||
if (isset($vevent->LOCATION)) {
|
||||
$template->addBodyListItem((string) $vevent->LOCATION, $l10n->t('Where:'),
|
||||
$this->getAbsoluteImagePath('actions/address.svg'));
|
||||
}
|
||||
if ($eventData['description']) {
|
||||
$template->addBodyListItem((string) $eventData['description'], $this->l10n->t('Description:'),
|
||||
if (isset($vevent->DESCRIPTION)) {
|
||||
$template->addBodyListItem((string) $vevent->DESCRIPTION, $l10n->t('Description:'),
|
||||
$this->getAbsoluteImagePath('actions/more.svg'));
|
||||
}
|
||||
if ($eventData['url']) {
|
||||
$template->addBodyListItem((string) $eventData['url'], $this->l10n->t('Link:'),
|
||||
$this->getAbsoluteImagePath('places/link.svg'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -151,4 +175,355 @@ class EmailProvider extends AbstractNotificationProvider {
|
|||
$this->urlGenerator->imagePath('core', $path)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param VEvent $vevent
|
||||
* @return array|null
|
||||
*/
|
||||
private function getOrganizerEMailAndNameFromEvent(VEvent $vevent):?array {
|
||||
if (!$vevent->ORGANIZER) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$organizer = $vevent->ORGANZIER;
|
||||
if (strcasecmp($organizer->getValue(), 'mailto:') !== 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$organizerEMail = substr($organizer->getValue(), 7);
|
||||
|
||||
$name = $organizer->offsetGet('CN');
|
||||
if ($name instanceof Parameter) {
|
||||
return [$organizerEMail => $name];
|
||||
}
|
||||
|
||||
return [$organizerEMail];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $sortedByLanguage
|
||||
* @param IUser[] $users
|
||||
* @param string $defaultLanguage
|
||||
*/
|
||||
private function sortUsersByLanguage(array &$sortedByLanguage,
|
||||
array $users,
|
||||
string $defaultLanguage):void {
|
||||
/**
|
||||
* @var array $sortedByLanguage
|
||||
* [
|
||||
* 'de' => ['a@b.com', 'c@d.com'],
|
||||
* ...
|
||||
* ]
|
||||
*/
|
||||
foreach($users as $user) {
|
||||
/** @var IUser $user */
|
||||
$emailAddress = $user->getEMailAddress();
|
||||
$lang = $this->config->getUserValue($user->getUID(),
|
||||
'core', 'lang', $defaultLanguage);
|
||||
|
||||
if (!isset($sortedByLanguage[$lang])) {
|
||||
$sortedByLanguage[$lang] = [];
|
||||
}
|
||||
|
||||
$sortedByLanguage[$lang][] = $emailAddress;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $emails
|
||||
* @param string $defaultLanguage
|
||||
* @return array
|
||||
*/
|
||||
private function sortEMailAddressesByLanguage(array $emails,
|
||||
string $defaultLanguage):array {
|
||||
$sortedByLanguage = [];
|
||||
|
||||
foreach($emails as $emailAddress => $parameters) {
|
||||
if (isset($parameters['LANG'])) {
|
||||
$lang = $parameters['LANG'];
|
||||
} else {
|
||||
$lang = $defaultLanguage;
|
||||
}
|
||||
|
||||
if (!isset($sortedByLanguage[$lang])) {
|
||||
$sortedByLanguage[$lang] = [];
|
||||
}
|
||||
|
||||
$sortedByLanguage[$lang][] = $emailAddress;
|
||||
}
|
||||
|
||||
return $sortedByLanguage;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param VEvent $vevent
|
||||
* @return array
|
||||
*/
|
||||
private function getAllEMailAddressesFromEvent(VEvent $vevent):array {
|
||||
$emailAddresses = [];
|
||||
|
||||
if (isset($vevent->ATTENDEE)) {
|
||||
foreach ($vevent->ATTENDEE as $attendee) {
|
||||
if (!($attendee instanceof VObject\Property)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$cuType = $this->getCUTypeOfAttendee($attendee);
|
||||
if (\in_array($cuType, ['RESOURCE', 'ROOM', 'UNKNOWN'])) {
|
||||
// Don't send emails to things
|
||||
continue;
|
||||
}
|
||||
|
||||
$partstat = $this->getPartstatOfAttendee($attendee);
|
||||
if ($partstat === 'DECLINED') {
|
||||
// Don't send out emails to people who declined
|
||||
continue;
|
||||
}
|
||||
if ($partstat === 'DELEGATED') {
|
||||
$delegates = $attendee->offsetGet('DELEGATED-TO');
|
||||
if (!($delegates instanceof VObject\Parameter)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$emailAddressesOfDelegates = $delegates->getParts();
|
||||
foreach($emailAddressesOfDelegates as $addressesOfDelegate) {
|
||||
if (strcasecmp($addressesOfDelegate, 'mailto:') === 0) {
|
||||
$emailAddresses[substr($addressesOfDelegate, 7)] = [];
|
||||
}
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
$emailAddressOfAttendee = $this->getEMailAddressOfAttendee($attendee);
|
||||
if ($emailAddressOfAttendee !== null) {
|
||||
$properties = [];
|
||||
|
||||
$langProp = $attendee->offsetGet('LANG');
|
||||
if ($langProp instanceof VObject\Parameter) {
|
||||
$properties['LANG'] = $langProp->getValue();
|
||||
}
|
||||
|
||||
$emailAddresses[$emailAddressOfAttendee] = $properties;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($vevent->ORGANIZER)) {
|
||||
$emailAddresses[$this->getEMailAddressOfAttendee($vevent->ORGANIZER)] = [];
|
||||
}
|
||||
|
||||
return $emailAddresses;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @param VObject\Property $attendee
|
||||
* @return string
|
||||
*/
|
||||
private function getCUTypeOfAttendee(VObject\Property $attendee):string {
|
||||
$cuType = $attendee->offsetGet('CUTYPE');
|
||||
if ($cuType instanceof VObject\Parameter) {
|
||||
return strtoupper($cuType->getValue());
|
||||
}
|
||||
|
||||
return 'INDIVIDUAL';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param VObject\Property $attendee
|
||||
* @return string
|
||||
*/
|
||||
private function getPartstatOfAttendee(VObject\Property $attendee):string {
|
||||
$partstat = $attendee->offsetGet('PARTSTAT');
|
||||
if ($partstat instanceof VObject\Parameter) {
|
||||
return strtoupper($partstat->getValue());
|
||||
}
|
||||
|
||||
return 'NEEDS-ACTION';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param VObject\Property $attendee
|
||||
* @return bool
|
||||
*/
|
||||
private function hasAttendeeMailURI(VObject\Property $attendee):bool {
|
||||
return strcasecmp($attendee->getValue(), 'mailto:') === 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param VObject\Property $attendee
|
||||
* @return string|null
|
||||
*/
|
||||
private function getEMailAddressOfAttendee(VObject\Property $attendee):?string {
|
||||
if (!$this->hasAttendeeMailURI($attendee)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return substr($attendee->getValue(), 7);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $users
|
||||
* @return array
|
||||
*/
|
||||
private function getEMailAddressesOfAllUsersWithWriteAccessToCalendar(array $users):array {
|
||||
$emailAddresses = [];
|
||||
|
||||
foreach($users as $user) {
|
||||
$emailAddress = $user->getEMailAddress();
|
||||
if ($emailAddress) {
|
||||
$lang = $this->getLangForUser($user);
|
||||
if ($lang) {
|
||||
$emailAddresses[$emailAddress] = [
|
||||
'LANG' => $lang,
|
||||
];
|
||||
} else {
|
||||
$emailAddresses[$emailAddress] = [];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return array_unique($emailAddresses);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param IUser $user
|
||||
* @return string
|
||||
*/
|
||||
private function getLangForUser(IUser $user): ?string {
|
||||
return $this->config->getUserValue($user->getUID(), 'core', 'lang', null);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param IL10N $l10n
|
||||
* @param VEvent $vevent
|
||||
* @return string
|
||||
* @throws \Exception
|
||||
*/
|
||||
private function generateDateString(IL10N $l10n, VEvent $vevent):string {
|
||||
$isAllDay = $vevent->DTSTART instanceof Property\ICalendar\Date;
|
||||
|
||||
/** @var Property\ICalendar\Date | Property\ICalendar\DateTime $dtstart */
|
||||
/** @var Property\ICalendar\Date | Property\ICalendar\DateTime $dtend */
|
||||
/** @var \DateTimeImmutable $dtstartDt */
|
||||
$dtstartDt = $vevent->DTSTART->getDateTime();
|
||||
/** @var \DateTimeImmutable $dtendDt */
|
||||
$dtendDt = $this->getDTEndFromEvent($vevent)->getDateTime();
|
||||
|
||||
$diff = $dtstartDt->diff($dtendDt);
|
||||
|
||||
$dtstartDt = new \DateTime($dtstartDt->format(\DateTime::ATOM));
|
||||
$dtendDt = new \DateTime($dtendDt->format(\DateTime::ATOM));
|
||||
|
||||
if ($isAllDay) {
|
||||
// One day event
|
||||
if ($diff->days === 1) {
|
||||
return $this->getDateString($l10n, $dtstartDt);
|
||||
}
|
||||
|
||||
return implode(' - ', [
|
||||
$this->getDateString($l10n, $dtstartDt),
|
||||
$this->getDateString($l10n, $dtendDt),
|
||||
]);
|
||||
}
|
||||
|
||||
$startTimezone = $endTimezone = null;
|
||||
if (!$vevent->DTSTART->isFloating()) {
|
||||
$startTimezone = $vevent->DTSTART->getDateTime()->getTimezone()->getName();
|
||||
$endTimezone = $this->getDTEndFromEvent($vevent)->getDateTime()->getTimezone()->getName();
|
||||
}
|
||||
|
||||
$localeStart = implode(', ', [
|
||||
$this->getWeekDayName($l10n, $dtstartDt),
|
||||
$this->getDateTimeString($l10n, $dtstartDt)
|
||||
]);
|
||||
|
||||
// always show full date with timezone if timezones are different
|
||||
if ($startTimezone !== $endTimezone) {
|
||||
$localeEnd = implode(', ', [
|
||||
$this->getWeekDayName($l10n, $dtendDt),
|
||||
$this->getDateTimeString($l10n, $dtendDt)
|
||||
]);
|
||||
|
||||
return $localeStart
|
||||
. ' (' . $startTimezone . ') '
|
||||
. ' - '
|
||||
. $localeEnd
|
||||
. ' (' . $endTimezone . ')';
|
||||
}
|
||||
|
||||
// Show only the time if the day is the same
|
||||
$localeEnd = $this->isDayEqual($dtstartDt, $dtendDt)
|
||||
? $this->getTimeString($l10n, $dtendDt)
|
||||
: implode(', ', [
|
||||
$this->getWeekDayName($l10n, $dtendDt),
|
||||
$this->getDateTimeString($l10n, $dtendDt)
|
||||
]);
|
||||
|
||||
return $localeStart
|
||||
. ' - '
|
||||
. $localeEnd
|
||||
. ' (' . $startTimezone . ')';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param DateTime $dtStart
|
||||
* @param DateTime $dtEnd
|
||||
* @return bool
|
||||
*/
|
||||
private function isDayEqual(DateTime $dtStart,
|
||||
DateTime $dtEnd):bool {
|
||||
return $dtStart->format('Y-m-d') === $dtEnd->format('Y-m-d');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param IL10N $l10n
|
||||
* @param DateTime $dt
|
||||
* @return string
|
||||
*/
|
||||
private function getWeekDayName(IL10N $l10n, DateTime $dt):string {
|
||||
return $l10n->l('weekdayName', $dt, ['width' => 'abbreviated']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param IL10N $l10n
|
||||
* @param DateTime $dt
|
||||
* @return string
|
||||
*/
|
||||
private function getDateString(IL10N $l10n, DateTime $dt):string {
|
||||
return $l10n->l('date', $dt, ['width' => 'medium']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param IL10N $l10n
|
||||
* @param DateTime $dt
|
||||
* @return string
|
||||
*/
|
||||
private function getDateTimeString(IL10N $l10n, DateTime $dt):string {
|
||||
return $l10n->l('datetime', $dt, ['width' => 'medium|short']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param IL10N $l10n
|
||||
* @param DateTime $dt
|
||||
* @return string
|
||||
*/
|
||||
private function getTimeString(IL10N $l10n, DateTime $dt):string {
|
||||
return $l10n->l('time', $dt, ['width' => 'short']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param VEvent $vevent
|
||||
* @param IL10N $l10n
|
||||
* @return string
|
||||
*/
|
||||
private function getTitleFromVEvent(VEvent $vevent, IL10N $l10n):string {
|
||||
if (isset($vevent->SUMMARY)) {
|
||||
return (string)$vevent->SUMMARY;
|
||||
}
|
||||
|
||||
return $l10n->t('Untitled event');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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(),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,11 @@
|
|||
<?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 AGPL-3.0
|
||||
*
|
||||
|
@ -19,36 +24,55 @@
|
|||
*/
|
||||
namespace OCA\DAV\CalDAV\Reminder;
|
||||
|
||||
use OCA\DAV\CalDAV\Reminder\NotificationProvider\ProviderNotAvailableException;
|
||||
|
||||
/**
|
||||
* Class NotificationProviderManager
|
||||
*
|
||||
* @package OCA\DAV\CalDAV\Reminder
|
||||
*/
|
||||
class NotificationProviderManager {
|
||||
|
||||
/** @var array */
|
||||
/** @var INotificationProvider[] */
|
||||
private $providers = [];
|
||||
|
||||
/**
|
||||
* Checks whether a provider for a given ACTION exists
|
||||
*
|
||||
* @param string $type
|
||||
* @return bool
|
||||
*/
|
||||
public function hasProvider(string $type):bool {
|
||||
return (\in_array($type, ReminderService::REMINDER_TYPES, true)
|
||||
&& isset($this->providers[$type]));
|
||||
}
|
||||
|
||||
/**
|
||||
* @var string $type
|
||||
* @return AbstractNotificationProvider
|
||||
* @throws ProviderNotAvailableException
|
||||
* Get provider for a given ACTION
|
||||
*
|
||||
* @param string $type
|
||||
* @return INotificationProvider
|
||||
* @throws NotificationProvider\ProviderNotAvailableException
|
||||
* @throws NotificationTypeDoesNotExistException
|
||||
*/
|
||||
public function getProvider(string $type):AbstractNotificationProvider {
|
||||
public function getProvider(string $type):INotificationProvider {
|
||||
if (in_array($type, ReminderService::REMINDER_TYPES, true)) {
|
||||
if (isset($this->providers[$type])) {
|
||||
return $this->providers[$type];
|
||||
}
|
||||
throw new ProviderNotAvailableException($type);
|
||||
throw new NotificationProvider\ProviderNotAvailableException($type);
|
||||
}
|
||||
throw new NotificationTypeDoesNotExistException($type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a new provider
|
||||
*
|
||||
* @param string $providerClassName
|
||||
* @throws \OCP\AppFramework\QueryException
|
||||
*/
|
||||
public function registerProvider(string $providerClassName):void {
|
||||
$provider = \OC::$server->query($providerClassName);
|
||||
|
||||
if (!$provider instanceof AbstractNotificationProvider) {
|
||||
if (!$provider instanceof INotificationProvider) {
|
||||
throw new \InvalidArgumentException('Invalid notification provider registered');
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* @copyright Copyright (c) 2018 Thomas Citharel <tcit@tcit.fr>
|
||||
* @copyright Copyright (c) 2019, Thomas Citharel
|
||||
*
|
||||
* @author Thomas Citharel <tcit@tcit.fr>
|
||||
*
|
||||
|
|
|
@ -1,8 +1,11 @@
|
|||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* @copyright Copyright (c) 2019 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 AGPL-3.0
|
||||
*
|
||||
|
@ -22,37 +25,47 @@
|
|||
|
||||
namespace OCA\DAV\CalDAV\Reminder;
|
||||
|
||||
use DateTime;
|
||||
use OCA\DAV\AppInfo\Application;
|
||||
use OCP\AppFramework\Utility\ITimeFactory;
|
||||
use OCP\IL10N;
|
||||
use OCP\L10N\IFactory;
|
||||
use OCP\Notification\INotification;
|
||||
use OCP\Notification\INotifier;
|
||||
use OCP\IURLGenerator;
|
||||
|
||||
/**
|
||||
* Class Notifier
|
||||
*
|
||||
* @package OCA\DAV\CalDAV\Reminder
|
||||
*/
|
||||
class Notifier implements INotifier {
|
||||
|
||||
/** @var array */
|
||||
public static $units = [
|
||||
'y' => 'year',
|
||||
'm' => 'month',
|
||||
'd' => 'day',
|
||||
'h' => 'hour',
|
||||
'i' => 'minute',
|
||||
's' => 'second',
|
||||
];
|
||||
|
||||
/** @var IFactory */
|
||||
protected $factory;
|
||||
private $l10nFactory;
|
||||
|
||||
/** @var IURLGenerator */
|
||||
protected $urlGenerator;
|
||||
private $urlGenerator;
|
||||
|
||||
/** @var IL10N */
|
||||
protected $l;
|
||||
private $l10n;
|
||||
|
||||
public function __construct(IFactory $factory, IURLGenerator $urlGenerator) {
|
||||
$this->factory = $factory;
|
||||
/** @var ITimeFactory */
|
||||
private $timeFactory;
|
||||
|
||||
/**
|
||||
* Notifier constructor.
|
||||
*
|
||||
* @param IFactory $factory
|
||||
* @param IURLGenerator $urlGenerator
|
||||
* @param ITimeFactory $timeFactory
|
||||
*/
|
||||
public function __construct(IFactory $factory,
|
||||
IURLGenerator $urlGenerator,
|
||||
ITimeFactory $timeFactory) {
|
||||
$this->l10nFactory = $factory;
|
||||
$this->urlGenerator = $urlGenerator;
|
||||
$this->timeFactory = $timeFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -62,7 +75,7 @@ class Notifier implements INotifier {
|
|||
* @since 17.0.0
|
||||
*/
|
||||
public function getID():string {
|
||||
return 'dav';
|
||||
return Application::APP_ID;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -72,89 +85,236 @@ class Notifier implements INotifier {
|
|||
* @since 17.0.0
|
||||
*/
|
||||
public function getName():string {
|
||||
return $this->factory->get('dav')->t('Calendar');
|
||||
if ($this->l10n) {
|
||||
return $this->l10n->t('Calendar');
|
||||
}
|
||||
|
||||
return $this->l10nFactory->get('dav')->t('Calendar');
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare sending the notification
|
||||
*
|
||||
* @param INotification $notification
|
||||
* @param string $languageCode The code of the language that should be used to prepare the notification
|
||||
* @return INotification
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function prepare(INotification $notification, string $languageCode):INotification {
|
||||
public function prepare(INotification $notification,
|
||||
string $languageCode):INotification {
|
||||
if ($notification->getApp() !== Application::APP_ID) {
|
||||
throw new \InvalidArgumentException('Notification not from this app');
|
||||
}
|
||||
|
||||
// Read the language from the notification
|
||||
$this->l = $this->factory->get('dav', $languageCode);
|
||||
$this->l10n = $this->l10nFactory->get('dav', $languageCode);
|
||||
|
||||
if ($notification->getSubject() === 'calendar_reminder') {
|
||||
$subjectParameters = $notification->getSubjectParameters();
|
||||
$notification->setParsedSubject($this->processEventTitle($subjectParameters));
|
||||
// Handle notifier subjects
|
||||
switch($notification->getSubject()) {
|
||||
case 'calendar_reminder':
|
||||
return $this->prepareReminderNotification($notification);
|
||||
|
||||
default:
|
||||
throw new \InvalidArgumentException('Unknown subject');
|
||||
|
||||
$messageParameters = $notification->getMessageParameters();
|
||||
$notification->setParsedMessage($this->processEventDescription($messageParameters));
|
||||
$notification->setIcon($this->urlGenerator->getAbsoluteURL($this->urlGenerator->imagePath('core', 'places/calendar.svg')));
|
||||
return $notification;
|
||||
}
|
||||
// Unknown subject => Unknown notification => throw
|
||||
throw new \InvalidArgumentException('Unknown subject');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $event
|
||||
* @param INotification $notification
|
||||
* @return INotification
|
||||
*/
|
||||
private function prepareReminderNotification(INotification $notification):INotification {
|
||||
$imagePath = $this->urlGenerator->imagePath('core', 'places/calendar.svg');
|
||||
$iconUrl = $this->urlGenerator->getAbsoluteURL($imagePath);
|
||||
$notification->setIcon($iconUrl);
|
||||
|
||||
$this->prepareNotificationSubject($notification);
|
||||
$this->prepareNotificationMessage($notification);
|
||||
|
||||
return $notification;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the notification subject based on the parameters set in PushProvider
|
||||
*
|
||||
* @param INotification $notification
|
||||
*/
|
||||
private function prepareNotificationSubject(INotification $notification): void {
|
||||
$parameters = $notification->getSubjectParameters();
|
||||
|
||||
$startTime = \DateTime::createFromFormat(\DateTimeInterface::ATOM, $parameters['start_atom']);
|
||||
$now = $this->timeFactory->getDateTime();
|
||||
$title = $this->getTitleFromParameters($parameters);
|
||||
|
||||
$diff = $startTime->diff($now);
|
||||
if ($diff === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
$components = [];
|
||||
if ($diff->y) {
|
||||
$components[] = $this->l10n->n('%n year', '%n years', $diff->y);
|
||||
}
|
||||
if ($diff->m) {
|
||||
$components[] = $this->l10n->n('%n month', '%n months', $diff->m);
|
||||
}
|
||||
if ($diff->d) {
|
||||
$components[] = $this->l10n->n('%n day', '%n days', $diff->d);
|
||||
}
|
||||
if ($diff->h) {
|
||||
$components[] = $this->l10n->n('%n hour', '%n hours', $diff->h);
|
||||
}
|
||||
if ($diff->i) {
|
||||
$components[] = $this->l10n->n('%n minute', '%n minutes', $diff->i);
|
||||
}
|
||||
|
||||
// Limiting to the first three components to prevent
|
||||
// the string from getting too long
|
||||
$firstThreeComponents = array_slice($components, 0, 2);
|
||||
$diffLabel = implode(', ', $firstThreeComponents);
|
||||
|
||||
if ($diff->invert) {
|
||||
$title = $this->l10n->t('%s (in %s)', [$title, $diffLabel]);
|
||||
} else {
|
||||
$title = $this->l10n->t('%s (%s ago)', [$title, $diffLabel]);
|
||||
}
|
||||
|
||||
$notification->setParsedSubject($title);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the notification message based on the parameters set in PushProvider
|
||||
*
|
||||
* @param INotification $notification
|
||||
*/
|
||||
private function prepareNotificationMessage(INotification $notification): void {
|
||||
$parameters = $notification->getMessageParameters();
|
||||
|
||||
$description = [
|
||||
$this->l10n->t('Calendar: %s', $parameters['calendar_displayname']),
|
||||
$this->l10n->t('Date: %s', $this->generateDateString($parameters)),
|
||||
];
|
||||
if ($parameters['description']) {
|
||||
$description[] = $this->l10n->t('Description: %s', $parameters['description']);
|
||||
}
|
||||
if ($parameters['location']) {
|
||||
$description[] = $this->l10n->t('Where: %s', $parameters['location']);
|
||||
}
|
||||
|
||||
$message = implode("\r\n", $description);
|
||||
$notification->setParsedMessage($message);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $parameters
|
||||
* @return string
|
||||
*/
|
||||
private function getTitleFromParameters(array $parameters):string {
|
||||
return $parameters['title'] ?? $this->l10n->t('Untitled event');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $parameters
|
||||
* @return string
|
||||
* @throws \Exception
|
||||
*/
|
||||
private function processEventTitle(array $event):string {
|
||||
$event_datetime = new \DateTime();
|
||||
$event_datetime->setTimestamp($event['start']);
|
||||
$now = new \DateTime();
|
||||
private function generateDateString(array $parameters):string {
|
||||
$startDateTime = DateTime::createFromFormat(DATE_ATOM, $parameters['start_atom']);
|
||||
$endDateTime = DateTime::createFromFormat(DATE_ATOM, $parameters['end_atom']);
|
||||
$isAllDay = $parameters['all_day'];
|
||||
$diff = $startDateTime->diff($endDateTime);
|
||||
|
||||
$diff = $event_datetime->diff($now);
|
||||
if ($isAllDay) {
|
||||
// One day event
|
||||
if ($diff->days === 1) {
|
||||
return $this->getDateString($startDateTime);
|
||||
}
|
||||
|
||||
foreach (self::$units as $attribute => $unit) {
|
||||
$count = $diff->$attribute;
|
||||
if (0 !== $count) {
|
||||
return $this->getPluralizedTitle($count, $diff->invert, $unit, $event['title']);
|
||||
}
|
||||
}
|
||||
return '';
|
||||
return implode(' - ', [
|
||||
$this->getDateString($startDateTime),
|
||||
$this->getDateString($endDateTime),
|
||||
]);
|
||||
}
|
||||
|
||||
$startTimezone = $endTimezone = null;
|
||||
if (!$parameters['start_is_floating']) {
|
||||
$startTimezone = $parameters['start_timezone'];
|
||||
$endTimezone = $parameters['end_timezone'];
|
||||
}
|
||||
|
||||
$localeStart = implode(', ', [
|
||||
$this->getWeekDayName($startDateTime),
|
||||
$this->getDateTimeString($startDateTime)
|
||||
]);
|
||||
|
||||
// always show full date with timezone if timezones are different
|
||||
if ($startTimezone !== $endTimezone) {
|
||||
$localeEnd = implode(', ', [
|
||||
$this->getWeekDayName($endDateTime),
|
||||
$this->getDateTimeString($endDateTime)
|
||||
]);
|
||||
|
||||
return $localeStart
|
||||
. ' (' . $startTimezone . ') '
|
||||
. ' - '
|
||||
. $localeEnd
|
||||
. ' (' . $endTimezone . ')';
|
||||
}
|
||||
|
||||
// Show only the time if the day is the same
|
||||
$localeEnd = $this->isDayEqual($startDateTime, $endDateTime)
|
||||
? $this->getTimeString($endDateTime)
|
||||
: implode(', ', [
|
||||
$this->getWeekDayName($endDateTime),
|
||||
$this->getDateTimeString($endDateTime)
|
||||
]);
|
||||
|
||||
return $localeStart
|
||||
. ' - '
|
||||
. $localeEnd
|
||||
. ' (' . $startTimezone . ')';
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param int $count
|
||||
* @param int $invert
|
||||
* @param string $unit
|
||||
* @param string $title
|
||||
* @return string
|
||||
* @param DateTime $dtStart
|
||||
* @param DateTime $dtEnd
|
||||
* @return bool
|
||||
*/
|
||||
private function getPluralizedTitle(int $count, int $invert, string $unit, string $title):string {
|
||||
if ($invert) {
|
||||
return $this->l->n('%s (in one %s)', '%s (in %n %ss)', $count, [$title, $unit]);
|
||||
}
|
||||
// This should probably not show up
|
||||
return $this->l->n('%s (one %s ago)', '%s (%n %ss ago)', $count, [$title, $unit]);
|
||||
private function isDayEqual(DateTime $dtStart,
|
||||
DateTime $dtEnd):bool {
|
||||
return $dtStart->format('Y-m-d') === $dtEnd->format('Y-m-d');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $event
|
||||
* @param DateTime $dt
|
||||
* @return string
|
||||
*/
|
||||
private function processEventDescription(array $event):string {
|
||||
$description = [
|
||||
$this->l->t('Calendar: %s', $event['calendar']),
|
||||
$this->l->t('Date: %s', $event['when']),
|
||||
];
|
||||
private function getWeekDayName(DateTime $dt):string {
|
||||
return $this->l10n->l('weekdayName', $dt, ['width' => 'abbreviated']);
|
||||
}
|
||||
|
||||
if ($event['description']) {
|
||||
$description[] = $this->l->t('Description: %s', $event['description']);
|
||||
}
|
||||
if ($event['location']) {
|
||||
$description[] = $this->l->t('Where: %s', $event['location']);
|
||||
}
|
||||
return implode('<br>', $description);
|
||||
/**
|
||||
* @param DateTime $dt
|
||||
* @return string
|
||||
*/
|
||||
private function getDateString(DateTime $dt):string {
|
||||
return $this->l10n->l('date', $dt, ['width' => 'medium']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param DateTime $dt
|
||||
* @return string
|
||||
*/
|
||||
private function getDateTimeString(DateTime $dt):string {
|
||||
return $this->l10n->l('datetime', $dt, ['width' => 'medium|short']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param DateTime $dt
|
||||
* @return string
|
||||
*/
|
||||
private function getTimeString(DateTime $dt):string {
|
||||
return $this->l10n->l('time', $dt, ['width' => 'short']);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,11 @@
|
|||
<?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 AGPL-3.0
|
||||
*
|
||||
|
@ -19,14 +24,19 @@
|
|||
*/
|
||||
namespace OCA\DAV\CalDAV\Reminder;
|
||||
|
||||
use OC\User\NoUserException;
|
||||
use DateTimeImmutable;
|
||||
use OCA\DAV\CalDAV\CalDavBackend;
|
||||
use OCP\AppFramework\Utility\ITimeFactory;
|
||||
use OCP\IGroup;
|
||||
use OCP\IGroupManager;
|
||||
use OCP\IUser;
|
||||
use OCP\IUserManager;
|
||||
use OCP\IUserSession;
|
||||
use Sabre\VObject;
|
||||
use Sabre\VObject\Component\VAlarm;
|
||||
use Sabre\VObject\Reader;
|
||||
use Sabre\VObject\Component\VEvent;
|
||||
use Sabre\VObject\ParseException;
|
||||
use Sabre\VObject\Recur\EventIterator;
|
||||
use Sabre\VObject\Recur\NoInstancesException;
|
||||
|
||||
class ReminderService {
|
||||
|
||||
|
@ -42,144 +52,710 @@ class ReminderService {
|
|||
/** @var IGroupManager */
|
||||
private $groupManager;
|
||||
|
||||
/** @var IUserSession */
|
||||
private $userSession;
|
||||
/** @var CalDavBackend */
|
||||
private $caldavBackend;
|
||||
|
||||
/** @var ITimeFactory */
|
||||
private $timeFactory;
|
||||
|
||||
public const REMINDER_TYPE_EMAIL = 'EMAIL';
|
||||
public const REMINDER_TYPE_DISPLAY = 'DISPLAY';
|
||||
public const REMINDER_TYPE_AUDIO = 'AUDIO';
|
||||
|
||||
/**
|
||||
* @var String[]
|
||||
*
|
||||
* Official RFC5545 reminder types
|
||||
*/
|
||||
public const REMINDER_TYPES = [
|
||||
self::REMINDER_TYPE_EMAIL,
|
||||
self::REMINDER_TYPE_DISPLAY,
|
||||
self::REMINDER_TYPE_AUDIO
|
||||
];
|
||||
|
||||
/**
|
||||
* ReminderService constructor.
|
||||
*
|
||||
* @param Backend $backend
|
||||
* @param NotificationProviderManager $notificationProviderManager
|
||||
* @param IUserManager $userManager
|
||||
* @param IGroupManager $groupManager
|
||||
* @param CalDavBackend $caldavBackend
|
||||
* @param ITimeFactory $timeFactory
|
||||
*/
|
||||
public function __construct(Backend $backend,
|
||||
NotificationProviderManager $notificationProviderManager,
|
||||
IUserManager $userManager,
|
||||
IGroupManager $groupManager,
|
||||
IUserSession $userSession) {
|
||||
CalDavBackend $caldavBackend,
|
||||
ITimeFactory $timeFactory) {
|
||||
$this->backend = $backend;
|
||||
$this->notificationProviderManager = $notificationProviderManager;
|
||||
$this->userManager = $userManager;
|
||||
$this->groupManager = $groupManager;
|
||||
$this->userSession = $userSession;
|
||||
$this->caldavBackend = $caldavBackend;
|
||||
$this->timeFactory = $timeFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process reminders to activate
|
||||
*
|
||||
* @throws NoUserException
|
||||
* @throws NotificationProvider\ProviderNotAvailableException
|
||||
* @throws NotificationTypeDoesNotExistException
|
||||
*/
|
||||
public function processReminders():void {
|
||||
$reminders = $this->backend->getRemindersToProcess();
|
||||
|
||||
foreach ($reminders as $reminder) {
|
||||
$calendarData = Reader::read($reminder['calendardata']);
|
||||
foreach($reminders as $reminder) {
|
||||
$vcalendar = $this->parseCalendarData($reminder['calendardata']);
|
||||
if (!$vcalendar) {
|
||||
$this->backend->removeReminder($reminder['id']);
|
||||
continue;
|
||||
}
|
||||
|
||||
$user = $this->userManager->get($reminder['uid']);
|
||||
$vevent = $this->getVEventByRecurrenceId($vcalendar, $reminder['recurrence_id'], $reminder['is_recurrence_exception']);
|
||||
if (!$vevent) {
|
||||
$this->backend->removeReminder($reminder['id']);
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($user === null) {
|
||||
throw new NoUserException('User not found for calendar');
|
||||
if ($this->wasEventCancelled($vevent)) {
|
||||
$this->deleteOrProcessNext($reminder, $vevent);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!$this->notificationProviderManager->hasProvider($reminder['type'])) {
|
||||
$this->deleteOrProcessNext($reminder, $vevent);
|
||||
continue;
|
||||
}
|
||||
|
||||
$users = $this->getAllUsersWithWriteAccessToCalendar($reminder['calendar_id']);
|
||||
$user = $this->getUserFromPrincipalURI($reminder['principaluri']);
|
||||
if ($user) {
|
||||
$users[] = $user;
|
||||
}
|
||||
|
||||
$notificationProvider = $this->notificationProviderManager->getProvider($reminder['type']);
|
||||
$notificationProvider->send($calendarData, $reminder['displayname'], $user);
|
||||
$this->backend->removeReminder($reminder['id']);
|
||||
$notificationProvider->send($vevent, $reminder['displayname'], $users);
|
||||
|
||||
$this->deleteOrProcessNext($reminder, $vevent);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves reminders when a calendar object with some alarms was created/updated/deleted
|
||||
*
|
||||
* @param string $action
|
||||
* @param array $calendarData
|
||||
* @param array $shares
|
||||
* @param array $objectData
|
||||
* @return void
|
||||
* @throws VObject\InvalidDataException
|
||||
* @throws NoUserException
|
||||
*/
|
||||
public function onTouchCalendarObject(string $action, array $calendarData, array $shares, array $objectData):void {
|
||||
if (!isset($calendarData['principaluri'])) {
|
||||
public function onTouchCalendarObject(string $action,
|
||||
array $objectData):void {
|
||||
// We only support VEvents for now
|
||||
if (strcasecmp($objectData['component'], 'vevent') !== 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Always remove existing reminders for this event
|
||||
$this->backend->cleanRemindersForEvent($objectData['calendarid'], $objectData['uri']);
|
||||
switch($action) {
|
||||
case '\OCA\DAV\CalDAV\CalDavBackend::createCalendarObject':
|
||||
$this->onCalendarObjectCreate($objectData);
|
||||
break;
|
||||
|
||||
/**
|
||||
* If we are deleting the event, no need to go further
|
||||
*/
|
||||
if ($action === '\OCA\DAV\CalDAV\CalDavBackend::deleteCalendarObject') {
|
||||
case '\OCA\DAV\CalDAV\CalDavBackend::updateCalendarObject':
|
||||
$this->onCalendarObjectEdit($objectData);
|
||||
break;
|
||||
|
||||
case '\OCA\DAV\CalDAV\CalDavBackend::deleteCalendarObject':
|
||||
$this->onCalendarObjectDelete($objectData);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $objectData
|
||||
*/
|
||||
private function onCalendarObjectCreate(array $objectData):void {
|
||||
/** @var VObject\Component\VCalendar $vcalendar */
|
||||
$vcalendar = $this->parseCalendarData($objectData['calendardata']);
|
||||
if (!$vcalendar) {
|
||||
return;
|
||||
}
|
||||
|
||||
$user = $this->userSession->getUser();
|
||||
|
||||
if ($user === null) {
|
||||
throw new NoUserException('No user in session');
|
||||
$vevents = $this->getAllVEventsFromVCalendar($vcalendar);
|
||||
if (count($vevents) === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
$users = $this->getUsersForShares($shares);
|
||||
$uid = (string) $vevents[0]->UID;
|
||||
$recurrenceExceptions = $this->getRecurrenceExceptionFromListOfVEvents($vevents);
|
||||
$masterItem = $this->getMasterItemFromListOfVEvents($vevents);
|
||||
$now = $this->timeFactory->getDateTime();
|
||||
$isRecurring = $masterItem ? $this->isRecurring($masterItem) : false;
|
||||
|
||||
$users[] = $user->getUID();
|
||||
foreach($recurrenceExceptions as $recurrenceException) {
|
||||
$eventHash = $this->getEventHash($recurrenceException);
|
||||
|
||||
$vobject = VObject\Reader::read($objectData['calendardata']);
|
||||
|
||||
foreach ($vobject->VEVENT->VALARM as $alarm) {
|
||||
if ($alarm instanceof VAlarm) {
|
||||
$type = strtoupper($alarm->ACTION->getValue());
|
||||
if (in_array($type, self::REMINDER_TYPES, true)) {
|
||||
$time = $alarm->getEffectiveTriggerTime();
|
||||
|
||||
foreach ($users as $uid) {
|
||||
$this->backend->insertReminder(
|
||||
$uid,
|
||||
$objectData['calendarid'],
|
||||
$objectData['uri'],
|
||||
$type,
|
||||
$time->getTimestamp(),
|
||||
$vobject->VEVENT->DTSTART->getDateTime()->getTimestamp());
|
||||
|
||||
}
|
||||
foreach($recurrenceException->VALARM as $valarm) {
|
||||
/** @var VAlarm $valarm */
|
||||
$alarmHash = $this->getAlarmHash($valarm);
|
||||
$triggerTime = $valarm->getEffectiveTriggerTime();
|
||||
$diff = $now->diff($triggerTime);
|
||||
if ($diff->invert === 1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$alarms = $this->getRemindersForVAlarm($valarm, $objectData,
|
||||
$eventHash, $alarmHash, true, true);
|
||||
$this->writeRemindersToDatabase($alarms);
|
||||
}
|
||||
}
|
||||
|
||||
if ($masterItem) {
|
||||
$processedAlarms = [];
|
||||
$masterAlarms = [];
|
||||
$masterHash = $this->getEventHash($masterItem);
|
||||
|
||||
foreach($masterItem->VALARM as $valarm) {
|
||||
$masterAlarms[] = $this->getAlarmHash($valarm);
|
||||
}
|
||||
|
||||
try {
|
||||
$iterator = new EventIterator($vevents, $uid);
|
||||
} catch (NoInstancesException $e) {
|
||||
// This event is recurring, but it doesn't have a single
|
||||
// instance. We are skipping this event from the output
|
||||
// entirely.
|
||||
return;
|
||||
}
|
||||
|
||||
while($iterator->valid() && count($processedAlarms) < count($masterAlarms)) {
|
||||
$event = $iterator->getEventObject();
|
||||
|
||||
// Recurrence-exceptions are handled separately, so just ignore them here
|
||||
if (\in_array($event, $recurrenceExceptions, true)) {
|
||||
$iterator->next();
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach($event->VALARM as $valarm) {
|
||||
/** @var VAlarm $valarm */
|
||||
$alarmHash = $this->getAlarmHash($valarm);
|
||||
if (\in_array($alarmHash, $processedAlarms, true)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!\in_array((string) $valarm->ACTION, self::REMINDER_TYPES, true)) {
|
||||
// Action allows x-name, we don't insert reminders
|
||||
// into the database if they are not standard
|
||||
$processedAlarms[] = $alarmHash;
|
||||
continue;
|
||||
}
|
||||
|
||||
$triggerTime = $valarm->getEffectiveTriggerTime();
|
||||
|
||||
// If effective trigger time is in the past
|
||||
// just skip and generate for next event
|
||||
$diff = $now->diff($triggerTime);
|
||||
if ($diff->invert === 1) {
|
||||
// If an absolute alarm is in the past,
|
||||
// just add it to processedAlarms, so
|
||||
// we don't extend till eternity
|
||||
if (!$this->isAlarmRelative($valarm)) {
|
||||
$processedAlarms[] = $alarmHash;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
$alarms = $this->getRemindersForVAlarm($valarm, $objectData, $masterHash, $alarmHash, $isRecurring, false);
|
||||
$this->writeRemindersToDatabase($alarms);
|
||||
$processedAlarms[] = $alarmHash;
|
||||
}
|
||||
|
||||
$iterator->next();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $objectData
|
||||
*/
|
||||
private function onCalendarObjectEdit(array $objectData):void {
|
||||
// TODO - this can be vastly improved
|
||||
// - get cached reminders
|
||||
// - ...
|
||||
|
||||
$this->onCalendarObjectDelete($objectData);
|
||||
$this->onCalendarObjectCreate($objectData);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all users that have access to a given calendar
|
||||
*
|
||||
* @param array $shares
|
||||
* @return string[]
|
||||
* @param array $objectData
|
||||
*/
|
||||
private function getUsersForShares(array $shares):array {
|
||||
$users = $groups = [];
|
||||
private function onCalendarObjectDelete(array $objectData):void {
|
||||
$this->backend->cleanRemindersForEvent((int) $objectData['id']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param VAlarm $valarm
|
||||
* @param array $objectData
|
||||
* @param string|null $eventHash
|
||||
* @param string|null $alarmHash
|
||||
* @param bool $isRecurring
|
||||
* @param bool $isRecurrenceException
|
||||
* @return array
|
||||
*/
|
||||
private function getRemindersForVAlarm(VAlarm $valarm,
|
||||
array $objectData,
|
||||
string $eventHash=null,
|
||||
string $alarmHash=null,
|
||||
bool $isRecurring=false,
|
||||
bool $isRecurrenceException=false):array {
|
||||
if ($eventHash === null) {
|
||||
$eventHash = $this->getEventHash($valarm->parent);
|
||||
}
|
||||
if ($alarmHash === null) {
|
||||
$alarmHash = $this->getAlarmHash($valarm);
|
||||
}
|
||||
|
||||
$recurrenceId = $this->getEffectiveRecurrenceIdOfVEvent($valarm->parent);
|
||||
$isRelative = $this->isAlarmRelative($valarm);
|
||||
/** @var DateTimeImmutable $notificationDate */
|
||||
$notificationDate = $valarm->getEffectiveTriggerTime();
|
||||
$clonedNotificationDate = new \DateTime('now', $notificationDate->getTimezone());
|
||||
$clonedNotificationDate->setTimestamp($notificationDate->getTimestamp());
|
||||
|
||||
$alarms = [];
|
||||
|
||||
$alarms[] = [
|
||||
'calendar_id' => $objectData['calendarid'],
|
||||
'object_id' => $objectData['id'],
|
||||
'uid' => (string) $valarm->parent->UID,
|
||||
'is_recurring' => $isRecurring,
|
||||
'recurrence_id' => $recurrenceId,
|
||||
'is_recurrence_exception' => $isRecurrenceException,
|
||||
'event_hash' => $eventHash,
|
||||
'alarm_hash' => $alarmHash,
|
||||
'type' => (string) $valarm->ACTION,
|
||||
'is_relative' => $isRelative,
|
||||
'notification_date' => $notificationDate->getTimestamp(),
|
||||
'is_repeat_based' => false,
|
||||
];
|
||||
|
||||
$repeat = $valarm->REPEAT ? (int) $valarm->REPEAT : 0;
|
||||
for($i = 0; $i < $repeat; $i++) {
|
||||
if ($valarm->DURATION === null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$clonedNotificationDate->add($valarm->DURATION->getDateInterval());
|
||||
$alarms[] = [
|
||||
'calendar_id' => $objectData['calendarid'],
|
||||
'object_id' => $objectData['id'],
|
||||
'uid' => (string) $valarm->parent->UID,
|
||||
'is_recurring' => $isRecurring,
|
||||
'recurrence_id' => $recurrenceId,
|
||||
'is_recurrence_exception' => $isRecurrenceException,
|
||||
'event_hash' => $eventHash,
|
||||
'alarm_hash' => $alarmHash,
|
||||
'type' => (string) $valarm->ACTION,
|
||||
'is_relative' => $isRelative,
|
||||
'notification_date' => $clonedNotificationDate->getTimestamp(),
|
||||
'is_repeat_based' => true,
|
||||
];
|
||||
}
|
||||
|
||||
return $alarms;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $reminders
|
||||
*/
|
||||
private function writeRemindersToDatabase(array $reminders): void {
|
||||
foreach($reminders as $reminder) {
|
||||
$this->backend->insertReminder(
|
||||
(int) $reminder['calendar_id'],
|
||||
(int) $reminder['object_id'],
|
||||
$reminder['uid'],
|
||||
$reminder['is_recurring'],
|
||||
(int) $reminder['recurrence_id'],
|
||||
$reminder['is_recurrence_exception'],
|
||||
$reminder['event_hash'],
|
||||
$reminder['alarm_hash'],
|
||||
$reminder['type'],
|
||||
$reminder['is_relative'],
|
||||
(int) $reminder['notification_date'],
|
||||
$reminder['is_repeat_based']
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $reminder
|
||||
* @param VEvent $vevent
|
||||
*/
|
||||
private function deleteOrProcessNext(array $reminder,
|
||||
VObject\Component\VEvent $vevent):void {
|
||||
if ($reminder['is_repeat_based'] ||
|
||||
!$reminder['is_recurring'] ||
|
||||
!$reminder['is_relative'] ||
|
||||
$reminder['is_recurrence_exception']) {
|
||||
|
||||
$this->backend->removeReminder($reminder['id']);
|
||||
return;
|
||||
}
|
||||
|
||||
$vevents = $this->getAllVEventsFromVCalendar($vevent->parent);
|
||||
$recurrenceExceptions = $this->getRecurrenceExceptionFromListOfVEvents($vevents);
|
||||
$now = $this->timeFactory->getDateTime();
|
||||
|
||||
try {
|
||||
$iterator = new EventIterator($vevents, $reminder['uid']);
|
||||
} catch (NoInstancesException $e) {
|
||||
// This event is recurring, but it doesn't have a single
|
||||
// instance. We are skipping this event from the output
|
||||
// entirely.
|
||||
return;
|
||||
}
|
||||
|
||||
while($iterator->valid()) {
|
||||
$event = $iterator->getEventObject();
|
||||
|
||||
// Recurrence-exceptions are handled separately, so just ignore them here
|
||||
if (\in_array($event, $recurrenceExceptions, true)) {
|
||||
$iterator->next();
|
||||
continue;
|
||||
}
|
||||
|
||||
$recurrenceId = $this->getEffectiveRecurrenceIdOfVEvent($event);
|
||||
if ($reminder['recurrence_id'] >= $recurrenceId) {
|
||||
$iterator->next();
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach($event->VALARM as $valarm) {
|
||||
/** @var VAlarm $valarm */
|
||||
$alarmHash = $this->getAlarmHash($valarm);
|
||||
if ($alarmHash !== $reminder['alarm_hash']) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$triggerTime = $valarm->getEffectiveTriggerTime();
|
||||
|
||||
// If effective trigger time is in the past
|
||||
// just skip and generate for next event
|
||||
$diff = $now->diff($triggerTime);
|
||||
if ($diff->invert === 1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$this->backend->removeReminder($reminder['id']);
|
||||
$alarms = $this->getRemindersForVAlarm($valarm, [
|
||||
'calendarid' => $reminder['calendar_id'],
|
||||
'id' => $reminder['object_id'],
|
||||
], $reminder['event_hash'], $alarmHash, true, false);
|
||||
$this->writeRemindersToDatabase($alarms);
|
||||
|
||||
// Abort generating reminders after creating one successfully
|
||||
return;
|
||||
}
|
||||
|
||||
$iterator->next();
|
||||
}
|
||||
|
||||
$this->backend->removeReminder($reminder['id']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $calendarId
|
||||
* @return IUser[]
|
||||
*/
|
||||
private function getAllUsersWithWriteAccessToCalendar(int $calendarId):array {
|
||||
$shares = $this->caldavBackend->getShares($calendarId);
|
||||
|
||||
$users = [];
|
||||
$userIds = [];
|
||||
$groups = [];
|
||||
foreach ($shares as $share) {
|
||||
// Only consider writable shares
|
||||
if ($share['readOnly']) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$principal = explode('/', $share['{http://owncloud.org/ns}principal']);
|
||||
if ($principal[1] === 'users') {
|
||||
$users[] = $principal[2];
|
||||
$user = $this->userManager->get($principal[2]);
|
||||
if ($user) {
|
||||
$users[] = $user;
|
||||
$userIds[] = $principal[2];
|
||||
}
|
||||
} else if ($principal[1] === 'groups') {
|
||||
$groups[] = $principal[2];
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($groups)) {
|
||||
foreach ($groups as $gid) {
|
||||
$group = $this->groupManager->get($gid);
|
||||
if ($group instanceof IGroup) {
|
||||
foreach ($group->getUsers() as $user) {
|
||||
$users[] = $user->getUID();
|
||||
foreach ($groups as $gid) {
|
||||
$group = $this->groupManager->get($gid);
|
||||
if ($group instanceof IGroup) {
|
||||
foreach ($group->getUsers() as $user) {
|
||||
if (!\in_array($user->getUID(), $userIds, true)) {
|
||||
$users[] = $user;
|
||||
$userIds[] = $user->getUID();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return array_unique($users);
|
||||
return $users;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a hash of the event.
|
||||
* If the hash changes, we have to update all relative alarms.
|
||||
*
|
||||
* @param VEvent $vevent
|
||||
* @return string
|
||||
*/
|
||||
private function getEventHash(VEvent $vevent):string {
|
||||
$properties = [
|
||||
(string) $vevent->DTSTART->serialize(),
|
||||
];
|
||||
|
||||
if ($vevent->DTEND) {
|
||||
$properties[] = (string) $vevent->DTEND->serialize();
|
||||
}
|
||||
if ($vevent->DURATION) {
|
||||
$properties[] = (string) $vevent->DURATION->serialize();
|
||||
}
|
||||
if ($vevent->{'RECURRENCE-ID'}) {
|
||||
$properties[] = (string) $vevent->{'RECURRENCE-ID'}->serialize();
|
||||
}
|
||||
if ($vevent->RRULE) {
|
||||
$properties[] = (string) $vevent->RRULE->serialize();
|
||||
}
|
||||
if ($vevent->EXDATE) {
|
||||
$properties[] = (string) $vevent->EXDATE->serialize();
|
||||
}
|
||||
if ($vevent->RDATE) {
|
||||
$properties[] = (string) $vevent->RDATE->serialize();
|
||||
}
|
||||
|
||||
return md5(implode('::', $properties));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a hash of the alarm.
|
||||
* If the hash changes, we have to update oc_dav_reminders.
|
||||
*
|
||||
* @param VAlarm $valarm
|
||||
* @return string
|
||||
*/
|
||||
private function getAlarmHash(VAlarm $valarm):string {
|
||||
$properties = [
|
||||
(string) $valarm->ACTION->serialize(),
|
||||
(string) $valarm->TRIGGER->serialize(),
|
||||
];
|
||||
|
||||
if ($valarm->DURATION) {
|
||||
$properties[] = (string) $valarm->DURATION->serialize();
|
||||
}
|
||||
if ($valarm->REPEAT) {
|
||||
$properties[] = (string) $valarm->REPEAT->serialize();
|
||||
}
|
||||
|
||||
return md5(implode('::', $properties));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param VObject\Component\VCalendar $vcalendar
|
||||
* @param int $recurrenceId
|
||||
* @param bool $isRecurrenceException
|
||||
* @return VEvent|null
|
||||
*/
|
||||
private function getVEventByRecurrenceId(VObject\Component\VCalendar $vcalendar,
|
||||
int $recurrenceId,
|
||||
bool $isRecurrenceException):?VEvent {
|
||||
$vevents = $this->getAllVEventsFromVCalendar($vcalendar);
|
||||
if (count($vevents) === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$uid = (string) $vevents[0]->UID;
|
||||
$recurrenceExceptions = $this->getRecurrenceExceptionFromListOfVEvents($vevents);
|
||||
$masterItem = $this->getMasterItemFromListOfVEvents($vevents);
|
||||
|
||||
// Handle recurrence-exceptions first, because recurrence-expansion is expensive
|
||||
if ($isRecurrenceException) {
|
||||
foreach($recurrenceExceptions as $recurrenceException) {
|
||||
if ($this->getEffectiveRecurrenceIdOfVEvent($recurrenceException) === $recurrenceId) {
|
||||
return $recurrenceException;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
if ($masterItem) {
|
||||
try {
|
||||
$iterator = new EventIterator($vevents, $uid);
|
||||
} catch (NoInstancesException $e) {
|
||||
// This event is recurring, but it doesn't have a single
|
||||
// instance. We are skipping this event from the output
|
||||
// entirely.
|
||||
return null;
|
||||
}
|
||||
|
||||
while ($iterator->valid()) {
|
||||
$event = $iterator->getEventObject();
|
||||
|
||||
// Recurrence-exceptions are handled separately, so just ignore them here
|
||||
if (\in_array($event, $recurrenceExceptions, true)) {
|
||||
$iterator->next();
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($this->getEffectiveRecurrenceIdOfVEvent($event) === $recurrenceId) {
|
||||
return $event;
|
||||
}
|
||||
|
||||
$iterator->next();
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param VEvent $vevent
|
||||
* @return string
|
||||
*/
|
||||
private function getStatusOfEvent(VEvent $vevent):string {
|
||||
if ($vevent->STATUS) {
|
||||
return (string) $vevent->STATUS;
|
||||
}
|
||||
|
||||
// Doesn't say so in the standard,
|
||||
// but we consider events without a status
|
||||
// to be confirmed
|
||||
return 'CONFIRMED';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param VObject\Component\VEvent $vevent
|
||||
* @return bool
|
||||
*/
|
||||
private function wasEventCancelled(VObject\Component\VEvent $vevent):bool {
|
||||
return $this->getStatusOfEvent($vevent) === 'CANCELLED';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $calendarData
|
||||
* @return VObject\Component\VCalendar|null
|
||||
*/
|
||||
private function parseCalendarData(string $calendarData):?VObject\Component\VCalendar {
|
||||
try {
|
||||
return VObject\Reader::read($calendarData,
|
||||
VObject\Reader::OPTION_FORGIVING);
|
||||
} catch(ParseException $ex) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $principalUri
|
||||
* @return IUser|null
|
||||
*/
|
||||
private function getUserFromPrincipalURI(string $principalUri):?IUser {
|
||||
if (!$principalUri) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (strcasecmp($principalUri, 'principals/users/') !== 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$userId = substr($principalUri, 17);
|
||||
return $this->userManager->get($userId);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param VObject\Component\VCalendar $vcalendar
|
||||
* @return VObject\Component\VEvent[]
|
||||
*/
|
||||
private function getAllVEventsFromVCalendar(VObject\Component\VCalendar $vcalendar):array {
|
||||
$vevents = [];
|
||||
|
||||
foreach($vcalendar->children() as $child) {
|
||||
if (!($child instanceof VObject\Component)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($child->name !== 'VEVENT') {
|
||||
continue;
|
||||
}
|
||||
|
||||
$vevents[] = $child;
|
||||
}
|
||||
|
||||
return $vevents;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $vevents
|
||||
* @return VObject\Component\VEvent[]
|
||||
*/
|
||||
private function getRecurrenceExceptionFromListOfVEvents(array $vevents):array {
|
||||
return array_values(array_filter($vevents, function(VEvent $vevent) {
|
||||
return $vevent->{'RECURRENCE-ID'} !== null;
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $vevents
|
||||
* @return VEvent|null
|
||||
*/
|
||||
private function getMasterItemFromListOfVEvents(array $vevents):?VEvent {
|
||||
$elements = array_values(array_filter($vevents, function(VEvent $vevent) {
|
||||
return $vevent->{'RECURRENCE-ID'} === null;
|
||||
}));
|
||||
|
||||
if (count($elements) === 0) {
|
||||
return null;
|
||||
}
|
||||
if (count($elements) > 1) {
|
||||
throw new \TypeError('Multiple master objects');
|
||||
}
|
||||
|
||||
return $elements[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param VAlarm $valarm
|
||||
* @return bool
|
||||
*/
|
||||
private function isAlarmRelative(VAlarm $valarm):bool {
|
||||
$trigger = $valarm->TRIGGER;
|
||||
return $trigger instanceof VObject\Property\ICalendar\Duration;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param VEvent $vevent
|
||||
* @return int
|
||||
*/
|
||||
private function getEffectiveRecurrenceIdOfVEvent(VEvent $vevent):int {
|
||||
if (isset($vevent->{'RECURRENCE-ID'})) {
|
||||
return $vevent->{'RECURRENCE-ID'}->getDateTime()->getTimestamp();
|
||||
}
|
||||
|
||||
return $vevent->DTSTART->getDateTime()->getTimestamp();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param VEvent $vevent
|
||||
* @return bool
|
||||
*/
|
||||
private function isRecurring(VEvent $vevent):bool {
|
||||
return isset($vevent->RRULE) || isset($vevent->RDATE);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,6 +27,11 @@ use Symfony\Component\Console\Command\Command;
|
|||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
/**
|
||||
* Class SendEventReminders
|
||||
*
|
||||
* @package OCA\DAV\Command
|
||||
*/
|
||||
class SendEventReminders extends Command {
|
||||
|
||||
/** @var ReminderService */
|
||||
|
@ -35,10 +40,13 @@ class SendEventReminders extends Command {
|
|||
/** @var IConfig */
|
||||
protected $config;
|
||||
|
||||
public function __construct(string $name = null,
|
||||
ReminderService $reminderService,
|
||||
/**
|
||||
* @param ReminderService $reminderService
|
||||
* @param IConfig $config
|
||||
*/
|
||||
public function __construct(ReminderService $reminderService,
|
||||
IConfig $config) {
|
||||
parent::__construct($name);
|
||||
parent::__construct();
|
||||
$this->reminderService = $reminderService;
|
||||
$this->config = $config;
|
||||
}
|
||||
|
|
|
@ -324,8 +324,7 @@ class Version1004Date20170825134824 extends SimpleMigrationStep {
|
|||
'length' => 1,
|
||||
]);
|
||||
$table->addColumn('stripattachments', 'smallint', [
|
||||
'notnull' => false,
|
||||
'length' => 1,
|
||||
|
||||
]);
|
||||
$table->addColumn('lastmodified', 'integer', [
|
||||
'notnull' => false,
|
||||
|
|
|
@ -1,82 +0,0 @@
|
|||
<?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) {
|
||||
}
|
||||
}
|
116
apps/dav/lib/Migration/Version1012Date20190808122342.php
Normal file
116
apps/dav/lib/Migration/Version1012Date20190808122342.php
Normal file
|
@ -0,0 +1,116 @@
|
|||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* @copyright 2019, Georg Ehrke <oc.list@georgehrke.com>
|
||||
*
|
||||
* @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\Migration;
|
||||
|
||||
use Doctrine\DBAL\Types\Type;
|
||||
use OCP\DB\ISchemaWrapper;
|
||||
use OCP\Migration\SimpleMigrationStep;
|
||||
use OCP\Migration\IOutput;
|
||||
|
||||
/**
|
||||
* Auto-generated migration step: Please modify to your needs!
|
||||
*/
|
||||
class Version1012Date20190808122342 extends SimpleMigrationStep {
|
||||
|
||||
/**
|
||||
* @param IOutput $output
|
||||
* @param \Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper`
|
||||
* @param array $options
|
||||
* @return null|ISchemaWrapper
|
||||
* @since 13.0.0
|
||||
*/
|
||||
public function changeSchema(IOutput $output,
|
||||
\Closure $schemaClosure,
|
||||
array $options):?ISchemaWrapper {
|
||||
/** @var ISchemaWrapper $schema */
|
||||
$schema = $schemaClosure();
|
||||
|
||||
if (!$schema->hasTable('calendar_reminders')) {
|
||||
$table = $schema->createTable('calendar_reminders');
|
||||
|
||||
$table->addColumn('id', Type::BIGINT, [
|
||||
'autoincrement' => true,
|
||||
'notnull' => true,
|
||||
'length' => 11,
|
||||
'unsigned' => true,
|
||||
]);
|
||||
$table->addColumn('calendar_id', Type::BIGINT, [
|
||||
'notnull' => true,
|
||||
'length' => 11,
|
||||
]);
|
||||
$table->addColumn('object_id', Type::BIGINT, [
|
||||
'notnull' => true,
|
||||
'length' => 11,
|
||||
]);
|
||||
$table->addColumn('is_recurring', Type::SMALLINT, [
|
||||
'notnull' => true,
|
||||
'length' => 1,
|
||||
]);
|
||||
$table->addColumn('uid', Type::STRING, [
|
||||
'notnull' => true,
|
||||
'length' => 255,
|
||||
]);
|
||||
$table->addColumn('recurrence_id', Type::BIGINT, [
|
||||
'notnull' => false,
|
||||
'length' => 11,
|
||||
'unsigned' => true,
|
||||
]);
|
||||
$table->addColumn('is_recurrence_exception', Type::SMALLINT, [
|
||||
'notnull' => true,
|
||||
'length' => 1,
|
||||
]);
|
||||
$table->addColumn('event_hash', Type::STRING, [
|
||||
'notnull' => true,
|
||||
'length' => 255,
|
||||
]);
|
||||
$table->addColumn('alarm_hash', Type::STRING, [
|
||||
'notnull' => true,
|
||||
'length' => 255,
|
||||
]);
|
||||
$table->addColumn('type', Type::STRING, [
|
||||
'notnull' => true,
|
||||
'length' => 255,
|
||||
]);
|
||||
$table->addColumn('is_relative', Type::SMALLINT, [
|
||||
'notnull' => true,
|
||||
'length' => 1,
|
||||
]);
|
||||
$table->addColumn('notification_date', Type::BIGINT, [
|
||||
'notnull' => true,
|
||||
'length' => 11,
|
||||
'unsigned' => true,
|
||||
]);
|
||||
$table->addColumn('is_repeat_based', Type::SMALLINT, [
|
||||
'notnull' => true,
|
||||
'length' => 1,
|
||||
]);
|
||||
|
||||
$table->setPrimaryKey(['id']);
|
||||
$table->addIndex(['object_id'], 'calendar_reminder_objid');
|
||||
$table->addIndex(['uid', 'recurrence_id'], 'calendar_reminder_uidrec');
|
||||
|
||||
return $schema;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue