Completed
Pull Request — master (#7193)
by Georg
16:25
created
apps/dav/lib/CalDAV/Schedule/IMipPlugin.php 2 patches
Indentation   +410 added lines, -410 removed lines patch added patch discarded remove patch
@@ -59,414 +59,414 @@
 block discarded – undo
59 59
  */
60 60
 class IMipPlugin extends SabreIMipPlugin {
61 61
 
62
-	/** @var string */
63
-	private $userId;
64
-
65
-	/** @var IConfig */
66
-	private $config;
67
-
68
-	/** @var IMailer */
69
-	private $mailer;
70
-
71
-	/** @var ILogger */
72
-	private $logger;
73
-
74
-	/** @var ITimeFactory */
75
-	private $timeFactory;
76
-
77
-	/** @var L10NFactory */
78
-	private $l10nFactory;
79
-
80
-	/** @var IURLGenerator */
81
-	private $urlGenerator;
82
-
83
-	/** @var Defaults */
84
-	private $defaults;
85
-
86
-	const MAX_DATE = '2038-01-01';
87
-
88
-	const METHOD_REQUEST = 'request';
89
-	const METHOD_REPLY = 'reply';
90
-	const METHOD_CANCEL = 'cancel';
91
-
92
-	/**
93
-	 * @param IConfig $config
94
-	 * @param IMailer $mailer
95
-	 * @param ILogger $logger
96
-	 * @param ITimeFactory $timeFactory
97
-	 * @param L10NFactory $l10nFactory
98
-	 * @param IUrlGenerator $urlGenerator
99
-	 * @param Defaults $defaults
100
-	 * @param string $userId
101
-	 */
102
-	public function __construct(IConfig $config, IMailer $mailer, ILogger $logger, ITimeFactory $timeFactory, L10NFactory $l10nFactory, IURLGenerator $urlGenerator, Defaults $defaults, $userId) {
103
-		parent::__construct('');
104
-		$this->userId = $userId;
105
-		$this->config = $config;
106
-		$this->mailer = $mailer;
107
-		$this->logger = $logger;
108
-		$this->timeFactory = $timeFactory;
109
-		$this->l10nFactory = $l10nFactory;
110
-		$this->urlGenerator = $urlGenerator;
111
-		$this->defaults = $defaults;
112
-	}
113
-
114
-	/**
115
-	 * Event handler for the 'schedule' event.
116
-	 *
117
-	 * @param Message $iTipMessage
118
-	 * @return void
119
-	 */
120
-	public function schedule(Message $iTipMessage) {
121
-
122
-		// Not sending any emails if the system considers the update
123
-		// insignificant.
124
-		if (!$iTipMessage->significantChange) {
125
-			if (!$iTipMessage->scheduleStatus) {
126
-				$iTipMessage->scheduleStatus = '1.0;We got the message, but it\'s not significant enough to warrant an email';
127
-			}
128
-			return;
129
-		}
130
-
131
-		$summary = $iTipMessage->message->VEVENT->SUMMARY;
132
-
133
-		if (parse_url($iTipMessage->sender, PHP_URL_SCHEME) !== 'mailto') {
134
-			return;
135
-		}
136
-
137
-		if (parse_url($iTipMessage->recipient, PHP_URL_SCHEME) !== 'mailto') {
138
-			return;
139
-		}
140
-
141
-		// don't send out mails for events that already took place
142
-		if ($this->isEventInThePast($iTipMessage->message)) {
143
-			return;
144
-		}
145
-
146
-		// Strip off mailto:
147
-		$sender = substr($iTipMessage->sender, 7);
148
-		$recipient = substr($iTipMessage->recipient, 7);
149
-
150
-		$senderName = $iTipMessage->senderName ?: null;
151
-		$recipientName = $iTipMessage->recipientName ?: null;
152
-
153
-		/** @var VEvent $vevent */
154
-		$vevent = $iTipMessage->message->VEVENT;
155
-
156
-		$attendee = $this->getCurrentAttendee($iTipMessage);
157
-		$defaultLang = $this->config->getUserValue($this->userId, 'core', 'lang', $this->l10nFactory->findLanguage());
158
-		$lang = $this->getAttendeeLangOrDefault($defaultLang, $attendee);
159
-		$l10n = $this->l10nFactory->get('dav', $lang);
160
-
161
-		$meetingAttendeeName = $recipientName ?: $recipient;
162
-		$meetingInviteeName = $senderName ?: $sender;
163
-
164
-		$meetingTitle = $vevent->SUMMARY;
165
-		$meetingDescription = $vevent->DESCRIPTION;
166
-
167
-		$start = $vevent->DTSTART;
168
-		if (isset($vevent->DTEND)) {
169
-			$end = $vevent->DTEND;
170
-		} elseif (isset($vevent->DURATION)) {
171
-			$isFloating = $vevent->DTSTART->isFloating();
172
-			$end = clone $vevent->DTSTART;
173
-			$endDateTime = $end->getDateTime();
174
-			$endDateTime = $endDateTime->add(DateTimeParser::parse($vevent->DURATION->getValue()));
175
-			$end->setDateTime($endDateTime, $isFloating);
176
-		} elseif (!$vevent->DTSTART->hasTime()) {
177
-			$isFloating = $vevent->DTSTART->isFloating();
178
-			$end = clone $vevent->DTSTART;
179
-			$endDateTime = $end->getDateTime();
180
-			$endDateTime = $endDateTime->modify('+1 day');
181
-			$end->setDateTime($endDateTime, $isFloating);
182
-		} else {
183
-			$end = clone $vevent->DTSTART;
184
-		}
185
-
186
-		$meetingWhen = $this->generateWhenString($l10n, $start, $end);
187
-
188
-		$meetingUrl = $vevent->URL;
189
-		$meetingLocation = $vevent->LOCATION;
190
-
191
-		$defaultVal = '--';
192
-
193
-		$method = self::METHOD_REQUEST;
194
-		switch (strtolower($iTipMessage->method)) {
195
-			case self::METHOD_REPLY:
196
-				$method = self::METHOD_REPLY;
197
-				break;
198
-			case self::METHOD_CANCEL:
199
-				$method = self::METHOD_CANCEL;
200
-				break;
201
-		}
202
-
203
-		$data = array(
204
-			'attendee_name' => (string)$meetingAttendeeName ?: $defaultVal,
205
-			'invitee_name' => (string)$meetingInviteeName ?: $defaultVal,
206
-			'meeting_title' => (string)$meetingTitle ?: $defaultVal,
207
-			'meeting_description' => (string)$meetingDescription ?: $defaultVal,
208
-			'meeting_url' => (string)$meetingUrl ?: $defaultVal,
209
-		);
210
-
211
-		$fromEMail = \OCP\Util::getDefaultEmailAddress('invitations-noreply');
212
-		$fromName = $l10n->t('%s via %s', [$senderName, $this->defaults->getName()]);
213
-
214
-		$message = $this->mailer->createMessage()
215
-			->setFrom([$fromEMail => $fromName])
216
-			->setReplyTo([$sender => $senderName])
217
-			->setTo([$recipient => $recipientName]);
218
-
219
-		$template = $this->mailer->createEMailTemplate('dav.calendarInvite.' . $method, $data);
220
-		$template->addHeader();
221
-
222
-		$this->addSubjectAndHeading($template, $l10n, $method, $summary,
223
-			$meetingAttendeeName, $meetingInviteeName);
224
-		$this->addBulletList($template, $l10n, $meetingWhen, $meetingLocation,
225
-			$meetingDescription, $meetingUrl);
226
-
227
-		$template->addFooter();
228
-		$message->useTemplate($template);
229
-
230
-		$attachment = $this->mailer->createAttachment(
231
-			$iTipMessage->message->serialize(),
232
-			'event.ics',// TODO(leon): Make file name unique, e.g. add event id
233
-			'text/calendar; method=' . $iTipMessage->method
234
-		);
235
-		$message->attach($attachment);
236
-
237
-		try {
238
-			$failed = $this->mailer->send($message);
239
-			$iTipMessage->scheduleStatus = '1.1; Scheduling message is sent via iMip';
240
-			if ($failed) {
241
-				$this->logger->error('Unable to deliver message to {failed}', ['app' => 'dav', 'failed' =>  implode(', ', $failed)]);
242
-				$iTipMessage->scheduleStatus = '5.0; EMail delivery failed';
243
-			}
244
-		} catch(\Exception $ex) {
245
-			$this->logger->logException($ex, ['app' => 'dav']);
246
-			$iTipMessage->scheduleStatus = '5.0; EMail delivery failed';
247
-		}
248
-	}
249
-
250
-	/**
251
-	 * check if event took place in the past already
252
-	 * @param VCalendar $vObject
253
-	 * @return bool
254
-	 */
255
-	private function isEventInThePast(VCalendar $vObject) {
256
-		/** @var VEvent $component */
257
-		$component = $vObject->VEVENT;
258
-
259
-		$firstOccurrence = $component->DTSTART->getDateTime()->getTimeStamp();
260
-		// Finding the last occurrence is a bit harder
261
-		if (!isset($component->RRULE)) {
262
-			if (isset($component->DTEND)) {
263
-				$lastOccurrence = $component->DTEND->getDateTime()->getTimeStamp();
264
-			} elseif (isset($component->DURATION)) {
265
-				/** @var \DateTime $endDate */
266
-				$endDate = clone $component->DTSTART->getDateTime();
267
-				// $component->DTEND->getDateTime() returns DateTimeImmutable
268
-				$endDate = $endDate->add(DateTimeParser::parse($component->DURATION->getValue()));
269
-				$lastOccurrence = $endDate->getTimestamp();
270
-			} elseif (!$component->DTSTART->hasTime()) {
271
-				/** @var \DateTime $endDate */
272
-				$endDate = clone $component->DTSTART->getDateTime();
273
-				// $component->DTSTART->getDateTime() returns DateTimeImmutable
274
-				$endDate = $endDate->modify('+1 day');
275
-				$lastOccurrence = $endDate->getTimestamp();
276
-			} else {
277
-				$lastOccurrence = $firstOccurrence;
278
-			}
279
-		} else {
280
-			$it = new EventIterator($vObject, (string)$component->UID);
281
-			$maxDate = new \DateTime(self::MAX_DATE);
282
-			if ($it->isInfinite()) {
283
-				$lastOccurrence = $maxDate->getTimestamp();
284
-			} else {
285
-				$end = $it->getDtEnd();
286
-				while($it->valid() && $end < $maxDate) {
287
-					$end = $it->getDtEnd();
288
-					$it->next();
289
-
290
-				}
291
-				$lastOccurrence = $end->getTimestamp();
292
-			}
293
-		}
294
-
295
-		$currentTime = $this->timeFactory->getTime();
296
-		return $lastOccurrence < $currentTime;
297
-	}
298
-
299
-
300
-	/**
301
-	 * @param Message $iTipMessage
302
-	 * @return null|Property
303
-	 */
304
-	private function getCurrentAttendee(Message $iTipMessage) {
305
-		/** @var VEvent $vevent */
306
-		$vevent = $iTipMessage->message->VEVENT;
307
-		$attendees = $vevent->select('ATTENDEE');
308
-		foreach ($attendees as $attendee) {
309
-			/** @var Property $attendee */
310
-			if (strcasecmp($attendee->getValue(), $iTipMessage->recipient) === 0) {
311
-				return $attendee;
312
-			}
313
-		}
314
-		return null;
315
-	}
316
-
317
-	/**
318
-	 * @param string $default
319
-	 * @param Property|null $attendee
320
-	 * @return string
321
-	 */
322
-	private function getAttendeeLangOrDefault($default, Property $attendee = null) {
323
-		if ($attendee !== null) {
324
-			$lang = $attendee->offsetGet('LANGUAGE');
325
-			if ($lang instanceof Parameter) {
326
-				return $lang->getValue();
327
-			}
328
-		}
329
-		return $default;
330
-	}
331
-
332
-	/**
333
-	 * @param IL10N $l10n
334
-	 * @param Property $dtstart
335
-	 * @param Property $dtend
336
-	 */
337
-	private function generateWhenString(IL10N $l10n, Property $dtstart, Property $dtend) {
338
-		$isAllDay = $dtstart instanceof Property\ICalendar\Date;
339
-
340
-		/** @var Property\ICalendar\Date | Property\ICalendar\DateTime $dtstart */
341
-		/** @var Property\ICalendar\Date | Property\ICalendar\DateTime $dtend */
342
-		/** @var \DateTimeImmutable $dtstartDt */
343
-		$dtstartDt = $dtstart->getDateTime();
344
-		/** @var \DateTimeImmutable $dtendDt */
345
-		$dtendDt = $dtend->getDateTime();
346
-
347
-		$diff = $dtstartDt->diff($dtendDt);
348
-
349
-		$dtstartDt = new \DateTime($dtstartDt->format(\DateTime::ATOM));
350
-		$dtendDt = new \DateTime($dtendDt->format(\DateTime::ATOM));
351
-
352
-		if ($isAllDay) {
353
-			// One day event
354
-			if ($diff->days === 1) {
355
-				return $l10n->l('date', $dtstartDt, ['width' => 'medium']);
356
-			}
357
-
358
-			//event that spans over multiple days
359
-			$localeStart = $l10n->l('date', $dtstartDt, ['width' => 'medium']);
360
-			$localeEnd = $l10n->l('date', $dtendDt, ['width' => 'medium']);
361
-
362
-			return $localeStart . ' - ' . $localeEnd;
363
-		}
364
-
365
-		/** @var Property\ICalendar\DateTime $dtstart */
366
-		/** @var Property\ICalendar\DateTime $dtend */
367
-		$isFloating = $dtstart->isFloating();
368
-		$startTimezone = $endTimezone = null;
369
-		if (!$isFloating) {
370
-			$prop = $dtstart->offsetGet('TZID');
371
-			if ($prop instanceof Parameter) {
372
-				$startTimezone = $prop->getValue();
373
-			}
374
-
375
-			$prop = $dtend->offsetGet('TZID');
376
-			if ($prop instanceof Parameter) {
377
-				$endTimezone = $prop->getValue();
378
-			}
379
-		}
380
-
381
-		$localeStart = $l10n->l('weekdayName', $dtstartDt, ['width' => 'abbreviated']) . ', ' .
382
-			$l10n->l('datetime', $dtstartDt, ['width' => 'medium|short']);
383
-
384
-		// always show full date with timezone if timezones are different
385
-		if ($startTimezone !== $endTimezone) {
386
-			$localeEnd = $l10n->l('datetime', $dtendDt, ['width' => 'medium|short']);
387
-
388
-			return $localeStart . ' (' . $startTimezone . ') - ' .
389
-				$localeEnd . ' (' . $endTimezone . ')';
390
-		}
391
-
392
-		// show only end time if date is the same
393
-		if ($this->isDayEqual($dtstartDt, $dtendDt)) {
394
-			$localeEnd = $l10n->l('time', $dtendDt, ['width' => 'short']);
395
-		} else {
396
-			$localeEnd = $l10n->l('weekdayName', $dtendDt, ['width' => 'abbreviated']) . ', ' .
397
-				$l10n->l('datetime', $dtendDt, ['width' => 'medium|short']);
398
-		}
399
-
400
-		return  $localeStart . ' - ' . $localeEnd . ' (' . $startTimezone . ')';
401
-	}
402
-
403
-	/**
404
-	 * @param \DateTime $dtStart
405
-	 * @param \DateTime $dtEnd
406
-	 * @return bool
407
-	 */
408
-	private function isDayEqual(\DateTime $dtStart, \DateTime $dtEnd) {
409
-		return $dtStart->format('Y-m-d') === $dtEnd->format('Y-m-d');
410
-	}
411
-
412
-	/**
413
-	 * @param IEMailTemplate $template
414
-	 * @param IL10N $l10n
415
-	 * @param string $method
416
-	 * @param string $summary
417
-	 * @param string $attendeeName
418
-	 * @param string $inviteeName
419
-	 */
420
-	private function addSubjectAndHeading(IEMailTemplate $template, IL10N $l10n,
421
-										  $method, $summary, $attendeeName, $inviteeName) {
422
-		if ($method === self::METHOD_CANCEL) {
423
-			$template->setSubject('Cancelled: ' . $summary);
424
-			$template->addHeading($l10n->t('Invitation canceled'), $l10n->t('Hello %s,', [$attendeeName]));
425
-			$template->addBodyText($l10n->t('The meeting »%s« with %s was canceled.', [$summary, $inviteeName]));
426
-		} else if ($method === self::METHOD_REPLY) {
427
-			$template->setSubject('Re: ' . $summary);
428
-			$template->addHeading($l10n->t('Invitation updated'), $l10n->t('Hello %s,', [$attendeeName]));
429
-			$template->addBodyText($l10n->t('The meeting »%s« with %s was updated.', [$summary, $inviteeName]));
430
-		} else {
431
-			$template->setSubject('Invitation: ' . $summary);
432
-			$template->addHeading($l10n->t('%s invited you to »%s«', [$inviteeName, $summary]), $l10n->t('Hello %s,', [$attendeeName]));
433
-		}
434
-
435
-	}
436
-
437
-	/**
438
-	 * @param IEMailTemplate $template
439
-	 * @param IL10N $l10n
440
-	 * @param string $time
441
-	 * @param string $location
442
-	 * @param string $description
443
-	 * @param string $url
444
-	 */
445
-	private function addBulletList(IEMailTemplate $template, IL10N $l10n, $time, $location, $description, $url) {
446
-		$template->addBodyListItem($time, $l10n->t('When:'),
447
-			$this->getAbsoluteImagePath('filetypes/text-calendar.svg'));
448
-
449
-		if ($location) {
450
-			$template->addBodyListItem($location, $l10n->t('Where:'),
451
-				$this->getAbsoluteImagePath('filetypes/location.svg'));
452
-		}
453
-		if ($description) {
454
-			$template->addBodyListItem((string)$description, $l10n->t('Description:'),
455
-				$this->getAbsoluteImagePath('filetypes/text.svg'));
456
-		}
457
-		if ($url) {
458
-			$template->addBodyListItem((string)$url, $l10n->t('Link:'),
459
-				$this->getAbsoluteImagePath('filetypes/link.svg'));
460
-		}
461
-	}
462
-
463
-	/**
464
-	 * @param string $path
465
-	 * @return string
466
-	 */
467
-	private function getAbsoluteImagePath($path) {
468
-		return $this->urlGenerator->getAbsoluteURL(
469
-			$this->urlGenerator->imagePath('core', $path)
470
-		);
471
-	}
62
+    /** @var string */
63
+    private $userId;
64
+
65
+    /** @var IConfig */
66
+    private $config;
67
+
68
+    /** @var IMailer */
69
+    private $mailer;
70
+
71
+    /** @var ILogger */
72
+    private $logger;
73
+
74
+    /** @var ITimeFactory */
75
+    private $timeFactory;
76
+
77
+    /** @var L10NFactory */
78
+    private $l10nFactory;
79
+
80
+    /** @var IURLGenerator */
81
+    private $urlGenerator;
82
+
83
+    /** @var Defaults */
84
+    private $defaults;
85
+
86
+    const MAX_DATE = '2038-01-01';
87
+
88
+    const METHOD_REQUEST = 'request';
89
+    const METHOD_REPLY = 'reply';
90
+    const METHOD_CANCEL = 'cancel';
91
+
92
+    /**
93
+     * @param IConfig $config
94
+     * @param IMailer $mailer
95
+     * @param ILogger $logger
96
+     * @param ITimeFactory $timeFactory
97
+     * @param L10NFactory $l10nFactory
98
+     * @param IUrlGenerator $urlGenerator
99
+     * @param Defaults $defaults
100
+     * @param string $userId
101
+     */
102
+    public function __construct(IConfig $config, IMailer $mailer, ILogger $logger, ITimeFactory $timeFactory, L10NFactory $l10nFactory, IURLGenerator $urlGenerator, Defaults $defaults, $userId) {
103
+        parent::__construct('');
104
+        $this->userId = $userId;
105
+        $this->config = $config;
106
+        $this->mailer = $mailer;
107
+        $this->logger = $logger;
108
+        $this->timeFactory = $timeFactory;
109
+        $this->l10nFactory = $l10nFactory;
110
+        $this->urlGenerator = $urlGenerator;
111
+        $this->defaults = $defaults;
112
+    }
113
+
114
+    /**
115
+     * Event handler for the 'schedule' event.
116
+     *
117
+     * @param Message $iTipMessage
118
+     * @return void
119
+     */
120
+    public function schedule(Message $iTipMessage) {
121
+
122
+        // Not sending any emails if the system considers the update
123
+        // insignificant.
124
+        if (!$iTipMessage->significantChange) {
125
+            if (!$iTipMessage->scheduleStatus) {
126
+                $iTipMessage->scheduleStatus = '1.0;We got the message, but it\'s not significant enough to warrant an email';
127
+            }
128
+            return;
129
+        }
130
+
131
+        $summary = $iTipMessage->message->VEVENT->SUMMARY;
132
+
133
+        if (parse_url($iTipMessage->sender, PHP_URL_SCHEME) !== 'mailto') {
134
+            return;
135
+        }
136
+
137
+        if (parse_url($iTipMessage->recipient, PHP_URL_SCHEME) !== 'mailto') {
138
+            return;
139
+        }
140
+
141
+        // don't send out mails for events that already took place
142
+        if ($this->isEventInThePast($iTipMessage->message)) {
143
+            return;
144
+        }
145
+
146
+        // Strip off mailto:
147
+        $sender = substr($iTipMessage->sender, 7);
148
+        $recipient = substr($iTipMessage->recipient, 7);
149
+
150
+        $senderName = $iTipMessage->senderName ?: null;
151
+        $recipientName = $iTipMessage->recipientName ?: null;
152
+
153
+        /** @var VEvent $vevent */
154
+        $vevent = $iTipMessage->message->VEVENT;
155
+
156
+        $attendee = $this->getCurrentAttendee($iTipMessage);
157
+        $defaultLang = $this->config->getUserValue($this->userId, 'core', 'lang', $this->l10nFactory->findLanguage());
158
+        $lang = $this->getAttendeeLangOrDefault($defaultLang, $attendee);
159
+        $l10n = $this->l10nFactory->get('dav', $lang);
160
+
161
+        $meetingAttendeeName = $recipientName ?: $recipient;
162
+        $meetingInviteeName = $senderName ?: $sender;
163
+
164
+        $meetingTitle = $vevent->SUMMARY;
165
+        $meetingDescription = $vevent->DESCRIPTION;
166
+
167
+        $start = $vevent->DTSTART;
168
+        if (isset($vevent->DTEND)) {
169
+            $end = $vevent->DTEND;
170
+        } elseif (isset($vevent->DURATION)) {
171
+            $isFloating = $vevent->DTSTART->isFloating();
172
+            $end = clone $vevent->DTSTART;
173
+            $endDateTime = $end->getDateTime();
174
+            $endDateTime = $endDateTime->add(DateTimeParser::parse($vevent->DURATION->getValue()));
175
+            $end->setDateTime($endDateTime, $isFloating);
176
+        } elseif (!$vevent->DTSTART->hasTime()) {
177
+            $isFloating = $vevent->DTSTART->isFloating();
178
+            $end = clone $vevent->DTSTART;
179
+            $endDateTime = $end->getDateTime();
180
+            $endDateTime = $endDateTime->modify('+1 day');
181
+            $end->setDateTime($endDateTime, $isFloating);
182
+        } else {
183
+            $end = clone $vevent->DTSTART;
184
+        }
185
+
186
+        $meetingWhen = $this->generateWhenString($l10n, $start, $end);
187
+
188
+        $meetingUrl = $vevent->URL;
189
+        $meetingLocation = $vevent->LOCATION;
190
+
191
+        $defaultVal = '--';
192
+
193
+        $method = self::METHOD_REQUEST;
194
+        switch (strtolower($iTipMessage->method)) {
195
+            case self::METHOD_REPLY:
196
+                $method = self::METHOD_REPLY;
197
+                break;
198
+            case self::METHOD_CANCEL:
199
+                $method = self::METHOD_CANCEL;
200
+                break;
201
+        }
202
+
203
+        $data = array(
204
+            'attendee_name' => (string)$meetingAttendeeName ?: $defaultVal,
205
+            'invitee_name' => (string)$meetingInviteeName ?: $defaultVal,
206
+            'meeting_title' => (string)$meetingTitle ?: $defaultVal,
207
+            'meeting_description' => (string)$meetingDescription ?: $defaultVal,
208
+            'meeting_url' => (string)$meetingUrl ?: $defaultVal,
209
+        );
210
+
211
+        $fromEMail = \OCP\Util::getDefaultEmailAddress('invitations-noreply');
212
+        $fromName = $l10n->t('%s via %s', [$senderName, $this->defaults->getName()]);
213
+
214
+        $message = $this->mailer->createMessage()
215
+            ->setFrom([$fromEMail => $fromName])
216
+            ->setReplyTo([$sender => $senderName])
217
+            ->setTo([$recipient => $recipientName]);
218
+
219
+        $template = $this->mailer->createEMailTemplate('dav.calendarInvite.' . $method, $data);
220
+        $template->addHeader();
221
+
222
+        $this->addSubjectAndHeading($template, $l10n, $method, $summary,
223
+            $meetingAttendeeName, $meetingInviteeName);
224
+        $this->addBulletList($template, $l10n, $meetingWhen, $meetingLocation,
225
+            $meetingDescription, $meetingUrl);
226
+
227
+        $template->addFooter();
228
+        $message->useTemplate($template);
229
+
230
+        $attachment = $this->mailer->createAttachment(
231
+            $iTipMessage->message->serialize(),
232
+            'event.ics',// TODO(leon): Make file name unique, e.g. add event id
233
+            'text/calendar; method=' . $iTipMessage->method
234
+        );
235
+        $message->attach($attachment);
236
+
237
+        try {
238
+            $failed = $this->mailer->send($message);
239
+            $iTipMessage->scheduleStatus = '1.1; Scheduling message is sent via iMip';
240
+            if ($failed) {
241
+                $this->logger->error('Unable to deliver message to {failed}', ['app' => 'dav', 'failed' =>  implode(', ', $failed)]);
242
+                $iTipMessage->scheduleStatus = '5.0; EMail delivery failed';
243
+            }
244
+        } catch(\Exception $ex) {
245
+            $this->logger->logException($ex, ['app' => 'dav']);
246
+            $iTipMessage->scheduleStatus = '5.0; EMail delivery failed';
247
+        }
248
+    }
249
+
250
+    /**
251
+     * check if event took place in the past already
252
+     * @param VCalendar $vObject
253
+     * @return bool
254
+     */
255
+    private function isEventInThePast(VCalendar $vObject) {
256
+        /** @var VEvent $component */
257
+        $component = $vObject->VEVENT;
258
+
259
+        $firstOccurrence = $component->DTSTART->getDateTime()->getTimeStamp();
260
+        // Finding the last occurrence is a bit harder
261
+        if (!isset($component->RRULE)) {
262
+            if (isset($component->DTEND)) {
263
+                $lastOccurrence = $component->DTEND->getDateTime()->getTimeStamp();
264
+            } elseif (isset($component->DURATION)) {
265
+                /** @var \DateTime $endDate */
266
+                $endDate = clone $component->DTSTART->getDateTime();
267
+                // $component->DTEND->getDateTime() returns DateTimeImmutable
268
+                $endDate = $endDate->add(DateTimeParser::parse($component->DURATION->getValue()));
269
+                $lastOccurrence = $endDate->getTimestamp();
270
+            } elseif (!$component->DTSTART->hasTime()) {
271
+                /** @var \DateTime $endDate */
272
+                $endDate = clone $component->DTSTART->getDateTime();
273
+                // $component->DTSTART->getDateTime() returns DateTimeImmutable
274
+                $endDate = $endDate->modify('+1 day');
275
+                $lastOccurrence = $endDate->getTimestamp();
276
+            } else {
277
+                $lastOccurrence = $firstOccurrence;
278
+            }
279
+        } else {
280
+            $it = new EventIterator($vObject, (string)$component->UID);
281
+            $maxDate = new \DateTime(self::MAX_DATE);
282
+            if ($it->isInfinite()) {
283
+                $lastOccurrence = $maxDate->getTimestamp();
284
+            } else {
285
+                $end = $it->getDtEnd();
286
+                while($it->valid() && $end < $maxDate) {
287
+                    $end = $it->getDtEnd();
288
+                    $it->next();
289
+
290
+                }
291
+                $lastOccurrence = $end->getTimestamp();
292
+            }
293
+        }
294
+
295
+        $currentTime = $this->timeFactory->getTime();
296
+        return $lastOccurrence < $currentTime;
297
+    }
298
+
299
+
300
+    /**
301
+     * @param Message $iTipMessage
302
+     * @return null|Property
303
+     */
304
+    private function getCurrentAttendee(Message $iTipMessage) {
305
+        /** @var VEvent $vevent */
306
+        $vevent = $iTipMessage->message->VEVENT;
307
+        $attendees = $vevent->select('ATTENDEE');
308
+        foreach ($attendees as $attendee) {
309
+            /** @var Property $attendee */
310
+            if (strcasecmp($attendee->getValue(), $iTipMessage->recipient) === 0) {
311
+                return $attendee;
312
+            }
313
+        }
314
+        return null;
315
+    }
316
+
317
+    /**
318
+     * @param string $default
319
+     * @param Property|null $attendee
320
+     * @return string
321
+     */
322
+    private function getAttendeeLangOrDefault($default, Property $attendee = null) {
323
+        if ($attendee !== null) {
324
+            $lang = $attendee->offsetGet('LANGUAGE');
325
+            if ($lang instanceof Parameter) {
326
+                return $lang->getValue();
327
+            }
328
+        }
329
+        return $default;
330
+    }
331
+
332
+    /**
333
+     * @param IL10N $l10n
334
+     * @param Property $dtstart
335
+     * @param Property $dtend
336
+     */
337
+    private function generateWhenString(IL10N $l10n, Property $dtstart, Property $dtend) {
338
+        $isAllDay = $dtstart instanceof Property\ICalendar\Date;
339
+
340
+        /** @var Property\ICalendar\Date | Property\ICalendar\DateTime $dtstart */
341
+        /** @var Property\ICalendar\Date | Property\ICalendar\DateTime $dtend */
342
+        /** @var \DateTimeImmutable $dtstartDt */
343
+        $dtstartDt = $dtstart->getDateTime();
344
+        /** @var \DateTimeImmutable $dtendDt */
345
+        $dtendDt = $dtend->getDateTime();
346
+
347
+        $diff = $dtstartDt->diff($dtendDt);
348
+
349
+        $dtstartDt = new \DateTime($dtstartDt->format(\DateTime::ATOM));
350
+        $dtendDt = new \DateTime($dtendDt->format(\DateTime::ATOM));
351
+
352
+        if ($isAllDay) {
353
+            // One day event
354
+            if ($diff->days === 1) {
355
+                return $l10n->l('date', $dtstartDt, ['width' => 'medium']);
356
+            }
357
+
358
+            //event that spans over multiple days
359
+            $localeStart = $l10n->l('date', $dtstartDt, ['width' => 'medium']);
360
+            $localeEnd = $l10n->l('date', $dtendDt, ['width' => 'medium']);
361
+
362
+            return $localeStart . ' - ' . $localeEnd;
363
+        }
364
+
365
+        /** @var Property\ICalendar\DateTime $dtstart */
366
+        /** @var Property\ICalendar\DateTime $dtend */
367
+        $isFloating = $dtstart->isFloating();
368
+        $startTimezone = $endTimezone = null;
369
+        if (!$isFloating) {
370
+            $prop = $dtstart->offsetGet('TZID');
371
+            if ($prop instanceof Parameter) {
372
+                $startTimezone = $prop->getValue();
373
+            }
374
+
375
+            $prop = $dtend->offsetGet('TZID');
376
+            if ($prop instanceof Parameter) {
377
+                $endTimezone = $prop->getValue();
378
+            }
379
+        }
380
+
381
+        $localeStart = $l10n->l('weekdayName', $dtstartDt, ['width' => 'abbreviated']) . ', ' .
382
+            $l10n->l('datetime', $dtstartDt, ['width' => 'medium|short']);
383
+
384
+        // always show full date with timezone if timezones are different
385
+        if ($startTimezone !== $endTimezone) {
386
+            $localeEnd = $l10n->l('datetime', $dtendDt, ['width' => 'medium|short']);
387
+
388
+            return $localeStart . ' (' . $startTimezone . ') - ' .
389
+                $localeEnd . ' (' . $endTimezone . ')';
390
+        }
391
+
392
+        // show only end time if date is the same
393
+        if ($this->isDayEqual($dtstartDt, $dtendDt)) {
394
+            $localeEnd = $l10n->l('time', $dtendDt, ['width' => 'short']);
395
+        } else {
396
+            $localeEnd = $l10n->l('weekdayName', $dtendDt, ['width' => 'abbreviated']) . ', ' .
397
+                $l10n->l('datetime', $dtendDt, ['width' => 'medium|short']);
398
+        }
399
+
400
+        return  $localeStart . ' - ' . $localeEnd . ' (' . $startTimezone . ')';
401
+    }
402
+
403
+    /**
404
+     * @param \DateTime $dtStart
405
+     * @param \DateTime $dtEnd
406
+     * @return bool
407
+     */
408
+    private function isDayEqual(\DateTime $dtStart, \DateTime $dtEnd) {
409
+        return $dtStart->format('Y-m-d') === $dtEnd->format('Y-m-d');
410
+    }
411
+
412
+    /**
413
+     * @param IEMailTemplate $template
414
+     * @param IL10N $l10n
415
+     * @param string $method
416
+     * @param string $summary
417
+     * @param string $attendeeName
418
+     * @param string $inviteeName
419
+     */
420
+    private function addSubjectAndHeading(IEMailTemplate $template, IL10N $l10n,
421
+                                            $method, $summary, $attendeeName, $inviteeName) {
422
+        if ($method === self::METHOD_CANCEL) {
423
+            $template->setSubject('Cancelled: ' . $summary);
424
+            $template->addHeading($l10n->t('Invitation canceled'), $l10n->t('Hello %s,', [$attendeeName]));
425
+            $template->addBodyText($l10n->t('The meeting »%s« with %s was canceled.', [$summary, $inviteeName]));
426
+        } else if ($method === self::METHOD_REPLY) {
427
+            $template->setSubject('Re: ' . $summary);
428
+            $template->addHeading($l10n->t('Invitation updated'), $l10n->t('Hello %s,', [$attendeeName]));
429
+            $template->addBodyText($l10n->t('The meeting »%s« with %s was updated.', [$summary, $inviteeName]));
430
+        } else {
431
+            $template->setSubject('Invitation: ' . $summary);
432
+            $template->addHeading($l10n->t('%s invited you to »%s«', [$inviteeName, $summary]), $l10n->t('Hello %s,', [$attendeeName]));
433
+        }
434
+
435
+    }
436
+
437
+    /**
438
+     * @param IEMailTemplate $template
439
+     * @param IL10N $l10n
440
+     * @param string $time
441
+     * @param string $location
442
+     * @param string $description
443
+     * @param string $url
444
+     */
445
+    private function addBulletList(IEMailTemplate $template, IL10N $l10n, $time, $location, $description, $url) {
446
+        $template->addBodyListItem($time, $l10n->t('When:'),
447
+            $this->getAbsoluteImagePath('filetypes/text-calendar.svg'));
448
+
449
+        if ($location) {
450
+            $template->addBodyListItem($location, $l10n->t('Where:'),
451
+                $this->getAbsoluteImagePath('filetypes/location.svg'));
452
+        }
453
+        if ($description) {
454
+            $template->addBodyListItem((string)$description, $l10n->t('Description:'),
455
+                $this->getAbsoluteImagePath('filetypes/text.svg'));
456
+        }
457
+        if ($url) {
458
+            $template->addBodyListItem((string)$url, $l10n->t('Link:'),
459
+                $this->getAbsoluteImagePath('filetypes/link.svg'));
460
+        }
461
+    }
462
+
463
+    /**
464
+     * @param string $path
465
+     * @return string
466
+     */
467
+    private function getAbsoluteImagePath($path) {
468
+        return $this->urlGenerator->getAbsoluteURL(
469
+            $this->urlGenerator->imagePath('core', $path)
470
+        );
471
+    }
472 472
 }
Please login to merge, or discard this patch.
Spacing   +22 added lines, -22 removed lines patch added patch discarded remove patch
@@ -201,11 +201,11 @@  discard block
 block discarded – undo
201 201
 		}
