fixes introduced by Mistral

This commit is contained in:
stefan.tollkuehn committed 2026-10-07 22:02:19 +02:00
1 parent 7aac2718b5
commit 61079a5542
2 files changed
+87 -178

No files matched your search

+79 -176
View File
@@ -4,22 +4,27 @@ declare(strict_types=1);
namespace OCA\JitsiCalendar\Controller; namespace OCA\JitsiCalendar\Controller;
use OCA\JitsiCalendar\AppInfo\Application;
use OCP\AppFramework\Controller; use OCP\AppFramework\Controller;
use OCP\AppFramework\Http\RedirectResponse; use OCP\AppFramework\Http\RedirectResponse;
use OCP\AppFramework\Http\TemplateResponse; use OCP\AppFramework\Http\TemplateResponse;
use OCP\Calendar\ICreateFromString;
use OCP\Calendar\IManager; use OCP\Calendar\IManager;
use OCP\IRequest; use OCP\IRequest;
use OCP\IUserSession;
use OCP\IURLGenerator; use OCP\IURLGenerator;
use OCP\Util; use OCP\IUserSession;
use OCP\IConfig;
class CalendarController extends Controller class CalendarController extends Controller
{ {
/*
* datetime-local format as sent by jitsi-launch,
* e.g. "2026-10-07T14:15".
*/
private const DATETIME_LOCAL = 'Y-m-d\TH:i';
private IManager $calendarManager; private IManager $calendarManager;
private IUserSession $userSession; private IUserSession $userSession;
private IURLGenerator $urlGenerator; private IURLGenerator $urlGenerator;
private IConfig $config;
public function __construct( public function __construct(
string $appName, string $appName,
@@ -62,18 +67,18 @@ class CalendarController extends Controller
); );
return new TemplateResponse( return new TemplateResponse(
self::APP_ID, Application::APP_ID,
'confirm', 'confirm',
[ [
'summary' => $data['summary'], 'summary' => $data['summary'],
'description' => $data['description'], 'description' => $data['description'],
'start' => $data['start'], 'start' => $data['start']->format(self::DATETIME_LOCAL),
'end' => $data['end'], 'end' => $data['end']->format(self::DATETIME_LOCAL),
'organizer' => $data['organizer'], 'organizer' => $data['organizer'],
'location' => $data['location'], 'location' => $data['location'],
'url' => $data['url'], 'url' => $data['url'],
'createUrl' => $this->urlGenerator->linkToRoute( 'createUrl' => $this->urlGenerator->linkToRoute(
self::APP_ID . '.calendar.create' Application::APP_ID . '.calendar.create'
), ),
] ]
); );
@@ -107,49 +112,15 @@ class CalendarController extends Controller
throw new \RuntimeException('No authenticated Nextcloud user.'); throw new \RuntimeException('No authenticated Nextcloud user.');
} }
$calendars = $this->calendarManager->getCalendarsForUser( $calendar = $this->findWritableCalendar($user->getUID());
$user->getUID()
);
$writableCalendar = null; if ($calendar === null) {
foreach ($calendars as $calendar) {
/*
* getCalendarsForUser() returns ICalendar objects.
* Only calendars implementing ICreateFromString are writable.
*/
if ($calendar instanceof \OCP\Calendar\ICreateFromString) {
$writableCalendar = $calendar;
break;
}
}
if ($writableCalendar === null) {
throw new \RuntimeException( throw new \RuntimeException(
'No writable calendar is available for this user.' 'No writable calendar is available for this user.'
); );
} }
$uid = 'jitsi-' . bin2hex(random_bytes(16)) $this->createEvent($calendar, $data);
. '@meet.bnvb.eu';
$ics = $this->buildIcs(
$uid,
$data['summary'],
$data['description'],
$data['start'],
$data['end'],
$data['organizer'],
$data['location'],
$data['url']
);
$filename = $uid . '.ics';
$writableCalendar->createFromString(
$filename,
$ics
);
/* /*
* Open the Nextcloud Calendar application. * Open the Nextcloud Calendar application.
@@ -161,6 +132,52 @@ class CalendarController extends Controller
return new RedirectResponse($calendarUrl); return new RedirectResponse($calendarUrl);
} }
/**
* First writable calendar of the user.
*
* getCalendarsForUser() returns ICalendar objects.
* Only calendars implementing ICreateFromString are writable.
*/
private function findWritableCalendar(string $userId): ?ICreateFromString
{
$calendars = $this->calendarManager->getCalendarsForUser($userId);
foreach ($calendars as $calendar) {
if ($calendar instanceof ICreateFromString) {
return $calendar;
}
}
return null;
}
/**
* Create the event through the platform event builder,
* which handles escaping and ICS serialization.
*/
private function createEvent(ICreateFromString $calendar, array $data): void
{
$builder = $this->calendarManager->createEventBuilder();
$builder->setStartDate($data['start']);
$builder->setEndDate($data['end']);
$builder->setSummary($data['summary']);
if ($data['description'] !== '') {
$builder->setDescription($data['description']);
}
if ($data['location'] !== '') {
$builder->setLocation($data['location']);
}
if ($data['organizer'] !== '') {
$builder->setOrganizer($data['organizer']);
}
$builder->createInCalendar($calendar);
}
/** /**
* Validate and normalize all event input. * Validate and normalize all event input.
*/ */
@@ -209,39 +226,11 @@ class CalendarController extends Controller
} }
if ($location !== '') { if ($location !== '') {
$locationParts = parse_url($location); $this->assertHttpsUrl($location, 'Location must be an HTTPS URL.');
if (
$locationParts === false ||
!isset($locationParts['scheme']) ||
!in_array(
strtolower($locationParts['scheme']),
['https'],
true
)
) {
throw new \InvalidArgumentException(
'Location must be an HTTPS URL.'
);
}
} }
if ($url !== '') { if ($url !== '') {
$urlParts = parse_url($url); $this->assertHttpsUrl($url, 'Meeting URL must be an HTTPS URL.');
if (
$urlParts === false ||
!isset($urlParts['scheme']) ||
!in_array(
strtolower($urlParts['scheme']),
['https'],
true
)
) {
throw new \InvalidArgumentException(
'Meeting URL must be an HTTPS URL.'
);
}
} }
if ($organizer !== '' && !filter_var( if ($organizer !== '' && !filter_var(
@@ -264,6 +253,19 @@ class CalendarController extends Controller
]; ];
} }
private function assertHttpsUrl(string $value, string $message): void
{
$parts = parse_url($value);
$valid = $parts !== false
&& isset($parts['scheme'])
&& strtolower($parts['scheme']) === 'https';
if (!$valid) {
throw new \InvalidArgumentException($message);
}
}
/** /**
* Parse datetime-local values. * Parse datetime-local values.
* *
@@ -273,7 +275,7 @@ class CalendarController extends Controller
private function parseLocalDateTime(string $value): \DateTimeImmutable private function parseLocalDateTime(string $value): \DateTimeImmutable
{ {
$date = \DateTimeImmutable::createFromFormat( $date = \DateTimeImmutable::createFromFormat(
'Y-m-d\TH:i', self::DATETIME_LOCAL,
$value $value
); );
@@ -292,103 +294,4 @@ class CalendarController extends Controller
return $date; return $date;
} }
}
/**
* Build an RFC 5545 iCalendar event.
*/
private function buildIcs(
string $uid,
string $summary,
string $description,
\DateTimeImmutable $start,
\DateTimeImmutable $end,
string $organizer,
string $location,
string $url
): string {
$now = new \DateTimeImmutable('now');
$dtStamp = $now->setTimezone(
new \DateTimeZone('UTC')
)->format('Ymd\THis\Z');
$dtStart = $start->format('Ymd\THis');
$dtEnd = $end->format('Ymd\THis');
$lines = [
'BEGIN:VCALENDAR',
'VERSION:2.0',
'PRODID:-//BNVB//Jitsi Calendar//EN',
'CALSCALE:GREGORIAN',
'METHOD:PUBLISH',
'BEGIN:VEVENT',
'UID:' . $this->escapeIcsText($uid),
'DTSTAMP:' . $dtStamp,
'DTSTART:' . $dtStart,
'DTEND:' . $dtEnd,
'SUMMARY:' . $this->escapeIcsText($summary),
];
if ($description !== '') {
$lines[] =
'DESCRIPTION:' .
$this->escapeIcsText($description);
}
if ($location !== '') {
$lines[] =
'LOCATION:' .
$this->escapeIcsText($location);
}
if ($organizer !== '') {
$lines[] =
'ORGANIZER:MAILTO:' .
$this->escapeIcsText($organizer);
}
if ($url !== '') {
$lines[] =
'URL:' .
$this->escapeIcsText($url);
}
$lines[] = 'STATUS:CONFIRMED';
$lines[] = 'SEQUENCE:0';
$lines[] = 'TRANSP:OPAQUE';
$lines[] = 'BEGIN:VALARM';
$lines[] = 'ACTION:DISPLAY';
$lines[] = 'DESCRIPTION:Reminder';
$lines[] = 'TRIGGER:-PT15M';
$lines[] = 'END:VALARM';
$lines[] = 'END:VEVENT';
$lines[] = 'END:VCALENDAR';
return implode("\r\n", $lines) . "\r\n";
}
private function escapeIcsText(string $value): string
{
return str_replace(
[
'\\',
';',
',',
"\r\n",
"\r",
"\n",
],
[
'\\\\',
'\;',
'\,',
'\n',
'\n',
'\n',
],
$value
);
}
}
+8 -2
View File
@@ -43,12 +43,12 @@ $h = static function (string $value): string {
<div class="jitsi-calendar-row"> <div class="jitsi-calendar-row">
<strong>Start</strong> <strong>Start</strong>
<span><?= $h($_['start']) ?></span> <span><?= $h(str_replace('T', ' ', $_['start'])) ?></span>
</div> </div>
<div class="jitsi-calendar-row"> <div class="jitsi-calendar-row">
<strong>End</strong> <strong>End</strong>
<span><?= $h($_['end']) ?></span> <span><?= $h(str_replace('T', ' ', $_['end'])) ?></span>
</div> </div>
<?php if ($_['organizer'] !== ''): ?> <?php if ($_['organizer'] !== ''): ?>
@@ -79,6 +79,12 @@ $h = static function (string $value): string {
method="post" method="post"
action="<?= $h($_['createUrl']) ?>" action="<?= $h($_['createUrl']) ?>"
> >
<input
type="hidden"
name="requesttoken"
value="<?= $h($_['requesttoken']) ?>"
>
<input <input
type="hidden" type="hidden"
name="summary" name="summary"