Completed
Pull Request — master (#101)
by Joas
02:34
created

FilesHooks::getUserPathsFromPath()   C

Complexity

Conditions 7
Paths 5

Size

Total Lines 26
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 56

Importance

Changes 0
Metric Value
dl 0
loc 26
ccs 0
cts 17
cp 0
rs 6.7272
c 0
b 0
f 0
cc 7
eloc 18
nc 5
nop 2
crap 56
1
<?php
2
/**
3
 * @copyright Copyright (c) 2016, ownCloud, Inc.
4
 *
5
 * @author Frank Karlitschek <[email protected]>
6
 * @author Joas Schilling <[email protected]>
7
 * @author Thomas Müller <[email protected]>
8
 *
9
 * @license AGPL-3.0
10
 *
11
 * This code is free software: you can redistribute it and/or modify
12
 * it under the terms of the GNU Affero General Public License, version 3,
13
 * as published by the Free Software Foundation.
14
 *
15
 * This program is distributed in the hope that it will be useful,
16
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18
 * GNU Affero General Public License for more details.
19
 *
20
 * You should have received a copy of the GNU Affero General Public License, version 3,
21
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
22
 *
23
 */
24
25
namespace OCA\Activity;
26
27
use OC\Files\Filesystem;
28
use OC\Files\View;
29
use OC\Share20\ShareHelper;
30
use OCA\Activity\Extension\Files;
31
use OCA\Activity\Extension\Files_Sharing;
32
use OCP\Activity\IManager;
33
use OCP\Files\IRootFolder;
34
use OCP\Files\Mount\IMountPoint;
35
use OCP\Files\Node;
36
use OCP\Files\NotFoundException;
37
use OCP\IDBConnection;
38
use OCP\IGroup;
39
use OCP\IGroupManager;
40
use OCP\IURLGenerator;
41
use OCP\IUser;
42
use OCP\Share;
43
use OCP\Share\IManager as IShareManager;
44
45
/**
46
 * The class to handle the filesystem hooks
47
 */
48
class FilesHooks {
49
	const USER_BATCH_SIZE = 50;
50
51
	/** @var \OCP\Activity\IManager */
52
	protected $manager;
53
54
	/** @var \OCA\Activity\Data */
55
	protected $activityData;
56
57
	/** @var \OCA\Activity\UserSettings */
58
	protected $userSettings;
59
60
	/** @var \OCP\IGroupManager */
61
	protected $groupManager;
62
63
	/** @var \OCP\IDBConnection */
64
	protected $connection;
65
66
	/** @var \OC\Files\View */
67
	protected $view;
68
69
	/** @var IRootFolder */
70
	protected $rootFolder;
71
72
	/** @var IShareManager */
73
	protected $shareManager;
74
75
	/** @var IURLGenerator */
76
	protected $urlGenerator;
77
78
	/** @var CurrentUser */
79
	protected $currentUser;
80
81
	/** @var string|bool */
82
	protected $moveCase = false;
83
	/** @var string[] */
84
	protected $oldParentUsers;
85
	/** @var string */
86
	protected $oldParentPath;
87
	/** @var string */
88
	protected $oldParentOwner;
89
	/** @var string */
90
	protected $oldParentId;
91
92
	/**
93
	 * Constructor
94
	 *
95
	 * @param IManager $manager
96
	 * @param Data $activityData
97
	 * @param UserSettings $userSettings
98
	 * @param IGroupManager $groupManager
99
	 * @param View $view
100
	 * @param IRootFolder $rootFolder
101
	 * @param IShareManager $shareManager
102
	 * @param IDBConnection $connection
103
	 * @param IURLGenerator $urlGenerator
104
	 * @param CurrentUser $currentUser
105
	 */
106 1
	public function __construct(IManager $manager, Data $activityData, UserSettings $userSettings, IGroupManager $groupManager, View $view, IRootFolder $rootFolder, IShareManager $shareManager, IDBConnection $connection, IURLGenerator $urlGenerator, CurrentUser $currentUser) {
107 1
		$this->manager = $manager;
108 1
		$this->activityData = $activityData;
109 1
		$this->userSettings = $userSettings;
110 1
		$this->groupManager = $groupManager;
111 1
		$this->view = $view;
112 1
		$this->rootFolder = $rootFolder;
113 1
		$this->shareManager = $shareManager;
114 1
		$this->connection = $connection;
115 1
		$this->urlGenerator = $urlGenerator;
116 1
		$this->currentUser = $currentUser;
117 1
	}
118
119
	/**
120
	 * Store the create hook events
121
	 * @param string $path Path of the file that has been created
122
	 */
123
	public function fileCreate($path) {
124
		if ($this->currentUser->getUserIdentifier() !== '') {
125
			$this->addNotificationsForFileAction($path, Files::TYPE_SHARE_CREATED, 'created_self', 'created_by');
126
		} else {
127
			$this->addNotificationsForFileAction($path, Files::TYPE_SHARE_CREATED, '', 'created_public');
128
		}
129
	}
130
131
	/**
132
	 * Store the update hook events
133
	 * @param string $path Path of the file that has been modified
134
	 */
135
	public function fileUpdate($path) {
136
		$this->addNotificationsForFileAction($path, Files::TYPE_SHARE_CHANGED, 'changed_self', 'changed_by');
137
	}
138
139
	/**
140
	 * Store the delete hook events
141
	 * @param string $path Path of the file that has been deleted
142
	 */
143
	public function fileDelete($path) {
144
		$this->addNotificationsForFileAction($path, Files::TYPE_SHARE_DELETED, 'deleted_self', 'deleted_by');
145
	}
146
147
	/**
148
	 * Store the restore hook events
149
	 * @param string $path Path of the file that has been restored
150
	 */
151
	public function fileRestore($path) {
152
		$this->addNotificationsForFileAction($path, Files::TYPE_SHARE_RESTORED, 'restored_self', 'restored_by');
153
	}
154
155
	/**
156
	 * Creates the entries for file actions on $file_path
157
	 *
158
	 * @param string $filePath         The file that is being changed
159
	 * @param int    $activityType     The activity type
160
	 * @param string $subject          The subject for the actor
161
	 * @param string $subjectBy        The subject for other users (with "by $actor")
162
	 */
163
	protected function addNotificationsForFileAction($filePath, $activityType, $subject, $subjectBy) {
164
		// Do not add activities for .part-files
165
		if (substr($filePath, -5) === '.part') {
166
			return;
167
		}
168
169
		list($filePath, $uidOwner, $fileId) = $this->getSourcePathAndOwner($filePath);
170
		if ($fileId === 0) {
171
			// Could not find the file for the owner ...
172
			return;
173
		}
174
175
		$affectedUsers = $this->getUserPathsFromPath($filePath, $uidOwner);
176
		$filteredStreamUsers = $this->userSettings->filterUsersBySetting(array_keys($affectedUsers), 'stream', $activityType);
177
		$filteredEmailUsers = $this->userSettings->filterUsersBySetting(array_keys($affectedUsers), 'email', $activityType);
178
179
		foreach ($affectedUsers as $user => $path) {
180
			if (empty($filteredStreamUsers[$user]) && empty($filteredEmailUsers[$user])) {
181
				continue;
182
			}
183
184
			if ($user === $this->currentUser->getUID()) {
185
				$userSubject = $subject;
186
				$userParams = [[$fileId => $path]];
187
			} else {
188
				$userSubject = $subjectBy;
189
				$userParams = [[$fileId => $path], $this->currentUser->getUserIdentifier()];
190
			}
191
192
			$this->addNotificationsForUser(
193
				$user, $userSubject, $userParams,
194
				$fileId, $path, true,
195
				!empty($filteredStreamUsers[$user]),
196
				!empty($filteredEmailUsers[$user]) ? $filteredEmailUsers[$user] : 0,
197
				$activityType
198
			);
199
		}
200
	}
201
202
	/**
203
	 * Collect some information for move/renames
204
	 *
205
	 * @param string $oldPath Path of the file that has been moved
206
	 * @param string $newPath Path of the file that has been moved
207
	 */
208
	public function fileMove($oldPath, $newPath) {
209
		if (substr($oldPath, -5) === '.part' || substr($newPath, -5) === '.part') {
210
			// Do not add activities for .part-files
211
			$this->moveCase = false;
212
			return;
213
		}
214
215
		$oldDir = dirname($oldPath);
216
		$newDir = dirname($newPath);
217
218
		if ($oldDir === $newDir) {
219
			/**
220
			 * a/b moved to a/c
221
			 *
222
			 * Cases:
223
			 * - a/b shared: no visible change
224
			 * - a/ shared: rename
225
			 */
226
			$this->moveCase = 'rename';
227
			return;
228
		}
229
230
		if (strpos($oldDir, $newDir) === 0) {
231
			/**
232
			 * a/b/c moved to a/c
233
			 *
234
			 * Cases:
235
			 * - a/b/c shared: no visible change
236
			 * - a/b/ shared: delete
237
			 * - a/ shared: move/rename
238
			 */
239
			$this->moveCase = 'moveUp';
240
		} else if (strpos($newDir, $oldDir) === 0) {
241
			/**
242
			 * a/b moved to a/c/b
243
			 *
244
			 * Cases:
245
			 * - a/b shared: no visible change
246
			 * - a/c/ shared: add
247
			 * - a/ shared: move/rename
248
			 */
249
			$this->moveCase = 'moveDown';
250
		} else {
251
			/**
252
			 * a/b/c moved to a/d/c
253
			 *
254
			 * Cases:
255
			 * - a/b/c shared: no visible change
256
			 * - a/b/ shared: delete
257
			 * - a/d/ shared: add
258
			 * - a/ shared: move/rename
259
			 */
260
			$this->moveCase = 'moveCross';
261
		}
262
263
		list($this->oldParentPath, $this->oldParentOwner, $this->oldParentId) = $this->getSourcePathAndOwner($oldDir);
264
		if ($this->oldParentId === 0) {
265
			// Could not find the file for the owner ...
266
			$this->moveCase = false;
267
			return;
268
		}
269
		$this->oldParentUsers = $this->getUserPathsFromPath($this->oldParentPath, $this->oldParentOwner);
270
	}
271
272
273
	/**
274
	 * Store the move hook events
275
	 *
276
	 * @param string $oldPath Path of the file that has been moved
277
	 * @param string $newPath Path of the file that has been moved
278
	 */
279
	public function fileMovePost($oldPath, $newPath) {
280
		// Do not add activities for .part-files
281
		if ($this->moveCase === false) {
282
			return;
283
		}
284
285
		switch ($this->moveCase) {
286
			case 'rename':
287
				$this->fileRenaming($oldPath, $newPath);
288
				break;
289
			case 'moveUp':
290
			case 'moveDown':
291
			case 'moveCross':
292
				$this->fileMoving($oldPath, $newPath);
293
				break;
294
		}
295
296
		$this->moveCase = false;
297
	}
298
299
300
	/**
301
	 * Renaming a file inside the same folder (a/b to a/c)
302
	 *
303
	 * @param string $oldPath
304
	 * @param string $newPath
305
	 */
306
	protected function fileRenaming($oldPath, $newPath) {
307
		$dirName = dirname($newPath);
308
		$fileName = basename($newPath);
309
		$oldFileName = basename($oldPath);
310
311
		list(, , $fileId) = $this->getSourcePathAndOwner($newPath);
312
		list($parentPath, $parentOwner, $parentId) = $this->getSourcePathAndOwner($dirName);
313
		if ($fileId === 0 || $parentId === 0) {
314
			// Could not find the file for the owner ...
315
			return;
316
		}
317
		$affectedUsers = $this->getUserPathsFromPath($parentPath, $parentOwner);
318
319
		$filteredStreamUsers = $this->userSettings->filterUsersBySetting(array_keys($affectedUsers), 'stream', Files::TYPE_SHARE_CHANGED);
320
		$filteredEmailUsers = $this->userSettings->filterUsersBySetting(array_keys($affectedUsers), 'email', Files::TYPE_SHARE_CHANGED);
321
322
		foreach ($affectedUsers as $user => $path) {
323
			if (empty($filteredStreamUsers[$user]) && empty($filteredEmailUsers[$user])) {
324
				continue;
325
			}
326
327
			if ($user === $this->currentUser->getUID()) {
328
				$userSubject = 'renamed_self';
329
				$userParams = [
330
					[$fileId => $path . '/' . $fileName],
331
					[$fileId => $path . '/' . $oldFileName],
332
				];
333 View Code Duplication
			} else {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
334
				$userSubject = 'renamed_by';
335
				$userParams = [
336
					[$fileId => $path . '/' . $fileName],
337
					$this->currentUser->getUserIdentifier(),
338
					[$fileId => $path . '/' . $oldFileName],
339
				];
340
			}
341
342
			$this->addNotificationsForUser(
343
				$user, $userSubject, $userParams,
344
				$fileId, $path . '/' . $fileName, true,
345
				!empty($filteredStreamUsers[$user]),
346
				!empty($filteredEmailUsers[$user]) ? $filteredEmailUsers[$user] : 0,
347
				Files::TYPE_SHARE_CHANGED
348
			);
349
		}
350
	}
351
352
	/**
353
	 * Moving a file from one folder to another
354
	 *
355
	 * @param string $oldPath
356
	 * @param string $newPath
357
	 */
358
	protected function fileMoving($oldPath, $newPath) {
359
		$dirName = dirname($newPath);
360
		$fileName = basename($newPath);
361
		$oldFileName = basename($oldPath);
362
363
		list(, , $fileId) = $this->getSourcePathAndOwner($newPath);
364
		list($parentPath, $parentOwner, $parentId) = $this->getSourcePathAndOwner($dirName);
365
		if ($fileId === 0 || $parentId === 0) {
366
			// Could not find the file for the owner ...
367
			return;
368
		}
369
		$affectedUsers = $this->getUserPathsFromPath($parentPath, $parentOwner);
370
371
		$beforeUsers = array_keys($this->oldParentUsers);
372
		$afterUsers = array_keys($affectedUsers);
373
374
		$deleteUsers = array_diff($beforeUsers, $afterUsers);
375
		$this->generateDeleteActivities($deleteUsers, $this->oldParentUsers, $fileId, $oldFileName);
376
377
		$addUsers = array_diff($afterUsers, $beforeUsers);
378
		$this->generateAddActivities($addUsers, $affectedUsers, $fileId, $fileName);
379
380
		$moveUsers = array_intersect($beforeUsers, $afterUsers);
381
		$this->generateMoveActivities($moveUsers, $this->oldParentUsers, $affectedUsers, $fileId, $oldFileName, $fileName);
382
	}
383
384
	/**
385
	 * @param string[] $users
386
	 * @param string[] $pathMap
387
	 * @param int $fileId
388
	 * @param string $oldFileName
389
	 */
390 View Code Duplication
	protected function generateDeleteActivities($users, $pathMap, $fileId, $oldFileName) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
391
		if (empty($users)) {
392
			return;
393
		}
394
395
		$filteredStreamUsers = $this->userSettings->filterUsersBySetting($users, 'stream', Files::TYPE_SHARE_DELETED);
396
		$filteredEmailUsers = $this->userSettings->filterUsersBySetting($users, 'email', Files::TYPE_SHARE_DELETED);
397
398
		foreach ($users as $user) {
399
			if (empty($filteredStreamUsers[$user]) && empty($filteredEmailUsers[$user])) {
400
				continue;
401
			}
402
403
			$path = $pathMap[$user];
404
405
			if ($user === $this->currentUser->getUID()) {
406
				$userSubject = 'deleted_self';
407
				$userParams = [[$fileId => $path . '/' . $oldFileName]];
408
			} else {
409
				$userSubject = 'deleted_by';
410
				$userParams = [[$fileId => $path . '/' . $oldFileName], $this->currentUser->getUserIdentifier()];
411
			}
412
413
			$this->addNotificationsForUser(
414
				$user, $userSubject, $userParams,
415
				$fileId, $path . '/' . $oldFileName, true,
416
				!empty($filteredStreamUsers[$user]),
417
				!empty($filteredEmailUsers[$user]) ? $filteredEmailUsers[$user] : 0,
418
				Files::TYPE_SHARE_DELETED
419
			);
420
		}
421
	}
422
423
	/**
424
	 * @param string[] $users
425
	 * @param string[] $pathMap
426
	 * @param int $fileId
427
	 * @param string $fileName
428
	 */
429 View Code Duplication
	protected function generateAddActivities($users, $pathMap, $fileId, $fileName) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
430
		if (empty($users)) {
431
			return;
432
		}
433
434
		$filteredStreamUsers = $this->userSettings->filterUsersBySetting($users, 'stream', Files::TYPE_SHARE_CREATED);
435
		$filteredEmailUsers = $this->userSettings->filterUsersBySetting($users, 'email', Files::TYPE_SHARE_CREATED);
436
437
		foreach ($users as $user) {
438
			if (empty($filteredStreamUsers[$user]) && empty($filteredEmailUsers[$user])) {
439
				continue;
440
			}
441
442
			$path = $pathMap[$user];
443
444
			if ($user === $this->currentUser->getUID()) {
445
				$userSubject = 'created_self';
446
				$userParams = [[$fileId => $path . '/' . $fileName]];
447
			} else {
448
				$userSubject = 'created_by';
449
				$userParams = [[$fileId => $path . '/' . $fileName], $this->currentUser->getUserIdentifier()];
450
			}
451
452
			$this->addNotificationsForUser(
453
				$user, $userSubject, $userParams,
454
				$fileId, $path . '/' . $fileName, true,
455
				!empty($filteredStreamUsers[$user]),
456
				!empty($filteredEmailUsers[$user]) ? $filteredEmailUsers[$user] : 0,
457
				Files::TYPE_SHARE_CREATED
458
			);
459
		}
460
	}
