Completed
Pull Request — master (#153)
by Maxence
02:45
created

FilesHooks::addNotificationsForUser()   C

Complexity

Conditions 14
Paths 81

Size

Total Lines 42
Code Lines 26

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 24
CRAP Score 14.0891

Importance

Changes 0
Metric Value
dl 0
loc 42
ccs 24
cts 26
cp 0.9231
rs 5.0864
c 0
b 0
f 0
cc 14
eloc 26
nc 81
nop 9
crap 14.0891

How to fix   Complexity    Many Parameters   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

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 OCA\Activity\BackgroundJob\RemoteActivity;
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\ILogger;
41
use OCP\IURLGenerator;
42
use OCP\IUser;
43
use OCP\Share;
44
use OCP\Share\IShare;
45
use OCP\Share\IShareHelper;
46
47
/**
48
 * The class to handle the filesystem hooks
49
 */
50
class FilesHooks {
51
	const USER_BATCH_SIZE = 50;
52
53
	/** @var \OCP\Activity\IManager */
54
	protected $manager;
55
56
	/** @var \OCA\Activity\Data */
57
	protected $activityData;
58
59
	/** @var \OCA\Activity\UserSettings */
60
	protected $userSettings;
61
62
	/** @var \OCP\IGroupManager */
63
	protected $groupManager;
64
65
	/** @var \OCP\IDBConnection */
66
	protected $connection;
67
68
	/** @var \OC\Files\View */
69
	protected $view;
70
71
	/** @var IRootFolder */
72
	protected $rootFolder;
73
74
	/** @var IShareHelper */
75
	protected $shareHelper;
76
77
	/** @var IURLGenerator */
78
	protected $urlGenerator;
79
80
	/** @var ILogger */
81
	protected $logger;
82
83
	/** @var CurrentUser */
84
	protected $currentUser;
85
86
	/** @var string|bool */
87
	protected $moveCase = false;
88
	/** @var array */
89
	protected $oldAccessList;
90
	/** @var string */
91
	protected $oldParentPath;
92
	/** @var string */
93
	protected $oldParentOwner;
94
	/** @var string */
95
	protected $oldParentId;
96
97
	/**
98
	 * Constructor
99
	 *
100
	 * @param IManager $manager
101
	 * @param Data $activityData
102
	 * @param UserSettings $userSettings
103
	 * @param IGroupManager $groupManager
104
	 * @param View $view
105
	 * @param IRootFolder $rootFolder
106
	 * @param IShareHelper $shareHelper
107
	 * @param IDBConnection $connection
108
	 * @param IURLGenerator $urlGenerator
109
	 * @param ILogger $logger
110
	 * @param CurrentUser $currentUser
111
	 */
112 49
	public function __construct(IManager $manager,
113
								Data $activityData,
114
								UserSettings $userSettings,
115
								IGroupManager $groupManager,
116
								View $view,
117
								IRootFolder $rootFolder,
118
								IShareHelper $shareHelper,
119
								IDBConnection $connection,
120
								IURLGenerator $urlGenerator,
121
								ILogger $logger,
122
								CurrentUser $currentUser) {
123 49
		$this->manager = $manager;
124 49
		$this->activityData = $activityData;
125 49
		$this->userSettings = $userSettings;
126 49
		$this->groupManager = $groupManager;
127 49
		$this->view = $view;
128 49
		$this->rootFolder = $rootFolder;
129 49
		$this->shareHelper = $shareHelper;
130 49
		$this->connection = $connection;
131 49
		$this->urlGenerator = $urlGenerator;
132 49
		$this->logger = $logger;
133 49
		$this->currentUser = $currentUser;
134 49
	}
135
136
	/**
137
	 * Store the create hook events
138
	 * @param string $path Path of the file that has been created
139
	 */
140 4
	public function fileCreate($path) {
141 4
		if ($path === '/') {
142 2
			return;
143
		}
144
145 2
		if ($this->currentUser->getUserIdentifier() !== '') {
146 1
			$this->addNotificationsForFileAction($path, Files::TYPE_SHARE_CREATED, 'created_self', 'created_by');
147
		} else {
148 1
			$this->addNotificationsForFileAction($path, Files::TYPE_SHARE_CREATED, '', 'created_public');
149
		}
150 2
	}
151
152
	/**
153
	 * Store the update hook events
154
	 * @param string $path Path of the file that has been modified
155
	 */
156 1
	public function fileUpdate($path) {
157 1
		$this->addNotificationsForFileAction($path, Files::TYPE_SHARE_CHANGED, 'changed_self', 'changed_by');
158 1
	}
159
160
	/**
161
	 * Store the delete hook events
162
	 * @param string $path Path of the file that has been deleted
163
	 */
164 1
	public function fileDelete($path) {
165 1
		$this->addNotificationsForFileAction($path, Files::TYPE_SHARE_DELETED, 'deleted_self', 'deleted_by');
166 1
	}
167
168
	/**
169
	 * Store the restore hook events
170
	 * @param string $path Path of the file that has been restored
171
	 */
172 1
	public function fileRestore($path) {
173 1
		$this->addNotificationsForFileAction($path, Files::TYPE_SHARE_RESTORED, 'restored_self', 'restored_by');
174 1
	}
175
176
	/**
177
	 * Creates the entries for file actions on $file_path
178
	 *
179
	 * @param string $filePath         The file that is being changed
180
	 * @param int    $activityType     The activity type
181
	 * @param string $subject          The subject for the actor
182
	 * @param string $subjectBy        The subject for other users (with "by $actor")
183
	 */
184 3
	protected function addNotificationsForFileAction($filePath, $activityType, $subject, $subjectBy) {
185
		// Do not add activities for .part-files
186 3
		if (substr($filePath, -5) === '.part') {
187 1
			return;
188
		}
189
190 2
		list($filePath, $uidOwner, $fileId) = $this->getSourcePathAndOwner($filePath);
191 2
		if ($fileId === 0) {
192
			// Could not find the file for the owner ...
193
			return;
194
		}
195
196 2
		$accessList = $this->getUserPathsFromPath($filePath, $uidOwner);
197
198 2
		$this->generateRemoteActivity($accessList['remotes'], $activityType, time(), $this->currentUser->getCloudId(), $accessList['ownerPath']);
199
200 2
		$affectedUsers = $accessList['users'];
201 2
		$filteredStreamUsers = $this->userSettings->filterUsersBySetting(array_keys($affectedUsers), 'stream', $activityType);
202 2
		$filteredEmailUsers = $this->userSettings->filterUsersBySetting(array_keys($affectedUsers), 'email', $activityType);
203
204 2
		foreach ($affectedUsers as $user => $path) {
205 2
			$user = (string) $user;
206 2
			if (empty($filteredStreamUsers[$user]) && empty($filteredEmailUsers[$user])) {
207 2
				continue;
208
			}
209
210 2
			if ($user === $this->currentUser->getUID()) {
211 1
				$userSubject = $subject;
212 1
				$userParams = [[$fileId => $path]];
213
			} else {
214 1
				$userSubject = $subjectBy;
215 1
				$userParams = [[$fileId => $path], $this->currentUser->getUserIdentifier()];
216
			}
217
218 2
			$this->addNotificationsForUser(
219
				$user, $userSubject, $userParams,
220 2
				$fileId, $path, true,
221 2
				!empty($filteredStreamUsers[$user]),
222 2
				!empty($filteredEmailUsers[$user]) ? $filteredEmailUsers[$user] : 0,
223 2
				$activityType
224
			);
225
		}
226 2
	}
227
228 2
	protected function generateRemoteActivity(array $remoteUsers, $type, $time, $actor, $ownerPath = false) {
229 2
		foreach ($remoteUsers as $remoteUser => $info) {
230
			if ($actor === $remoteUser) {
231
				// Current user receives the notification on their own instance already
232
				continue;
233
			}
234
235
			$arguments = [
236
				$remoteUser,
237
				$info['token'],
238
				$ownerPath !== false ? substr($ownerPath, strlen($info['node_path'])) : $info['node_path'],
239
				$type,
240
				$time,
241
				$actor,
242
			];
243
244
			if (isset($info['second_path'])) {
245
				$arguments[] = $info['second_path'];
246
			}
247
248
			\OC::$server->getJobList()->add(RemoteActivity::class, $arguments);
249
		}
250 2
	}
251
252
	/**
253
	 * Collect some information for move/renames
254
	 *
255
	 * @param string $oldPath Path of the file that has been moved
256
	 * @param string $newPath Path of the file that has been moved
257
	 */
258
	public function fileMove($oldPath, $newPath) {
259
		if (substr($oldPath, -5) === '.part' || substr($newPath, -5) === '.part') {
260
			// Do not add activities for .part-files
261
			$this->moveCase = false;
262
			return;
263
		}
264
265
		$oldDir = dirname($oldPath);
266
		$newDir = dirname($newPath);
267
268
		if ($oldDir === $newDir) {
269
			/**
270
			 * a/b moved to a/c
271
			 *
272
			 * Cases:
273
			 * - a/b shared: no visible change
274
			 * - a/ shared: rename
275
			 */
276
			$this->moveCase = 'rename';
277
			return;
278
		}
279
280
		if (strpos($oldDir, $newDir) === 0) {
281
			/**
282
			 * a/b/c moved to a/c
283
			 *
284
			 * Cases:
285
			 * - a/b/c shared: no visible change
286
			 * - a/b/ shared: delete
287
			 * - a/ shared: move/rename
288
			 */
289
			$this->moveCase = 'moveUp';
290
		} else if (strpos($newDir, $oldDir) === 0) {
291
			/**
292
			 * a/b moved to a/c/b
293
			 *
294
			 * Cases:
295
			 * - a/b shared: no visible change
296
			 * - a/c/ shared: add
297
			 * - a/ shared: move/rename
298
			 */
299
			$this->moveCase = 'moveDown';
300
		} else {
301
			/**
302
			 * a/b/c moved to a/d/c
303
			 *
304
			 * Cases:
305
			 * - a/b/c shared: no visible change
306
			 * - a/b/ shared: delete
307
			 * - a/d/ shared: add
308
			 * - a/ shared: move/rename
309
			 */
310
			$this->moveCase = 'moveCross';
311
		}
312
313
		list($this->oldParentPath, $this->oldParentOwner, $this->oldParentId) = $this->getSourcePathAndOwner($oldDir);
314
		if ($this->oldParentId === 0) {
315
			// Could not find the file for the owner ...
316
			$this->moveCase = false;
317
			return;
318
		}
319
		$this->oldAccessList = $this->getUserPathsFromPath($this->oldParentPath, $this->oldParentOwner);
320
	}
321
322
323
	/**
324
	 * Store the move hook events
325
	 *
326
	 * @param string $oldPath Path of the file that has been moved
327
	 * @param string $newPath Path of the file that has been moved
328
	 */
329
	public function fileMovePost($oldPath, $newPath) {
330
		// Do not add activities for .part-files
331
		if ($this->moveCase === false) {
332
			return;
333
		}
334
335
		switch ($this->moveCase) {
336
			case 'rename':
337
				$this->fileRenaming($oldPath, $newPath);
338
				break;
339
			case 'moveUp':
340
			case 'moveDown':
341
			case 'moveCross':
342
				$this->fileMoving($oldPath, $newPath);
343
				break;
344
		}
345
346
		$this->moveCase = false;
347
	}
348
349
350
	/**
351
	 * Renaming a file inside the same folder (a/b to a/c)
352
	 *
353
	 * @param string $oldPath
354
	 * @param string $newPath
355
	 */
356
	protected function fileRenaming($oldPath, $newPath) {
357
		$dirName = dirname($newPath);
358
		$fileName = basename($newPath);
359
		$oldFileName = basename($oldPath);
360
361
		list(, , $fileId) = $this->getSourcePathAndOwner($newPath);
362
		list($parentPath, $parentOwner, $parentId) = $this->getSourcePathAndOwner($dirName);
363
		if ($fileId === 0 || $parentId === 0) {
364
			// Could not find the file for the owner ...
365
			return;
366
		}
367
		$accessList = $this->getUserPathsFromPath($parentPath, $parentOwner);
368
369
		$renameRemotes = [];
370
		foreach ($accessList['remotes'] as $remote => $info) {
371
			$renameRemotes[$remote] = [
372
				'token'       => $info['token'],
373
				'node_path'   => substr($newPath, strlen($info['node_path'])),
374
				'second_path' => substr($oldPath, strlen($info['node_path'])),
375
			];
376
		}
377
		$this->generateRemoteActivity($renameRemotes, Files::TYPE_SHARE_CHANGED, time(), $this->currentUser->getCloudId());
378
379
		$affectedUsers = $accessList['users'];
380
		$filteredStreamUsers = $this->userSettings->filterUsersBySetting(array_keys($affectedUsers), 'stream', Files::TYPE_SHARE_CHANGED);
381
		$filteredEmailUsers = $this->userSettings->filterUsersBySetting(array_keys($affectedUsers), 'email', Files::TYPE_SHARE_CHANGED);
382
383
		foreach ($affectedUsers as $user => $path) {
384
			if (empty($filteredStreamUsers[$user]) && empty($filteredEmailUsers[$user])) {
385
				continue;
386
			}
387
388
			if ($user === $this->currentUser->getUID()) {
389
				$userSubject = 'renamed_self';
390
				$userParams = [
391
					[$fileId => $path . '/' . $fileName],
392
					[$fileId => $path . '/' . $oldFileName],
393
				];
394
			} else {
395
				$userSubject = 'renamed_by';
396
				$userParams = [
397
					[$fileId => $path . '/' . $fileName],
398
					$this->currentUser->getUserIdentifier(),
399
					[$fileId => $path . '/' . $oldFileName],
400
				];
401
			}
402
403
			$this->addNotificationsForUser(
404
				$user, $userSubject, $userParams,
405
				$fileId, $path . '/' . $fileName, true,
406
				!empty($filteredStreamUsers[$user]),
407
				!empty($filteredEmailUsers[$user]) ? $filteredEmailUsers[$user] : 0,
408
				Files::TYPE_SHARE_CHANGED
409
			);
410
		}
411
	}
412
413
	/**
414
	 * Moving a file from one folder to another
415
	 *
416
	 * @param string $oldPath
417
	 * @param string $newPath
418
	 */
419
	protected function fileMoving($oldPath, $newPath) {
420
		$dirName = dirname($newPath);
421
		$fileName = basename($newPath);
422
		$oldFileName = basename($oldPath);
423
424
		list(, , $fileId) = $this->getSourcePathAndOwner($newPath);
425
		list($parentPath, $parentOwner, $parentId) = $this->getSourcePathAndOwner($dirName);
426
		if ($fileId === 0 || $parentId === 0) {
427
			// Could not find the file for the owner ...
428
			return;
429
		}
430
		$accessList = $this->getUserPathsFromPath($parentPath, $parentOwner);
431
		$affectedUsers = $accessList['users'];
432
		$oldUsers = $this->oldAccessList['users'];
433
434
		$beforeUsers = array_keys($oldUsers);
435
		$afterUsers = array_keys($affectedUsers);
436
437
		$deleteUsers = array_diff($beforeUsers, $afterUsers);
438
		$this->generateDeleteActivities($deleteUsers, $oldUsers, $fileId, $oldFileName);
439
440
		$addUsers = array_diff($afterUsers, $beforeUsers);
441
		$this->generateAddActivities($addUsers, $affectedUsers, $fileId, $fileName);
442
443
		$moveUsers = array_intersect($beforeUsers, $afterUsers);
444
		$this->generateMoveActivities($moveUsers, $oldUsers, $affectedUsers, $fileId, $oldFileName, $parentId, $fileName);
445
446
		$beforeRemotes = $this->oldAccessList['remotes'];
447
		$afterRemotes = $accessList['remotes'];
448
449
		$addRemotes = $deleteRemotes = $moveRemotes = [];
450
		foreach ($afterRemotes as $remote => $info) {
451
			if (isset($beforeRemotes[$remote])) {
452
				// Move
453
				$info['node_path'] = substr($newPath, strlen($info['node_path']));
454
				$info['second_path'] = substr($oldPath, strlen($beforeRemotes[$remote]['node_path']));
455
				$moveRemotes[$remote] = $info;
456
			} else {
457
				$info['node_path'] = substr($newPath, strlen($info['node_path']));
458
				$addRemotes[$remote] = $info;
459
			}
460
		}
461
462
		foreach ($beforeRemotes as $remote => $info) {
463
			if (!isset($afterRemotes[$remote])) {
464
				$info['node_path'] = substr($oldPath, strlen($info['node_path']));
465
				$deleteRemotes[$remote] = $info;
466
			}
467
		}
468
469
		$this->generateRemoteActivity($deleteRemotes, Files::TYPE_SHARE_DELETED, time(), $this->currentUser->getCloudId());
470
		$this->generateRemoteActivity($addRemotes, Files::TYPE_SHARE_CREATED, time(), $this->currentUser->getCloudId());
471
		$this->generateRemoteActivity($moveRemotes, Files::TYPE_SHARE_CHANGED, time(), $this->currentUser->getCloudId());
472
	}
473
474
	/**
475
	 * @param string[] $users
476
	 * @param string[] $pathMap
477
	 * @param int $fileId
478
	 * @param string $oldFileName
479
	 */
480 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...
481
		if (empty($users)) {
482
			return;
483
		}
484
485
		$filteredStreamUsers = $this->userSettings->filterUsersBySetting($users, 'stream', Files::TYPE_SHARE_DELETED);
486
		$filteredEmailUsers = $this->userSettings->filterUsersBySetting($users, 'email', Files::TYPE_SHARE_DELETED);
487
488
		foreach ($users as $user) {
489
			if (empty($filteredStreamUsers[$user]) && empty($filteredEmailUsers[$user])) {
490
				continue;
491
			}
492
493
			$path = $pathMap[$user];
494
495
			if ($user === $this->currentUser->getUID()) {
496
				$userSubject = 'deleted_self';
497
				$userParams = [[$fileId => $path . '/' . $oldFileName]];
498
			} else {
499
				$userSubject = 'deleted_by';
500
				$userParams = [[$fileId => $path . '/' . $oldFileName], $this->currentUser->getUserIdentifier()];
501
			}
502
503
			$this->addNotificationsForUser(
504
				$user, $userSubject, $userParams,
505
				$fileId, $path . '/' . $oldFileName, true,
506
				!empty($filteredStreamUsers[$user]),
507
				!empty($filteredEmailUsers[$user]) ? $filteredEmailUsers[$user] : 0,
508
				Files::TYPE_SHARE_DELETED
509
			);
510
		}
511
	}
512
513
	/**
514
	 * @param string[] $users
515
	 * @param string[] $pathMap
516
	 * @param int $fileId
517
	 * @param string $fileName
518
	 */
519 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...
520
		if (empty($users)) {
521
			return;
522
		}
523
524
		$filteredStreamUsers = $this->userSettings->filterUsersBySetting($users, 'stream', Files::TYPE_SHARE_CREATED);
525
		$filteredEmailUsers = $this->userSettings->filterUsersBySetting($users, 'email', Files::TYPE_SHARE_CREATED);
526
527
		foreach ($users as $user) {
528
			if (empty($filteredStreamUsers[$user]) && empty($filteredEmailUsers[$user])) {
529
				continue;
530
			}
531
532
			$path = $pathMap[$user];
533
534
			if ($user === $this->currentUser->getUID()) {
535
				$userSubject = 'created_self';
536
				$userParams = [[$fileId => $path . '/' . $fileName]];
537
			} else {
538
				$userSubject = 'created_by';
539
				$userParams = [[$fileId => $path . '/' . $fileName], $this->currentUser->getUserIdentifier()];
540
			}
541
542
			$this->addNotificationsForUser(
543
				$user, $userSubject, $userParams,
544
				$fileId, $path . '/' . $fileName, true,
545
				!empty($filteredStreamUsers[$user]),
546
				!empty($filteredEmailUsers[$user]) ? $filteredEmailUsers[$user] : 0,
547
				Files::TYPE_SHARE_CREATED
548
			);
549
		}
550
	}
