Passed
Push — master ( 50ad70...e26666 )
by Roeland
13:47 queued 12s
created
apps/dav/lib/CalDAV/Schedule/IMipPlugin.php 2 patches
Indentation   +539 added lines, -539 removed lines patch added patch discarded remove patch
@@ -69,207 +69,207 @@  discard block
 block discarded – undo
69 69
  */
70 70
 class IMipPlugin extends SabreIMipPlugin {
71 71
 
72
-	/** @var string */
73
-	private $userId;
74
-
75
-	/** @var IConfig */
76
-	private $config;
77
-
78
-	/** @var IMailer */
79
-	private $mailer;
80
-
81
-	/** @var ILogger */
82
-	private $logger;
83
-
84
-	/** @var ITimeFactory */
85
-	private $timeFactory;
86
-
87
-	/** @var L10NFactory */
88
-	private $l10nFactory;
89
-
90
-	/** @var IURLGenerator */
91
-	private $urlGenerator;
92
-
93
-	/** @var ISecureRandom */
94
-	private $random;
95
-
96
-	/** @var IDBConnection */
97
-	private $db;
98
-
99
-	/** @var Defaults */
100
-	private $defaults;
101
-
102
-	/** @var IUserManager */
103
-	private $userManager;
104
-
105
-	const MAX_DATE = '2038-01-01';
106
-
107
-	const METHOD_REQUEST = 'request';
108
-	const METHOD_REPLY = 'reply';
109
-	const METHOD_CANCEL = 'cancel';
110
-
111
-	/**
112
-	 * @param IConfig $config
113
-	 * @param IMailer $mailer
114
-	 * @param ILogger $logger
115
-	 * @param ITimeFactory $timeFactory
116
-	 * @param L10NFactory $l10nFactory
117
-	 * @param IUrlGenerator $urlGenerator
118
-	 * @param Defaults $defaults
119
-	 * @param ISecureRandom $random
120
-	 * @param IDBConnection $db
121
-	 * @param string $userId
122
-	 */
123
-	public function __construct(IConfig $config, IMailer $mailer, ILogger $logger,
124
-								ITimeFactory $timeFactory, L10NFactory $l10nFactory,
125
-								IURLGenerator $urlGenerator, Defaults $defaults,
126
-								ISecureRandom $random, IDBConnection $db, IUserManager $userManager,
127
-								$userId) {
128
-		parent::__construct('');
129
-		$this->userId = $userId;
130
-		$this->config = $config;
131
-		$this->mailer = $mailer;
132
-		$this->logger = $logger;
133
-		$this->timeFactory = $timeFactory;
134
-		$this->l10nFactory = $l10nFactory;
135
-		$this->urlGenerator = $urlGenerator;
136
-		$this->random = $random;
137
-		$this->db = $db;
138
-		$this->defaults = $defaults;
139
-		$this->userManager = $userManager;
140
-	}
141
-
142
-	/**
143
-	 * Event handler for the 'schedule' event.
144
-	 *
145
-	 * @param Message $iTipMessage
146
-	 * @return void
147
-	 */
148
-	public function schedule(Message $iTipMessage) {
149
-
150
-		// Not sending any emails if the system considers the update
151
-		// insignificant.
152
-		if (!$iTipMessage->significantChange) {
153
-			if (!$iTipMessage->scheduleStatus) {
154
-				$iTipMessage->scheduleStatus = '1.0;We got the message, but it\'s not significant enough to warrant an email';
155
-			}
156
-			return;
157
-		}
158
-
159
-		$summary = $iTipMessage->message->VEVENT->SUMMARY;
160
-
161
-		if (parse_url($iTipMessage->sender, PHP_URL_SCHEME) !== 'mailto') {
162
-			return;
163
-		}
164
-
165
-		if (parse_url($iTipMessage->recipient, PHP_URL_SCHEME) !== 'mailto') {
166
-			return;
167
-		}
168
-
169
-		// don't send out mails for events that already took place
170
-		$lastOccurrence = $this->getLastOccurrence($iTipMessage->message);
171
-		$currentTime = $this->timeFactory->getTime();
172
-		if ($lastOccurrence < $currentTime) {
173
-			return;
174
-		}
175
-
176
-		// Strip off mailto:
177
-		$sender = substr($iTipMessage->sender, 7);
178
-		$recipient = substr($iTipMessage->recipient, 7);
179
-
180
-		$senderName = $iTipMessage->senderName ?: null;
181
-		$recipientName = $iTipMessage->recipientName ?: null;
182
-
183
-		if ($senderName === null || empty(trim($senderName))) {
184
-			$user = $this->userManager->get($this->userId);
185
-			if ($user) {
186
-				// getDisplayName automatically uses the uid
187
-				// if no display-name is set
188
-				$senderName = $user->getDisplayName();
189
-			}
190
-		}
191
-
192
-		/** @var VEvent $vevent */
193
-		$vevent = $iTipMessage->message->VEVENT;
194
-
195
-		$attendee = $this->getCurrentAttendee($iTipMessage);
196
-		$defaultLang = $this->l10nFactory->findLanguage();
197
-		$lang = $this->getAttendeeLangOrDefault($defaultLang, $attendee);
198
-		$l10n = $this->l10nFactory->get('dav', $lang);
199
-
200
-		$meetingAttendeeName = $recipientName ?: $recipient;
201
-		$meetingInviteeName = $senderName ?: $sender;
202
-
203
-		$meetingTitle = $vevent->SUMMARY;
204
-		$meetingDescription = $vevent->DESCRIPTION;
205
-
206
-		$start = $vevent->DTSTART;
207
-		if (isset($vevent->DTEND)) {
208
-			$end = $vevent->DTEND;
209
-		} elseif (isset($vevent->DURATION)) {
210
-			$isFloating = $vevent->DTSTART->isFloating();
211
-			$end = clone $vevent->DTSTART;
212
-			$endDateTime = $end->getDateTime();
213
-			$endDateTime = $endDateTime->add(DateTimeParser::parse($vevent->DURATION->getValue()));
214
-			$end->setDateTime($endDateTime, $isFloating);
215
-		} elseif (!$vevent->DTSTART->hasTime()) {
216
-			$isFloating = $vevent->DTSTART->isFloating();
217
-			$end = clone $vevent->DTSTART;
218
-			$endDateTime = $end->getDateTime();
219
-			$endDateTime = $endDateTime->modify('+1 day');
220
-			$end->setDateTime($endDateTime, $isFloating);
221
-		} else {
222
-			$end = clone $vevent->DTSTART;
223
-		}
224
-
225
-		$meetingWhen = $this->generateWhenString($l10n, $start, $end);
226
-
227
-		$meetingUrl = $vevent->URL;
228
-		$meetingLocation = $vevent->LOCATION;
229
-
230
-		$defaultVal = '--';
231
-
232
-		$method = self::METHOD_REQUEST;
233
-		switch (strtolower($iTipMessage->method)) {
234
-			case self::METHOD_REPLY:
235
-				$method = self::METHOD_REPLY;
236
-				break;
237
-			case self::METHOD_CANCEL:
238
-				$method = self::METHOD_CANCEL;
239
-				break;
240
-		}
241
-
242
-		$data = [
243
-			'attendee_name' => (string)$meetingAttendeeName ?: $defaultVal,
244
-			'invitee_name' => (string)$meetingInviteeName ?: $defaultVal,
245
-			'meeting_title' => (string)$meetingTitle ?: $defaultVal,
246
-			'meeting_description' => (string)$meetingDescription ?: $defaultVal,
247
-			'meeting_url' => (string)$meetingUrl ?: $defaultVal,
248
-		];
249
-
250
-		$fromEMail = Util::getDefaultEmailAddress('invitations-noreply');
251
-		$fromName = $l10n->t('%1$s via %2$s', [$senderName, $this->defaults->getName()]);
252
-
253
-		$message = $this->mailer->createMessage()
254
-			->setFrom([$fromEMail => $fromName])
255
-			->setReplyTo([$sender => $senderName])
256
-			->setTo([$recipient => $recipientName]);
257
-
258
-		$template = $this->mailer->createEMailTemplate('dav.calendarInvite.' . $method, $data);
259
-		$template->addHeader();
260
-
261
-		$summary = ((string) $summary !== '') ? (string) $summary : $l10n->t('Untitled event');
262
-
263
-		$this->addSubjectAndHeading($template, $l10n, $method, $summary,
264
-			$meetingAttendeeName, $meetingInviteeName);
265
-		$this->addBulletList($template, $l10n, $meetingWhen, $meetingLocation,
266
-			$meetingDescription, $meetingUrl);
267
-
268
-
269
-		// Only add response buttons to invitation requests: Fix Issue #11230
270
-		if (($method == self::METHOD_REQUEST) && $this->getAttendeeRSVP($attendee)) {
271
-
272
-			/*
72
+    /** @var string */
73
+    private $userId;
74
+
75
+    /** @var IConfig */
76
+    private $config;
77
+
78
+    /** @var IMailer */
79
+    private $mailer;
80
+
81
+    /** @var ILogger */
82
+    private $logger;
83
+
84
+    /** @var ITimeFactory */
85
+    private $timeFactory;
86
+
87
+    /** @var L10NFactory */
88
+    private $l10nFactory;
89
+
90
+    /** @var IURLGenerator */
91
+    private $urlGenerator;
92
+
93
+    /** @var ISecureRandom */
94
+    private $random;
95
+
96
+    /** @var IDBConnection */
97
+    private $db;
98
+
99
+    /** @var Defaults */
100
+    private $defaults;
101
+
102
+    /** @var IUserManager */
103
+    private $userManager;
104
+
105
+    const MAX_DATE = '2038-01-01';
106
+
107
+    const METHOD_REQUEST = 'request';
108
+    const METHOD_REPLY = 'reply';
109
+    const METHOD_CANCEL = 'cancel';
110
+
111
+    /**
112
+     * @param IConfig $config
113
+     * @param IMailer $mailer
114
+     * @param ILogger $logger
115
+     * @param ITimeFactory $timeFactory
116
+     * @param L10NFactory $l10nFactory
117
+     * @param IUrlGenerator $urlGenerator
118
+     * @param Defaults $defaults
119
+     * @param ISecureRandom $random
120
+     * @param IDBConnection $db
121
+     * @param string $userId
122
+     */
123
+    public function __construct(IConfig $config, IMailer $mailer, ILogger $logger,
124
+                                ITimeFactory $timeFactory, L10NFactory $l10nFactory,
125
+                                IURLGenerator $urlGenerator, Defaults $defaults,
126
+                                ISecureRandom $random, IDBConnection $db, IUserManager $userManager,
127
+                                $userId) {
128
+        parent::__construct('');
129
+        $this->userId = $userId;
130
+        $this->config = $config;
131
+        $this->mailer = $mailer;
132
+        $this->logger = $logger;
133
+        $this->timeFactory = $timeFactory;
134
+        $this->l10nFactory = $l10nFactory;
135
+        $this->urlGenerator = $urlGenerator;
136
+        $this->random = $random;
137
+        $this->db = $db;
138
+        $this->defaults = $defaults;
139
+        $this->userManager = $userManager;
140
+    }
141
+
142
+    /**
143
+     * Event handler for the 'schedule' event.
144
+     *
145
+     * @param Message $iTipMessage
146
+     * @return void
147
+     */
148
+    public function schedule(Message $iTipMessage) {
149
+
150
+        // Not sending any emails if the system considers the update
151
+        // insignificant.
152
+        if (!$iTipMessage->significantChange) {
153
+            if (!$iTipMessage->scheduleStatus) {
154
+                $iTipMessage->scheduleStatus = '1.0;We got the message, but it\'s not significant enough to warrant an email';
155
+            }
156
+            return;
157
+        }
158
+
159
+        $summary = $iTipMessage->message->VEVENT->SUMMARY;
160
+
161
+        if (parse_url($iTipMessage->sender, PHP_URL_SCHEME) !== 'mailto') {
162
+            return;
163
+        }
164
+
165
+        if (parse_url($iTipMessage->recipient, PHP_URL_SCHEME) !== 'mailto') {
166
+            return;
167
+        }
168
+
169
+        // don't send out mails for events that already took place
170
+        $lastOccurrence = $this->getLastOccurrence($iTipMessage->message);
171
+        $currentTime = $this->timeFactory->getTime();
172
+        if ($lastOccurrence < $currentTime) {
173
+            return;
174
+        }
175
+
176
+        // Strip off mailto:
177
+        $sender = substr($iTipMessage->sender, 7);
178
+        $recipient = substr($iTipMessage->recipient, 7);
179
+
180
+        $senderName = $iTipMessage->senderName ?: null;
181
+        $recipientName = $iTipMessage->recipientName ?: null;
182
+
183
+        if ($senderName === null || empty(trim($senderName))) {
184
+            $user = $this->userManager->get($this->userId);
185
+            if ($user) {
186
+                // getDisplayName automatically uses the uid
187
+                // if no display-name is set
188
+                $senderName = $user->getDisplayName();
189
+            }
190
+        }
191
+
192
+        /** @var VEvent $vevent */
193
+        $vevent = $iTipMessage->message->VEVENT;
194
+
195
+        $attendee = $this->getCurrentAttendee($iTipMessage);
196
+        $defaultLang = $this->l10nFactory->findLanguage();
197
+        $lang = $this->getAttendeeLangOrDefault($defaultLang, $attendee);
198
+        $l10n = $this->l10nFactory->get('dav', $lang);
199
+
200
+        $meetingAttendeeName = $recipientName ?: $recipient;
201
+        $meetingInviteeName = $senderName ?: $sender;
202
+
203
+        $meetingTitle = $vevent->SUMMARY;
204
+        $meetingDescription = $vevent->DESCRIPTION;
205
+
206
+        $start = $vevent->DTSTART;
207
+        if (isset($vevent->DTEND)) {
208
+            $end = $vevent->DTEND;
209
+        } elseif (isset($vevent->DURATION)) {
210
+            $isFloating = $vevent->DTSTART->isFloating();
211
+            $end = clone $vevent->DTSTART;
212
+            $endDateTime = $end->getDateTime();
213
+            $endDateTime = $endDateTime->add(DateTimeParser::parse($vevent->DURATION->getValue()));
214
+            $end->setDateTime($endDateTime, $isFloating);
215
+        } elseif (!$vevent->DTSTART->hasTime()) {
216
+            $isFloating = $vevent->DTSTART->isFloating();
217
+            $end = clone $vevent->DTSTART;
218
+            $endDateTime = $end->getDateTime();
219
+            $endDateTime = $endDateTime->modify('+1 day');
220
+            $end->setDateTime($endDateTime, $isFloating);
221
+        } else {
222
+            $end = clone $vevent->DTSTART;
223
+        }
224
+
225
+        $meetingWhen = $this->generateWhenString($l10n, $start, $end);
226
+
227
+        $meetingUrl = $vevent->URL;
228
+        $meetingLocation = $vevent->LOCATION;
229
+
230
+        $defaultVal = '--';
231
+
232
+        $method = self::METHOD_REQUEST;
233
+        switch (strtolower($iTipMessage->method)) {
234
+            case self::METHOD_REPLY:
235
+                $method = self::METHOD_REPLY;
236
+                break;
237
+            case self::METHOD_CANCEL:
238
+                $method = self::METHOD_CANCEL;
239
+                break;
240
+        }
241
+
242
+        $data = [
243
+            'attendee_name' => (string)$meetingAttendeeName ?: $defaultVal,
244
+            'invitee_name' => (string)$meetingInviteeName ?: $defaultVal,
245
+            'meeting_title' => (string)$meetingTitle ?: $defaultVal,
246
+            'meeting_description' => (string)$meetingDescription ?: $defaultVal,
247
+            'meeting_url' => (string)$meetingUrl ?: $defaultVal,
248
+        ];
249
+
250
+        $fromEMail = Util::getDefaultEmailAddress('invitations-noreply');
251
+        $fromName = $l10n->t('%1$s via %2$s', [$senderName, $this->defaults->getName()]);
252
+
253
+        $message = $this->mailer->createMessage()
254
+            ->setFrom([$fromEMail => $fromName])
255
+            ->setReplyTo([$sender => $senderName])
256
+            ->setTo([$recipient => $recipientName]);
257
+
258
+        $template = $this->mailer->createEMailTemplate('dav.calendarInvite.' . $method, $data);
259
+        $template->addHeader();
260
+
261
+        $summary = ((string) $summary !== '') ? (string) $summary : $l10n->t('Untitled event');
262
+
263
+        $this->addSubjectAndHeading($template, $l10n, $method, $summary,
264
+            $meetingAttendeeName, $meetingInviteeName);
265
+        $this->addBulletList($template, $l10n, $meetingWhen, $meetingLocation,
266
+            $meetingDescription, $meetingUrl);
267
+
268
+
269
+        // Only add response buttons to invitation requests: Fix Issue #11230
270
+        if (($method == self::METHOD_REQUEST) && $this->getAttendeeRSVP($attendee)) {
271
+
272
+            /*
273 273
 			** Only offer invitation accept/reject buttons, which link back to the
274 274
 			** nextcloud server, to recipients who can access the nextcloud server via
275 275
 			** their internet/intranet.  Issue #12156
@@ -288,342 +288,342 @@  discard block
 block discarded – undo
288 288
 			** To suppress URLs entirely, set invitation_link_recipients to boolean "no".
289 289
 			*/
290 290
 
291
-			$recipientDomain = substr(strrchr($recipient, "@"), 1);
292
-			$invitationLinkRecipients = explode(',', preg_replace('/\s+/', '', strtolower($this->config->getAppValue('dav', 'invitation_link_recipients', 'yes'))));
293
-
294
-			if (strcmp('yes', $invitationLinkRecipients[0]) === 0
295
-				 || in_array(strtolower($recipient), $invitationLinkRecipients)
296
-				 || in_array(strtolower($recipientDomain), $invitationLinkRecipients)) {
297
-				$this->addResponseButtons($template, $l10n, $iTipMessage, $lastOccurrence);
298
-			}
299
-		}
300
-
301
-		$template->addFooter();
302
-
303
-		$message->useTemplate($template);
304
-
305
-		$attachment = $this->mailer->createAttachment(
306
-			$iTipMessage->message->serialize(),
307
-			'event.ics',// TODO(leon): Make file name unique, e.g. add event id
308
-			'text/calendar; method=' . $iTipMessage->method
309
-		);
310
-		$message->attach($attachment);
311
-
312
-		try {
313
-			$failed = $this->mailer->send($message);
314
-			$iTipMessage->scheduleStatus = '1.1; Scheduling message is sent via iMip';
315
-			if ($failed) {
316
-				$this->logger->error('Unable to deliver message to {failed}', ['app' => 'dav', 'failed' =>  implode(', ', $failed)]);
317
-				$iTipMessage->scheduleStatus = '5.0; EMail delivery failed';
318
-			}
319
-		} catch(\Exception $ex) {
320
-			$this->logger->logException($ex, ['app' => 'dav']);
321
-			$iTipMessage->scheduleStatus = '5.0; EMail delivery failed';
322
-		}
323
-	}
324
-
325
-	/**
326
-	 * check if event took place in the past already
327
-	 * @param VCalendar $vObject
328
-	 * @return int
329
-	 */
330
-	private function getLastOccurrence(VCalendar $vObject) {
331
-		/** @var VEvent $component */
332
-		$component = $vObject->VEVENT;
333
-
334
-		$firstOccurrence = $component->DTSTART->getDateTime()->getTimeStamp();
335
-		// Finding the last occurrence is a bit harder
336
-		if (!isset($component->RRULE)) {
337
-			if (isset($component->DTEND)) {
338
-				$lastOccurrence = $component->DTEND->getDateTime()->getTimeStamp();
339
-			} elseif (isset($component->DURATION)) {
340
-				/** @var \DateTime $endDate */
341
-				$endDate = clone $component->DTSTART->getDateTime();
342
-				// $component->DTEND->getDateTime() returns DateTimeImmutable
343
-				$endDate = $endDate->add(DateTimeParser::parse($component->DURATION->getValue()));
344
-				$lastOccurrence = $endDate->getTimestamp();
345
-			} elseif (!$component->DTSTART->hasTime()) {
346
-				/** @var \DateTime $endDate */
347
-				$endDate = clone $component->DTSTART->getDateTime();
348
-				// $component->DTSTART->getDateTime() returns DateTimeImmutable
349
-				$endDate = $endDate->modify('+1 day');
350
-				$lastOccurrence = $endDate->getTimestamp();
351
-			} else {
352
-				$lastOccurrence = $firstOccurrence;
353
-			}
354
-		} else {
355
-			$it = new EventIterator($vObject, (string)$component->UID);
356
-			$maxDate = new \DateTime(self::MAX_DATE);
357
-			if ($it->isInfinite()) {
358
-				$lastOccurrence = $maxDate->getTimestamp();
359
-			} else {
360
-				$end = $it->getDtEnd();
361
-				while($it->valid() && $end < $maxDate) {
362
-					$end = $it->getDtEnd();
363
-					$it->next();
364
-
365
-				}
366
-				$lastOccurrence = $end->getTimestamp();
367
-			}
368
-		}
369
-
370
-		return $lastOccurrence;
371
-	}
372
-
373
-
374
-	/**
375
-	 * @param Message $iTipMessage
376
-	 * @return null|Property
377
-	 */
378
-	private function getCurrentAttendee(Message $iTipMessage) {
379
-		/** @var VEvent $vevent */
380
-		$vevent = $iTipMessage->message->VEVENT;
381
-		$attendees = $vevent->select('ATTENDEE');
382
-		foreach ($attendees as $attendee) {
383
-			/** @var Property $attendee */
384
-			if (strcasecmp($attendee->getValue(), $iTipMessage->recipient) === 0) {
385
-				return $attendee;
386
-			}
387
-		}
388
-		return null;
389
-	}
390
-
391
-	/**
392
-	 * @param string $default
393
-	 * @param Property|null $attendee
394
-	 * @return string
395
-	 */
396
-	private function getAttendeeLangOrDefault($default, Property $attendee = null) {
397
-		if ($attendee !== null) {
398
-			$lang = $attendee->offsetGet('LANGUAGE');
399
-			if ($lang instanceof Parameter) {
400
-				return $lang->getValue();
401
-			}
402
-		}
403
-		return $default;
404
-	}
405
-
406
-	/**
407
-	 * @param Property|null $attendee
408
-	 * @return bool
409
-	 */
410
-	private function getAttendeeRSVP(Property $attendee = null) {
411
-		if ($attendee !== null) {
412
-			$rsvp = $attendee->offsetGet('RSVP');
413
-			if (($rsvp instanceof Parameter) && (strcasecmp($rsvp->getValue(), 'TRUE') === 0)) {
414
-				return true;
415
-			}
416
-		}
417
-		// RFC 5545 3.2.17: default RSVP is false
418
-		return false;
419
-	}
420
-
421
-	/**
422
-	 * @param IL10N $l10n
423
-	 * @param Property $dtstart
424
-	 * @param Property $dtend
425
-	 */
426
-	private function generateWhenString(IL10N $l10n, Property $dtstart, Property $dtend) {
427
-		$isAllDay = $dtstart instanceof Property\ICalendar\Date;
428
-
429
-		/** @var Property\ICalendar\Date | Property\ICalendar\DateTime $dtstart */
430
-		/** @var Property\ICalendar\Date | Property\ICalendar\DateTime $dtend */
431
-		/** @var \DateTimeImmutable $dtstartDt */
432
-		$dtstartDt = $dtstart->getDateTime();
433
-		/** @var \DateTimeImmutable $dtendDt */
434
-		$dtendDt = $dtend->getDateTime();
435
-
436
-		$diff = $dtstartDt->diff($dtendDt);
437
-
438
-		$dtstartDt = new \DateTime($dtstartDt->format(\DateTime::ATOM));
439
-		$dtendDt = new \DateTime($dtendDt->format(\DateTime::ATOM));
440
-
441
-		if ($isAllDay) {
442
-			// One day event
443
-			if ($diff->days === 1) {
444
-				return $l10n->l('date', $dtstartDt, ['width' => 'medium']);
445
-			}
446
-
447
-			// DTEND is exclusive, so if the ics data says 2020-01-01 to 2020-01-05,
448
-			// the email should show 2020-01-01 to 2020-01-04.
449
-			$dtendDt->modify('-1 day');
450
-
451
-			//event that spans over multiple days
452
-			$localeStart = $l10n->l('date', $dtstartDt, ['width' => 'medium']);
453
-			$localeEnd = $l10n->l('date', $dtendDt, ['width' => 'medium']);
454
-
455
-			return $localeStart . ' - ' . $localeEnd;
456
-		}
457
-
458
-		/** @var Property\ICalendar\DateTime $dtstart */
459
-		/** @var Property\ICalendar\DateTime $dtend */
460
-		$isFloating = $dtstart->isFloating();
461
-		$startTimezone = $endTimezone = null;
462
-		if (!$isFloating) {
463
-			$prop = $dtstart->offsetGet('TZID');
464
-			if ($prop instanceof Parameter) {
465
-				$startTimezone = $prop->getValue();
466
-			}
467
-
468
-			$prop = $dtend->offsetGet('TZID');
469
-			if ($prop instanceof Parameter) {
470
-				$endTimezone = $prop->getValue();
471
-			}
472
-		}
473
-
474
-		$localeStart = $l10n->l('weekdayName', $dtstartDt, ['width' => 'abbreviated']) . ', ' .
475
-			$l10n->l('datetime', $dtstartDt, ['width' => 'medium|short']);
476
-
477
-		// always show full date with timezone if timezones are different
478
-		if ($startTimezone !== $endTimezone) {
479
-			$localeEnd = $l10n->l('datetime', $dtendDt, ['width' => 'medium|short']);
480
-
481
-			return $localeStart . ' (' . $startTimezone . ') - ' .
482
-				$localeEnd . ' (' . $endTimezone . ')';
483
-		}
484
-
485
-		// show only end time if date is the same
486
-		if ($this->isDayEqual($dtstartDt, $dtendDt)) {
487
-			$localeEnd = $l10n->l('time', $dtendDt, ['width' => 'short']);
488
-		} else {
489
-			$localeEnd = $l10n->l('weekdayName', $dtendDt, ['width' => 'abbreviated']) . ', ' .
490
-				$l10n->l('datetime', $dtendDt, ['width' => 'medium|short']);
491
-		}
492
-
493
-		return  $localeStart . ' - ' . $localeEnd . ' (' . $startTimezone . ')';
494
-	}
495
-
496
-	/**
497
-	 * @param \DateTime $dtStart
498
-	 * @param \DateTime $dtEnd
499
-	 * @return bool
500
-	 */
501
-	private function isDayEqual(\DateTime $dtStart, \DateTime $dtEnd) {
502
-		return $dtStart->format('Y-m-d') === $dtEnd->format('Y-m-d');
503
-	}
504
-
505
-	/**
506
-	 * @param IEMailTemplate $template
507
-	 * @param IL10N $l10n
508
-	 * @param string $method
509
-	 * @param string $summary
510
-	 * @param string $attendeeName
511
-	 * @param string $inviteeName
512
-	 */
513
-	private function addSubjectAndHeading(IEMailTemplate $template, IL10N $l10n,
514
-										  $method, $summary, $attendeeName, $inviteeName) {
515
-		if ($method === self::METHOD_CANCEL) {
516
-			$template->setSubject('Cancelled: ' . $summary);
517
-			$template->addHeading($l10n->t('Invitation canceled'), $l10n->t('Hello %s,', [$attendeeName]));
518
-			$template->addBodyText($l10n->t('The meeting »%1$s« with %2$s was canceled.', [$summary, $inviteeName]));
519
-		} else if ($method === self::METHOD_REPLY) {
520
-			$template->setSubject('Re: ' . $summary);
521
-			$template->addHeading($l10n->t('Invitation updated'), $l10n->t('Hello %s,', [$attendeeName]));
522
-			$template->addBodyText($l10n->t('The meeting »%1$s« with %2$s was updated.', [$summary, $inviteeName]));
523
-		} else {
524
-			$template->setSubject('Invitation: ' . $summary);
525
-			$template->addHeading($l10n->t('%1$s invited you to »%2$s«', [$inviteeName, $summary]), $l10n->t('Hello %s,', [$attendeeName]));
526
-		}
527
-	}
528
-
529
-	/**
530
-	 * @param IEMailTemplate $template
531
-	 * @param IL10N $l10n
532
-	 * @param string $time
533
-	 * @param string $location
534
-	 * @param string $description
535
-	 * @param string $url
536
-	 */
537
-	private function addBulletList(IEMailTemplate $template, IL10N $l10n, $time, $location, $description, $url) {
538
-		$template->addBodyListItem($time, $l10n->t('When:'),
539
-			$this->getAbsoluteImagePath('filetypes/text-calendar.svg'));
540
-
541
-		if ($location) {
542
-			$template->addBodyListItem($location, $l10n->t('Where:'),
543
-				$this->getAbsoluteImagePath('filetypes/location.svg'));
544
-		}
545
-		if ($description) {
546
-			$template->addBodyListItem((string)$description, $l10n->t('Description:'),
547
-				$this->getAbsoluteImagePath('filetypes/text.svg'));
548
-		}
549
-		if ($url) {
550
-			$template->addBodyListItem((string)$url, $l10n->t('Link:'),
551
-				$this->getAbsoluteImagePath('filetypes/link.svg'));
552
-		}
553
-	}
554
-
555
-	/**
556
-	 * @param IEMailTemplate $template
557
-	 * @param IL10N $l10n
558
-	 * @param Message $iTipMessage
559
-	 * @param int $lastOccurrence
560
-	 */
561
-	private function addResponseButtons(IEMailTemplate $template, IL10N $l10n,
562
-										Message $iTipMessage, $lastOccurrence) {
563
-		$token = $this->createInvitationToken($iTipMessage, $lastOccurrence);
564
-
565
-		$template->addBodyButtonGroup(
566
-			$l10n->t('Accept'),
567
-			$this->urlGenerator->linkToRouteAbsolute('dav.invitation_response.accept', [
568
-				'token' => $token,
569
-			]),
570
-			$l10n->t('Decline'),
571
-			$this->urlGenerator->linkToRouteAbsolute('dav.invitation_response.decline', [
572
-				'token' => $token,
573
-			])
574
-		);
575
-
576
-		$moreOptionsURL = $this->urlGenerator->linkToRouteAbsolute('dav.invitation_response.options', [
577
-			'token' => $token,
578
-		]);
579
-		$html = vsprintf('<small><a href="%s">%s</a></small>', [
580
-			$moreOptionsURL, $l10n->t('More options …')
581
-		]);
582
-		$text = $l10n->t('More options at %s', [$moreOptionsURL]);
583
-
584
-		$template->addBodyText($html, $text);
585
-	}
586
-
587
-	/**
588
-	 * @param string $path
589
-	 * @return string
590
-	 */
591
-	private function getAbsoluteImagePath($path) {
592
-		return $this->urlGenerator->getAbsoluteURL(
593
-			$this->urlGenerator->imagePath('core', $path)
594
-		);
595
-	}
596
-
597
-	/**
598
-	 * @param Message $iTipMessage
599
-	 * @param int $lastOccurrence
600
-	 * @return string
601
-	 */
602
-	private function createInvitationToken(Message $iTipMessage, $lastOccurrence):string {
603
-		$token = $this->random->generate(60, ISecureRandom::CHAR_UPPER . ISecureRandom::CHAR_LOWER . ISecureRandom::CHAR_DIGITS);
604
-
605
-		/** @var VEvent $vevent */
606
-		$vevent = $iTipMessage->message->VEVENT;
607
-		$attendee = $iTipMessage->recipient;
608
-		$organizer = $iTipMessage->sender;
609
-		$sequence = $iTipMessage->sequence;
610
-		$recurrenceId = isset($vevent->{'RECURRENCE-ID'}) ?
611
-			$vevent->{'RECURRENCE-ID'}->serialize() : null;
612
-		$uid = $vevent->{'UID'};
613
-
614
-		$query = $this->db->getQueryBuilder();
615
-		$query->insert('calendar_invitations')
616
-			->values([
617
-				'token' => $query->createNamedParameter($token),
618
-				'attendee' => $query->createNamedParameter($attendee),
619
-				'organizer' => $query->createNamedParameter($organizer),
620
-				'sequence' => $query->createNamedParameter($sequence),
621
-				'recurrenceid' => $query->createNamedParameter($recurrenceId),
622
-				'expiration' => $query->createNamedParameter($lastOccurrence),
623
-				'uid' => $query->createNamedParameter($uid)
624
-			])
625
-			->execute();
626
-
627
-		return $token;
628
-	}
291
+            $recipientDomain = substr(strrchr($recipient, "@"), 1);
292
+            $invitationLinkRecipients = explode(',', preg_replace('/\s+/', '', strtolower($this->config->getAppValue('dav', 'invitation_link_recipients', 'yes'))));
293
+
294
+            if (strcmp('yes', $invitationLinkRecipients[0]) === 0
295
+                 || in_array(strtolower($recipient), $invitationLinkRecipients)
296
+                 || in_array(strtolower($recipientDomain), $invitationLinkRecipients)) {
297
+                $this->addResponseButtons($template, $l10n, $iTipMessage, $lastOccurrence);
298
+            }
299
+        }
300
+
301
+        $template->addFooter();
302
+
303
+        $message->useTemplate($template);
304
+
305
+        $attachment = $this->mailer->createAttachment(
306
+            $iTipMessage->message->serialize(),
307
+            'event.ics',// TODO(leon): Make file name unique, e.g. add event id
308
+            'text/calendar; method=' . $iTipMessage->method
309
+        );
310
+        $message->attach($attachment);
311
+
312
+        try {
313
+            $failed = $this->mailer->send($message);
314
+            $iTipMessage->scheduleStatus = '1.1; Scheduling message is sent via iMip';
315
+            if ($failed) {
316
+                $this->logger->error('Unable to deliver message to {failed}', ['app' => 'dav', 'failed' =>  implode(', ', $failed)]);
317
+                $iTipMessage->scheduleStatus = '5.0; EMail delivery failed';
318
+            }
319
+        } catch(\Exception $ex) {
320
+            $this->logger->logException($ex, ['app' => 'dav']);
321
+            $iTipMessage->scheduleStatus = '5.0; EMail delivery failed';
322
+        }
323
+    }
324
+
325
+    /**
326
+     * check if event took place in the past already
327
+     * @param VCalendar $vObject
328
+     * @return int
329
+     */
330
+    private function getLastOccurrence(VCalendar $vObject) {
331
+        /** @var VEvent $component */
332
+        $component = $vObject->VEVENT;
333
+
334
+        $firstOccurrence = $component->DTSTART->getDateTime()->getTimeStamp();
335
+        // Finding the last occurrence is a bit harder
336
+        if (!isset($component->RRULE)) {
337
+            if (isset($component->DTEND)) {
338
+                $lastOccurrence = $component->DTEND->getDateTime()->getTimeStamp();
339
+            } elseif (isset($component->DURATION)) {
340
+                /** @var \DateTime $endDate */
341
+                $endDate = clone $component->DTSTART->getDateTime();
342
+                // $component->DTEND->getDateTime() returns DateTimeImmutable
343
+                $endDate = $endDate->add(DateTimeParser::parse($component->DURATION->getValue()));
344
+                $lastOccurrence = $endDate->getTimestamp();
345
+            } elseif (!$component->DTSTART->hasTime()) {
346
+                /** @var \DateTime $endDate */
347
+                $endDate = clone $component->DTSTART->getDateTime();
348
+                // $component->DTSTART->getDateTime() returns DateTimeImmutable
349
+                $endDate = $endDate->modify('+1 day');
350
+                $lastOccurrence = $endDate->getTimestamp();
351
+            } else {
352
+                $lastOccurrence = $firstOccurrence;
353
+            }
354
+        } else {
355
+            $it = new EventIterator($vObject, (string)$component->UID);
356
+            $maxDate = new \DateTime(self::MAX_DATE);
357
+            if ($it->isInfinite()) {
358
+                $lastOccurrence = $maxDate->getTimestamp();
359
+            } else {
360
+                $end = $it->getDtEnd();
361
+                while($it->valid() && $end < $maxDate) {
362
+                    $end = $it->getDtEnd();
363
+                    $it->next();
364
+
365
+                }
366
+                $lastOccurrence = $end->getTimestamp();
367
+            }
368
+        }
369
+
370
+        return $lastOccurrence;
371
+    }
372
+
373
+
374
+    /**
375
+     * @param Message $iTipMessage
376
+     * @return null|Property
377
+     */
378
+    private function getCurrentAttendee(Message $iTipMessage) {
379
+        /** @var VEvent $vevent */
380
+        $vevent = $iTipMessage->message->VEVENT;
381
+        $attendees = $vevent->select('ATTENDEE');
382
+        foreach ($attendees as $attendee) {
383
+            /** @var Property $attendee */
384
+            if (strcasecmp($attendee->getValue(), $iTipMessage->recipient) === 0) {
385
+                return $attendee;
386
+            }
387
+        }
388
+        return null;
389
+    }
390
+
391
+    /**
392
+     * @param string $default
393
+     * @param Property|null $attendee
394
+     * @return string
395
+     */
396
+    private function getAttendeeLangOrDefault($default, Property $attendee = null) {
397
+        if ($attendee !== null) {
398
+            $lang = $attendee->offsetGet('LANGUAGE');
399
+            if ($lang instanceof Parameter) {
400
+                return $lang->getValue();
401
+            }
402
+        }
403
+        return $default;
404
+    }
405
+
406
+    /**
407
+     * @param Property|null $attendee
408
+     * @return bool
409
+     */
410
+    private function getAttendeeRSVP(Property $attendee = null) {
411
+        if ($attendee !== null) {
412
+            $rsvp = $attendee->offsetGet('RSVP');
413
+            if (($rsvp instanceof Parameter) && (strcasecmp($rsvp->getValue(), 'TRUE') === 0)) {
414
+                return true;
415
+            }
416
+        }
417
+        // RFC 5545 3.2.17: default RSVP is false
418
+        return false;
419
+    }
420
+
421
+    /**
422
+     * @param IL10N $l10n
423
+     * @param Property $dtstart
424
+     * @param Property $dtend
425
+     */
426
+    private function generateWhenString(IL10N $l10n, Property $dtstart, Property $dtend) {
427
+        $isAllDay = $dtstart instanceof Property\ICalendar\Date;
428
+
429
+        /** @var Property\ICalendar\Date | Property\ICalendar\DateTime $dtstart */
430
+        /** @var Property\ICalendar\Date | Property\ICalendar\DateTime $dtend */
431
+        /** @var \DateTimeImmutable $dtstartDt */
432
+        $dtstartDt = $dtstart->getDateTime();
433
+        /** @var \DateTimeImmutable $dtendDt */
434
+        $dtendDt = $dtend->getDateTime();
435
+
436
+        $diff = $dtstartDt->diff($dtendDt);
437
+
438
+        $dtstartDt = new \DateTime($dtstartDt->format(\DateTime::ATOM));
439
+        $dtendDt = new \DateTime($dtendDt->format(\DateTime::ATOM));
440
+
441
+        if ($isAllDay) {
442
+            // One day event
443
+            if ($diff->days === 1) {
444
+                return $l10n->l('date', $dtstartDt, ['width' => 'medium']);
445
+            }
446
+
447
+            // DTEND is exclusive, so if the ics data says 2020-01-01 to 2020-01-05,
448
+            // the email should show 2020-01-01 to 2020-01-04.
449
+            $dtendDt->modify('-1 day');
450
+
451
+            //event that spans over multiple days
452
+            $localeStart = $l10n->l('date', $dtstartDt, ['width' => 'medium']);
453
+            $localeEnd = $l10n->l('date', $dtendDt, ['width' => 'medium']);
454
+
455
+            return $localeStart . ' - ' . $localeEnd;
456
+        }
457
+
458
+        /** @var Property\ICalendar\DateTime $dtstart */
459
+        /** @var Property\ICalendar\DateTime $dtend */
460
+        $isFloating = $dtstart->isFloating();
461
+        $startTimezone = $endTimezone = null;
462
+        if (!$isFloating) {
463
+            $prop = $dtstart->offsetGet('TZID');
464
+            if ($prop instanceof Parameter) {
465
+                $startTimezone = $prop->getValue();
466
+            }
467
+
468
+            $prop = $dtend->offsetGet('TZID');
469
+            if ($prop instanceof Parameter) {
470
+                $endTimezone = $prop->getValue();
471
+            }
472
+        }
473
+
474
+        $localeStart = $l10n->l('weekdayName', $dtstartDt, ['width' => 'abbreviated']) . ', ' .
475
+            $l10n->l('datetime', $dtstartDt, ['width' => 'medium|short']);
476
+
477
+        // always show full date with timezone if timezones are different
478
+        if ($startTimezone !== $endTimezone) {
479
+            $localeEnd = $l10n->l('datetime', $dtendDt, ['width' => 'medium|short']);
480
+
481
+            return $localeStart . ' (' . $startTimezone . ') - ' .
482
+                $localeEnd . ' (' . $endTimezone . ')';
483
+        }
484
+
485
+        // show only end time if date is the same
486
+        if ($this->isDayEqual($dtstartDt, $dtendDt)) {
487
+            $localeEnd = $l10n->l('time', $dtendDt, ['width' => 'short']);
488
+        } else {
489
+            $localeEnd = $l10n->l('weekdayName', $dtendDt, ['width' => 'abbreviated']) . ', ' .
490
+                $l10n->l('datetime', $dtendDt, ['width' => 'medium|short']);
491
+        }
492
+
493
+        return  $localeStart . ' - ' . $localeEnd . ' (' . $startTimezone . ')';
494
+    }
495
+
496
+    /**
497
+     * @param \DateTime $dtStart
498
+     * @param \DateTime $dtEnd
499
+     * @return bool
500
+     */
501
+    private function isDayEqual(\DateTime $dtStart, \DateTime $dtEnd) {
502
+        return $dtStart->format('Y-m-d') === $dtEnd->format('Y-m-d');
503
+    }
504
+
505
+    /**
506
+     * @param IEMailTemplate $template
507
+     * @param IL10N $l10n
508
+     * @param string $method
509
+     * @param string $summary
510
+     * @param string $attendeeName
511
+     * @param string $inviteeName
512
+     */
513
+    private function addSubjectAndHeading(IEMailTemplate $template, IL10N $l10n,
514
+                                            $method, $summary, $attendeeName, $inviteeName) {
515
+        if ($method === self::METHOD_CANCEL) {
516
+            $template->setSubject('Cancelled: ' . $summary);
517
+            $template->addHeading($l10n->t('Invitation canceled'), $l10n->t('Hello %s,', [$attendeeName]));
518
+            $template->addBodyText($l10n->t('The meeting »%1$s« with %2$s was canceled.', [$summary, $inviteeName]));
519
+        } else if ($method === self::METHOD_REPLY) {
520
+            $template->setSubject('Re: ' . $summary);
521
+            $template->addHeading($l10n->t('Invitation updated'), $l10n->t('Hello %s,', [$attendeeName]));
522
+            $template->addBodyText($l10n->t('The meeting »%1$s« with %2$s was updated.', [$summary, $inviteeName]));
523
+        } else {
524
+            $template->setSubject('Invitation: ' . $summary);
525
+            $template->addHeading($l10n->t('%1$s invited you to »%2$s«', [$inviteeName, $summary]), $l10n->t('Hello %s,', [$attendeeName]));
526
+        }
527
+    }
528
+
529
+    /**
530
+     * @param IEMailTemplate $template
531
+     * @param IL10N $l10n
532
+     * @param string $time
533
+     * @param string $location
534
+     * @param string $description
535
+     * @param string $url
536
+     */
537
+    private function addBulletList(IEMailTemplate $template, IL10N $l10n, $time, $location, $description, $url) {
538
+        $template->addBodyListItem($time, $l10n->t('When:'),
539
+            $this->getAbsoluteImagePath('filetypes/text-calendar.svg'));
540
+
541
+        if ($location) {
542
+            $template->addBodyListItem($location, $l10n->t('Where:'),
543
+                $this->getAbsoluteImagePath('filetypes/location.svg'));
544
+        }
545
+        if ($description) {
546
+            $template->addBodyListItem((string)$description, $l10n->t('Description:'),
547
+                $this->getAbsoluteImagePath('filetypes/text.svg'));
548
+        }
549
+        if ($url) {
550
+            $template->addBodyListItem((string)$url, $l10n->t('Link:'),
551
+                $this->getAbsoluteImagePath('filetypes/link.svg'));
552
+        }
553
+    }
554
+
555
+    /**
556
+     * @param IEMailTemplate $template
557
+     * @param IL10N $l10n
558
+     * @param Message $iTipMessage
559
+     * @param int $lastOccurrence
560
+     */
561
+    private function addResponseButtons(IEMailTemplate $template, IL10N $l10n,
562
+                                        Message $iTipMessage, $lastOccurrence) {
563
+        $token = $this->createInvitationToken($iTipMessage, $lastOccurrence);
564
+
565
+        $template->addBodyButtonGroup(
566
+            $l10n->t('Accept'),
567
+            $this->urlGenerator->linkToRouteAbsolute('dav.invitation_response.accept', [
568
+                'token' => $token,
569
+            ]),
570
+            $l10n->t('Decline'),
571
+            $this->urlGenerator->linkToRouteAbsolute('dav.invitation_response.decline', [
572
+                'token' => $token,
573
+            ])
574
+        );
575
+
576
+        $moreOptionsURL = $this->urlGenerator->linkToRouteAbsolute('dav.invitation_response.options', [
577
+            'token' => $token,
578
+        ]);
579
+        $html = vsprintf('<small><a href="%s">%s</a></small>', [
580
+            $moreOptionsURL, $l10n->t('More options …')
581
+        ]);
582
+        $text = $l10n->t('More options at %s', [$moreOptionsURL]);
583
+
584
+        $template->addBodyText($html, $text);
585
+    }
586
+
587
+    /**
588
+     * @param string $path
589
+     * @return string
590
+     */
591
+    private function getAbsoluteImagePath($path) {
592
+        return $this->urlGenerator->getAbsoluteURL(
593
+            $this->urlGenerator->imagePath('core', $path)
594
+        );
595
+    }
596
+
597
+    /**
598
+     * @param Message $iTipMessage
599
+     * @param int $lastOccurrence
600
+     * @return string
601
+     */
602
+    private function createInvitationToken(Message $iTipMessage, $lastOccurrence):string {
603
+        $token = $this->random->generate(60, ISecureRandom::CHAR_UPPER . ISecureRandom::CHAR_LOWER . ISecureRandom::CHAR_DIGITS);
604
+
605
+        /** @var VEvent $vevent */
606
+        $vevent = $iTipMessage->message->VEVENT;
607
+        $attendee = $iTipMessage->recipient;
608
+        $organizer = $iTipMessage->sender;
609
+        $sequence = $iTipMessage->sequence;
610
+        $recurrenceId = isset($vevent->{'RECURRENCE-ID'}) ?
611
+            $vevent->{'RECURRENCE-ID'}->serialize() : null;
612
+        $uid = $vevent->{'UID'};
613
+
614
+        $query = $this->db->getQueryBuilder();
615
+        $query->insert('calendar_invitations')
616
+            ->values([
617
+                'token' => $query->createNamedParameter($token),
618
+                'attendee' => $query->createNamedParameter($attendee),
619
+                'organizer' => $query->createNamedParameter($organizer),
620
+                'sequence' => $query->createNamedParameter($sequence),
621
+                'recurrenceid' => $query->createNamedParameter($recurrenceId),
622
+                'expiration' => $query->createNamedParameter($lastOccurrence),
623
+                'uid' => $query->createNamedParameter($uid)
624
+            ])
625
+            ->execute();
626
+
627
+        return $token;
628
+    }
629 629
 }