461
462
	/**
463
	 * @param string[] $users
464
	 * @param string[] $beforePathMap
465
	 * @param string[] $afterPathMap
466
	 * @param int $fileId
467
	 * @param string $oldFileName
468
	 * @param string $fileName
469
	 */
470
	protected function generateMoveActivities($users, $beforePathMap, $afterPathMap, $fileId, $oldFileName, $fileName) {
471
		if (empty($users)) {
472
			return;
473
		}
474
475
		$filteredStreamUsers = $this->userSettings->filterUsersBySetting($users, 'stream', Files::TYPE_SHARE_CHANGED);
476
		$filteredEmailUsers = $this->userSettings->filterUsersBySetting($users, 'email', Files::TYPE_SHARE_CHANGED);
477
478
		foreach ($users as $user) {
479
			if (empty($filteredStreamUsers[$user]) && empty($filteredEmailUsers[$user])) {
480
				continue;
481
			}
482
483
			if ($user === $this->currentUser->getUID()) {
484
				$userSubject = 'moved_self';
485
				$userParams = [
486
					[$fileId => $afterPathMap[$user] . '/' . $fileName],
487
					[$fileId => $beforePathMap[$user] . '/' . $oldFileName],
488
				];
489 View Code Duplication
			} else {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
490
				$userSubject = 'moved_by';
491
				$userParams = [
492
					[$fileId => $afterPathMap[$user] . '/' . $fileName],
493
					$this->currentUser->getUserIdentifier(),
494
					[$fileId => $beforePathMap[$user] . '/' . $oldFileName],
495
				];
496
			}
497
498
			$this->addNotificationsForUser(
499
				$user, $userSubject, $userParams,
500
				$fileId, $afterPathMap[$user] . '/' . $fileName, true,
501
				!empty($filteredStreamUsers[$user]),
502
				!empty($filteredEmailUsers[$user]) ? $filteredEmailUsers[$user] : 0,
503
				Files::TYPE_SHARE_CHANGED
504
			);
505
		}
506
	}