551
552
	/**
553
	 * @param string[] $users
554
	 * @param string[] $beforePathMap
555
	 * @param string[] $afterPathMap
556
	 * @param int $fileId
557
	 * @param string $oldFileName
558
	 * @param int $newParentId
559
	 * @param string $fileName
560
	 */
561
	protected function generateMoveActivities($users, $beforePathMap, $afterPathMap, $fileId, $oldFileName, $newParentId, $fileName) {
562
		if (empty($users)) {
563
			return;
564
		}
565
566
		$filteredStreamUsers = $this->userSettings->filterUsersBySetting($users, 'stream', Files::TYPE_SHARE_CHANGED);
567
		$filteredEmailUsers = $this->userSettings->filterUsersBySetting($users, 'email', Files::TYPE_SHARE_CHANGED);
568
569
		foreach ($users as $user) {
570
			if (empty($filteredStreamUsers[$user]) && empty($filteredEmailUsers[$user])) {
571
				continue;
572
			}
573
574
			if ($oldFileName === $fileName) {
575
				$userParams = [[$newParentId => $afterPathMap[$user] . '/']];
576
			} else {
577
				$userParams = [[$fileId => $afterPathMap[$user] . '/' . $fileName]];
578
			}
579
580
			if ($user === $this->currentUser->getUID()) {
581
				$userSubject = 'moved_self';
582
			} else {
583
				$userSubject = 'moved_by';
584
				$userParams[] = $this->currentUser->getUserIdentifier();
585
			}
586
			$userParams[] = [$fileId => $beforePathMap[$user] . '/' . $oldFileName];
587
588
			$this->addNotificationsForUser(
589
				$user, $userSubject, $userParams,
590
				$fileId, $afterPathMap[$user] . '/' . $fileName, true,
591
				!empty($filteredStreamUsers[$user]),
592
				!empty($filteredEmailUsers[$user]) ? $filteredEmailUsers[$user] : 0,
593
				Files::TYPE_SHARE_CHANGED
594
			);
595
		}
596
	}
