Completed
Push — master ( 66c260...0e0abc )
by
unknown
32:31 queued 14s
created
apps/files/lib/Activity/Provider.php 2 patches
Indentation   +499 added lines, -499 removed lines patch added patch discarded remove patch
@@ -23,503 +23,503 @@
 block discarded – undo
23 23
 use OCP\L10N\IFactory;
24 24
 
25 25
 class Provider implements IProvider {
26
-	/** @var IL10N */
27
-	protected $l;
28
-
29
-	/** @var string[] cached displayNames - key is the cloud id and value the displayname */
30
-	protected $displayNames = [];
31
-
32
-	protected $fileIsEncrypted = false;
33
-
34
-	public function __construct(
35
-		protected IFactory $languageFactory,
36
-		protected IURLGenerator $url,
37
-		protected IManager $activityManager,
38
-		protected IUserManager $userManager,
39
-		protected IRootFolder $rootFolder,
40
-		protected ICloudIdManager $cloudIdManager,
41
-		protected IContactsManager $contactsManager,
42
-		protected IEventMerger $eventMerger,
43
-	) {
44
-	}
45
-
46
-	/**
47
-	 * @param string $language
48
-	 * @param IEvent $event
49
-	 * @param IEvent|null $previousEvent
50
-	 * @return IEvent
51
-	 * @throws UnknownActivityException
52
-	 * @since 11.0.0
53
-	 */
54
-	public function parse($language, IEvent $event, ?IEvent $previousEvent = null) {
55
-		if ($event->getApp() !== 'files') {
56
-			throw new UnknownActivityException();
57
-		}
58
-
59
-		$this->l = $this->languageFactory->get('files', $language);
60
-
61
-		if ($this->activityManager->isFormattingFilteredObject()) {
62
-			try {
63
-				return $this->parseShortVersion($event, $previousEvent);
64
-			} catch (UnknownActivityException) {
65
-				// Ignore and simply use the long version...
66
-			}
67
-		}
68
-
69
-		return $this->parseLongVersion($event, $previousEvent);
70
-	}
71
-
72
-	protected function setIcon(IEvent $event, string $icon, string $app = 'files') {
73
-		if ($this->activityManager->getRequirePNG()) {
74
-			$event->setIcon($this->url->getAbsoluteURL($this->url->imagePath($app, $icon . '.png')));
75
-		} else {
76
-			$event->setIcon($this->url->getAbsoluteURL($this->url->imagePath($app, $icon . '.svg')));
77
-		}
78
-	}
79
-
80
-	/**
81
-	 * @param IEvent $event
82
-	 * @param IEvent|null $previousEvent
83
-	 * @return IEvent
84
-	 * @throws UnknownActivityException
85
-	 * @since 11.0.0
86
-	 */
87
-	public function parseShortVersion(IEvent $event, ?IEvent $previousEvent = null): IEvent {
88
-		$parsedParameters = $this->getParameters($event);
89
-
90
-		if ($event->getSubject() === 'created_by') {
91
-			$subject = $this->l->t('Created by {user}');
92
-			$this->setIcon($event, 'add-color');
93
-		} elseif ($event->getSubject() === 'changed_by') {
94
-			$subject = $this->l->t('Changed by {user}');
95
-			$this->setIcon($event, 'change');
96
-		} elseif ($event->getSubject() === 'deleted_by') {
97
-			$subject = $this->l->t('Deleted by {user}');
98
-			$this->setIcon($event, 'delete-color');
99
-		} elseif ($event->getSubject() === 'restored_by') {
100
-			$subject = $this->l->t('Restored by {user}');
101
-			$this->setIcon($event, 'actions/history', 'core');
102
-		} elseif ($event->getSubject() === 'renamed_by') {
103
-			$subject = $this->l->t('Renamed by {user}');
104
-			$this->setIcon($event, 'change');
105
-		} elseif ($event->getSubject() === 'moved_by') {
106
-			$subject = $this->l->t('Moved by {user}');
107
-			$this->setIcon($event, 'change');
108
-		} else {
109
-			throw new UnknownActivityException();
110
-		}
111
-
112
-		if (!isset($parsedParameters['user'])) {
113
-			// External user via public link share
114
-			$subject = str_replace('{user}', $this->l->t('"remote account"'), $subject);
115
-		}
116
-
117
-		$this->setSubjects($event, $subject, $parsedParameters);
118
-
119
-		return $this->eventMerger->mergeEvents('user', $event, $previousEvent);
120
-	}
121
-
122
-	/**
123
-	 * @param IEvent $event
124
-	 * @param IEvent|null $previousEvent
125
-	 * @return IEvent
126
-	 * @throws UnknownActivityException
127
-	 * @since 11.0.0
128
-	 */
129
-	public function parseLongVersion(IEvent $event, ?IEvent $previousEvent = null): IEvent {
130
-		$this->fileIsEncrypted = false;
131
-		$parsedParameters = $this->getParameters($event);
132
-
133
-		if ($event->getSubject() === 'created_self') {
134
-			$subject = $this->l->t('You created {file}');
135
-			if ($this->fileIsEncrypted) {
136
-				$subject = $this->l->t('You created an encrypted file in {file}');
137
-			}
138
-			$this->setIcon($event, 'add-color');
139
-		} elseif ($event->getSubject() === 'created_by') {
140
-			$subject = $this->l->t('{user} created {file}');
141
-			if ($this->fileIsEncrypted) {
142
-				$subject = $this->l->t('{user} created an encrypted file in {file}');
143
-			}
144
-			$this->setIcon($event, 'add-color');
145
-		} elseif ($event->getSubject() === 'created_public') {
146
-			$subject = $this->l->t('{file} was created in a public folder');
147
-			$this->setIcon($event, 'add-color');
148
-		} elseif ($event->getSubject() === 'changed_self') {
149
-			$subject = $this->l->t('You changed {file}');
150
-			if ($this->fileIsEncrypted) {
151
-				$subject = $this->l->t('You changed an encrypted file in {file}');
152
-			}
153
-			$this->setIcon($event, 'change');
154
-		} elseif ($event->getSubject() === 'changed_by') {
155
-			$subject = $this->l->t('{user} changed {file}');
156
-			if ($this->fileIsEncrypted) {
157
-				$subject = $this->l->t('{user} changed an encrypted file in {file}');
158
-			}
159
-			$this->setIcon($event, 'change');
160
-		} elseif ($event->getSubject() === 'deleted_self') {
161
-			$subject = $this->l->t('You deleted {file}');
162
-			if ($this->fileIsEncrypted) {
163
-				$subject = $this->l->t('You deleted an encrypted file in {file}');
164
-			}
165
-			$this->setIcon($event, 'delete-color');
166
-		} elseif ($event->getSubject() === 'deleted_by') {
167
-			$subject = $this->l->t('{user} deleted {file}');
168
-			if ($this->fileIsEncrypted) {
169
-				$subject = $this->l->t('{user} deleted an encrypted file in {file}');
170
-			}
171
-			$this->setIcon($event, 'delete-color');
172
-		} elseif ($event->getSubject() === 'restored_self') {
173
-			$subject = $this->l->t('You restored {file}');
174
-			$this->setIcon($event, 'actions/history', 'core');
175
-		} elseif ($event->getSubject() === 'restored_by') {
176
-			$subject = $this->l->t('{user} restored {file}');
177
-			$this->setIcon($event, 'actions/history', 'core');
178
-		} elseif ($event->getSubject() === 'renamed_self') {
179
-			$oldFileName = $parsedParameters['oldfile']['name'];
180
-			$newFileName = $parsedParameters['newfile']['name'];
181
-
182
-			if ($this->isHiddenFile($oldFileName)) {
183
-				if ($this->isHiddenFile($newFileName)) {
184
-					$subject = $this->l->t('You renamed {oldfile} (hidden) to {newfile} (hidden)');
185
-				} else {
186
-					$subject = $this->l->t('You renamed {oldfile} (hidden) to {newfile}');
187
-				}
188
-			} else {
189
-				if ($this->isHiddenFile($newFileName)) {
190
-					$subject = $this->l->t('You renamed {oldfile} to {newfile} (hidden)');
191
-				} else {
192
-					$subject = $this->l->t('You renamed {oldfile} to {newfile}');
193
-				}
194
-			}
195
-
196
-			$this->setIcon($event, 'change');
197
-		} elseif ($event->getSubject() === 'renamed_by') {
198
-			$oldFileName = $parsedParameters['oldfile']['name'];
199
-			$newFileName = $parsedParameters['newfile']['name'];
200
-
201
-			if ($this->isHiddenFile($oldFileName)) {
202
-				if ($this->isHiddenFile($newFileName)) {
203
-					$subject = $this->l->t('{user} renamed {oldfile} (hidden) to {newfile} (hidden)');
204
-				} else {
205
-					$subject = $this->l->t('{user} renamed {oldfile} (hidden) to {newfile}');
206
-				}
207
-			} else {
208
-				if ($this->isHiddenFile($newFileName)) {
209
-					$subject = $this->l->t('{user} renamed {oldfile} to {newfile} (hidden)');
210
-				} else {
211
-					$subject = $this->l->t('{user} renamed {oldfile} to {newfile}');
212
-				}
213
-			}
214
-
215
-			$this->setIcon($event, 'change');
216
-		} elseif ($event->getSubject() === 'moved_self') {
217
-			$subject = $this->l->t('You moved {oldfile} to {newfile}');
218
-			$this->setIcon($event, 'change');
219
-		} elseif ($event->getSubject() === 'moved_by') {
220
-			$subject = $this->l->t('{user} moved {oldfile} to {newfile}');
221
-			$this->setIcon($event, 'change');
222
-		} else {
223
-			throw new UnknownActivityException();
224
-		}
225
-
226
-		if ($this->fileIsEncrypted) {
227
-			$event->setSubject($event->getSubject() . '_enc', $event->getSubjectParameters());
228
-		}
229
-
230
-		if (!isset($parsedParameters['user'])) {
231
-			// External user via public link share
232
-			$subject = str_replace('{user}', $this->l->t('"remote account"'), $subject);
233
-		}
234
-
235
-		$this->setSubjects($event, $subject, $parsedParameters);
236
-
237
-		if ($event->getSubject() === 'moved_self' || $event->getSubject() === 'moved_by') {
238
-			$event = $this->eventMerger->mergeEvents('oldfile', $event, $previousEvent);
239
-		} else {
240
-			$event = $this->eventMerger->mergeEvents('file', $event, $previousEvent);
241
-		}
242
-
243
-		if ($event->getChildEvent() === null) {
244
-			// Couldn't group by file, maybe we can group by user
245
-			$event = $this->eventMerger->mergeEvents('user', $event, $previousEvent);
246
-		}
247
-
248
-		return $event;
249
-	}
250
-
251
-	private function isHiddenFile(string $filename): bool {
252
-		return strlen($filename) > 0 && $filename[0] === '.';
253
-	}
254
-
255
-	protected function setSubjects(IEvent $event, string $subject, array $parameters): void {
256
-		$event->setRichSubject($subject, $parameters);
257
-	}
258
-
259
-	/**
260
-	 * @param IEvent $event
261
-	 * @return array
262
-	 * @throws UnknownActivityException
263
-	 */
264
-	protected function getParameters(IEvent $event): array {
265
-		$parameters = $event->getSubjectParameters();
266
-		switch ($event->getSubject()) {
267
-			case 'created_self':
268
-			case 'created_public':
269
-			case 'changed_self':
270
-			case 'deleted_self':
271
-			case 'restored_self':
272
-				return [
273
-					'file' => $this->getFile($parameters[0], $event),
274
-				];
275
-			case 'created_by':
276
-			case 'changed_by':
277
-			case 'deleted_by':
278
-			case 'restored_by':
279
-				if ($parameters[1] === '') {
280
-					// External user via public link share
281
-					return [
282
-						'file' => $this->getFile($parameters[0], $event),
283
-					];
284
-				}
285
-				return [
286
-					'file' => $this->getFile($parameters[0], $event),
287
-					'user' => $this->getUser($parameters[1]),
288
-				];
289
-			case 'renamed_self':
290
-			case 'moved_self':
291
-				return [
292
-					'newfile' => $this->getFile($parameters[0]),
293
-					'oldfile' => $this->getFile($parameters[1]),
294
-				];
295
-			case 'renamed_by':
296
-			case 'moved_by':
297
-				if ($parameters[1] === '') {
298
-					// External user via public link share
299
-					return [
300
-						'newfile' => $this->getFile($parameters[0]),
301
-						'oldfile' => $this->getFile($parameters[2]),
302
-					];
303
-				}
304
-				return [
305
-					'newfile' => $this->getFile($parameters[0]),
306
-					'user' => $this->getUser($parameters[1]),
307
-					'oldfile' => $this->getFile($parameters[2]),
308
-				];
309
-		}
310
-		return [];
311
-	}
312
-
313
-	/**
314
-	 * @param array|string $parameter
315
-	 * @param IEvent|null $event
316
-	 * @return array
317
-	 * @throws UnknownActivityException
318
-	 */
319
-	protected function getFile($parameter, ?IEvent $event = null): array {
320
-		if (is_array($parameter)) {
321
-			$path = reset($parameter);
322
-			$id = (int)key($parameter);
323
-		} elseif ($event !== null) {
324
-			// Legacy from before ownCloud 8.2
325
-			$path = $parameter;
326
-			$id = $event->getObjectId();
327
-		} else {
328
-			throw new UnknownActivityException('Could not generate file parameter');
329
-		}
330
-
331
-		$encryptionContainer = $this->getEndToEndEncryptionContainer($id, $path);
332
-		if ($encryptionContainer instanceof Folder) {
333
-			$this->fileIsEncrypted = true;
334
-			try {
335
-				$fullPath = rtrim($encryptionContainer->getPath(), '/');
336
-				// Remove /user/files/...
337
-				[,,, $path] = explode('/', $fullPath, 4);
338
-				if (!$path) {
339
-					throw new InvalidPathException('Path could not be split correctly');
340
-				}
341
-
342
-				return [
343
-					'type' => 'file',
344
-					'id' => (string)$encryptionContainer->getId(),
345
-					'name' => $encryptionContainer->getName(),
346
-					'path' => $path,
347
-					'link' => $this->url->linkToRouteAbsolute('files.viewcontroller.showFile', ['fileid' => $encryptionContainer->getId()]),
348
-				];
349
-			} catch (\Exception $e) {
350
-				// fall back to the normal one
351
-				$this->fileIsEncrypted = false;
352
-			}
353
-		}
354
-
355
-		return [
356
-			'type' => 'file',
357
-			'id' => (string)$id,
358
-			'name' => basename($path),
359
-			'path' => trim($path, '/'),
360
-			'link' => $this->url->linkToRouteAbsolute('files.viewcontroller.showFile', ['fileid' => $id]),
361
-		];
362
-	}
363
-
364
-	protected $fileEncrypted = [];
365
-
366
-	/**
367
-	 * Check if a file is end2end encrypted
368
-	 * @param int $fileId
369
-	 * @param string $path
370
-	 * @return Folder|null
371
-	 */
372
-	protected function getEndToEndEncryptionContainer($fileId, $path) {
373
-		if (isset($this->fileEncrypted[$fileId])) {
374
-			return $this->fileEncrypted[$fileId];
375
-		}
376
-
377
-		$fileName = basename($path);
378
-		if (!preg_match('/^[0-9a-fA-F]{32}$/', $fileName)) {
379
-			$this->fileEncrypted[$fileId] = false;
380
-			return $this->fileEncrypted[$fileId];
381
-		}
382
-
383
-		$userFolder = $this->rootFolder->getUserFolder($this->activityManager->getCurrentUserId());
384
-		$file = $userFolder->getFirstNodeById($fileId);
385
-		if (!$file) {
386
-			try {
387
-				// Deleted, try with parent
388
-				$file = $this->findExistingParent($userFolder, dirname($path));
389
-			} catch (NotFoundException $e) {
390
-				return null;
391
-			}
392
-
393
-			if (!$file instanceof Folder || !$file->isEncrypted()) {
394
-				return null;
395
-			}
396
-
397
-			$this->fileEncrypted[$fileId] = $file;
398
-			return $file;
399
-		}
400
-
401
-		if ($file instanceof Folder && $file->isEncrypted()) {
402
-			// If the folder is encrypted, it is the Container,
403
-			// but can be the name is just fine.
404
-			$this->fileEncrypted[$fileId] = true;
405
-			return null;
406
-		}
407
-
408
-		$this->fileEncrypted[$fileId] = $this->getParentEndToEndEncryptionContainer($userFolder, $file);
409
-		return $this->fileEncrypted[$fileId];
410
-	}
411
-
412
-	/**
413
-	 * @param Folder $userFolder
414
-	 * @param string $path
415
-	 * @return Folder
416
-	 * @throws NotFoundException
417
-	 */
418
-	protected function findExistingParent(Folder $userFolder, $path) {
419
-		if ($path === '/') {
420
-			throw new NotFoundException('Reached the root');
421
-		}
422
-
423
-		try {
424
-			$folder = $userFolder->get(dirname($path));
425
-		} catch (NotFoundException $e) {
426
-			return $this->findExistingParent($userFolder, dirname($path));
427
-		}
428
-
429
-		return $folder;
430
-	}
431
-
432
-	/**
433
-	 * Check all parents until the user's root folder if one is encrypted
434
-	 *
435
-	 * @param Folder $userFolder
436
-	 * @param Node $file
437
-	 * @return Node|null
438
-	 */
439
-	protected function getParentEndToEndEncryptionContainer(Folder $userFolder, Node $file) {
440
-		try {
441
-			$parent = $file->getParent();
442
-
443
-			if ($userFolder->getId() === $parent->getId()) {
444
-				return null;
445
-			}
446
-		} catch (\Exception $e) {
447
-			return null;
448
-		}
449
-
450
-		if ($parent->isEncrypted()) {
451
-			return $parent;
452
-		}
453
-
454
-		return $this->getParentEndToEndEncryptionContainer($userFolder, $parent);
455
-	}
456
-
457
-	/**
458
-	 * @param string $uid
459
-	 * @return array
460
-	 */
461
-	protected function getUser($uid) {
462
-		// First try local user
463
-		$displayName = $this->userManager->getDisplayName($uid);
464
-		if ($displayName !== null) {
465
-			return [
466
-				'type' => 'user',
467
-				'id' => $uid,
468
-				'name' => $displayName,
469
-			];
470
-		}
471
-
472
-		// Then a contact from the addressbook
473
-		if ($this->cloudIdManager->isValidCloudId($uid)) {
474
-			$cloudId = $this->cloudIdManager->resolveCloudId($uid);
475
-			return [
476
-				'type' => 'user',
477
-				'id' => $cloudId->getUser(),
478
-				'name' => $this->getDisplayNameFromAddressBook($cloudId->getDisplayId()),
479
-				'server' => $cloudId->getRemote(),
480
-			];
481
-		}
482
-
483
-		// Fallback to empty dummy data
484
-		return [
485
-			'type' => 'user',
486
-			'id' => $uid,
487
-			'name' => $uid,
488
-		];
489
-	}
490
-
491
-	protected function getDisplayNameFromAddressBook(string $search): string {
492
-		if (isset($this->displayNames[$search])) {
493
-			return $this->displayNames[$search];
494
-		}
495
-
496
-		$addressBookContacts = $this->contactsManager->search($search, ['CLOUD'], [
497
-			'limit' => 1,
498
-			'enumeration' => false,
499
-			'fullmatch' => false,
500
-			'strict_search' => true,
501
-		]);
502
-		foreach ($addressBookContacts as $contact) {
503
-			if (isset($contact['isLocalSystemBook'])) {
504
-				continue;
505
-			}
506
-
507
-			if (isset($contact['CLOUD'])) {
508
-				$cloudIds = $contact['CLOUD'];
509
-				if (is_string($cloudIds)) {
510
-					$cloudIds = [$cloudIds];
511
-				}
512
-
513
-				$lowerSearch = strtolower($search);
514
-				foreach ($cloudIds as $cloudId) {
515
-					if (strtolower($cloudId) === $lowerSearch) {
516
-						$this->displayNames[$search] = $contact['FN'] . " ($cloudId)";
517
-						return $this->displayNames[$search];
518
-					}
519
-				}
520
-			}
521
-		}
522
-
523
-		return $search;
524
-	}
26
+    /** @var IL10N */
27
+    protected $l;
28
+
29
+    /** @var string[] cached displayNames - key is the cloud id and value the displayname */
30
+    protected $displayNames = [];
31
+
32
+    protected $fileIsEncrypted = false;
33
+
34
+    public function __construct(
35
+        protected IFactory $languageFactory,
36
+        protected IURLGenerator $url,
37
+        protected IManager $activityManager,
38
+        protected IUserManager $userManager,
39
+        protected IRootFolder $rootFolder,
40
+        protected ICloudIdManager $cloudIdManager,
41
+        protected IContactsManager $contactsManager,
42
+        protected IEventMerger $eventMerger,
43
+    ) {
44
+    }
45
+
46
+    /**
47
+     * @param string $language
48
+     * @param IEvent $event
49
+     * @param IEvent|null $previousEvent
50
+     * @return IEvent
51
+     * @throws UnknownActivityException
52
+     * @since 11.0.0
53
+     */
54
+    public function parse($language, IEvent $event, ?IEvent $previousEvent = null) {
55
+        if ($event->getApp() !== 'files') {
56
+            throw new UnknownActivityException();
57
+        }
58
+
59
+        $this->l = $this->languageFactory->get('files', $language);
60
+
61
+        if ($this->activityManager->isFormattingFilteredObject()) {
62
+            try {
63
+                return $this->parseShortVersion($event, $previousEvent);
64
+            } catch (UnknownActivityException) {
65
+                // Ignore and simply use the long version...
66
+            }
67
+        }
68
+
69
+        return $this->parseLongVersion($event, $previousEvent);
70
+    }
71
+
72
+    protected function setIcon(IEvent $event, string $icon, string $app = 'files') {
73
+        if ($this->activityManager->getRequirePNG()) {
74
+            $event->setIcon($this->url->getAbsoluteURL($this->url->imagePath($app, $icon . '.png')));
75
+        } else {
76
+            $event->setIcon($this->url->getAbsoluteURL($this->url->imagePath($app, $icon . '.svg')));
77
+        }
78
+    }
79
+
80
+    /**
81
+     * @param IEvent $event
82
+     * @param IEvent|null $previousEvent
83
+     * @return IEvent
84
+     * @throws UnknownActivityException
85
+     * @since 11.0.0
86
+     */
87
+    public function parseShortVersion(IEvent $event, ?IEvent $previousEvent = null): IEvent {
88
+        $parsedParameters = $this->getParameters($event);
89
+
90
+        if ($event->getSubject() === 'created_by') {
91
+            $subject = $this->l->t('Created by {user}');
92
+            $this->setIcon($event, 'add-color');
93
+        } elseif ($event->getSubject() === 'changed_by') {
94
+            $subject = $this->l->t('Changed by {user}');
95
+            $this->setIcon($event, 'change');
96
+        } elseif ($event->getSubject() === 'deleted_by') {
97
+            $subject = $this->l->t('Deleted by {user}');
98
+            $this->setIcon($event, 'delete-color');
99
+        } elseif ($event->getSubject() === 'restored_by') {
100
+            $subject = $this->l->t('Restored by {user}');
101
+            $this->setIcon($event, 'actions/history', 'core');
102
+        } elseif ($event->getSubject() === 'renamed_by') {
103
+            $subject = $this->l->t('Renamed by {user}');
104
+            $this->setIcon($event, 'change');
105
+        } elseif ($event->getSubject() === 'moved_by') {
106
+            $subject = $this->l->t('Moved by {user}');
107
+            $this->setIcon($event, 'change');
108
+        } else {
109
+            throw new UnknownActivityException();
110
+        }
111
+
112
+        if (!isset($parsedParameters['user'])) {
113
+            // External user via public link share
114
+            $subject = str_replace('{user}', $this->l->t('"remote account"'), $subject);
115
+        }
116
+
117
+        $this->setSubjects($event, $subject, $parsedParameters);
118
+
119
+        return $this->eventMerger->mergeEvents('user', $event, $previousEvent);
120
+    }
121
+
122
+    /**
123
+     * @param IEvent $event
124
+     * @param IEvent|null $previousEvent
125
+     * @return IEvent
126
+     * @throws UnknownActivityException
127
+     * @since 11.0.0
128
+     */
129
+    public function parseLongVersion(IEvent $event, ?IEvent $previousEvent = null): IEvent {
130
+        $this->fileIsEncrypted = false;
131
+        $parsedParameters = $this->getParameters($event);
132
+
133
+        if ($event->getSubject() === 'created_self') {
134
+            $subject = $this->l->t('You created {file}');
135
+            if ($this->fileIsEncrypted) {
136
+                $subject = $this->l->t('You created an encrypted file in {file}');
137
+            }
138
+            $this->setIcon($event, 'add-color');
139
+        } elseif ($event->getSubject() === 'created_by') {
140
+            $subject = $this->l->t('{user} created {file}');
141
+            if ($this->fileIsEncrypted) {
142
+                $subject = $this->l->t('{user} created an encrypted file in {file}');
143
+            }
144
+            $this->setIcon($event, 'add-color');
145
+        } elseif ($event->getSubject() === 'created_public') {
146
+            $subject = $this->l->t('{file} was created in a public folder');
147
+            $this->setIcon($event, 'add-color');
148
+        } elseif ($event->getSubject() === 'changed_self') {
149
+            $subject = $this->l->t('You changed {file}');
150
+            if ($this->fileIsEncrypted) {
151
+                $subject = $this->l->t('You changed an encrypted file in {file}');
152
+            }
153
+            $this->setIcon($event, 'change');
154
+        } elseif ($event->getSubject() === 'changed_by') {
155
+            $subject = $this->l->t('{user} changed {file}');
156
+            if ($this->fileIsEncrypted) {
157
+                $subject = $this->l->t('{user} changed an encrypted file in {file}');
158
+            }
159
+            $this->setIcon($event, 'change');
160
+        } elseif ($event->getSubject() === 'deleted_self') {
161
+            $subject = $this->l->t('You deleted {file}');
162
+            if ($this->fileIsEncrypted) {
163
+                $subject = $this->l->t('You deleted an encrypted file in {file}');
164
+            }
165
+            $this->setIcon($event, 'delete-color');
166
+        } elseif ($event->getSubject() === 'deleted_by') {
167
+            $subject = $this->l->t('{user} deleted {file}');
168
+            if ($this->fileIsEncrypted) {
169
+                $subject = $this->l->t('{user} deleted an encrypted file in {file}');
170
+            }
171
+            $this->setIcon($event, 'delete-color');
172
+        } elseif ($event->getSubject() === 'restored_self') {
173
+            $subject = $this->l->t('You restored {file}');
174
+            $this->setIcon($event, 'actions/history', 'core');
175
+        } elseif ($event->getSubject() === 'restored_by') {
176
+            $subject = $this->l->t('{user} restored {file}');
177
+            $this->setIcon($event, 'actions/history', 'core');
178
+        } elseif ($event->getSubject() === 'renamed_self') {
179
+            $oldFileName = $parsedParameters['oldfile']['name'];
180
+            $newFileName = $parsedParameters['newfile']['name'];
181
+
182
+            if ($this->isHiddenFile($oldFileName)) {
183
+                if ($this->isHiddenFile($newFileName)) {
184
+                    $subject = $this->l->t('You renamed {oldfile} (hidden) to {newfile} (hidden)');
185
+                } else {
186
+                    $subject = $this->l->t('You renamed {oldfile} (hidden) to {newfile}');
187
+                }
188
+            } else {
189
+                if ($this->isHiddenFile($newFileName)) {
190
+                    $subject = $this->l->t('You renamed {oldfile} to {newfile} (hidden)');
191
+                } else {
192
+                    $subject = $this->l->t('You renamed {oldfile} to {newfile}');
193
+                }
194
+            }
195
+
196
+            $this->setIcon($event, 'change');
197
+        } elseif ($event->getSubject() === 'renamed_by') {
198
+            $oldFileName = $parsedParameters['oldfile']['name'];
199
+            $newFileName = $parsedParameters['newfile']['name'];
200
+
201
+            if ($this->isHiddenFile($oldFileName)) {
202
+                if ($this->isHiddenFile($newFileName)) {
203
+                    $subject = $this->l->t('{user} renamed {oldfile} (hidden) to {newfile} (hidden)');
204
+                } else {
205
+                    $subject = $this->l->t('{user} renamed {oldfile} (hidden) to {newfile}');
206
+                }
207
+            } else {
208
+                if ($this->isHiddenFile($newFileName)) {
209
+                    $subject = $this->l->t('{user} renamed {oldfile} to {newfile} (hidden)');
210
+                } else {
211
+                    $subject = $this->l->t('{user} renamed {oldfile} to {newfile}');
212
+                }
213
+            }
214
+
215
+            $this->setIcon($event, 'change');
216
+        } elseif ($event->getSubject() === 'moved_self') {
217
+            $subject = $this->l->t('You moved {oldfile} to {newfile}');
218
+            $this->setIcon($event, 'change');
219
+        } elseif ($event->getSubject() === 'moved_by') {
220
+            $subject = $this->l->t('{user} moved {oldfile} to {newfile}');
221
+            $this->setIcon($event, 'change');
222
+        } else {
223
+            throw new UnknownActivityException();
224
+        }
225
+
226
+        if ($this->fileIsEncrypted) {
227
+            $event->setSubject($event->getSubject() . '_enc', $event->getSubjectParameters());
228
+        }
229
+
230
+        if (!isset($parsedParameters['user'])) {
231
+            // External user via public link share
232
+            $subject = str_replace('{user}', $this->l->t('"remote account"'), $subject);
233
+        }
234
+
235
+        $this->setSubjects($event, $subject, $parsedParameters);
236
+
237
+        if ($event->getSubject() === 'moved_self' || $event->getSubject() === 'moved_by') {
238
+            $event = $this->eventMerger->mergeEvents('oldfile', $event, $previousEvent);
239
+        } else {
240
+            $event = $this->eventMerger->mergeEvents('file', $event, $previousEvent);
241
+        }
242
+
243
+        if ($event->getChildEvent() === null) {
244
+            // Couldn't group by file, maybe we can group by user
245
+            $event = $this->eventMerger->mergeEvents('user', $event, $previousEvent);
246
+        }
247
+
248
+        return $event;
249
+    }
250
+
251
+    private function isHiddenFile(string $filename): bool {
252
+        return strlen($filename) > 0 && $filename[0] === '.';
253
+    }
254
+
255
+    protected function setSubjects(IEvent $event, string $subject, array $parameters): void {
256
+        $event->setRichSubject($subject, $parameters);
257
+    }
258
+
259
+    /**
260
+     * @param IEvent $event
261
+     * @return array
262
+     * @throws UnknownActivityException
263
+     */
264
+    protected function getParameters(IEvent $event): array {
265
+        $parameters = $event->getSubjectParameters();
266
+        switch ($event->getSubject()) {
267
+            case 'created_self':
268
+            case 'created_public':
269
+            case 'changed_self':
270
+            case 'deleted_self':
271
+            case 'restored_self':
272
+                return [
273
+                    'file' => $this->getFile($parameters[0], $event),
274
+                ];
275
+            case 'created_by':
276
+            case 'changed_by':
277
+            case 'deleted_by':
278
+            case 'restored_by':
279
+                if ($parameters[1] === '') {
280
+                    // External user via public link share
281
+                    return [
282
+                        'file' => $this->getFile($parameters[0], $event),
283
+                    ];
284
+                }
285
+                return [
286
+                    'file' => $this->getFile($parameters[0], $event),
287
+                    'user' => $this->getUser($parameters[1]),
288
+                ];
289
+            case 'renamed_self':
290
+            case 'moved_self':
291
+                return [
292
+                    'newfile' => $this->getFile($parameters[0]),
293
+                    'oldfile' => $this->getFile($parameters[1]),
294
+                ];
295
+            case 'renamed_by':
296
+            case 'moved_by':
297
+                if ($parameters[1] === '') {
298
+                    // External user via public link share
299
+                    return [
300
+                        'newfile' => $this->getFile($parameters[0]),
301
+                        'oldfile' => $this->getFile($parameters[2]),
302
+                    ];
303
+                }
304
+                return [
305
+                    'newfile' => $this->getFile($parameters[0]),
306
+                    'user' => $this->getUser($parameters[1]),
307
+                    'oldfile' => $this->getFile($parameters[2]),
308
+                ];
309
+        }
310
+        return [];
311
+    }
312
+
313
+    /**
314
+     * @param array|string $parameter
315
+     * @param IEvent|null $event
316
+     * @return array
317
+     * @throws UnknownActivityException
318
+     */
319
+    protected function getFile($parameter, ?IEvent $event = null): array {
320
+        if (is_array($parameter)) {
321
+            $path = reset($parameter);
322
+            $id = (int)key($parameter);
323
+        } elseif ($event !== null) {
324
+            // Legacy from before ownCloud 8.2
325
+            $path = $parameter;
326
+            $id = $event->getObjectId();
327
+        } else {
328
+            throw new UnknownActivityException('Could not generate file parameter');
329
+        }
330
+
331
+        $encryptionContainer = $this->getEndToEndEncryptionContainer($id, $path);
332
+        if ($encryptionContainer instanceof Folder) {
333
+            $this->fileIsEncrypted = true;
334
+            try {
335
+                $fullPath = rtrim($encryptionContainer->getPath(), '/');
336
+                // Remove /user/files/...
337
+                [,,, $path] = explode('/', $fullPath, 4);
338
+                if (!$path) {
339
+                    throw new InvalidPathException('Path could not be split correctly');
340
+                }
341
+
342
+                return [
343
+                    'type' => 'file',
344
+                    'id' => (string)$encryptionContainer->getId(),
345
+                    'name' => $encryptionContainer->getName(),
346
+                    'path' => $path,
347
+                    'link' => $this->url->linkToRouteAbsolute('files.viewcontroller.showFile', ['fileid' => $encryptionContainer->getId()]),
348
+                ];
349
+            } catch (\Exception $e) {
350
+                // fall back to the normal one
351
+                $this->fileIsEncrypted = false;
352
+            }
353
+        }
354
+
355
+        return [
356
+            'type' => 'file',
357
+            'id' => (string)$id,
358
+            'name' => basename($path),
359
+            'path' => trim($path, '/'),
360
+            'link' => $this->url->linkToRouteAbsolute('files.viewcontroller.showFile', ['fileid' => $id]),
361
+        ];
362
+    }
363
+
364
+    protected $fileEncrypted = [];
365
+
366
+    /**
367
+     * Check if a file is end2end encrypted
368
+     * @param int $fileId
369
+     * @param string $path
370
+     * @return Folder|null
371
+     */
372
+    protected function getEndToEndEncryptionContainer($fileId, $path) {
373
+        if (isset($this->fileEncrypted[$fileId])) {
374
+            return $this->fileEncrypted[$fileId];
375
+        }
376
+
377
+        $fileName = basename($path);
378
+        if (!preg_match('/^[0-9a-fA-F]{32}$/', $fileName)) {
379
+            $this->fileEncrypted[$fileId] = false;
380
+            return $this->fileEncrypted[$fileId];
381
+        }
382
+
383
+        $userFolder = $this->rootFolder->getUserFolder($this->activityManager->getCurrentUserId());
384
+        $file = $userFolder->getFirstNodeById($fileId);
385
+        if (!$file) {
386
+            try {
387
+                // Deleted, try with parent
388
+                $file = $this->findExistingParent($userFolder, dirname($path));
389
+            } catch (NotFoundException $e) {
390
+                return null;
391
+            }
392
+
393
+            if (!$file instanceof Folder || !$file->isEncrypted()) {
394
+                return null;
395
+            }
396
+
397
+            $this->fileEncrypted[$fileId] = $file;
398
+            return $file;
399
+        }
400
+
401
+        if ($file instanceof Folder && $file->isEncrypted()) {
402
+            // If the folder is encrypted, it is the Container,
403
+            // but can be the name is just fine.
404
+            $this->fileEncrypted[$fileId] = true;
405
+            return null;
406
+        }
407
+
408
+        $this->fileEncrypted[$fileId] = $this->getParentEndToEndEncryptionContainer($userFolder, $file);
409
+        return $this->fileEncrypted[$fileId];
410
+    }
411
+
412
+    /**
413
+     * @param Folder $userFolder
414
+     * @param string $path
415
+     * @return Folder
416
+     * @throws NotFoundException
417
+     */
418
+    protected function findExistingParent(Folder $userFolder, $path) {
419
+        if ($path === '/') {
420
+            throw new NotFoundException('Reached the root');
421
+        }
422
+
423
+        try {
424
+            $folder = $userFolder->get(dirname($path));
425
+        } catch (NotFoundException $e) {
426
+            return $this->findExistingParent($userFolder, dirname($path));
427
+        }
428
+
429
+        return $folder;
430
+    }
431
+
432
+    /**
433
+     * Check all parents until the user's root folder if one is encrypted
434
+     *
435
+     * @param Folder $userFolder
436
+     * @param Node $file
437
+     * @return Node|null
438
+     */
439
+    protected function getParentEndToEndEncryptionContainer(Folder $userFolder, Node $file) {
440
+        try {
441
+            $parent = $file->getParent();
442
+
443
+            if ($userFolder->getId() === $parent->getId()) {
444
+                return null;
445
+            }
446
+        } catch (\Exception $e) {
447
+            return null;
448
+        }
449
+
450
+        if ($parent->isEncrypted()) {
451
+            return $parent;
452
+        }
453
+
454
+        return $this->getParentEndToEndEncryptionContainer($userFolder, $parent);
455
+    }
456
+
457
+    /**
458
+     * @param string $uid
459
+     * @return array
460
+     */
461
+    protected function getUser($uid) {
462
+        // First try local user
463
+        $displayName = $this->userManager->getDisplayName($uid);
464
+        if ($displayName !== null) {
465
+            return [
466
+                'type' => 'user',
467
+                'id' => $uid,
468
+                'name' => $displayName,
469
+            ];
470
+        }
471
+
472
+        // Then a contact from the addressbook
473
+        if ($this->cloudIdManager->isValidCloudId($uid)) {
474
+            $cloudId = $this->cloudIdManager->resolveCloudId($uid);
475
+            return [
476
+                'type' => 'user',
477
+                'id' => $cloudId->getUser(),
478
+                'name' => $this->getDisplayNameFromAddressBook($cloudId->getDisplayId()),
479
+                'server' => $cloudId->getRemote(),
480
+            ];
481
+        }
482
+
483
+        // Fallback to empty dummy data
484
+        return [
485
+            'type' => 'user',
486
+            'id' => $uid,
487
+            'name' => $uid,
488
+        ];
489
+    }
490
+
491
+    protected function getDisplayNameFromAddressBook(string $search): string {
492
+        if (isset($this->displayNames[$search])) {
493
+            return $this->displayNames[$search];
494
+        }
495
+
496
+        $addressBookContacts = $this->contactsManager->search($search, ['CLOUD'], [
497
+            'limit' => 1,
498
+            'enumeration' => false,
499
+            'fullmatch' => false,
500
+            'strict_search' => true,
501
+        ]);
502
+        foreach ($addressBookContacts as $contact) {
503
+            if (isset($contact['isLocalSystemBook'])) {
504
+                continue;
505
+            }
506
+
507
+            if (isset($contact['CLOUD'])) {
508
+                $cloudIds = $contact['CLOUD'];
509
+                if (is_string($cloudIds)) {
510
+                    $cloudIds = [$cloudIds];
511
+                }
512
+
513
+                $lowerSearch = strtolower($search);
514
+                foreach ($cloudIds as $cloudId) {
515
+                    if (strtolower($cloudId) === $lowerSearch) {
516
+                        $this->displayNames[$search] = $contact['FN'] . " ($cloudId)";
517
+                        return $this->displayNames[$search];
518
+                    }
519
+                }
520
+            }
521
+        }
522
+
523
+        return $search;
524
+    }
525 525
 }