507
508
	/**
509
	 * Returns a "username => path" map for all affected users
510
	 *
511
	 * @param string $path
512
	 * @param string $uidOwner
513
	 * @return array
514
	 */
515
	protected function getUserPathsFromPath($path, $uidOwner) {
516
		try {
517
			$node = $this->rootFolder->getUserFolder($uidOwner)->get($path);
518
		} catch (NotFoundException $e) {
0 ignored issues
show
Bug introduced by
The class OCP\Files\NotFoundException does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
519
			return [];
520
		}
521
522
		if (!$node instanceof Node) {
0 ignored issues
show
Bug introduced by
The class OCP\Files\Node does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
523
			return [];
524
		}
525
		$accessList = $this->shareManager->getAccessList($node, true, true);
526
		$helper = new ShareHelper($this->rootFolder); // FIXME
527
528
		$nodes = $helper->getPathsForAccessList($node, $accessList['users']);
529
		$paths = [];
530
		foreach ($nodes as $uid => $userNodes) {
531
			$userNode = $userNodes[0];
532
			/** @var Node $userNode */
533
			$userPath = $userNode->getPath();
534
			$splitPath = explode('/', $userPath, 4);
535
			if ($splitPath[0] === '' && $splitPath[1] === $uid && $splitPath[2] === 'files') {
536
				$paths[$uid] = $splitPath[3];
537
			}
538
		}
539
		return $paths;
540
	}