Please login to merge, or discard this patch.
Spacing   +23 added lines, -23 removed lines patch added patch discarded remove patch
@@ -240,11 +240,11 @@  discard block
 block discarded – undo
240 240
 		}
241 241
 
242 242
 		$data = [
243
-			'attendee_name' => (string)$meetingAttendeeName ?: $defaultVal,
244
-			'invitee_name' => (string)$meetingInviteeName ?: $defaultVal,
245
-			'meeting_title' => (string)$meetingTitle ?: $defaultVal,
246
-			'meeting_description' => (string)$meetingDescription ?: $defaultVal,
247
-			'meeting_url' => (string)$meetingUrl ?: $defaultVal,
243
+			'attendee_name' => (string) $meetingAttendeeName ?: $defaultVal,
244
+			'invitee_name' => (string) $meetingInviteeName ?: $defaultVal,
245
+			'meeting_title' => (string) $meetingTitle ?: $defaultVal,
246
+			'meeting_description' => (string) $meetingDescription ?: $defaultVal,
247
+			'meeting_url' => (string) $meetingUrl ?: $defaultVal,
248 248
 		];
249 249
 
250 250
 		$fromEMail = Util::getDefaultEmailAddress('invitations-noreply');
@@ -255,7 +255,7 @@  discard block
 block discarded – undo