597
598
	/**
599
	 * Returns a "username => path" map for all affected users
600
	 *
601
	 * @param string $path
602
	 * @param string $uidOwner
603
	 * @return array
604
	 */
605
	protected function getUserPathsFromPath($path, $uidOwner) {
606
		try {
607
			$node = $this->rootFolder->getUserFolder($uidOwner)->get($path);
608
		} 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...
609
			return [];
610
		}
611
612
		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...
613
			return [];
614
		}
615
616
		$accessList = $this->shareHelper->getPathsForAccessList($node);
617
618
		$path = $node->getPath();
619
		$sections = explode('/', $path, 4);
620
		$accessList['ownerPath'] = '/' . $sections[3];
621
622
		return $accessList;
623
	}
624
625
	/**
626
	 * Return the source
627
	 *
628
	 * @param string $path
629
	 * @return array
630
	 */
631
	protected function getSourcePathAndOwner($path) {
632
		$view = Filesystem::getView();
633
		$owner = $view->getOwner($path);
634
		$owner = !is_string($owner) || $owner === '' ? null : $owner;
635
		$fileId = 0;
636
		$currentUser = $this->currentUser->getUID();
637
638
		if ($owner === null || $owner !== $currentUser) {
639
			/** @var \OCP\Files\Storage\IStorage $storage */
640
			list($storage,) = $view->resolvePath($path);
641
642
			if ($owner !== null && !$storage->instanceOfStorage('OCA\Files_Sharing\External\Storage')) {
643
				Filesystem::initMountPoints($owner);
644
			} else {
645
				// Probably a remote user, let's try to at least generate activities
646
				// for the current user
647
				if ($currentUser === null) {
648
					list(,$owner,) = explode('/', $view->getAbsolutePath($path), 3);
649
				} else {
650
					$owner = $currentUser;
651
				}
652
			}
653
		}
654
655
		$info = Filesystem::getFileInfo($path);
656
		if ($info !== false) {
657
			$ownerView = new View('/' . $owner . '/files');
658
			$fileId = (int) $info['fileid'];
659
			$path = $ownerView->getPath($fileId);
660
		}
661
662
		return array($path, $owner, $fileId);
663
	}