541
542
	/**
543
	 * Return the source
544
	 *
545
	 * @param string $path
546
	 * @return array
547
	 */
548
	protected function getSourcePathAndOwner($path) {
549
		$view = Filesystem::getView();
550
		$uidOwner = $view->getOwner($path);
551
		$fileId = 0;
552
553
		if ($uidOwner !== $this->currentUser->getUID()) {
554
			/** @var \OCP\Files\Storage\IStorage $storage */
555
			list($storage,) = $view->resolvePath($path);
556
			if (!$storage->instanceOfStorage('OCA\Files_Sharing\External\Storage')) {
557
				Filesystem::initMountPoints($uidOwner);
558
			} else {
559
				// Probably a remote user, let's try to at least generate activities
560
				// for the current user
561
				$uidOwner = $this->currentUser->getUID();
562
			}
563
		}
564
565
		$info = Filesystem::getFileInfo($path);
566
		if ($info !== false) {
567
			$ownerView = new View('/' . $uidOwner . '/files');
568
			$fileId = (int) $info['fileid'];
569
			$path = $ownerView->getPath($fileId);
570
		}
571
572
		return array($path, $uidOwner, $fileId);
573
	}
574
575
	/**
576
	 * Manage sharing events
577
	 * @param array $params The hook params
578
	 */
