Completed
Pull Request — master (#128)
by Joas
27:57 queued 23:53
created

FilesHooks::addNotificationsForFileAction()   C

Complexity

Conditions 8
Paths 6

Size

Total Lines 43
Code Lines 27

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 24
CRAP Score 8.004

Importance

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