664
665
	/**
666
	 * Manage sharing events
667
	 * @param array $params The hook params
668
	 */
669 3
	public function share($params) {
670 3
		if ($params['itemType'] === 'file' || $params['itemType'] === 'folder') {
671 3
			if ((int) $params['shareType'] === Share::SHARE_TYPE_USER) {
672 1
				$this->shareWithUser($params['shareWith'], (int) $params['fileSource'], $params['itemType'], $params['fileTarget']);
673 2
			} else if ((int) $params['shareType'] === Share::SHARE_TYPE_GROUP) {
674 1
				$this->shareWithGroup($params['shareWith'], (int) $params['fileSource'], $params['itemType'], $params['fileTarget'], (int) $params['id']);
675 1
			} else if ((int) $params['shareType'] === Share::SHARE_TYPE_LINK) {
676 1
				$this->shareByLink((int) $params['fileSource'], $params['itemType'], $params['uidOwner']);
677
			}
678
		}
679 3
	}
680
681
	/**
682
	 * Sharing a file or folder with a user
683
	 *
684
	 * @param string $shareWith
685
	 * @param int $fileSource File ID that is being shared
686
	 * @param string $itemType File type that is being shared (file or folder)
687
	 * @param string $fileTarget File path
688
	 */
689 2
	protected function shareWithUser($shareWith, $fileSource, $itemType, $fileTarget) {
690
		// User performing the share
691 2
		$this->shareNotificationForSharer('shared_user_self', $shareWith, $fileSource, $itemType);
692 2 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...
693 2
			$this->shareNotificationForOriginalOwners($this->currentUser->getUID(), 'reshared_user_by', $shareWith, $fileSource, $itemType);
694
		}