579 View Code Duplication
	public function share($params) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
580
		if ($params['itemType'] === 'file' || $params['itemType'] === 'folder') {
581
			if ((int) $params['shareType'] === Share::SHARE_TYPE_USER) {
582
				$this->shareFileOrFolderWithUser($params['shareWith'], (int) $params['fileSource'], $params['itemType'], $params['fileTarget'], true);
583
			} else if ((int) $params['shareType'] === Share::SHARE_TYPE_GROUP) {
584
				$this->shareFileOrFolderWithGroup($params['shareWith'], (int) $params['fileSource'], $params['itemType'], $params['fileTarget'], (int) $params['id'], true);
585
			} else if ((int) $params['shareType'] === Share::SHARE_TYPE_LINK) {
586
				$this->shareFileOrFolderByLink((int) $params['fileSource'], $params['itemType'], $params['uidOwner'], true);
587
			}
588
		}
589
	}
590
591
	/**
592
	 * Manage sharing events
593
	 * @param array $params The hook params
594
	 */
595 View Code Duplication
	public function unShare($params) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
596
		if ($params['itemType'] === 'file' || $params['itemType'] === 'folder') {
597
			if ((int) $params['shareType'] === Share::SHARE_TYPE_USER) {
598
				$this->shareFileOrFolderWithUser($params['shareWith'], (int) $params['fileSource'], $params['itemType'], $params['fileTarget'], false);
599
			} else if ((int) $params['shareType'] === Share::SHARE_TYPE_GROUP) {
600
				$this->shareFileOrFolderWithGroup($params['shareWith'], (int) $params['fileSource'], $params['itemType'], $params['fileTarget'], (int) $params['id'], false);
601
			} else if ((int) $params['shareType'] === Share::SHARE_TYPE_LINK) {
602
				$this->shareFileOrFolderByLink((int) $params['fileSource'], $params['itemType'], $params['uidOwner'], false);
603
			}
604
		}
605
	}
606
607
	/**
608
	 * Sharing a file or folder with a user
609
	 *
610
	 * @param string $shareWith
611
	 * @param int $fileSource File ID that is being shared
612
	 * @param string $itemType File type that is being shared (file or folder)
613
	 * @param string $fileTarget File path
614
	 * @param bool $isSharing True if sharing, false if unsharing
615
	 */
616
	protected function shareFileOrFolderWithUser($shareWith, $fileSource, $itemType, $fileTarget, $isSharing) {
617
		if ($isSharing) {
618
			$actionSharer = 'shared_user_self';
619
			$actionOwner = 'reshared_user_by';
620
			$actionUser = 'shared_with_by';
621
		} else {
622
			$actionSharer = 'unshared_user_self';
623
			$actionOwner = 'unshared_user_by';
624
			$actionUser = 'unshared_by';
625
		}
626
627
		// User performing the share
628
		$this->shareNotificationForSharer($actionSharer, $shareWith, $fileSource, $itemType);
629 View Code Duplication
		if ($this->currentUser->getUID() !== null) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
630
			$this->shareNotificationForOriginalOwners($this->currentUser->getUID(), $actionOwner, $shareWith, $fileSource, $itemType);
631
		}
632
633
		// New shared user