255 255
 			->setReplyTo([$sender => $senderName])
256 256
 			->setTo([$recipient => $recipientName]);
257 257
 
258
-		$template = $this->mailer->createEMailTemplate('dav.calendarInvite.' . $method, $data);
258
+		$template = $this->mailer->createEMailTemplate('dav.calendarInvite.'.$method, $data);
259 259
 		$template->addHeader();
260 260
 
261 261
 		$summary = ((string) $summary !== '') ? (string) $summary : $l10n->t('Untitled event');
@@ -304,8 +304,8 @@  discard block
 block discarded – undo
304 304
 
305 305
 		$attachment = $this->mailer->createAttachment(
306 306
 			$iTipMessage->message->serialize(),
307
-			'event.ics',// TODO(leon): Make file name unique, e.g. add event id
308
-			'text/calendar; method=' . $iTipMessage->method
307
+			'event.ics', // TODO(leon): Make file name unique, e.g. add event id
308
+			'text/calendar; method='.$iTipMessage->method
309 309
 		);
310 310
 		$message->attach($attachment);
311 311
 
@@ -316,7 +316,7 @@  discard block
 block discarded – undo
316 316
 				$this->logger->error('Unable to deliver message to {failed}', ['app' => 'dav', 'failed' =>  implode(', ', $failed)]);