695
696
		// New shared user
697 2
		$this->addNotificationsForUser(
698 2
			$shareWith, 'shared_with_by', [[$fileSource => $fileTarget], $this->currentUser->getUserIdentifier()],
699 2
			(int) $fileSource, $fileTarget, $itemType === 'file',
700 2
			$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...
701 2
			$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...
702
		);
703 2
	}
704
705
	/**
706
	 * Sharing a file or folder with a group
707
	 *
708
	 * @param string $shareWith
709
	 * @param int $fileSource File ID that is being shared
710
	 * @param string $itemType File type that is being shared (file or folder)
711
	 * @param string $fileTarget File path
712
	 * @param int $shareId The Share ID of this share
713
	 */
714 6
	protected function shareWithGroup($shareWith, $fileSource, $itemType, $fileTarget, $shareId) {
715
		// Members of the new group
716 6
		$group = $this->groupManager->get($shareWith);
717 6
		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...
718 1
			return;
719
		}
720
721
		// User performing the share
722 5
		$this->shareNotificationForSharer('shared_group_self', $shareWith, $fileSource, $itemType);
723 5 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...
724 5
			$this->shareNotificationForOriginalOwners($this->currentUser->getUID(), 'reshared_group_by', $shareWith, $fileSource, $itemType);
725
		}