634
		$this->addNotificationsForUser(
635
			$shareWith, $actionUser, [[$fileSource => $fileTarget], $this->currentUser->getUserIdentifier()],
636
			(int) $fileSource, $fileTarget, ($itemType === 'file'),
637
			$this->userSettings->getUserSetting($shareWith, 'stream', Files_Sharing::TYPE_SHARED),
0 ignored issues
show
Bug introduced by
It seems like $this->userSettings->get...s_Sharing::TYPE_SHARED) targeting OCA\Activity\UserSettings::getUserSetting() can also be of type integer; however, OCA\Activity\FilesHooks::addNotificationsForUser() does only seem to accept boolean, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
638
			$this->userSettings->getUserSetting($shareWith, 'email', Files_Sharing::TYPE_SHARED) ? $this->userSettings->getUserSetting($shareWith, 'setting', 'batchtime') : 0
0 ignored issues
show
Bug introduced by
It seems like $this->userSettings->get...ting', 'batchtime') : 0 can also be of type boolean; however, OCA\Activity\FilesHooks::addNotificationsForUser() does only seem to accept integer, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
639
		);
640
	}
641
642
	/**
643
	 * Sharing a file or folder with a group
644
	 *
645
	 * @param string $shareWith
646
	 * @param int $fileSource File ID that is being shared
647
	 * @param string $itemType File type that is being shared (file or folder)
648
	 * @param string $fileTarget File path
649
	 * @param int $shareId The Share ID of this share
650
	 * @param bool $isSharing True if sharing, false if unsharing
651
	 */
652
	protected function shareFileOrFolderWithGroup($shareWith, $fileSource, $itemType, $fileTarget, $shareId, $isSharing) {
653
		if ($isSharing) {
654
			$actionSharer = 'shared_group_self';
655
			$actionOwner = 'reshared_group_by';
656
			$actionUser = 'shared_with_by';
657
		} else {
658
			$actionSharer = 'unshared_group_self';
659
			$actionOwner = 'unshared_group_by';
660
			$actionUser = 'unshared_by';
661
		}
662
663
		// Members of the new group
664
		$group = $this->groupManager->get($shareWith);
665
		if (!($group instanceof IGroup)) {
0 ignored issues
show
Bug introduced by
The class OCP\IGroup does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
666
			return;
667
		}
668
669
		// User performing the share
670
		$this->shareNotificationForSharer($actionSharer, $shareWith, $fileSource, $itemType);
671 View Code Duplication
		if ($this->currentUser->getUID() !== null) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
672
			$this->shareNotificationForOriginalOwners($this->currentUser->getUID(), $actionOwner, $shareWith, $fileSource, $itemType);
673
		}
674
675
		$offset = 0;
676
		$users = $group->searchUsers('', self::USER_BATCH_SIZE, $offset);
677
		while (!empty($users)) {
678
			$this->addNotificationsForGroupUsers($users, $actionUser, $fileSource, $itemType, $fileTarget, $shareId);
679
			$offset += self::USER_BATCH_SIZE;
680
			$users = $group->searchUsers('', self::USER_BATCH_SIZE, $offset);
681
		}
682
	}
683
684
	/**
685
	 * @param IUser[] $usersInGroup
686
	 * @param string $actionUser
687
	 * @param int $fileSource File ID that is being shared
688
	 * @param string $itemType File type that is being shared (file or folder)
689
	 * @param string $fileTarget File path
690
	 * @param int $shareId The Share ID of this share
691
	 */
692
	protected function addNotificationsForGroupUsers(array $usersInGroup, $actionUser, $fileSource, $itemType, $fileTarget, $shareId) {
693
		$affectedUsers = [];
694
695
		foreach ($usersInGroup as $user) {
696
			$affectedUsers[$user->getUID()] = $fileTarget;
697
		}
698
699
		// Remove the triggering user, we already managed his notifications
700
		unset($affectedUsers[$this->currentUser->getUID()]);
701
702
		if (empty($affectedUsers)) {
703
			return;
704
		}
705
706
		$userIds = array_keys($affectedUsers);
707
		$filteredStreamUsersInGroup = $this->userSettings->filterUsersBySetting($userIds, 'stream', Files_Sharing::TYPE_SHARED);
708
		$filteredEmailUsersInGroup = $this->userSettings->filterUsersBySetting($userIds, 'email', Files_Sharing::TYPE_SHARED);
709
710
		$affectedUsers = $this->fixPathsForShareExceptions($affectedUsers, $shareId);
711
		foreach ($affectedUsers as $user => $path) {
712
			if (empty($filteredStreamUsersInGroup[$user]) && empty($filteredEmailUsersInGroup[$user])) {
713
				continue;
714
			}
715
716
			$this->addNotificationsForUser(
717
				$user, $actionUser, [[$fileSource => $path], $this->currentUser->getUserIdentifier()],
718
				$fileSource, $path, ($itemType === 'file'),
719
				!empty($filteredStreamUsersInGroup[$user]),
720
				!empty($filteredEmailUsersInGroup[$user]) ? $filteredEmailUsersInGroup[$user] : 0
721
			);
722
		}
723
	}
724
725
	/**
726
	 * Check when there was a naming conflict and the target is different
727
	 * for some of the users
728
	 *
729
	 * @param array $affectedUsers
730
	 * @param int $shareId
731
	 * @return mixed
732
	 */