Please login to merge, or discard this patch.
Spacing   +7 added lines, -7 removed lines patch added patch discarded remove patch
@@ -71,9 +71,9 @@  discard block
 block discarded – undo
71 71
 
72 72
 	protected function setIcon(IEvent $event, string $icon, string $app = 'files') {
73 73
 		if ($this->activityManager->getRequirePNG()) {
74
-			$event->setIcon($this->url->getAbsoluteURL($this->url->imagePath($app, $icon . '.png')));
74
+			$event->setIcon($this->url->getAbsoluteURL($this->url->imagePath($app, $icon.'.png')));
75 75
 		} else {
76
-			$event->setIcon($this->url->getAbsoluteURL($this->url->imagePath($app, $icon . '.svg')));
76
+			$event->setIcon($this->url->getAbsoluteURL($this->url->imagePath($app, $icon.'.svg')));
77 77
 		}
78 78
 	}
79 79
 
@@ -224,7 +224,7 @@  discard block
 block discarded – undo
224 224
 		}
225 225
 
226 226
 		if ($this->fileIsEncrypted) {
227
-			$event->setSubject($event->getSubject() . '_enc', $event->getSubjectParameters());
227
+			$event->setSubject($event->getSubject().'_enc', $event->getSubjectParameters());
228 228
 		}