726
727 5
		$offset = 0;
728 5
		$users = $group->searchUsers('', self::USER_BATCH_SIZE, $offset);
729 5
		while (!empty($users)) {
730 4
			$this->addNotificationsForGroupUsers($users, 'shared_with_by', $fileSource, $itemType, $fileTarget, $shareId);
731 4
			$offset += self::USER_BATCH_SIZE;
732 4
			$users = $group->searchUsers('', self::USER_BATCH_SIZE, $offset);
733
		}
734 5
	}
735
736
	/**
737
	 * Sharing a file or folder via link/public
738
	 *
739
	 * @param int $fileSource File ID that is being shared
740
	 * @param string $itemType File type that is being shared (file or folder)
741
	 * @param string $linkOwner
742
	 */
743 2
	protected function shareByLink($fileSource, $itemType, $linkOwner) {
744 2
		$this->view->chroot('/' . $linkOwner . '/files');
745
746
		try {
747 2
			$path = $this->view->getPath($fileSource);
748 1
		} 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...
749 1
			return;
750
		}
751
752 1
		$this->shareNotificationForOriginalOwners($linkOwner, 'reshared_link_by', '', $fileSource, $itemType);
753
754 1
		$this->addNotificationsForUser(
755 1
			$linkOwner, 'shared_link_self', [[$fileSource => $path]],
756 1
			(int) $fileSource, $path, $itemType === 'file',
757 1
			$this->userSettings->getUserSetting($linkOwner, '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...
758 1
			$this->userSettings->getUserSetting($linkOwner, 'email', Files_Sharing::TYPE_SHARED) ? $this->userSettings->getUserSetting($linkOwner, '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...
759
		);
760 1
	}
761
762
	/**
763
	 * Manage unsharing events
764
	 * @param IShare $share
765
	 * @throws \OCP\Files\NotFoundException
766
	 */
767
	public function unShare(IShare $share) {
768
		if (in_array($share->getNodeType(), ['file', 'folder'], true)) {
769
			if ($share->getShareType() === Share::SHARE_TYPE_USER) {
770
				$this->unshareFromUser($share);
771
			} else if ($share->getShareType() === Share::SHARE_TYPE_GROUP) {
772
				$this->unshareFromGroup($share);
773
			} else if ($share->getShareType() === Share::SHARE_TYPE_LINK) {
774
				$this->unshareLink($share);
775
			}
776
		}
777
	}
778
779
	/**
780
	 * Unharing a file or folder from a user
781
	 *
782
	 * @param IShare $share
783
	 * @throws \OCP\Files\NotFoundException
784
	 */
785
	protected function unshareFromUser(IShare $share) {
786
		// User performing the share
787
		$this->shareNotificationForSharer('unshared_user_self', $share->getSharedWith(), $share->getNodeId(), $share->getNodeType());
788
789
		// Owner
790 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...
791
			$this->shareNotificationForOriginalOwners($this->currentUser->getUID(), 'unshared_user_by', $share->getSharedWith(), $share->getNodeId(), $share->getNodeType());
792
		}
793
794
		// Recipient
795
		$this->addNotificationsForUser(
796
			$share->getSharedWith(), 'unshared_by', [[$share->getNodeId() => $share->getTarget()], $this->currentUser->getUserIdentifier()],
797
			$share->getNodeId(), $share->getTarget(), $share->getNodeType() === 'file',
798
			$this->userSettings->getUserSetting($share->getSharedWith(), '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...
799
			$this->userSettings->getUserSetting($share->getSharedWith(), 'email', Files_Sharing::TYPE_SHARED) ? $this->userSettings->getUserSetting($share->getSharedWith(), '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...
800
		);
801
	}
802
803
	/**
804
	 * Unsharing a file or folder from a group
805
	 *
806
	 * @param IShare $share
807
	 * @throws \OCP\Files\NotFoundException
808
	 */
809
	protected function unshareFromGroup(IShare $share) {
810
		// Members of the new group
811
		$group = $this->groupManager->get($share->getSharedWith());
812
		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...
813
			return;
814
		}
815
816
		// User performing the share
817
		$this->shareNotificationForSharer('unshared_group_self', $share->getSharedWith(), $share->getNodeId(), $share->getNodeType());
818 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...
819
			$this->shareNotificationForOriginalOwners($this->currentUser->getUID(), 'unshared_group_by', $share->getSharedWith(), $share->getNodeId(), $share->getNodeType());
820
		}
821
822
		$offset = 0;
823
		$users = $group->searchUsers('', self::USER_BATCH_SIZE, $offset);
824
		while (!empty($users)) {
825
			$this->addNotificationsForGroupUsers($users, 'unshared_by', $share->getNodeId(), $share->getNodeType(), $share->getTarget(), $share->getId());
826
			$offset += self::USER_BATCH_SIZE;
827
			$users = $group->searchUsers('', self::USER_BATCH_SIZE, $offset);
828
		}
829
	}