733
	protected function fixPathsForShareExceptions(array $affectedUsers, $shareId) {
734
		$queryBuilder = $this->connection->getQueryBuilder();
735
		$queryBuilder->select(['share_with', 'file_target'])
736
			->from('share')
737
			->where($queryBuilder->expr()->eq('parent', $queryBuilder->createParameter('parent')))
738
			->setParameter('parent', (int) $shareId);
739
		$query = $queryBuilder->execute();
740
741
		while ($row = $query->fetch()) {
742
			$affectedUsers[$row['share_with']] = $row['file_target'];
743
		}
744
745
		return $affectedUsers;
746
	}
747
748
	/**
749
	 * Sharing a file or folder via link/public
750
	 *
751
	 * @param int $fileSource File ID that is being shared
752
	 * @param string $itemType File type that is being shared (file or folder)
753
	 * @param string $linkOwner
754
	 * @param bool $isSharing True if sharing, false if unsharing
755
	 */
756
	protected function shareFileOrFolderByLink($fileSource, $itemType, $linkOwner, $isSharing) {
757
		$owner = $this->currentUser->getUID();
758
		if ($isSharing) {
759
			$actionSharer = 'shared_link_self';
760
			$actionOwner = 'reshared_link_by';
761
		} else if ($owner !== $linkOwner) {
762
			// Link expired
763
			$actionSharer = 'link_expired';
764
			$actionOwner = 'link_by_expired';
765
			$owner = $linkOwner;
766
			\OC::$server->getUserFolder($linkOwner);
767
		} else {
768
			$actionSharer = 'unshared_link_self';
769
			$actionOwner = 'unshared_link_by';
770
		}
771
772
		$this->view->chroot('/' . $owner . '/files');
773
774
		try {
775
			$path = $this->view->getPath($fileSource);
776
		} catch (NotFoundException $e) {
0 ignored issues
show
Bug introduced by
The class OCP\Files\NotFoundException does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
777
			return;
778
		}
779
780
		$this->shareNotificationForOriginalOwners($owner, $actionOwner, '', $fileSource, $itemType);
781
782
		$this->addNotificationsForUser(
783
			$owner, $actionSharer, [[$fileSource => $path]],
784
			(int) $fileSource, $path, ($itemType === 'file'),
785
			$this->userSettings->getUserSetting($owner, 'stream', Files_Sharing::TYPE_SHARED),
0 ignored issues
show
Bug introduced by
It seems like $this->userSettings->get...s_Sharing::TYPE_SHARED) targeting OCA\Activity\UserSettings::getUserSetting() can also be of type integer; however, OCA\Activity\FilesHooks::addNotificationsForUser() does only seem to accept boolean, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
786
			$this->userSettings->getUserSetting($owner, 'email', Files_Sharing::TYPE_SHARED) ? $this->userSettings->getUserSetting($owner, 'setting', 'batchtime') : 0
0 ignored issues
show
Bug introduced by
It seems like $this->userSettings->get...ting', 'batchtime') : 0 can also be of type boolean; however, OCA\Activity\FilesHooks::addNotificationsForUser() does only seem to accept integer, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
787
		);
788
	}
789
790
	/**
791
	 * Add notifications for the user that shares a file/folder
792
	 *
793
	 * @param string $subject
794
	 * @param string $shareWith
795
	 * @param int $fileSource
796
	 * @param string $itemType
797
	 */
798
	protected function shareNotificationForSharer($subject, $shareWith, $fileSource, $itemType) {
799
		$sharer = $this->currentUser->getUID();
800
		if ($sharer === null) {
801
			return;
802
		}
803
804
		$this->view->chroot('/' . $sharer . '/files');
805
806
		try {
807
			$path = $this->view->getPath($fileSource);
808
		} catch (NotFoundException $e) {
0 ignored issues
show
Bug introduced by
The class OCP\Files\NotFoundException does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
809
			return;
810
		}
811
812
		$this->addNotificationsForUser(
813
			$sharer, $subject, [[$fileSource => $path], $shareWith],
814
			$fileSource, $path, ($itemType === 'file'),
815
			$this->userSettings->getUserSetting($sharer, 'stream', Files_Sharing::TYPE_SHARED),
0 ignored issues
show
Bug introduced by
It seems like $this->userSettings->get...s_Sharing::TYPE_SHARED) targeting OCA\Activity\UserSettings::getUserSetting() can also be of type integer; however, OCA\Activity\FilesHooks::addNotificationsForUser() does only seem to accept boolean, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
816
			$this->userSettings->getUserSetting($sharer, 'email', Files_Sharing::TYPE_SHARED) ? $this->userSettings->getUserSetting($sharer, 'setting', 'batchtime') : 0
0 ignored issues
show
Bug introduced by
It seems like $this->userSettings->get...ting', 'batchtime') : 0 can also be of type boolean; however, OCA\Activity\FilesHooks::addNotificationsForUser() does only seem to accept integer, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
817
		);
818
	}
819
820
	/**
821
	 * Add notifications for the user that shares a file/folder
822
	 *
823
	 * @param string $owner
824
	 * @param string $subject
825
	 * @param string $shareWith
826
	 * @param int $fileSource
827
	 * @param string $itemType
828
	 */