202 202
 
203 203
 		$data = array(
204
-			'attendee_name' => (string)$meetingAttendeeName ?: $defaultVal,
205
-			'invitee_name' => (string)$meetingInviteeName ?: $defaultVal,
206
-			'meeting_title' => (string)$meetingTitle ?: $defaultVal,
207
-			'meeting_description' => (string)$meetingDescription ?: $defaultVal,
208
-			'meeting_url' => (string)$meetingUrl ?: $defaultVal,
204
+			'attendee_name' => (string) $meetingAttendeeName ?: $defaultVal,
205
+			'invitee_name' => (string) $meetingInviteeName ?: $defaultVal,
206
+			'meeting_title' => (string) $meetingTitle ?: $defaultVal,
207
+			'meeting_description' => (string) $meetingDescription ?: $defaultVal,
208
+			'meeting_url' => (string) $meetingUrl ?: $defaultVal,
209 209
 		);
210 210
 
211 211
 		$fromEMail = \OCP\Util::getDefaultEmailAddress('invitations-noreply');
@@ -216,7 +216,7 @@  discard block
 block discarded – undo
216 216
 			->setReplyTo([$sender => $senderName])
217 217
 			->setTo([$recipient => $recipientName]);
218 218
 
219
-		$template = $this->mailer->createEMailTemplate('dav.calendarInvite.' . $method, $data);
219
+		$template = $this->mailer->createEMailTemplate('dav.calendarInvite.'.$method, $data);
220 220
 		$template->addHeader();
