Passed
Push — master ( 9c2d70...6ef7ba )
by Roeland
10:28
created

Notifier::getWeekDayName()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 2
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
c 0
b 0
f 0
nc 1
nop 1
dl 0
loc 2
rs 10
1
<?php
2
declare(strict_types=1);
3
/**
4
 * @copyright Copyright (c) 2019, Thomas Citharel
5
 * @copyright Copyright (c) 2019, Georg Ehrke
6
 *
7
 * @author Thomas Citharel <[email protected]>
8
 * @author Georg Ehrke <[email protected]>
9
 *
10
 * @license AGPL-3.0
11
 *
12
 * This code is free software: you can redistribute it and/or modify
13
 * it under the terms of the GNU Affero General Public License, version 3,
14
 * as published by the Free Software Foundation.
15
 *
16
 * This program is distributed in the hope that it will be useful,
17
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19
 * GNU Affero General Public License for more details.
20
 *
21
 * You should have received a copy of the GNU Affero General Public License, version 3,
22
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
23
 *
24
 */
25
26
namespace OCA\DAV\CalDAV\Reminder;
27
28
use \DateTime;
29
use OCA\DAV\AppInfo\Application;
30
use OCP\AppFramework\Utility\ITimeFactory;
31
use OCP\IL10N;
32
use OCP\L10N\IFactory;
33
use OCP\Notification\INotification;
34
use OCP\Notification\INotifier;
35
use OCP\IURLGenerator;
36
37
/**
38
 * Class Notifier
39
 *
40
 * @package OCA\DAV\CalDAV\Reminder
41
 */
