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