2021-11-19 08:49:22 +00:00
|
|
|
<?php
|
|
|
|
|
|
|
|
declare(strict_types=1);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Calendar App
|
|
|
|
*
|
|
|
|
* @copyright 2021 Anna Larch <anna.larch@gmx.net>
|
|
|
|
*
|
|
|
|
* @author Anna Larch <anna.larch@gmx.net>
|
|
|
|
*
|
|
|
|
* This library is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
|
|
|
|
* License as published by the Free Software Foundation; either
|
|
|
|
* version 3 of the License, or any later version.
|
|
|
|
*
|
|
|
|
* This library 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 library. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
namespace OCA\Calendar\Service\Appointments;
|
|
|
|
|
|
|
|
use DateTimeImmutable;
|
2021-11-26 15:47:03 +00:00
|
|
|
use OCA\Calendar\AppInfo\Application;
|
2021-11-19 08:49:22 +00:00
|
|
|
use OCA\Calendar\Db\AppointmentConfig;
|
|
|
|
use OCP\Calendar\Exceptions\CalendarException;
|
|
|
|
use OCP\Calendar\ICreateFromString;
|
|
|
|
use OCP\Calendar\IManager;
|
|
|
|
use OCP\IConfig;
|
2021-11-29 09:53:16 +00:00
|
|
|
use OCP\IL10N;
|
2021-11-19 08:49:22 +00:00
|
|
|
use OCP\IUserManager;
|
|
|
|
use OCP\Security\ISecureRandom;
|
|
|
|
use RuntimeException;
|
|
|
|
use Sabre\VObject\Component\VCalendar;
|
2021-11-26 15:47:03 +00:00
|
|
|
use function abs;
|
2021-11-19 08:49:22 +00:00
|
|
|
|
|
|
|
class BookingCalendarWriter {
|
|
|
|
/** @var IConfig */
|
|
|
|
private $config;
|
|
|
|
|
|
|
|
/** @var IManager */
|
|
|
|
private $manager;
|
|
|
|
|
|
|
|
/** @var IUserManager */
|
|
|
|
private $userManager;
|
|
|
|
|
|
|
|
/** @var ISecureRandom */
|
|
|
|
private $random;
|
2021-11-29 09:53:16 +00:00
|
|
|
/** @var IL10N */
|
|
|
|
private $l10n;
|
2021-11-19 08:49:22 +00:00
|
|
|
|
|
|
|
public function __construct(IConfig $config,
|
|
|
|
IManager $manager,
|
|
|
|
IUserManager $userManager,
|
2021-11-29 09:53:16 +00:00
|
|
|
ISecureRandom $random,
|
|
|
|
IL10N $l10n) {
|
2021-11-19 08:49:22 +00:00
|
|
|
$this->config = $config;
|
|
|
|
$this->manager = $manager;
|
|
|
|
$this->userManager = $userManager;
|
|
|
|
$this->random = $random;
|
2021-11-29 09:53:16 +00:00
|
|
|
$this->l10n = $l10n;
|
2021-11-19 08:49:22 +00:00
|
|
|
}
|
|
|
|
|
2021-11-26 15:47:03 +00:00
|
|
|
private function secondsToIso8601Duration(int $secs): string {
|
|
|
|
$day = 24 * 60 * 60;
|
|
|
|
$hour = 60 * 60;
|
|
|
|
$minute = 60;
|
|
|
|
if ($secs % $day === 0) {
|
|
|
|
return 'PT' . $secs / $day . 'S';
|
|
|
|
}
|
|
|
|
if ($secs % $hour === 0) {
|
|
|
|
return 'PT' . $secs / $hour . 'H';
|
|
|
|
}
|
|
|
|
if ($secs % $minute === 0) {
|
|
|
|
return 'PT' . $secs / $minute . 'M';
|
|
|
|
}
|
|
|
|
return 'PT' . $secs . 'S';
|
|
|
|
}
|
|
|
|
|
2021-11-19 08:49:22 +00:00
|
|
|
/**
|
|
|
|
* @param AppointmentConfig $config
|
|
|
|
* @param DateTimeImmutable $start
|
2022-10-03 16:04:03 +00:00
|
|
|
* @param string $displayName
|
2021-11-19 08:49:22 +00:00
|
|
|
* @param string $email
|
2022-10-03 16:04:03 +00:00
|
|
|
* @param string|null $description
|
2021-11-19 08:49:22 +00:00
|
|
|
*
|
2022-10-03 16:04:03 +00:00
|
|
|
* @return string
|
2021-11-19 08:49:22 +00:00
|
|
|
* @throws RuntimeException
|
|
|
|
*/
|
2022-10-03 16:04:03 +00:00
|
|
|
public function write(AppointmentConfig $config, DateTimeImmutable $start, string $displayName, string $email, ?string $description = null) : string {
|
2021-11-19 08:49:22 +00:00
|
|
|
$calendar = current($this->manager->getCalendarsForPrincipal($config->getPrincipalUri(), [$config->getTargetCalendarUri()]));
|
2022-10-03 16:04:03 +00:00
|
|
|
if (!($calendar instanceof ICreateFromString)) {
|
2021-11-19 08:49:22 +00:00
|
|
|
throw new RuntimeException('Could not find a public writable calendar for this principal');
|
|
|
|
}
|
|
|
|
|
|
|
|
$organizer = $this->userManager->get($config->getUserId());
|
|
|
|
if ($organizer === null) {
|
|
|
|
throw new RuntimeException('Organizer not registered user for this instance');
|
|
|
|
}
|
|
|
|
|
|
|
|
$vcalendar = new VCalendar([
|
|
|
|
'CALSCALE' => 'GREGORIAN',
|
|
|
|
'VERSION' => '2.0',
|
|
|
|
'VEVENT' => [
|
|
|
|
'SUMMARY' => $config->getName(),
|
2021-11-26 18:55:12 +00:00
|
|
|
'STATUS' => 'CONFIRMED',
|
2021-11-19 08:49:22 +00:00
|
|
|
'DTSTART' => $start,
|
2021-11-26 09:43:14 +00:00
|
|
|
'DTEND' => $start->setTimestamp($start->getTimestamp() + ($config->getLength()))
|
2021-11-19 08:49:22 +00:00
|
|
|
]
|
|
|
|
]);
|
|
|
|
|
2021-11-24 13:20:09 +00:00
|
|
|
if (!empty($description)) {
|
2021-11-19 08:49:22 +00:00
|
|
|
$vcalendar->VEVENT->add('DESCRIPTION', $description);
|
|
|
|
}
|
|
|
|
|
|
|
|
$vcalendar->VEVENT->add(
|
|
|
|
'ORGANIZER',
|
|
|
|
'mailto:' . $organizer->getEMailAddress(),
|
|
|
|
[
|
|
|
|
'CN' => $organizer->getDisplayName(),
|
|
|
|
'CUTYPE' => 'INDIVIDUAL',
|
|
|
|
'PARTSTAT' => 'ACCEPTED'
|
|
|
|
]
|
|
|
|
);
|
|
|
|
|
|
|
|
$vcalendar->VEVENT->add(
|
|
|
|
'ATTENDEE',
|
|
|
|
'mailto:' . $organizer->getEMailAddress(),
|
|
|
|
[
|
|
|
|
'CN' => $organizer->getDisplayName(),
|
|
|
|
'CUTYPE' => 'INDIVIDUAL',
|
|
|
|
'RSVP' => 'TRUE',
|
|
|
|
'ROLE' => 'REQ-PARTICIPANT',
|
|
|
|
'PARTSTAT' => 'ACCEPTED'
|
|
|
|
]
|
|
|
|
);
|
|
|
|
|
|
|
|
$vcalendar->VEVENT->add('ATTENDEE',
|
|
|
|
'mailto:' . $email,
|
|
|
|
[
|
|
|
|
'CN' => $displayName,
|
|
|
|
'CUTYPE' => 'INDIVIDUAL',
|
|
|
|
'RSVP' => 'TRUE',
|
|
|
|
'ROLE' => 'REQ-PARTICIPANT',
|
|
|
|
'PARTSTAT' => 'ACCEPTED'
|
|
|
|
]
|
|
|
|
);
|
|
|
|
|
2021-11-26 15:47:03 +00:00
|
|
|
$defaultReminder = $this->config->getUserValue(
|
|
|
|
$config->getUserId(),
|
|
|
|
Application::APP_ID,
|
|
|
|
'defaultReminder',
|
|
|
|
'none'
|
|
|
|
);
|
|
|
|
if ($defaultReminder !== 'none') {
|
|
|
|
$alarm = $vcalendar->createComponent('VALARM');
|
|
|
|
$alarm->add($vcalendar->createProperty('TRIGGER', '-' . $this->secondsToIso8601Duration(abs((int) $defaultReminder)), ['RELATED' => 'START']));
|
|
|
|
$alarm->add($vcalendar->createProperty('ACTION', 'DISPLAY'));
|
|
|
|
$vcalendar->VEVENT->add($alarm);
|
|
|
|
}
|
|
|
|
|
2022-11-21 13:49:50 +00:00
|
|
|
if ($config->getLocation() !== null) {
|
|
|
|
$vcalendar->VEVENT->add('LOCATION', $config->getLocation());
|
|
|
|
}
|
|
|
|
|
2021-11-19 08:49:22 +00:00
|
|
|
$vcalendar->VEVENT->add('X-NC-APPOINTMENT', $config->getToken());
|
|
|
|
|
|
|
|
$filename = $this->random->generate(32, ISecureRandom::CHAR_ALPHANUMERIC);
|
|
|
|
|
|
|
|
try {
|
|
|
|
$calendar->createFromString($filename . '.ics', $vcalendar->serialize());
|
|
|
|
} catch (CalendarException $e) {
|
2021-11-29 09:53:16 +00:00
|
|
|
throw new RuntimeException('Could not write event for appointment config id ' . $config->getId(). ' to calendar: ' . $e->getMessage(), 0, $e);
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($config->getPreparationDuration() !== 0) {
|
|
|
|
$string = $this->l10n->t('Prepare for %s', [$config->getName()]);
|
|
|
|
$prepStart = $start->setTimestamp($start->getTimestamp() - $config->getPreparationDuration());
|
|
|
|
$prepCalendar = new VCalendar([
|
|
|
|
'CALSCALE' => 'GREGORIAN',
|
|
|
|
'VERSION' => '2.0',
|
|
|
|
'VEVENT' => [
|
|
|
|
'SUMMARY' => $string,
|
|
|
|
'STATUS' => 'CONFIRMED',
|
|
|
|
'DTSTART' => $prepStart,
|
|
|
|
'DTEND' => $start
|
|
|
|
]
|
|
|
|
]);
|
|
|
|
|
2021-11-29 15:10:36 +00:00
|
|
|
$prepCalendar->VEVENT->add('RELATED-TO', $vcalendar->VEVENT->{'UID'});
|
|
|
|
$prepCalendar->VEVENT->add('RELTYPE', 'PARENT');
|
|
|
|
$prepCalendar->VEVENT->add('X-NC-PRE-APPOINTMENT', $config->getToken());
|
|
|
|
|
2021-11-29 09:53:16 +00:00
|
|
|
$prepFileName = $this->random->generate(32, ISecureRandom::CHAR_ALPHANUMERIC);
|
|
|
|
|
|
|
|
try {
|
|
|
|
$calendar->createFromString($prepFileName . '.ics', $prepCalendar->serialize());
|
|
|
|
} catch (CalendarException $e) {
|
|
|
|
throw new RuntimeException('Could not write event for appointment config id ' . $config->getId(). ' to calendar: ' . $e->getMessage(), 0, $e);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($config->getFollowupDuration() !== 0) {
|
|
|
|
$string = $this->l10n->t('Follow up for %s', [$config->getName()]);
|
|
|
|
$followupStart = $start->setTimestamp($start->getTimestamp() + $config->getLength());
|
|
|
|
$followUpEnd = $followupStart->setTimestamp($followupStart->getTimestamp() + $config->getFollowupDuration());
|
|
|
|
$followUpCalendar = new VCalendar([
|
|
|
|
'CALSCALE' => 'GREGORIAN',
|
|
|
|
'VERSION' => '2.0',
|
|
|
|
'VEVENT' => [
|
|
|
|
'SUMMARY' => $string,
|
|
|
|
'STATUS' => 'CONFIRMED',
|
|
|
|
'DTSTART' => $followupStart,
|
|
|
|
'DTEND' => $followUpEnd
|
|
|
|
]
|
|
|
|
]);
|
|
|
|
|
2021-11-29 15:10:36 +00:00
|
|
|
$followUpCalendar->VEVENT->add('RELATED-TO', $vcalendar->VEVENT->{'UID'});
|
|
|
|
$followUpCalendar->VEVENT->add('RELTYPE', 'PARENT');
|
|
|
|
$followUpCalendar->VEVENT->add('X-NC-POST-APPOINTMENT', $config->getToken());
|
|
|
|
|
2021-11-29 09:53:16 +00:00
|
|
|
$followUpFilename = $this->random->generate(32, ISecureRandom::CHAR_ALPHANUMERIC);
|
|
|
|
|
|
|
|
try {
|
|
|
|
$calendar->createFromString($followUpFilename . '.ics', $followUpCalendar->serialize());
|
|
|
|
} catch (CalendarException $e) {
|
|
|
|
throw new RuntimeException('Could not write event for appointment config id ' . $config->getId(). ' to calendar: ' . $e->getMessage(), 0, $e);
|
|
|
|
}
|
2021-11-19 08:49:22 +00:00
|
|
|
}
|
2022-10-03 16:04:03 +00:00
|
|
|
return $vcalendar->serialize();
|
2021-11-19 08:49:22 +00:00
|
|
|
}
|
|
|
|
}
|