Compare commits
7
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f759c2c8d5 | ||
|
|
a9b3bcc5d4 | ||
|
|
8b54abe7d4 | ||
|
|
de814e9530 | ||
|
|
828feb753a | ||
|
|
35d5185084 | ||
|
|
3be0490b68 |
No files matched your search
@@ -0,0 +1,35 @@
|
|||||||
|
{
|
||||||
|
"translations": {
|
||||||
|
"The following meeting will be added to your Nextcloud Calendar.": "Das folgende Meeting wird zu deinem Nextcloud-Kalender hinzugefügt.",
|
||||||
|
"This request came from an external link. Only continue if you trust its source.": "Diese Anfrage stammt von einem externen Link. Fahre nur fort, wenn du der Quelle vertraust.",
|
||||||
|
"Title": "Titel",
|
||||||
|
"Description": "Beschreibung",
|
||||||
|
"Start": "Beginn",
|
||||||
|
"End": "Ende",
|
||||||
|
"Organizer": "Organisator",
|
||||||
|
"Location": "Ort",
|
||||||
|
"Link": "Link",
|
||||||
|
"Calendar": "Kalender",
|
||||||
|
"Add to Nextcloud Calendar": "Zum Nextcloud-Kalender hinzufügen",
|
||||||
|
"Cancel": "Abbrechen",
|
||||||
|
"Meeting title is required.": "Ein Meeting-Titel ist erforderlich.",
|
||||||
|
"Meeting title is too long.": "Der Meeting-Titel ist zu lang.",
|
||||||
|
"Meeting title contains invalid characters.": "Der Meeting-Titel enthält ungültige Zeichen.",
|
||||||
|
"Meeting description is too long.": "Die Meeting-Beschreibung ist zu lang.",
|
||||||
|
"Meeting description contains invalid characters.": "Die Meeting-Beschreibung enthält ungültige Zeichen.",
|
||||||
|
"Invalid date/time.": "Ungültiges Datum / ungültige Uhrzeit.",
|
||||||
|
"End time must be after start time.": "Die Endzeit muss nach der Startzeit liegen.",
|
||||||
|
"Location is too long.": "Der Ort ist zu lang.",
|
||||||
|
"Location contains invalid characters.": "Der Ort enthält ungültige Zeichen.",
|
||||||
|
"Location must be an HTTPS URL.": "Der Ort muss eine HTTPS-URL sein.",
|
||||||
|
"Meeting URL is too long.": "Die Meeting-URL ist zu lang.",
|
||||||
|
"Meeting URL contains invalid characters.": "Die Meeting-URL enthält ungültige Zeichen.",
|
||||||
|
"Meeting URL must be an HTTPS URL.": "Die Meeting-URL muss eine HTTPS-URL sein.",
|
||||||
|
"Organizer email address is too long.": "Die E-Mail-Adresse des Organisators ist zu lang.",
|
||||||
|
"Organizer contains invalid characters.": "Der Organisator enthält ungültige Zeichen.",
|
||||||
|
"Invalid organizer email address.": "Ungültige E-Mail-Adresse des Organisators.",
|
||||||
|
"No writable calendar was found in your account. Create one in the Nextcloud Calendar app, then try again.": "In deinem Konto wurde kein beschreibbarer Kalender gefunden. Erstelle einen in der Nextcloud-Kalender-App und versuche es erneut.",
|
||||||
|
"The selected calendar is no longer writable. Pick another one and try again.": "Der ausgewählte Kalender ist nicht mehr beschreibbar. Wähle einen anderen aus und versuche es erneut."
|
||||||
|
},
|
||||||
|
"pluralForms": "nplurals=2; plural=(n != 1);"
|
||||||
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
{
|
||||||
|
"translations": {
|
||||||
|
"The following meeting will be added to your Nextcloud Calendar.": "Das folgende Meeting wird zu Ihrem Nextcloud-Kalender hinzugefügt.",
|
||||||
|
"This request came from an external link. Only continue if you trust its source.": "Diese Anfrage stammt von einem externen Link. Fahren Sie nur fort, wenn Sie der Quelle vertrauen.",
|
||||||
|
"Title": "Titel",
|
||||||
|
"Description": "Beschreibung",
|
||||||
|
"Start": "Beginn",
|
||||||
|
"End": "Ende",
|
||||||
|
"Organizer": "Organisator",
|
||||||
|
"Location": "Ort",
|
||||||
|
"Link": "Link",
|
||||||
|
"Calendar": "Kalender",
|
||||||
|
"Add to Nextcloud Calendar": "Zum Nextcloud-Kalender hinzufügen",
|
||||||
|
"Cancel": "Abbrechen",
|
||||||
|
"Meeting title is required.": "Ein Meeting-Titel ist erforderlich.",
|
||||||
|
"Meeting title is too long.": "Der Meeting-Titel ist zu lang.",
|
||||||
|
"Meeting title contains invalid characters.": "Der Meeting-Titel enthält ungültige Zeichen.",
|
||||||
|
"Meeting description is too long.": "Die Meeting-Beschreibung ist zu lang.",
|
||||||
|
"Meeting description contains invalid characters.": "Die Meeting-Beschreibung enthält ungültige Zeichen.",
|
||||||
|
"Invalid date/time.": "Ungültiges Datum / ungültige Uhrzeit.",
|
||||||
|
"End time must be after start time.": "Die Endzeit muss nach der Startzeit liegen.",
|
||||||
|
"Location is too long.": "Der Ort ist zu lang.",
|
||||||
|
"Location contains invalid characters.": "Der Ort enthält ungültige Zeichen.",
|
||||||
|
"Location must be an HTTPS URL.": "Der Ort muss eine HTTPS-URL sein.",
|
||||||
|
"Meeting URL is too long.": "Die Meeting-URL ist zu lang.",
|
||||||
|
"Meeting URL contains invalid characters.": "Die Meeting-URL enthält ungültige Zeichen.",
|
||||||
|
"Meeting URL must be an HTTPS URL.": "Die Meeting-URL muss eine HTTPS-URL sein.",
|
||||||
|
"Organizer email address is too long.": "Die E-Mail-Adresse des Organisators ist zu lang.",
|
||||||
|
"Organizer contains invalid characters.": "Der Organisator enthält ungültige Zeichen.",
|
||||||
|
"Invalid organizer email address.": "Ungültige E-Mail-Adresse des Organisators.",
|
||||||
|
"No writable calendar was found in your account. Create one in the Nextcloud Calendar app, then try again.": "In Ihrem Konto wurde kein beschreibbarer Kalender gefunden. Erstellen Sie einen in der Nextcloud-Kalender-App und versuchen Sie es erneut.",
|
||||||
|
"The selected calendar is no longer writable. Pick another one and try again.": "Der ausgewählte Kalender ist nicht mehr beschreibbar. Wählen Sie einen anderen aus und versuchen Sie es erneut."
|
||||||
|
},
|
||||||
|
"pluralForms": "nplurals=2; plural=(n != 1);"
|
||||||
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
{
|
||||||
|
"translations": {
|
||||||
|
"The following meeting will be added to your Nextcloud Calendar.": "The following meeting will be added to your Nextcloud Calendar.",
|
||||||
|
"This request came from an external link. Only continue if you trust its source.": "This request came from an external link. Only continue if you trust its source.",
|
||||||
|
"Title": "Title",
|
||||||
|
"Description": "Description",
|
||||||
|
"Start": "Start",
|
||||||
|
"End": "End",
|
||||||
|
"Organizer": "Organizer",
|
||||||
|
"Location": "Location",
|
||||||
|
"Link": "Link",
|
||||||
|
"Calendar": "Calendar",
|
||||||
|
"Add to Nextcloud Calendar": "Add to Nextcloud Calendar",
|
||||||
|
"Cancel": "Cancel",
|
||||||
|
"Meeting title is required.": "Meeting title is required.",
|
||||||
|
"Meeting title is too long.": "Meeting title is too long.",
|
||||||
|
"Meeting title contains invalid characters.": "Meeting title contains invalid characters.",
|
||||||
|
"Meeting description is too long.": "Meeting description is too long.",
|
||||||
|
"Meeting description contains invalid characters.": "Meeting description contains invalid characters.",
|
||||||
|
"Invalid date/time.": "Invalid date/time.",
|
||||||
|
"End time must be after start time.": "End time must be after start time.",
|
||||||
|
"Location is too long.": "Location is too long.",
|
||||||
|
"Location contains invalid characters.": "Location contains invalid characters.",
|
||||||
|
"Location must be an HTTPS URL.": "Location must be an HTTPS URL.",
|
||||||
|
"Meeting URL is too long.": "Meeting URL is too long.",
|
||||||
|
"Meeting URL contains invalid characters.": "Meeting URL contains invalid characters.",
|
||||||
|
"Meeting URL must be an HTTPS URL.": "Meeting URL must be an HTTPS URL.",
|
||||||
|
"Organizer email address is too long.": "Organizer email address is too long.",
|
||||||
|
"Organizer contains invalid characters.": "Organizer contains invalid characters.",
|
||||||
|
"Invalid organizer email address.": "Invalid organizer email address.",
|
||||||
|
"No writable calendar was found in your account. Create one in the Nextcloud Calendar app, then try again.": "No writable calendar was found in your account. Create one in the Nextcloud Calendar app, then try again.",
|
||||||
|
"The selected calendar is no longer writable. Pick another one and try again.": "The selected calendar is no longer writable. Pick another one and try again."
|
||||||
|
},
|
||||||
|
"pluralForms": "nplurals=2; plural=(n != 1);"
|
||||||
|
}
|
||||||
@@ -5,17 +5,21 @@ declare(strict_types=1);
|
|||||||
namespace OCA\AToCal\Controller;
|
namespace OCA\AToCal\Controller;
|
||||||
|
|
||||||
use OCA\AToCal\AppInfo\Application;
|
use OCA\AToCal\AppInfo\Application;
|
||||||
|
use OCP\App\IAppManager;
|
||||||
use OCP\AppFramework\Controller;
|
use OCP\AppFramework\Controller;
|
||||||
|
use OCP\AppFramework\Http;
|
||||||
use OCP\AppFramework\Http\Attribute\NoAdminRequired;
|
use OCP\AppFramework\Http\Attribute\NoAdminRequired;
|
||||||
use OCP\AppFramework\Http\Attribute\NoCSRFRequired;
|
use OCP\AppFramework\Http\Attribute\NoCSRFRequired;
|
||||||
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\ICreateFromString;
|
||||||
use OCP\Calendar\IManager;
|
use OCP\Calendar\IManager;
|
||||||
|
use OCP\Constants;
|
||||||
use OCP\IRequest;
|
use OCP\IRequest;
|
||||||
use OCP\IURLGenerator;
|
use OCP\IURLGenerator;
|
||||||
use OCP\IUserSession;
|
use OCP\IUserSession;
|
||||||
use OCP\IDateTimeZone;
|
use OCP\IDateTimeZone;
|
||||||
|
use OCP\IL10N;
|
||||||
use Sabre\VObject\Reader;
|
use Sabre\VObject\Reader;
|
||||||
|
|
||||||
class CalendarController extends Controller
|
class CalendarController extends Controller
|
||||||
@@ -25,6 +29,12 @@ class CalendarController extends Controller
|
|||||||
*/
|
*/
|
||||||
private const CALENDAR_NAME = 'Personal';
|
private const CALENDAR_NAME = 'Personal';
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Nextcloud Calendar app id, target of the redirect
|
||||||
|
* after the event was created.
|
||||||
|
*/
|
||||||
|
private const CALENDAR_APP_ID = 'calendar';
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Default reminder: 15 minutes before the event starts.
|
* Default reminder: 15 minutes before the event starts.
|
||||||
*/
|
*/
|
||||||
@@ -47,24 +57,30 @@ class CalendarController extends Controller
|
|||||||
private const MAX_EMAIL_LENGTH = 254;
|
private const MAX_EMAIL_LENGTH = 254;
|
||||||
|
|
||||||
private IManager $calendarManager;
|
private IManager $calendarManager;
|
||||||
|
private IAppManager $appManager;
|
||||||
private IUserSession $userSession;
|
private IUserSession $userSession;
|
||||||
private IURLGenerator $urlGenerator;
|
private IURLGenerator $urlGenerator;
|
||||||
private IDateTimeZone $dateTimeZone;
|
private IDateTimeZone $dateTimeZone;
|
||||||
|
private IL10N $l10n;
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
string $appName,
|
string $appName,
|
||||||
IRequest $request,
|
IRequest $request,
|
||||||
IManager $calendarManager,
|
IManager $calendarManager,
|
||||||
|
IAppManager $appManager,
|
||||||
IUserSession $userSession,
|
IUserSession $userSession,
|
||||||
IURLGenerator $urlGenerator,
|
IURLGenerator $urlGenerator,
|
||||||
IDateTimeZone $dateTimeZone
|
IDateTimeZone $dateTimeZone,
|
||||||
|
IL10N $l10n
|
||||||
) {
|
) {
|
||||||
parent::__construct($appName, $request);
|
parent::__construct($appName, $request);
|
||||||
|
|
||||||
$this->calendarManager = $calendarManager;
|
$this->calendarManager = $calendarManager;
|
||||||
|
$this->appManager = $appManager;
|
||||||
$this->userSession = $userSession;
|
$this->userSession = $userSession;
|
||||||
$this->urlGenerator = $urlGenerator;
|
$this->urlGenerator = $urlGenerator;
|
||||||
$this->dateTimeZone = $dateTimeZone;
|
$this->dateTimeZone = $dateTimeZone;
|
||||||
|
$this->l10n = $l10n;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -88,6 +104,7 @@ class CalendarController extends Controller
|
|||||||
string $location = '',
|
string $location = '',
|
||||||
string $url = ''
|
string $url = ''
|
||||||
): TemplateResponse {
|
): TemplateResponse {
|
||||||
|
try {
|
||||||
$data = $this->validateEventData(
|
$data = $this->validateEventData(
|
||||||
$summary,
|
$summary,
|
||||||
$description,
|
$description,
|
||||||
@@ -97,6 +114,9 @@ class CalendarController extends Controller
|
|||||||
$location,
|
$location,
|
||||||
$url
|
$url
|
||||||
);
|
);
|
||||||
|
} catch (\InvalidArgumentException $e) {
|
||||||
|
return $this->renderError($e);
|
||||||
|
}
|
||||||
|
|
||||||
$user = $this->userSession->getUser();
|
$user = $this->userSession->getUser();
|
||||||
|
|
||||||
@@ -128,6 +148,7 @@ class CalendarController extends Controller
|
|||||||
string $url = '',
|
string $url = '',
|
||||||
string $calendarId = ''
|
string $calendarId = ''
|
||||||
): RedirectResponse|TemplateResponse {
|
): RedirectResponse|TemplateResponse {
|
||||||
|
try {
|
||||||
$data = $this->validateEventData(
|
$data = $this->validateEventData(
|
||||||
$summary,
|
$summary,
|
||||||
$description,
|
$description,
|
||||||
@@ -137,6 +158,9 @@ class CalendarController extends Controller
|
|||||||
$location,
|
$location,
|
||||||
$url
|
$url
|
||||||
);
|
);
|
||||||
|
} catch (\InvalidArgumentException $e) {
|
||||||
|
return $this->renderError($e);
|
||||||
|
}
|
||||||
|
|
||||||
$user = $this->userSession->getUser();
|
$user = $this->userSession->getUser();
|
||||||
|
|
||||||
@@ -161,22 +185,28 @@ class CalendarController extends Controller
|
|||||||
$calendars,
|
$calendars,
|
||||||
$calendarId,
|
$calendarId,
|
||||||
$calendars === []
|
$calendars === []
|
||||||
? 'No writable calendar was found in your account. '
|
? $this->l10n->t(
|
||||||
|
'No writable calendar was found in your account. '
|
||||||
. 'Create one in the Nextcloud Calendar app, '
|
. 'Create one in the Nextcloud Calendar app, '
|
||||||
. 'then try again.'
|
. 'then try again.'
|
||||||
: 'The selected calendar is no longer writable. '
|
)
|
||||||
|
: $this->l10n->t(
|
||||||
|
'The selected calendar is no longer writable. '
|
||||||
. 'Pick another one and try again.'
|
. 'Pick another one and try again.'
|
||||||
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->createEvent($calendar, $data);
|
$this->createEvent($calendar, $data);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Open the Nextcloud Calendar application.
|
* Open the Nextcloud Calendar application if enabled,
|
||||||
|
* otherwise the Nextcloud start page. linkToRoute()
|
||||||
|
* would throw for a disabled Calendar app.
|
||||||
*/
|
*/
|
||||||
$calendarUrl = $this->urlGenerator->linkToRoute(
|
$calendarUrl = $this->appManager->isEnabledForUser(self::CALENDAR_APP_ID, $user)
|
||||||
'calendar.view.index'
|
? $this->urlGenerator->linkToRoute('calendar.view.index')
|
||||||
);
|
: $this->urlGenerator->getAbsoluteURL('/');
|
||||||
|
|
||||||
return new RedirectResponse($calendarUrl);
|
return new RedirectResponse($calendarUrl);
|
||||||
}
|
}
|
||||||
@@ -199,7 +229,8 @@ class CalendarController extends Controller
|
|||||||
$writable = [];
|
$writable = [];
|
||||||
|
|
||||||
foreach ($calendars as $calendar) {
|
foreach ($calendars as $calendar) {
|
||||||
if ($calendar instanceof ICreateFromString) {
|
if ($calendar instanceof ICreateFromString
|
||||||
|
&& ($calendar->getPermissions() & Constants::PERMISSION_CREATE) !== 0) {
|
||||||
$writable[] = $calendar;
|
$writable[] = $calendar;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -240,6 +271,25 @@ class CalendarController extends Controller
|
|||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Friendly error page (HTTP 400) for a malformed link,
|
||||||
|
* instead of an uncaught exception and a 500 error page.
|
||||||
|
*/
|
||||||
|
private function renderError(\InvalidArgumentException $e): TemplateResponse
|
||||||
|
{
|
||||||
|
return new TemplateResponse(
|
||||||
|
'core',
|
||||||
|
'error',
|
||||||
|
[
|
||||||
|
'errors' => [
|
||||||
|
['error' => $e->getMessage(), 'hint' => ''],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
TemplateResponse::RENDER_AS_ERROR,
|
||||||
|
Http::STATUS_BAD_REQUEST
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Render the confirmation page, optionally with an error message.
|
* Render the confirmation page, optionally with an error message.
|
||||||
*/
|
*/
|
||||||
@@ -261,6 +311,7 @@ class CalendarController extends Controller
|
|||||||
'location' => $data['location'],
|
'location' => $data['location'],
|
||||||
'url' => $data['url'],
|
'url' => $data['url'],
|
||||||
'error' => $error,
|
'error' => $error,
|
||||||
|
'l10n' => $this->l10n,
|
||||||
'calendars' => array_map(
|
'calendars' => array_map(
|
||||||
static function (ICreateFromString $calendar): array {
|
static function (ICreateFromString $calendar): array {
|
||||||
return [
|
return [
|
||||||
@@ -362,19 +413,32 @@ class CalendarController extends Controller
|
|||||||
|
|
||||||
if ($summary === '') {
|
if ($summary === '') {
|
||||||
throw new \InvalidArgumentException(
|
throw new \InvalidArgumentException(
|
||||||
'Meeting title is required.'
|
$this->l10n->t('Meeting title is required.')
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mb_strlen($summary) > 255) {
|
if (mb_strlen($summary) > 255) {
|
||||||
throw new \InvalidArgumentException(
|
throw new \InvalidArgumentException(
|
||||||
'Meeting title is too long.'
|
$this->l10n->t('Meeting title is too long.')
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$this->assertClean(
|
||||||
|
$summary,
|
||||||
|
$this->l10n->t('Meeting title contains invalid characters.')
|
||||||
|
);
|
||||||
|
|
||||||
if ($description !== '' && mb_strlen($description) > 10000) {
|
if ($description !== '' && mb_strlen($description) > 10000) {
|
||||||
throw new \InvalidArgumentException(
|
throw new \InvalidArgumentException(
|
||||||
'Meeting description is too long.'
|
$this->l10n->t('Meeting description is too long.')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($description !== '') {
|
||||||
|
$this->assertClean(
|
||||||
|
$description,
|
||||||
|
$this->l10n->t('Meeting description contains invalid characters.'),
|
||||||
|
true
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -383,7 +447,7 @@ class CalendarController extends Controller
|
|||||||
|
|
||||||
if ($endDate <= $startDate) {
|
if ($endDate <= $startDate) {
|
||||||
throw new \InvalidArgumentException(
|
throw new \InvalidArgumentException(
|
||||||
'End time must be after start time.'
|
$this->l10n->t('End time must be after start time.')
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -391,25 +455,37 @@ class CalendarController extends Controller
|
|||||||
$this->assertBoundedLength(
|
$this->assertBoundedLength(
|
||||||
$location,
|
$location,
|
||||||
self::MAX_URL_LENGTH,
|
self::MAX_URL_LENGTH,
|
||||||
'Location is too long.'
|
$this->l10n->t('Location is too long.')
|
||||||
);
|
);
|
||||||
$this->assertHttpsUrl($location, 'Location must be an HTTPS URL.');
|
$this->assertClean(
|
||||||
|
$location,
|
||||||
|
$this->l10n->t('Location contains invalid characters.')
|
||||||
|
);
|
||||||
|
$this->assertHttpsUrl($location, $this->l10n->t('Location must be an HTTPS URL.'));
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($url !== '') {
|
if ($url !== '') {
|
||||||
$this->assertBoundedLength(
|
$this->assertBoundedLength(
|
||||||
$url,
|
$url,
|
||||||
self::MAX_URL_LENGTH,
|
self::MAX_URL_LENGTH,
|
||||||
'Meeting URL is too long.'
|
$this->l10n->t('Meeting URL is too long.')
|
||||||
);
|
);
|
||||||
$this->assertHttpsUrl($url, 'Meeting URL must be an HTTPS URL.');
|
$this->assertClean(
|
||||||
|
$url,
|
||||||
|
$this->l10n->t('Meeting URL contains invalid characters.')
|
||||||
|
);
|
||||||
|
$this->assertHttpsUrl($url, $this->l10n->t('Meeting URL must be an HTTPS URL.'));
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($organizer !== '') {
|
if ($organizer !== '') {
|
||||||
$this->assertBoundedLength(
|
$this->assertBoundedLength(
|
||||||
$organizer,
|
$organizer,
|
||||||
self::MAX_EMAIL_LENGTH,
|
self::MAX_EMAIL_LENGTH,
|
||||||
'Organizer email address is too long.'
|
$this->l10n->t('Organizer email address is too long.')
|
||||||
|
);
|
||||||
|
$this->assertClean(
|
||||||
|
$organizer,
|
||||||
|
$this->l10n->t('Organizer contains invalid characters.')
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!filter_var(
|
if (!filter_var(
|
||||||
@@ -417,7 +493,7 @@ class CalendarController extends Controller
|
|||||||
FILTER_VALIDATE_EMAIL
|
FILTER_VALIDATE_EMAIL
|
||||||
)) {
|
)) {
|
||||||
throw new \InvalidArgumentException(
|
throw new \InvalidArgumentException(
|
||||||
'Invalid organizer email address.'
|
$this->l10n->t('Invalid organizer email address.')
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -439,6 +515,7 @@ class CalendarController extends Controller
|
|||||||
|
|
||||||
$valid = $parts !== false
|
$valid = $parts !== false
|
||||||
&& isset($parts['scheme'], $parts['host'])
|
&& isset($parts['scheme'], $parts['host'])
|
||||||
|
&& !isset($parts['user'])
|
||||||
&& strtolower($parts['scheme']) === 'https'
|
&& strtolower($parts['scheme']) === 'https'
|
||||||
&& $parts['host'] !== '';
|
&& $parts['host'] !== '';
|
||||||
|
|
||||||
@@ -484,10 +561,26 @@ class CalendarController extends Controller
|
|||||||
|
|
||||||
if ($date === false) {
|
if ($date === false) {
|
||||||
throw new \InvalidArgumentException(
|
throw new \InvalidArgumentException(
|
||||||
'Invalid date/time.'
|
$this->l10n->t('Invalid date/time.')
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $date;
|
return $date;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reject invalid UTF-8 and control characters. Newlines
|
||||||
|
* (\n, \r) are only allowed where multi-line text is
|
||||||
|
* expected, e.g. the description.
|
||||||
|
*/
|
||||||
|
private function assertClean(string $v, string $msg, bool $allowNewlines = false): void
|
||||||
|
{
|
||||||
|
$re = $allowNewlines
|
||||||
|
? '/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/'
|
||||||
|
: '/[\x00-\x1F\x7F]/';
|
||||||
|
|
||||||
|
if (!mb_check_encoding($v, 'UTF-8') || preg_match($re, $v) === 1) {
|
||||||
|
throw new \InvalidArgumentException($msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+23
-12
@@ -9,6 +9,8 @@ $h = static function (string $value): string {
|
|||||||
'UTF-8'
|
'UTF-8'
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
$l = $_['l10n'];
|
||||||
?>
|
?>
|
||||||
|
|
||||||
<div id="app-content">
|
<div id="app-content">
|
||||||
@@ -21,8 +23,17 @@ $h = static function (string $value): string {
|
|||||||
</h1>
|
</h1>
|
||||||
|
|
||||||
<p class="atocal-intro">
|
<p class="atocal-intro">
|
||||||
The following meeting will be added to your
|
<?= $h($l->t(
|
||||||
Nextcloud Calendar.
|
'The following meeting will be added to your '
|
||||||
|
. 'Nextcloud Calendar.'
|
||||||
|
)) ?>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p class="atocal-notice">
|
||||||
|
<?= $h($l->t(
|
||||||
|
'This request came from an external link. '
|
||||||
|
. 'Only continue if you trust its source.'
|
||||||
|
)) ?>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<?php if ($_['error'] !== ''): ?>
|
<?php if ($_['error'] !== ''): ?>
|
||||||
@@ -34,13 +45,13 @@ $h = static function (string $value): string {
|
|||||||
<div class="atocal-details">
|
<div class="atocal-details">
|
||||||
|
|
||||||
<div class="atocal-row">
|
<div class="atocal-row">
|
||||||
<strong>Title</strong>
|
<strong><?= $h($l->t('Title')) ?></strong>
|
||||||
<span><?= $h($_['summary']) ?></span>
|
<span><?= $h($_['summary']) ?></span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<?php if ($_['description'] !== ''): ?>
|
<?php if ($_['description'] !== ''): ?>
|
||||||
<div class="atocal-row">
|
<div class="atocal-row">
|
||||||
<strong>Description</strong>
|
<strong><?= $h($l->t('Description')) ?></strong>
|
||||||
<span class="description">
|
<span class="description">
|
||||||
<?= nl2br($h($_['description'])) ?>
|
<?= nl2br($h($_['description'])) ?>
|
||||||
</span>
|
</span>
|
||||||
@@ -48,25 +59,25 @@ $h = static function (string $value): string {
|
|||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
|
|
||||||
<div class="atocal-row">
|
<div class="atocal-row">
|
||||||
<strong>Start</strong>
|
<strong><?= $h($l->t('Start')) ?></strong>
|
||||||
<span><?= $h(str_replace('T', ' ', $_['start'])) ?></span>
|
<span><?= $h(str_replace('T', ' ', $_['start'])) ?></span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="atocal-row">
|
<div class="atocal-row">
|
||||||
<strong>End</strong>
|
<strong><?= $h($l->t('End')) ?></strong>
|
||||||
<span><?= $h(str_replace('T', ' ', $_['end'])) ?></span>
|
<span><?= $h(str_replace('T', ' ', $_['end'])) ?></span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<?php if ($_['organizer'] !== ''): ?>
|
<?php if ($_['organizer'] !== ''): ?>
|
||||||
<div class="atocal-row">
|
<div class="atocal-row">
|
||||||
<strong>Organizer</strong>
|
<strong><?= $h($l->t('Organizer')) ?></strong>
|
||||||
<span><?= $h($_['organizer']) ?></span>
|
<span><?= $h($_['organizer']) ?></span>
|
||||||
</div>
|
</div>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
|
|
||||||
<?php if ($_['location'] !== ''): ?>
|
<?php if ($_['location'] !== ''): ?>
|
||||||
<div class="atocal-row">
|
<div class="atocal-row">
|
||||||
<strong>Location</strong>
|
<strong><?= $h($l->t('Location')) ?></strong>
|
||||||
<span>
|
<span>
|
||||||
<a
|
<a
|
||||||
href="<?= $h($_['location']) ?>"
|
href="<?= $h($_['location']) ?>"
|
||||||
@@ -81,7 +92,7 @@ $h = static function (string $value): string {
|
|||||||
|
|
||||||
<?php if ($_['url'] !== '' && $_['url'] !== $_['location']): ?>
|
<?php if ($_['url'] !== '' && $_['url'] !== $_['location']): ?>
|
||||||
<div class="atocal-row">
|
<div class="atocal-row">
|
||||||
<strong>Link</strong>
|
<strong><?= $h($l->t('Link')) ?></strong>
|
||||||
<span>
|
<span>
|
||||||
<a
|
<a
|
||||||
href="<?= $h($_['url']) ?>"
|
href="<?= $h($_['url']) ?>"
|
||||||
@@ -103,7 +114,7 @@ $h = static function (string $value): string {
|
|||||||
<?php if ($_['calendars'] !== []): ?>
|
<?php if ($_['calendars'] !== []): ?>
|
||||||
<div class="atocal-row">
|
<div class="atocal-row">
|
||||||
<label for="atocal-calendar">
|
<label for="atocal-calendar">
|
||||||
<strong>Calendar</strong>
|
<strong><?= $h($l->t('Calendar')) ?></strong>
|
||||||
</label>
|
</label>
|
||||||
<select id="atocal-calendar" name="calendarId">
|
<select id="atocal-calendar" name="calendarId">
|
||||||
<?php foreach ($_['calendars'] as $calendar): ?>
|
<?php foreach ($_['calendars'] as $calendar): ?>
|
||||||
@@ -172,14 +183,14 @@ $h = static function (string $value): string {
|
|||||||
type="submit"
|
type="submit"
|
||||||
class="primary"
|
class="primary"
|
||||||
>
|
>
|
||||||
Add to Nextcloud Calendar
|
<?= $h($l->t('Add to Nextcloud Calendar')) ?>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<a
|
<a
|
||||||
href="<?= $h($_['cancelUrl']) ?>"
|
href="<?= $h($_['cancelUrl']) ?>"
|
||||||
class="secondary"
|
class="secondary"
|
||||||
>
|
>
|
||||||
Cancel
|
<?= $h($l->t('Cancel')) ?>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -22,6 +22,14 @@
|
|||||||
margin-bottom: 28px;
|
margin-bottom: 28px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.atocal-notice {
|
||||||
|
background: var(--color-warning);
|
||||||
|
color: var(--color-warning-text);
|
||||||
|
border-radius: 6px;
|
||||||
|
padding: 12px 16px;
|
||||||
|
margin-bottom: 28px;
|
||||||
|
}
|
||||||
|
|
||||||
.atocal-error {
|
.atocal-error {
|
||||||
background: var(--color-error);
|
background: var(--color-error);
|
||||||
color: var(--color-error-text);
|
color: var(--color-error-text);
|
||||||
|
|||||||
Reference in new issue
Block a user