Completed
Pull Request — master (#7173)
by Georg
12:55
created
apps/dav/lib/CalDAV/Schedule/IMipPlugin.php 3 patches
Doc Comments   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -95,7 +95,7 @@
 block discarded – undo
95 95
 	 * @param ILogger $logger
96 96
 	 * @param ITimeFactory $timeFactory
97 97
 	 * @param L10NFactory $l10nFactory
98
-	 * @param IUrlGenerator $urlGenerator
98
+	 * @param IURLGenerator $urlGenerator
99 99
 	 * @param Defaults $defaults
100 100
 	 * @param string $userId
101 101
 	 */
Please login to merge, or discard this patch.
Indentation   +408 added lines, -408 removed lines patch added patch discarded remove patch
@@ -59,412 +59,412 @@
 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('datetime', $dtstartDt, ['width' => 'medium']);
382
-
383
-		// always show full date with timezone if timezones are different
384
-		if ($startTimezone !== $endTimezone) {
385
-			$localeEnd = $l10n->l('datetime', $dtendDt, ['width' => 'medium']);
386
-
387
-			return $localeStart . ' (' . $startTimezone . ') - ' .
388
-				$localeEnd . ' (' . $endTimezone . ')';
389
-		}
390
-
391
-		// show only end time if date is the same
392
-		if ($this->isDayEqual($dtstartDt, $dtendDt)) {
393
-			$localeEnd = $l10n->l('time', $dtendDt, ['width' => 'medium']);
394
-		} else {
395
-			$localeEnd = $l10n->l('datetime', $dtendDt, ['width' => 'medium']);
396
-		}
397
-
398
-		return  $localeStart . ' - ' . $localeEnd . ' (' . $startTimezone . ')';
399
-	}
400
-
401
-	/**
402
-	 * @param \DateTime $dtStart
403
-	 * @param \DateTime $dtEnd
404
-	 * @return bool
405
-	 */
406
-	private function isDayEqual(\DateTime $dtStart, \DateTime $dtEnd) {
407
-		return $dtStart->format('Y-m-d') === $dtEnd->format('Y-m-d');
408
-	}
409
-
410
-	/**
411
-	 * @param IEMailTemplate $template
412
-	 * @param IL10N $l10n
413
-	 * @param string $method
414
-	 * @param string $summary
415
-	 * @param string $attendeeName
416
-	 * @param string $inviteeName
417
-	 */
418
-	private function addSubjectAndHeading(IEMailTemplate $template, IL10N $l10n,
419
-										  $method, $summary, $attendeeName, $inviteeName) {
420
-		if ($method === self::METHOD_CANCEL) {
421
-			$template->setSubject('Cancelled: ' . $summary);
422
-			$template->addHeading($l10n->t('Invitation canceled'), $l10n->t('Hello %s,', [$attendeeName]));
423
-			$template->addBodyText($l10n->t('The meeting »%s« with %s was canceled.', [$summary, $inviteeName]));
424
-		} else if ($method === self::METHOD_REPLY) {
425
-			$template->setSubject('Re: ' . $summary);
426
-			$template->addHeading($l10n->t('Invitation updated'), $l10n->t('Hello %s,', [$attendeeName]));
427
-			$template->addBodyText($l10n->t('The meeting »%s« with %s was updated.', [$summary, $inviteeName]));
428
-		} else {
429
-			$template->setSubject('Invitation: ' . $summary);
430
-			$template->addHeading($l10n->t('%s invited you to »%s«', [$inviteeName, $summary]), $l10n->t('Hello %s,', [$attendeeName]));
431
-		}
432
-
433
-	}
434
-
435
-	/**
436
-	 * @param IEMailTemplate $template
437
-	 * @param IL10N $l10n
438
-	 * @param string $time
439
-	 * @param string $location
440
-	 * @param string $description
441
-	 * @param string $url
442
-	 */
443
-	private function addBulletList(IEMailTemplate $template, IL10N $l10n, $time, $location, $description, $url) {
444
-		$template->addBodyListItem($time, $l10n->t('When:'),
445
-			$this->getAbsoluteImagePath('filetypes/text-calendar.svg'));
446
-
447
-		if ($location) {
448
-			$template->addBodyListItem($location, $l10n->t('Where:'),
449
-				$this->getAbsoluteImagePath('filetypes/location.svg'));
450
-		}
451
-		if ($description) {
452
-			$template->addBodyListItem((string)$description, $l10n->t('Description:'),
453
-				$this->getAbsoluteImagePath('filetypes/text.svg'));
454
-		}
455
-		if ($url) {
456
-			$template->addBodyListItem((string)$url, $l10n->t('Link:'),
457
-				$this->getAbsoluteImagePath('filetypes/link.svg'));
458
-		}
459
-	}
460
-
461
-	/**
462
-	 * @param string $path
463
-	 * @return string
464
-	 */
465
-	private function getAbsoluteImagePath($path) {
466
-		return $this->urlGenerator->getAbsoluteURL(
467
-			$this->urlGenerator->imagePath('core', $path)
468
-		);
469
-	}
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('datetime', $dtstartDt, ['width' => 'medium']);
382
+
383
+        // always show full date with timezone if timezones are different
384
+        if ($startTimezone !== $endTimezone) {
385
+            $localeEnd = $l10n->l('datetime', $dtendDt, ['width' => 'medium']);
386
+
387
+            return $localeStart . ' (' . $startTimezone . ') - ' .
388
+                $localeEnd . ' (' . $endTimezone . ')';
389
+        }
390
+
391
+        // show only end time if date is the same
392
+        if ($this->isDayEqual($dtstartDt, $dtendDt)) {
393
+            $localeEnd = $l10n->l('time', $dtendDt, ['width' => 'medium']);
394
+        } else {
395
+            $localeEnd = $l10n->l('datetime', $dtendDt, ['width' => 'medium']);
396
+        }
397
+
398
+        return  $localeStart . ' - ' . $localeEnd . ' (' . $startTimezone . ')';
399
+    }
400
+
401
+    /**
402
+     * @param \DateTime $dtStart
403
+     * @param \DateTime $dtEnd
404
+     * @return bool
405
+     */
406
+    private function isDayEqual(\DateTime $dtStart, \DateTime $dtEnd) {
407
+        return $dtStart->format('Y-m-d') === $dtEnd->format('Y-m-d');
408
+    }
409
+
410
+    /**
411
+     * @param IEMailTemplate $template
412
+     * @param IL10N $l10n
413
+     * @param string $method
414
+     * @param string $summary
415
+     * @param string $attendeeName
416
+     * @param string $inviteeName
417
+     */
418
+    private function addSubjectAndHeading(IEMailTemplate $template, IL10N $l10n,
419
+                                            $method, $summary, $attendeeName, $inviteeName) {
420
+        if ($method === self::METHOD_CANCEL) {
421
+            $template->setSubject('Cancelled: ' . $summary);
422
+            $template->addHeading($l10n->t('Invitation canceled'), $l10n->t('Hello %s,', [$attendeeName]));
423
+            $template->addBodyText($l10n->t('The meeting »%s« with %s was canceled.', [$summary, $inviteeName]));
424
+        } else if ($method === self::METHOD_REPLY) {
425
+            $template->setSubject('Re: ' . $summary);
426
+            $template->addHeading($l10n->t('Invitation updated'), $l10n->t('Hello %s,', [$attendeeName]));
427
+            $template->addBodyText($l10n->t('The meeting »%s« with %s was updated.', [$summary, $inviteeName]));
428
+        } else {
429
+            $template->setSubject('Invitation: ' . $summary);
430
+            $template->addHeading($l10n->t('%s invited you to »%s«', [$inviteeName, $summary]), $l10n->t('Hello %s,', [$attendeeName]));
431
+        }
432
+
433
+    }
434
+
435
+    /**
436
+     * @param IEMailTemplate $template
437
+     * @param IL10N $l10n
438
+     * @param string $time
439
+     * @param string $location
440
+     * @param string $description
441
+     * @param string $url
442
+     */
443
+    private function addBulletList(IEMailTemplate $template, IL10N $l10n, $time, $location, $description, $url) {
444
+        $template->addBodyListItem($time, $l10n->t('When:'),
445
+            $this->getAbsoluteImagePath('filetypes/text-calendar.svg'));
446
+
447
+        if ($location) {
448
+            $template->addBodyListItem($location, $l10n->t('Where:'),
449
+                $this->getAbsoluteImagePath('filetypes/location.svg'));
450
+        }
451
+        if ($description) {
452
+            $template->addBodyListItem((string)$description, $l10n->t('Description:'),
453
+                $this->getAbsoluteImagePath('filetypes/text.svg'));
454
+        }
455
+        if ($url) {
456
+            $template->addBodyListItem((string)$url, $l10n->t('Link:'),
457
+                $this->getAbsoluteImagePath('filetypes/link.svg'));
458
+        }
459
+    }
460
+
461
+    /**
462
+     * @param string $path
463
+     * @return string
464
+     */
465
+    private function getAbsoluteImagePath($path) {
466
+        return $this->urlGenerator->getAbsoluteURL(
467
+            $this->urlGenerator->imagePath('core', $path)
468
+        );
469
+    }
470 470
 }