221 221
 
222 222
 		$this->addSubjectAndHeading($template, $l10n, $method, $summary,
@@ -229,8 +229,8 @@  discard block
 block discarded – undo
229 229
 
230 230
 		$attachment = $this->mailer->createAttachment(
231 231
 			$iTipMessage->message->serialize(),
232
-			'event.ics',// TODO(leon): Make file name unique, e.g. add event id
233
-			'text/calendar; method=' . $iTipMessage->method
232
+			'event.ics', // TODO(leon): Make file name unique, e.g. add event id
233
+			'text/calendar; method='.$iTipMessage->method
234 234
 		);
235 235
 		$message->attach($attachment);
236 236
 
@@ -241,7 +241,7 @@  discard block
 block discarded – undo
241 241
 				$this->logger->error('Unable to deliver message to {failed}', ['app' => 'dav', 'failed' =>  implode(', ', $failed)]);
242 242
 				$iTipMessage->scheduleStatus = '5.0; EMail delivery failed';
243 243
 			}
244
-		} catch(\Exception $ex) {
244
+		} catch (\Exception $ex) {
245 245
 			$this->logger->logException($ex, ['app' => 'dav']);
246 246
 			$iTipMessage->scheduleStatus = '5.0; EMail delivery failed';
247 247
 		}