42
class Notifier implements INotifier {
43
44
	/** @var IFactory */
45
	private $l10nFactory;
46
47
	/** @var IURLGenerator */
48
	private $urlGenerator;
49
50
	/** @var IL10N */
51
	private $l10n;
52
53
	/** @var ITimeFactory */
54
	private $timeFactory;
55
56
	/**
57
	 * Notifier constructor.
58
	 *
59
	 * @param IFactory $factory
60
	 * @param IURLGenerator $urlGenerator
61
	 * @param ITimeFactory $timeFactory
62
	 */
63
	public function __construct(IFactory $factory,
64
								IURLGenerator $urlGenerator,
65
								ITimeFactory $timeFactory) {
66
		$this->l10nFactory = $factory;
67
		$this->urlGenerator = $urlGenerator;
68
		$this->timeFactory = $timeFactory;
69
	}
70
71
	/**
72
	 * Identifier of the notifier, only use [a-z0-9_]
73
	 *
74
	 * @return string
75
	 * @since 17.0.0
76
	 */
77
	public function getID():string {
78
		return Application::APP_ID;
79
	}
80
81
	/**
82
	 * Human readable name describing the notifier
83
	 *
84
	 * @return string
85
	 * @since 17.0.0
86
	 */
87
	public function getName():string {
88
		return $this->l10nFactory->get('dav')->t('Calendar');
89
	}
90
91
	/**
92
	 * Prepare sending the notification
93
	 *
94
	 * @param INotification $notification
95
	 * @param string $languageCode The code of the language that should be used to prepare the notification
96
	 * @return INotification
97
	 * @throws \Exception
98
	 */
99
	public function prepare(INotification $notification,
100
							string $languageCode):INotification {
101
		if ($notification->getApp() !== Application::APP_ID) {
102
			throw new \InvalidArgumentException('Notification not from this app');
103
		}
104
105
		// Read the language from the notification
106
		$this->l10n = $this->l10nFactory->get('dav', $languageCode);
107
108
		// Handle notifier subjects
109
		switch($notification->getSubject()) {
110
			case 'calendar_reminder':
111
				return $this->prepareReminderNotification($notification);
112
113
			default:
114
				throw new \InvalidArgumentException('Unknown subject');
115
116
		}
117
	}
118
119
	/**
120
	 * @param INotification $notification
121
	 * @return INotification
122
	 */
123
	private function prepareReminderNotification(INotification $notification):INotification {
124
		$imagePath = $this->urlGenerator->imagePath('core', 'places/calendar.svg');
125
		$iconUrl = $this->urlGenerator->getAbsoluteURL($imagePath);
126
		$notification->setIcon($iconUrl);
127
128
		$this->prepareNotificationSubject($notification);
129
		$this->prepareNotificationMessage($notification);
130
131
		return $notification;
132
	}
133
134
	/**
135
	 * Sets the notification subject based on the parameters set in PushProvider
136
	 *
137
	 * @param INotification $notification
138
	 */
139
	private function prepareNotificationSubject(INotification $notification): void {
140
		$parameters = $notification->getSubjectParameters();
141
142
		$startTime = \DateTime::createFromFormat(\DateTime::ATOM, $parameters['start_atom']);
143
		$now = $this->timeFactory->getDateTime();
144
		$title = $this->getTitleFromParameters($parameters);
145
146
		$diff = $startTime->diff($now);
147
		if ($diff === false) {
148
			return;
149
		}
150
151
		$components = [];
152
		if ($diff->y) {
153
			$components[] = $this->l10n->n('%n year', '%n years', $diff->y);
154
		}
155
		if ($diff->m) {
156
			$components[] = $this->l10n->n('%n month', '%n months', $diff->m);
157
		}
158
		if ($diff->d) {
159
			$components[] = $this->l10n->n('%n day', '%n days', $diff->d);
160
		}
161
		if ($diff->h) {
162
			$components[] = $this->l10n->n('%n hour', '%n hours', $diff->h);
163
		}
164
		if ($diff->i) {
165
			$components[] = $this->l10n->n('%n minute', '%n minutes', $diff->i);
166
		}
167
168
		// Limiting to the first three components to prevent
169
		// the string from getting too long
170
		$firstThreeComponents = array_slice($components, 0, 2);
171
		$diffLabel = implode(', ', $firstThreeComponents);
172
173
		if ($diff->invert) {
174
			$title = $this->l10n->t('%s (in %s)', [$title, $diffLabel]);
175
		} else {
176
			$title = $this->l10n->t('%s (%s ago)', [$title, $diffLabel]);
177
		}
178
179
		$notification->setParsedSubject($title);
180
	}
181
182
	/**
183
	 * Sets the notification message based on the parameters set in PushProvider
184
	 *
185
	 * @param INotification $notification
186
	 */
187
	private function prepareNotificationMessage(INotification $notification): void {
188
		$parameters = $notification->getMessageParameters();
189
190
		$description = [
191
			$this->l10n->t('Calendar: %s', $parameters['calendar_displayname']),
192
			$this->l10n->t('Date: %s', $this->generateDateString($parameters)),
193
		];
194
		if ($parameters['description']) {
195
			$description[] = $this->l10n->t('Description: %s', $parameters['description']);
196
		}
197
		if ($parameters['location']) {
198
			$description[] = $this->l10n->t('Where: %s', $parameters['location']);
199
		}
200
201
		$message = implode("\r\n", $description);
202
		$notification->setParsedMessage($message);
203
	}
204
205
	/**
206
	 * @param array $parameters
207
	 * @return string
208
	 */
209
	private function getTitleFromParameters(array $parameters):string {
210
		return $parameters['title'] ?? $this->l10n->t('Untitled event');
211
	}
212
213
	/**
214
	 * @param array $parameters
215
	 * @return string
216
	 * @throws \Exception
217
	 */
218
	private function generateDateString(array $parameters):string {
219
		$startDateTime = DateTime::createFromFormat(\DateTime::ATOM, $parameters['start_atom']);
220
		$endDateTime = DateTime::createFromFormat(\DateTime::ATOM, $parameters['end_atom']);
221
		$isAllDay = $parameters['all_day'];
222
		$diff = $startDateTime->diff($endDateTime);
223
224
		if ($isAllDay) {
225
			// One day event
226
			if ($diff->days === 1) {
227
				return $this->getDateString($startDateTime);
228
			}
229
230
			return implode(' - ', [
231
				$this->getDateString($startDateTime),
232
				$this->getDateString($endDateTime),
233
			]);
234
		}
235
236
		$startTimezone = $endTimezone = null;
237
		if (!$parameters['start_is_floating']) {
238
			$startTimezone = $parameters['start_timezone'];
239
			$endTimezone = $parameters['end_timezone'];
240
		}
241
242
		$localeStart = implode(', ', [
243
			$this->getWeekDayName($startDateTime),
244
			$this->getDateTimeString($startDateTime)
245
		]);
246
247
		// always show full date with timezone if timezones are different
248
		if ($startTimezone !== $endTimezone) {
249
			$localeEnd = implode(', ', [
250
				$this->getWeekDayName($endDateTime),
251
				$this->getDateTimeString($endDateTime)
252
			]);
253
254
			return $localeStart
255
				. ' (' . $startTimezone . ') '
256
				. ' - '
257
				. $localeEnd
258
				. ' (' . $endTimezone . ')';
259
		}
260
261
		// Show only the time if the day is the same
262
		$localeEnd = $this->isDayEqual($startDateTime, $endDateTime)
263
			? $this->getTimeString($endDateTime)
264
			: implode(', ', [
265
				$this->getWeekDayName($endDateTime),
266
				$this->getDateTimeString($endDateTime)
267
			]);
268
269
		return $localeStart
270
			. ' - '
271
			. $localeEnd
272
			. ' (' . $startTimezone . ')';
273
	}
274
275
	/**
276
	 * @param DateTime $dtStart
277
	 * @param DateTime $dtEnd
278
	 * @return bool
279
	 */
280
	private function isDayEqual(DateTime $dtStart,
281
								DateTime $dtEnd):bool {
282
		return $dtStart->format('Y-m-d') === $dtEnd->format('Y-m-d');
283
	}
284
285
	/**
286
	 * @param DateTime $dt
287
	 * @return string
288
	 */
289
	private function getWeekDayName(DateTime $dt):string {
290
		return $this->l10n->l('weekdayName', $dt, ['width' => 'abbreviated']);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->l10n->l('w...dth' => 'abbreviated')) could return the type false which is incompatible with the type-hinted return string. Consider adding an additional type-check to rule them out.
Loading history...
291
	}
292
293
	/**
294
	 * @param DateTime $dt
295
	 * @return string
296
	 */
297
	private function getDateString(DateTime $dt):string {
298
		return $this->l10n->l('date', $dt, ['width' => 'medium']);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->l10n->l('d...y('width' => 'medium')) could return the type false which is incompatible with the type-hinted return string. Consider adding an additional type-check to rule them out.
Loading history...
299
	}
300
301
	/**
302
	 * @param DateTime $dt
303
	 * @return string
304
	 */
305
	private function getDateTimeString(DateTime $dt):string {
306
		return $this->l10n->l('datetime', $dt, ['width' => 'medium|short']);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->l10n->l('d...th' => 'medium|short')) could return the type false which is incompatible with the type-hinted return string. Consider adding an additional type-check to rule them out.
Loading history...
307
	}
308
309
	/**
310
	 * @param DateTime $dt
311
	 * @return string
312
	 */
313
	private function getTimeString(DateTime $dt):string {
314
		return $this->l10n->l('time', $dt, ['width' => 'short']);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->l10n->l('t...ay('width' => 'short')) could return the type false which is incompatible with the type-hinted return string. Consider adding an additional type-check to rule them out.
Loading history...
315
	}
316
}
317