Please login to merge, or discard this patch.
Spacing   +20 added lines, -20 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 */
@@ -384,8 +384,8 @@  discard block
 block discarded – undo
384 384
 		if ($startTimezone !== $endTimezone) {
385 385
 			$localeEnd = $l10n->l('datetime', $dtendDt, ['width' => 'medium']);
386 386
 
387
-			return $localeStart . ' (' . $startTimezone . ') - ' .
388
-				$localeEnd . ' (' . $endTimezone . ')';
387
+			return $localeStart.' ('.$startTimezone.') - '.
388
+				$localeEnd.' ('.$endTimezone.')';
389 389
 		}
390 390
 
391 391
 		// show only end time if date is the same
@@ -395,7 +395,7 @@  discard block
 block discarded – undo
395 395
 			$localeEnd = $l10n->l('datetime', $dtendDt, ['width' => 'medium']);
396 396
 		}
397 397
 
398
-		return  $localeStart . ' - ' . $localeEnd . ' (' . $startTimezone . ')';
398
+		return  $localeStart.' - '.$localeEnd.' ('.$startTimezone.')';
399 399
 	}
400 400
 
401 401
 	/**
@@ -418,15 +418,15 @@  discard block
 block discarded – undo
418 418
 	private function addSubjectAndHeading(IEMailTemplate $template, IL10N $l10n,
419 419
 										  $method, $summary, $attendeeName, $inviteeName) {
420 420
 		if ($method === self::METHOD_CANCEL) {
421
-			$template->setSubject('Cancelled: ' . $summary);
421
+			$template->setSubject('Cancelled: '.$summary);
422 422
 			$template->addHeading($l10n->t('Invitation canceled'), $l10n->t('Hello %s,', [$attendeeName]));
423 423
 			$template->addBodyText($l10n->t('The meeting »%s« with %s was canceled.', [$summary, $inviteeName]));
424 424
 		} else if ($method === self::METHOD_REPLY) {
425
-			$template->setSubject('Re: ' . $summary);
425
+			$template->setSubject('Re: '.$summary);
426 426
 			$template->addHeading($l10n->t('Invitation updated'), $l10n->t('Hello %s,', [$attendeeName]));
427 427
 			$template->addBodyText($l10n->t('The meeting »%s« with %s was updated.', [$summary, $inviteeName]));
428 428
 		} else {
429
-			$template->setSubject('Invitation: ' . $summary);
429
+			$template->setSubject('Invitation: '.$summary);
430 430
 			$template->addHeading($l10n->t('%s invited you to »%s«', [$inviteeName, $summary]), $l10n->t('Hello %s,', [$attendeeName]));
431 431
 		}
432 432
 
@@ -449,11 +449,11 @@  discard block
 block discarded – undo
449 449
 				$this->getAbsoluteImagePath('filetypes/location.svg'));
450 450
 		}
451 451
 		if ($description) {
452
-			$template->addBodyListItem((string)$description, $l10n->t('Description:'),
452
+			$template->addBodyListItem((string) $description, $l10n->t('Description:'),
453 453
 				$this->getAbsoluteImagePath('filetypes/text.svg'));
454 454
 		}
455 455
 		if ($url) {
456
-			$template->addBodyListItem((string)$url, $l10n->t('Link:'),
456
+			$template->addBodyListItem((string) $url, $l10n->t('Link:'),
457 457
 				$this->getAbsoluteImagePath('filetypes/link.svg'));
458 458
 		}
459 459
 	}
Please login to merge, or discard this patch.