@@ -277,13 +277,13 @@  discard block
 block discarded – undo
277 277
 				$lastOccurrence = $firstOccurrence;
278 278
 			}
279 279
 		} else {
280
-			$it = new EventIterator($vObject, (string)$component->UID);
280
+			$it = new EventIterator($vObject, (string) $component->UID);
281 281
 			$maxDate = new \DateTime(self::MAX_DATE);
282 282
 			if ($it->isInfinite()) {
283 283
 				$lastOccurrence = $maxDate->getTimestamp();
284 284
 			} else {
285 285
 				$end = $it->getDtEnd();
286
-				while($it->valid() && $end < $maxDate) {
286
+				while ($it->valid() && $end < $maxDate) {
287 287
 					$end = $it->getDtEnd();
288 288
 					$it->next();
289 289
 
@@ -359,7 +359,7 @@  discard block
 block discarded – undo
359 359
 			$localeStart = $l10n->l('date', $dtstartDt, ['width' => 'medium']);
360 360
 			$localeEnd = $l10n->l('date', $dtendDt, ['width' => 'medium']);
361 361
 
362
-			return $localeStart . ' - ' . $localeEnd;
362
+			return $localeStart.' - '.$localeEnd;
363 363
 		}
364 364
 
365 365
 		/** @var Property\ICalendar\DateTime $dtstart */
@@ -378,26 +378,26 @@  discard block
 block discarded – undo
378 378
 			}
379 379
 		}
380 380
 
381
-		$localeStart = $l10n->l('weekdayName', $dtstartDt, ['width' => 'abbreviated']) . ', ' .
381
+		$localeStart = $l10n->l('weekdayName', $dtstartDt, ['width' => 'abbreviated']).', '.
382 382
 			$l10n->l('datetime', $dtstartDt, ['width' => 'medium|short']);
383 383
 
384 384
 		// always show full date with timezone if timezones are different
385 385
 		if ($startTimezone !== $endTimezone) {
386 386
 			$localeEnd = $l10n->l('datetime', $dtendDt, ['width' => 'medium|short']);
387 387
 
388
-			return $localeStart . ' (' . $startTimezone . ') - ' .
389
-				$localeEnd . ' (' . $endTimezone . ')';
388
+			return $localeStart.' ('.$startTimezone.') - '.
389
+				$localeEnd.' ('.$endTimezone.')';
390 390
 		}
391 391
 
392 392
 		// show only end time if date is the same
393 393
 		if ($this->isDayEqual($dtstartDt, $dtendDt)) {
394 394
 			$localeEnd = $l10n->l('time', $dtendDt, ['width' => 'short']);
395 395
 		} else {
396
-			$localeEnd = $l10n->l('weekdayName', $dtendDt, ['width' => 'abbreviated']) . ', ' .
396
+			$localeEnd = $l10n->l('weekdayName', $dtendDt, ['width' => 'abbreviated']).', '.
397 397
 				$l10n->l('datetime', $dtendDt, ['width' => 'medium|short']);
398 398
 		}
399 399
 
400
-		return  $localeStart . ' - ' . $localeEnd . ' (' . $startTimezone . ')';
400
+		return  $localeStart.' - '.$localeEnd.' ('.$startTimezone.')';
401 401
 	}