830
831
	/**
832
	 * Sharing a file or folder via link/public
833
	 *
834
	 * @param IShare $share
835
	 * @throws \OCP\Files\NotFoundException
836
	 */
837
	protected function unshareLink(IShare $share) {
838
		$owner = $share->getSharedBy();
839
		if ($this->currentUser->getUID() === null) {
840
			// Link expired
841
			$actionSharer = 'link_expired';
842
			$actionOwner = 'link_by_expired';
843
		} else {
844
			$actionSharer = 'unshared_link_self';
845
			$actionOwner = 'unshared_link_by';
846
		}
847
848
		$this->addNotificationsForUser(
849
			$owner, $actionSharer, [[$share->getNodeId() => $share->getTarget()]],
850
			$share->getNodeId(), $share->getTarget(), $share->getNodeType() === 'file',
851
			$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...
852
			$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...
853
		);
854
855
		if ($share->getSharedBy() !== $share->getShareOwner()) {
856
			$owner = $share->getShareOwner();
857
			$this->addNotificationsForUser(
858
				$owner, $actionOwner, [[$share->getNodeId() => $share->getTarget()], $share->getSharedBy()],
859
				$share->getNodeId(), $share->getTarget(), $share->getNodeType() === 'file',
860
				$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...
861
				$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...
862
			);
863
		}
864
	}
865
866
	/**
867
	 * @param IUser[] $usersInGroup
868
	 * @param string $actionUser
869
	 * @param int $fileSource File ID that is being shared
870
	 * @param string $itemType File type that is being shared (file or folder)
871
	 * @param string $fileTarget File path
872
	 * @param int $shareId The Share ID of this share
873
	 */
874 4
	protected function addNotificationsForGroupUsers(array $usersInGroup, $actionUser, $fileSource, $itemType, $fileTarget, $shareId) {
875 4
		$affectedUsers = [];
876
877 4
		foreach ($usersInGroup as $user) {
878 4
			$affectedUsers[$user->getUID()] = $fileTarget;
879
		}
880
881
		// Remove the triggering user, we already managed his notifications
882 4
		unset($affectedUsers[$this->currentUser->getUID()]);
883
884 4
		if (empty($affectedUsers)) {
885 1
			return;
886
		}
887
888 3
		$userIds = array_keys($affectedUsers);
889 3
		$filteredStreamUsersInGroup = $this->userSettings->filterUsersBySetting($userIds, 'stream', Files_Sharing::TYPE_SHARED);
890 3
		$filteredEmailUsersInGroup = $this->userSettings->filterUsersBySetting($userIds, 'email', Files_Sharing::TYPE_SHARED);
891
892 3
		$affectedUsers = $this->fixPathsForShareExceptions($affectedUsers, $shareId);
893 3
		foreach ($affectedUsers as $user => $path) {
894 3
			if (empty($filteredStreamUsersInGroup[$user]) && empty($filteredEmailUsersInGroup[$user])) {
895 2
				continue;
896
			}
897
898 1
			$this->addNotificationsForUser(
899 1
				$user, $actionUser, [[$fileSource => $path], $this->currentUser->getUserIdentifier()],
900 1
				$fileSource, $path, ($itemType === 'file'),
901 1
				!empty($filteredStreamUsersInGroup[$user]),
902 1
				!empty($filteredEmailUsersInGroup[$user]) ? $filteredEmailUsersInGroup[$user] : 0
903
			);
904
		}
905 3
	}
906
907
	/**
908
	 * Check when there was a naming conflict and the target is different
909
	 * for some of the users
910
	 *
911
	 * @param array $affectedUsers
912
	 * @param int $shareId
913
	 * @return mixed
914
	 */
915
	protected function fixPathsForShareExceptions(array $affectedUsers, $shareId) {
916
		$queryBuilder = $this->connection->getQueryBuilder();
917
		$queryBuilder->select(['share_with', 'file_target'])
918
			->from('share')
919
			->where($queryBuilder->expr()->eq('parent', $queryBuilder->createParameter('parent')))
920
			->setParameter('parent', (int) $shareId);
921
		$query = $queryBuilder->execute();
922
923
		while ($row = $query->fetch()) {
924
			$affectedUsers[$row['share_with']] = $row['file_target'];
925
		}
926
927
		return $affectedUsers;
928
	}
929
930
	/**
931
	 * Add notifications for the user that shares a file/folder
932
	 *
933
	 * @param string $subject
934
	 * @param string $shareWith
935
	 * @param int $fileSource
936
	 * @param string $itemType
937
	 */
938 2
	protected function shareNotificationForSharer($subject, $shareWith, $fileSource, $itemType) {
939 2
		$sharer = $this->currentUser->getUID();
940 2
		if ($sharer === null) {
941
			return;
942
		}
943
944 2
		$this->view->chroot('/' . $sharer . '/files');
945
946
		try {
947 2
			$path = $this->view->getPath($fileSource);
948 1
		} 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...
949 1
			return;
950
		}
951
952 1
		$this->addNotificationsForUser(
953 1
			$sharer, $subject, [[$fileSource => $path], $shareWith],
954 1
			$fileSource, $path, ($itemType === 'file'),
955 1
			$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...
956 1
			$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...
957
		);
958 1
	}