317 317
 				$iTipMessage->scheduleStatus = '5.0; EMail delivery failed';
318 318
 			}
319
-		} catch(\Exception $ex) {
319
+		} catch (\Exception $ex) {
320 320
 			$this->logger->logException($ex, ['app' => 'dav']);
321 321
 			$iTipMessage->scheduleStatus = '5.0; EMail delivery failed';
322 322
 		}
@@ -352,13 +352,13 @@  discard block
 block discarded – undo
352 352
 				$lastOccurrence = $firstOccurrence;
353 353
 			}
354 354
 		} else {
355
-			$it = new EventIterator($vObject, (string)$component->UID);
355
+			$it = new EventIterator($vObject, (string) $component->UID);
356 356
 			$maxDate = new \DateTime(self::MAX_DATE);
357 357
 			if ($it->isInfinite()) {
358 358
 				$lastOccurrence = $maxDate->getTimestamp();
359 359
 			} else {
360 360
 				$end = $it->getDtEnd();
361
-				while($it->valid() && $end < $maxDate) {
361
+				while ($it->valid() && $end < $maxDate) {
362 362
 					$end = $it->getDtEnd();
363 363
 					$it->next();
364 364
 
@@ -452,7 +452,7 @@  discard block
 block discarded – undo
452 452
 			$localeStart = $l10n->l('date', $dtstartDt, ['width' => 'medium']);
453 453
 			$localeEnd = $l10n->l('date', $dtendDt, ['width' => 'medium']);
454 454
 
455
-			return $localeStart . ' - ' . $localeEnd;
455
+			return $localeStart.' - '.$localeEnd;
456 456
 		}
457 457
 
458 458
 		/** @var Property\ICalendar\DateTime $dtstart */
@@ -471,26 +471,26 @@  discard block
 block discarded – undo
471 471
 			}
472 472
 		}
473 473
 
474
-		$localeStart = $l10n->l('weekdayName', $dtstartDt, ['width' => 'abbreviated']) . ', ' .
474
+		$localeStart = $l10n->l('weekdayName', $dtstartDt, ['width' => 'abbreviated']).', '.
475 475
 			$l10n->l('datetime', $dtstartDt, ['width' => 'medium|short']);
476 476
 
477 477
 		// always show full date with timezone if timezones are different
478 478
 		if ($startTimezone !== $endTimezone) {
479 479
 			$localeEnd = $l10n->l('datetime', $dtendDt, ['width' => 'medium|short']);
480 480
 
481
-			return $localeStart . ' (' . $startTimezone . ') - ' .
482
-				$localeEnd . ' (' . $endTimezone . ')';
481
+			return $localeStart.' ('.$startTimezone.') - '.
482
+				$localeEnd.' ('.$endTimezone.')';
483 483
 		}
484 484
 
485 485
 		// show only end time if date is the same
486 486
 		if ($this->isDayEqual($dtstartDt, $dtendDt)) {
487 487
 			$localeEnd = $l10n->l('time', $dtendDt, ['width' => 'short']);
488 488
 		} else {
489
-			$localeEnd = $l10n->l('weekdayName', $dtendDt, ['width' => 'abbreviated']) . ', ' .
489
+			$localeEnd = $l10n->l('weekdayName', $dtendDt, ['width' => 'abbreviated']).', '.
490 490
 				$l10n->l('datetime', $dtendDt, ['width' => 'medium|short']);
491 491
 		}
492 492
 
493
-		return  $localeStart . ' - ' . $localeEnd . ' (' . $startTimezone . ')';
493
+		return  $localeStart.' - '.$localeEnd.' ('.$startTimezone.')';
494 494
 	}