829
	protected function reshareNotificationForSharer($owner, $subject, $shareWith, $fileSource, $itemType) {
830
		$this->view->chroot('/' . $owner . '/files');
831
832
		try {
833
			$path = $this->view->getPath($fileSource);
834
		} catch (NotFoundException $e) {
0 ignored issues
show
Bug introduced by
The class OCP\Files\NotFoundException does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
835
			return;
836
		}
837
838
		$this->addNotificationsForUser(
839
			$owner, $subject, [[$fileSource => $path], $this->currentUser->getUserIdentifier(), $shareWith],
840
			$fileSource, $path, ($itemType === 'file'),
841
			$this->userSettings->getUserSetting($owner, 'stream', Files_Sharing::TYPE_SHARED),
0 ignored issues
show
Bug introduced by
It seems like $this->userSettings->get...s_Sharing::TYPE_SHARED) targeting OCA\Activity\UserSettings::getUserSetting() can also be of type integer; however, OCA\Activity\FilesHooks::addNotificationsForUser() does only seem to accept boolean, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
842
			$this->userSettings->getUserSetting($owner, 'email', Files_Sharing::TYPE_SHARED) ? $this->userSettings->getUserSetting($owner, 'setting', 'batchtime') : 0
0 ignored issues
show
Bug introduced by
It seems like $this->userSettings->get...ting', 'batchtime') : 0 can also be of type boolean; however, OCA\Activity\FilesHooks::addNotificationsForUser() does only seem to accept integer, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
843
		);
844
	}
845
846
	/**
847
	 * Add notifications for the owners whose files have been reshared
848
	 *
849
	 * @param string $currentOwner
850
	 * @param string $subject
851
	 * @param string $shareWith
852
	 * @param int $fileSource
853
	 * @param string $itemType
854
	 */
855
	protected function shareNotificationForOriginalOwners($currentOwner, $subject, $shareWith, $fileSource, $itemType) {
856
		// Get the full path of the current user
857
		$this->view->chroot('/' . $currentOwner . '/files');
858
859
		try {
860
			$path = $this->view->getPath($fileSource);
861
		} catch (NotFoundException $e) {
0 ignored issues
show
Bug introduced by
The class OCP\Files\NotFoundException does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
862
			return;
863
		}
864
865
		/**
866
		 * Get the original owner and his path
867
		 */
868
		$owner = $this->view->getOwner($path);
869
		if ($owner !== $currentOwner) {
870
			$this->reshareNotificationForSharer($owner, $subject, $shareWith, $fileSource, $itemType);
871
		}
872
873
		/**
874
		 * Get the sharee who shared the item with the currentUser
875
		 */
876
		$this->view->chroot('/' . $currentOwner . '/files');
877
		$mount = $this->view->getMount($path);
878
		if (!($mount instanceof IMountPoint)) {
0 ignored issues
show
Bug introduced by
The class OCP\Files\Mount\IMountPoint does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
879
			return;
880
		}
881
882
		$storage = $mount->getStorage();
883
		if (!$storage->instanceOfStorage('OCA\Files_Sharing\SharedStorage')) {
884
			return;
885
		}
886
887
		/** @var \OCA\Files_Sharing\SharedStorage $storage */
888
		$shareOwner = $storage->getSharedFrom();
889
		if ($shareOwner === '' || $shareOwner === null || $shareOwner === $owner || $shareOwner === $currentOwner) {
890
			return;
891
		}
892
893
		$this->reshareNotificationForSharer($shareOwner, $subject, $shareWith, $fileSource, $itemType);
894
	}
895
896
	/**
897
	 * Adds the activity and email for a user when the settings require it
898
	 *
899
	 * @param string $user
900
	 * @param string $subject
901
	 * @param array $subjectParams
902
	 * @param int $fileId
903
	 * @param string $path
904
	 * @param bool $isFile If the item is a file, we link to the parent directory
905
	 * @param bool $streamSetting
906
	 * @param int $emailSetting
907
	 * @param string $type
908
	 */
909
	protected function addNotificationsForUser($user, $subject, $subjectParams, $fileId, $path, $isFile, $streamSetting, $emailSetting, $type = Files_Sharing::TYPE_SHARED) {
910
		if (!$streamSetting && !$emailSetting) {
911
			return;
912
		}
913
914
		$selfAction = $user === $this->currentUser->getUID();
915
		$app = $type === Files_Sharing::TYPE_SHARED ? 'files_sharing' : 'files';
916
		$link = $this->urlGenerator->linkToRouteAbsolute('files.view.index', array(
917
			'dir' => ($isFile) ? dirname($path) : $path,
918
		));
919
920
		$objectType = ($fileId) ? 'files' : '';
921
922
		$event = $this->manager->generateEvent();
923
		$event->setApp($app)
924
			->setType($type)
925
			->setAffectedUser($user)
926
			->setAuthor($this->currentUser->getUID())
927
			->setTimestamp(time())
928
			->setSubject($subject, $subjectParams)
929
			->setObject($objectType, $fileId, $path)
930
			->setLink($link);
931
932
		// Add activity to stream
933
		if ($streamSetting && (!$selfAction || $this->userSettings->getUserSetting($this->currentUser->getUID(), 'setting', 'self'))) {
934
			$this->activityData->send($event);
935
		}
936
937
		// Add activity to mail queue
938
		if ($emailSetting && (!$selfAction || $this->userSettings->getUserSetting($this->currentUser->getUID(), 'setting', 'selfemail'))) {
939
			$latestSend = time() + $emailSetting;
940
			$this->activityData->storeMail($event, $latestSend);
941
		}
942
	}
943
}
944