959
960
	/**
961
	 * Add notifications for the user that shares a file/folder
962
	 *
963
	 * @param string $owner
964
	 * @param string $subject
965
	 * @param string $shareWith
966
	 * @param int $fileSource
967
	 * @param string $itemType
968
	 */
969 2
	protected function reshareNotificationForSharer($owner, $subject, $shareWith, $fileSource, $itemType) {
970 2
		$this->view->chroot('/' . $owner . '/files');
971
972
		try {
973 2
			$path = $this->view->getPath($fileSource);
974 1
		} 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...
975 1
			return;
976
		}
977
978 1
		$this->addNotificationsForUser(
979 1
			$owner, $subject, [[$fileSource => $path], $this->currentUser->getUserIdentifier(), $shareWith],
980 1
			$fileSource, $path, ($itemType === 'file'),
981 1
			$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...
982 1
			$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...
983
		);
984 1
	}
985
986
	/**
987
	 * Add notifications for the owners whose files have been reshared
988
	 *
989
	 * @param string $currentOwner
990
	 * @param string $subject
991
	 * @param string $shareWith
992
	 * @param int $fileSource
993
	 * @param string $itemType
994
	 */
995 10
	protected function shareNotificationForOriginalOwners($currentOwner, $subject, $shareWith, $fileSource, $itemType) {
996
		// Get the full path of the current user
997 10
		$this->view->chroot('/' . $currentOwner . '/files');
998
999
		try {
1000 10
			$path = $this->view->getPath($fileSource);
1001 1
		} 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...
1002 1
			return;
1003
		}
1004
1005
		/**
1006
		 * Get the original owner and his path
1007
		 */
1008 9
		$owner = $this->view->getOwner($path);
1009 9
		if ($owner !== $currentOwner) {
1010 7
			$this->reshareNotificationForSharer($owner, $subject, $shareWith, $fileSource, $itemType);
1011
		}
1012
1013
		/**
1014
		 * Get the sharee who shared the item with the currentUser
1015
		 */
1016 9
		$this->view->chroot('/' . $currentOwner . '/files');
1017 9
		$mount = $this->view->getMount($path);
1018 9
		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...
1019 1
			return;
1020
		}
1021
1022 8
		$storage = $mount->getStorage();
1023 8
		if (!$storage->instanceOfStorage('OCA\Files_Sharing\SharedStorage')) {
1024 1
			return;
1025
		}
1026
1027
		/** @var \OCA\Files_Sharing\SharedStorage $storage */
1028 7
		$shareOwner = $storage->getSharedFrom();
1029 7
		if ($shareOwner === '' || $shareOwner === null || $shareOwner === $owner || $shareOwner === $currentOwner) {
1030 5
			return;
1031
		}
1032
1033 2
		$this->reshareNotificationForSharer($shareOwner, $subject, $shareWith, $fileSource, $itemType);
1034 2
	}
1035
1036
	/**
1037
	 * Adds the activity and email for a user when the settings require it
1038
	 *
1039
	 * @param string $user
1040
	 * @param string $subject
1041
	 * @param array $subjectParams
1042
	 * @param int $fileId
1043
	 * @param string $path
1044
	 * @param bool $isFile If the item is a file, we link to the parent directory
1045
	 * @param bool $streamSetting
1046
	 * @param int $emailSetting
1047
	 * @param string $type
1048
	 */
1049 11
	protected function addNotificationsForUser($user, $subject, $subjectParams, $fileId, $path, $isFile, $streamSetting, $emailSetting, $type = Files_Sharing::TYPE_SHARED) {
1050 11
		if (!$streamSetting && !$emailSetting) {
1051 1
			return;
1052
		}
1053
1054 10
		$selfAction = $user === $this->currentUser->getUID();
1055 10
		$app = $type === Files_Sharing::TYPE_SHARED ? 'files_sharing' : 'files';
1056 10
		$link = $this->urlGenerator->linkToRouteAbsolute('files.view.index', array(
1057 10
			'dir' => ($isFile) ? dirname($path) : $path,
1058
		));
1059
1060 10
		$objectType = ($fileId) ? 'files' : '';
1061
1062 10
		$event = $this->manager->generateEvent();
1063
		try {
1064 10
			$event->setApp($app)
1065 10
				->setType($type)
1066 10
				->setAffectedUser($user)
1067 10
				->setTimestamp(time())
1068 10
				->setSubject($subject, $subjectParams)
1069 10
				->setObject($objectType, $fileId, $path)
1070 10
				->setLink($link);
1071
1072 10
			if ($this->currentUser->getUID() !== null) {
1073
				// Allow this to be empty for guests
1074 10
				$event->setAuthor($this->currentUser->getUID());
1075
			}
1076
		} catch (\InvalidArgumentException $e) {
1077
			$this->logger->logException($e);
1078
		}
1079
1080
		// Add activity to stream
1081 10
		if ($streamSetting && (!$selfAction || $this->userSettings->getUserSetting($this->currentUser->getUID(), 'setting', 'self'))) {
1082 3
			$this->activityData->send($event);
1083
		}
1084
1085
		// Add activity to mail queue
1086 10
		if ($emailSetting && (!$selfAction || $this->userSettings->getUserSetting($this->currentUser->getUID(), 'setting', 'selfemail'))) {
1087 5
			$latestSend = time() + $emailSetting;
1088 5
			$this->activityData->storeMail($event, $latestSend);
1089
		}
1090 10
	}
1091
}
1092