495 495
 
496 496
 	/**
@@ -513,15 +513,15 @@  discard block
 block discarded – undo
513 513
 	private function addSubjectAndHeading(IEMailTemplate $template, IL10N $l10n,
514 514
 										  $method, $summary, $attendeeName, $inviteeName) {
515 515
 		if ($method === self::METHOD_CANCEL) {
516
-			$template->setSubject('Cancelled: ' . $summary);
516
+			$template->setSubject('Cancelled: '.$summary);
517 517
 			$template->addHeading($l10n->t('Invitation canceled'), $l10n->t('Hello %s,', [$attendeeName]));
518 518
 			$template->addBodyText($l10n->t('The meeting »%1$s« with %2$s was canceled.', [$summary, $inviteeName]));
519 519
 		} else if ($method === self::METHOD_REPLY) {
520
-			$template->setSubject('Re: ' . $summary);
520
+			$template->setSubject('Re: '.$summary);
521 521
 			$template->addHeading($l10n->t('Invitation updated'), $l10n->t('Hello %s,', [$attendeeName]));
522 522
 			$template->addBodyText($l10n->t('The meeting »%1$s« with %2$s was updated.', [$summary, $inviteeName]));
523 523
 		} else {
524
-			$template->setSubject('Invitation: ' . $summary);
524
+			$template->setSubject('Invitation: '.$summary);
525 525
 			$template->addHeading($l10n->t('%1$s invited you to »%2$s«', [$inviteeName, $summary]), $l10n->t('Hello %s,', [$attendeeName]));
526 526
 		}
527 527
 	}
@@ -543,11 +543,11 @@  discard block
 block discarded – undo
543 543
 				$this->getAbsoluteImagePath('filetypes/location.svg'));
544 544
 		}
545 545
 		if ($description) {
546
-			$template->addBodyListItem((string)$description, $l10n->t('Description:'),
546
+			$template->addBodyListItem((string) $description, $l10n->t('Description:'),
547 547
 				$this->getAbsoluteImagePath('filetypes/text.svg'));
548 548
 		}
549 549
 		if ($url) {
550
-			$template->addBodyListItem((string)$url, $l10n->t('Link:'),
550
+			$template->addBodyListItem((string) $url, $l10n->t('Link:'),
551 551
 				$this->getAbsoluteImagePath('filetypes/link.svg'));
552 552
 		}
553 553
 	}
@@ -600,7 +600,7 @@  discard block
 block discarded – undo
600 600
 	 * @return string
601 601
 	 */
602 602
 	private function createInvitationToken(Message $iTipMessage, $lastOccurrence):string {
603
-		$token = $this->random->generate(60, ISecureRandom::CHAR_UPPER . ISecureRandom::CHAR_LOWER . ISecureRandom::CHAR_DIGITS);
603
+		$token = $this->random->generate(60, ISecureRandom::CHAR_UPPER.ISecureRandom::CHAR_LOWER.ISecureRandom::CHAR_DIGITS);
604 604
 
605 605
 		/** @var VEvent $vevent */
606 606
 		$vevent = $iTipMessage->message->VEVENT;
Please login to merge, or discard this patch.