402 402
 
403 403
 	/**
@@ -420,15 +420,15 @@  discard block
 block discarded – undo
420 420
 	private function addSubjectAndHeading(IEMailTemplate $template, IL10N $l10n,
421 421
 										  $method, $summary, $attendeeName, $inviteeName) {
422 422
 		if ($method === self::METHOD_CANCEL) {
423
-			$template->setSubject('Cancelled: ' . $summary);
423
+			$template->setSubject('Cancelled: '.$summary);
424 424
 			$template->addHeading($l10n->t('Invitation canceled'), $l10n->t('Hello %s,', [$attendeeName]));
425 425
 			$template->addBodyText($l10n->t('The meeting »%s« with %s was canceled.', [$summary, $inviteeName]));
426 426
 		} else if ($method === self::METHOD_REPLY) {
427
-			$template->setSubject('Re: ' . $summary);
427
+			$template->setSubject('Re: '.$summary);
428 428
 			$template->addHeading($l10n->t('Invitation updated'), $l10n->t('Hello %s,', [$attendeeName]));
429 429
 			$template->addBodyText($l10n->t('The meeting »%s« with %s was updated.', [$summary, $inviteeName]));
430 430
 		} else {
431
-			$template->setSubject('Invitation: ' . $summary);
431
+			$template->setSubject('Invitation: '.$summary);
432 432
 			$template->addHeading($l10n->t('%s invited you to »%s«', [$inviteeName, $summary]), $l10n->t('Hello %s,', [$attendeeName]));
433 433
 		}
434 434
 
@@ -451,11 +451,11 @@  discard block
 block discarded – undo
451 451
 				$this->getAbsoluteImagePath('filetypes/location.svg'));
452 452
 		}
453 453
 		if ($description) {
454
-			$template->addBodyListItem((string)$description, $l10n->t('Description:'),
454
+			$template->addBodyListItem((string) $description, $l10n->t('Description:'),
455 455
 				$this->getAbsoluteImagePath('filetypes/text.svg'));
456 456
 		}
457 457
 		if ($url) {
458
-			$template->addBodyListItem((string)$url, $l10n->t('Link:'),
458
+			$template->addBodyListItem((string) $url, $l10n->t('Link:'),
459 459
 				$this->getAbsoluteImagePath('filetypes/link.svg'));
460 460
 		}
461 461
 	}
Please login to merge, or discard this patch.