229 229
 
230 230
 		if (!isset($parsedParameters['user'])) {
@@ -319,7 +319,7 @@  discard block
 block discarded – undo
319 319
 	protected function getFile($parameter, ?IEvent $event = null): array {
320 320
 		if (is_array($parameter)) {
321 321
 			$path = reset($parameter);
322
-			$id = (int)key($parameter);
322
+			$id = (int) key($parameter);
323 323
 		} elseif ($event !== null) {
324 324
 			// Legacy from before ownCloud 8.2
325 325
 			$path = $parameter;
@@ -341,7 +341,7 @@  discard block
 block discarded – undo
341 341
 
342 342
 				return [
343 343
 					'type' => 'file',
344
-					'id' => (string)$encryptionContainer->getId(),
344
+					'id' => (string) $encryptionContainer->getId(),
345 345
 					'name' => $encryptionContainer->getName(),
346 346
 					'path' => $path,
347 347
 					'link' => $this->url->linkToRouteAbsolute('files.viewcontroller.showFile', ['fileid' => $encryptionContainer->getId()]),
@@ -354,7 +354,7 @@  discard block
 block discarded – undo
354 354
 
355 355
 		return [
356 356
 			'type' => 'file',
357
-			'id' => (string)$id,
357
+			'id' => (string) $id,
358 358
 			'name' => basename($path),
359 359
 			'path' => trim($path, '/'),
360 360
 			'link' => $this->url->linkToRouteAbsolute('files.viewcontroller.showFile', ['fileid' => $id]),
@@ -513,7 +513,7 @@  discard block
 block discarded – undo
513 513
 				$lowerSearch = strtolower($search);
514 514
 				foreach ($cloudIds as $cloudId) {
515 515
 					if (strtolower($cloudId) === $lowerSearch) {
516
-						$this->displayNames[$search] = $contact['FN'] . " ($cloudId)";
516
+						$this->displayNames[$search] = $contact['FN']." ($cloudId)";
517 517
 						return $this->displayNames[$search];
518 518
 					}
519 519
 				}
Please login to merge, or discard this patch.
apps/files/tests/Activity/ProviderTest.php 1 patch
Indentation   +179 added lines, -179 removed lines patch added patch discarded remove patch
@@ -27,183 +27,183 @@
 block discarded – undo
27 27
  */
28 28
 class ProviderTest extends TestCase {
29 29
 
30
-	/** @var IFactory|MockObject */
31
-	protected $l10nFactory;
32
-	/** @var IURLGenerator|MockObject */
33
-	protected $url;
34
-	/** @var IManager|MockObject */
35
-	protected $activityManager;
36
-	/** @var IUserManager|MockObject */
37
-	protected $userManager;
38
-	/** @var IRootFolder|MockObject */
39
-	protected $rootFolder;
40
-	/** @var ICloudIdManager|MockObject */
41
-	protected $cloudIdManager;
42
-	/** @var IContactsManager|MockObject */
43
-	protected $contactsManager;
44
-	/** @var IEventMerger|MockObject */
45
-	protected $eventMerger;
46
-
47
-	protected function setUp(): void {
48
-		parent::setUp();
49
-
50
-		$this->l10nFactory = $this->createMock(IFactory::class);
51
-		$this->url = $this->createMock(IURLGenerator::class);
52
-		$this->activityManager = $this->createMock(IManager::class);
53
-		$this->userManager = $this->createMock(IUserManager::class);
54
-		$this->rootFolder = $this->createMock(IRootFolder::class);
55
-		$this->cloudIdManager = $this->createMock(ICloudIdManager::class);
56
-		$this->contactsManager = $this->createMock(IContactsManager::class);
57
-		$this->eventMerger = $this->createMock(IEventMerger::class);
58
-	}
59
-
60
-	/**
61
-	 * @param string[] $methods
62
-	 * @return Provider|MockObject
63
-	 */
64
-	protected function getProvider(array $methods = []) {
65
-		if (!empty($methods)) {
66
-			return $this->getMockBuilder(Provider::class)
67
-				->setConstructorArgs([
68
-					$this->l10nFactory,
69
-					$this->url,
70
-					$this->activityManager,
71
-					$this->userManager,
72
-					$this->rootFolder,
73
-					$this->cloudIdManager,
74
-					$this->contactsManager,
75
-					$this->eventMerger,
76
-				])
77
-				->setMethods($methods)
78
-				->getMock();
79
-		}
80
-		return new Provider(
81
-			$this->l10nFactory,
82
-			$this->url,
83
-			$this->activityManager,
84
-			$this->userManager,
85
-			$this->rootFolder,
86
-			$this->cloudIdManager,
87
-			$this->contactsManager,
88
-			$this->eventMerger
89
-		);
90
-	}
91
-
92
-	public function dataGetFile() {
93
-		return [
94
-			[[42 => '/FortyTwo.txt'], null, '42', 'FortyTwo.txt', 'FortyTwo.txt'],
95
-			[['23' => '/Twenty/Three.txt'], null, '23', 'Three.txt', 'Twenty/Three.txt'],
96
-			['/Foo/Bar.txt', 128, '128', 'Bar.txt', 'Foo/Bar.txt'], // Legacy from ownCloud 8.2 and before
97
-		];
98
-	}
99
-
100
-	/**
101
-	 * @dataProvider dataGetFile
102
-	 * @param mixed $parameter
103
-	 * @param mixed $eventId
104
-	 * @param int $id
105
-	 * @param string $name
106
-	 * @param string $path
107
-	 */
108
-	public function testGetFile($parameter, $eventId, $id, $name, $path): void {
109
-		$provider = $this->getProvider();
110
-
111
-		if ($eventId !== null) {
112
-			$event = $this->createMock(IEvent::class);
113
-			$event->expects($this->once())
114
-				->method('getObjectId')
115
-				->willReturn($eventId);
116
-		} else {
117
-			$event = null;
118
-		}
119
-
120
-		$this->url->expects($this->once())
121
-			->method('linkToRouteAbsolute')
122
-			->with('files.viewcontroller.showFile', ['fileid' => $id])
123
-			->willReturn('link-' . $id);
124
-
125
-		$result = self::invokePrivate($provider, 'getFile', [$parameter, $event]);
126
-
127
-		$this->assertSame('file', $result['type']);
128
-		$this->assertSame($id, $result['id']);
129
-		$this->assertSame($name, $result['name']);
130
-		$this->assertSame($path, $result['path']);
131
-		$this->assertSame('link-' . $id, $result['link']);
132
-	}
133
-
134
-
135
-	public function testGetFileThrows(): void {
136
-		$this->expectException(UnknownActivityException::class);
137
-
138
-		$provider = $this->getProvider();
139
-		self::invokePrivate($provider, 'getFile', ['/Foo/Bar.txt', null]);
140
-	}
141
-
142
-	public function dataGetUser() {
143
-		return [
144
-			['test', 'Test user', null, ['type' => 'user', 'id' => 'test', 'name' => 'Test user']],
145
-			['test@http://localhost', null, ['user' => 'test', 'displayId' => 'test@localhost', 'remote' => 'localhost', 'name' => null], ['type' => 'user', 'id' => 'test', 'name' => 'test@localhost', 'server' => 'localhost']],
146
-			['test@http://localhost', null, ['user' => 'test', 'displayId' => 'test@localhost', 'remote' => 'localhost', 'name' => 'Remote user'], ['type' => 'user', 'id' => 'test', 'name' => 'Remote user (test@localhost)', 'server' => 'localhost']],
147
-			['test', null, null, ['type' => 'user', 'id' => 'test', 'name' => 'test']],
148
-		];
149
-	}
150
-
151
-	/**
152
-	 * @dataProvider dataGetUser
153
-	 * @param string $uid
154
-	 * @param string|null $userDisplayName
155
-	 * @param array|null $cloudIdData
156
-	 * @param array $expected
157
-	 */
158
-	public function testGetUser(string $uid, ?string $userDisplayName, ?array $cloudIdData, array $expected): void {
159
-		$provider = $this->getProvider();
160
-
161
-		if ($userDisplayName !== null) {
162
-			$this->userManager->expects($this->once())
163
-				->method('getDisplayName')
164
-				->with($uid)
165
-				->willReturn($userDisplayName);
166
-		}
167
-		if ($cloudIdData !== null) {
168
-			$this->cloudIdManager->expects($this->once())
169
-				->method('isValidCloudId')
170
-				->willReturn(true);
171
-
172
-			$cloudId = $this->createMock(ICloudId::class);
173
-			$cloudId->expects($this->once())
174
-				->method('getUser')
175
-				->willReturn($cloudIdData['user']);
176
-			$cloudId->expects($this->once())
177
-				->method('getDisplayId')
178
-				->willReturn($cloudIdData['displayId']);
179
-			$cloudId->expects($this->once())
180
-				->method('getRemote')
181
-				->willReturn($cloudIdData['remote']);
182
-
183
-			$this->cloudIdManager->expects($this->once())
184
-				->method('resolveCloudId')
185
-				->with($uid)
186
-				->willReturn($cloudId);
187
-
188
-			if ($cloudIdData['name'] !== null) {
189
-				$this->contactsManager->expects($this->once())
190
-					->method('search')
191
-					->with($cloudIdData['displayId'], ['CLOUD'])
192
-					->willReturn([
193
-						[
194
-							'CLOUD' => $cloudIdData['displayId'],
195
-							'FN' => $cloudIdData['name'],
196
-						]
197
-					]);
198
-			} else {
199
-				$this->contactsManager->expects($this->once())
200
-					->method('search')
201
-					->with($cloudIdData['displayId'], ['CLOUD'])
202
-					->willReturn([]);
203
-			}
204
-		}
205
-
206
-		$result = self::invokePrivate($provider, 'getUser', [$uid]);
207
-		$this->assertEquals($expected, $result);
208
-	}
30
+    /** @var IFactory|MockObject */
31
+    protected $l10nFactory;
32
+    /** @var IURLGenerator|MockObject */
33
+    protected $url;
34
+    /** @var IManager|MockObject */
35
+    protected $activityManager;
36
+    /** @var IUserManager|MockObject */
37
+    protected $userManager;
38
+    /** @var IRootFolder|MockObject */
39
+    protected $rootFolder;
40
+    /** @var ICloudIdManager|MockObject */
41
+    protected $cloudIdManager;
42
+    /** @var IContactsManager|MockObject */
43
+    protected $contactsManager;
44
+    /** @var IEventMerger|MockObject */
45
+    protected $eventMerger;
46
+
47
+    protected function setUp(): void {
48
+        parent::setUp();
49
+
50
+        $this->l10nFactory = $this->createMock(IFactory::class);
51
+        $this->url = $this->createMock(IURLGenerator::class);
52
+        $this->activityManager = $this->createMock(IManager::class);
53
+        $this->userManager = $this->createMock(IUserManager::class);
54
+        $this->rootFolder = $this->createMock(IRootFolder::class);
55
+        $this->cloudIdManager = $this->createMock(ICloudIdManager::class);
56
+        $this->contactsManager = $this->createMock(IContactsManager::class);
57
+        $this->eventMerger = $this->createMock(IEventMerger::class);
58
+    }
59
+
60
+    /**
61
+     * @param string[] $methods
62
+     * @return Provider|MockObject
63
+     */
64
+    protected function getProvider(array $methods = []) {
65
+        if (!empty($methods)) {
66
+            return $this->getMockBuilder(Provider::class)
67
+                ->setConstructorArgs([
68
+                    $this->l10nFactory,
69
+                    $this->url,
70
+                    $this->activityManager,
71
+                    $this->userManager,
72
+                    $this->rootFolder,
73
+                    $this->cloudIdManager,
74
+                    $this->contactsManager,
75
+                    $this->eventMerger,
76
+                ])
77
+                ->setMethods($methods)
78
+                ->getMock();
79
+        }
80
+        return new Provider(
81
+            $this->l10nFactory,
82
+            $this->url,
83
+            $this->activityManager,
84
+            $this->userManager,
85
+            $this->rootFolder,
86
+            $this->cloudIdManager,
87
+            $this->contactsManager,
88
+            $this->eventMerger
89
+        );
90
+    }
91
+
92
+    public function dataGetFile() {
93
+        return [
94
+            [[42 => '/FortyTwo.txt'], null, '42', 'FortyTwo.txt', 'FortyTwo.txt'],
95
+            [['23' => '/Twenty/Three.txt'], null, '23', 'Three.txt', 'Twenty/Three.txt'],
96
+            ['/Foo/Bar.txt', 128, '128', 'Bar.txt', 'Foo/Bar.txt'], // Legacy from ownCloud 8.2 and before
97
+        ];
98
+    }
99
+
100
+    /**
101
+     * @dataProvider dataGetFile
102
+     * @param mixed $parameter
103
+     * @param mixed $eventId
104
+     * @param int $id
105
+     * @param string $name
106
+     * @param string $path
107
+     */
108
+    public function testGetFile($parameter, $eventId, $id, $name, $path): void {
109
+        $provider = $this->getProvider();
110
+
111
+        if ($eventId !== null) {
112
+            $event = $this->createMock(IEvent::class);
113
+            $event->expects($this->once())
114
+                ->method('getObjectId')
115
+                ->willReturn($eventId);
116
+        } else {
117
+            $event = null;
118
+        }
119
+
120
+        $this->url->expects($this->once())
121
+            ->method('linkToRouteAbsolute')
122
+            ->with('files.viewcontroller.showFile', ['fileid' => $id])
123
+            ->willReturn('link-' . $id);
124
+
125
+        $result = self::invokePrivate($provider, 'getFile', [$parameter, $event]);
126
+
127
+        $this->assertSame('file', $result['type']);
128
+        $this->assertSame($id, $result['id']);
129
+        $this->assertSame($name, $result['name']);
130
+        $this->assertSame($path, $result['path']);
131
+        $this->assertSame('link-' . $id, $result['link']);
132
+    }
133
+
134
+
135
+    public function testGetFileThrows(): void {
136
+        $this->expectException(UnknownActivityException::class);
137
+
138
+        $provider = $this->getProvider();
139
+        self::invokePrivate($provider, 'getFile', ['/Foo/Bar.txt', null]);
140
+    }
141
+
142
+    public function dataGetUser() {
143
+        return [
144
+            ['test', 'Test user', null, ['type' => 'user', 'id' => 'test', 'name' => 'Test user']],
145
+            ['test@http://localhost', null, ['user' => 'test', 'displayId' => 'test@localhost', 'remote' => 'localhost', 'name' => null], ['type' => 'user', 'id' => 'test', 'name' => 'test@localhost', 'server' => 'localhost']],
146
+            ['test@http://localhost', null, ['user' => 'test', 'displayId' => 'test@localhost', 'remote' => 'localhost', 'name' => 'Remote user'], ['type' => 'user', 'id' => 'test', 'name' => 'Remote user (test@localhost)', 'server' => 'localhost']],
147
+            ['test', null, null, ['type' => 'user', 'id' => 'test', 'name' => 'test']],
148
+        ];
149
+    }
150
+
151
+    /**
152
+     * @dataProvider dataGetUser
153
+     * @param string $uid
154
+     * @param string|null $userDisplayName
155
+     * @param array|null $cloudIdData
156
+     * @param array $expected
157
+     */
158
+    public function testGetUser(string $uid, ?string $userDisplayName, ?array $cloudIdData, array $expected): void {
159
+        $provider = $this->getProvider();
160
+
161
+        if ($userDisplayName !== null) {
162
+            $this->userManager->expects($this->once())
163
+                ->method('getDisplayName')
164
+                ->with($uid)
165
+                ->willReturn($userDisplayName);
166
+        }
167
+        if ($cloudIdData !== null) {
168
+            $this->cloudIdManager->expects($this->once())
169
+                ->method('isValidCloudId')
170
+                ->willReturn(true);
171
+
172
+            $cloudId = $this->createMock(ICloudId::class);
173
+            $cloudId->expects($this->once())
174
+                ->method('getUser')
175
+                ->willReturn($cloudIdData['user']);
176
+            $cloudId->expects($this->once())
177
+                ->method('getDisplayId')
178
+                ->willReturn($cloudIdData['displayId']);
179
+            $cloudId->expects($this->once())
180
+                ->method('getRemote')
181
+                ->willReturn($cloudIdData['remote']);
182
+
183
+            $this->cloudIdManager->expects($this->once())
184
+                ->method('resolveCloudId')
185
+                ->with($uid)
186
+                ->willReturn($cloudId);
187
+
188
+            if ($cloudIdData['name'] !== null) {
189
+                $this->contactsManager->expects($this->once())
190
+                    ->method('search')
191
+                    ->with($cloudIdData['displayId'], ['CLOUD'])
192
+                    ->willReturn([
193
+                        [
194
+                            'CLOUD' => $cloudIdData['displayId'],
195
+                            'FN' => $cloudIdData['name'],
196
+                        ]
197
+                    ]);
198
+            } else {
199
+                $this->contactsManager->expects($this->once())
200
+                    ->method('search')
201
+                    ->with($cloudIdData['displayId'], ['CLOUD'])
202
+                    ->willReturn([]);
203
+            }
204
+        }
205
+
206
+        $result = self::invokePrivate($provider, 'getUser', [$uid]);
207
+        $this->assertEquals($expected, $result);
208
+    }
209 209
 }
Please login to merge, or discard this patch.