Completed
Push — master ( 37428d...11fdad )
by Morris
71:17 queued 71:12
created

FilesHooks::generateAddActivities()   B

Complexity

Conditions 6
Paths 5

Size

Total Lines 32
Code Lines 21

Duplication

Lines 32
Ratio 100 %

Code Coverage

Tests 0
CRAP Score 42

Importance

Changes 0
Metric Value
dl 32
loc 32
ccs 0
cts 21
cp 0
rs 8.439
c 0
b 0
f 0
cc 6
eloc 21
nc 5
nop 4
crap 42
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 === '/' || $path === '' || $path === null) {
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 2
				$user, $userSubject, $userParams,
220 2
				$fileId, $path, true,
221 2
				!empty($filteredStreamUsers[$user]),
222 2
				$filteredEmailUsers[$user] ?? false,
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
				$filteredEmailUsers[$user] ?? false,
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
				$filteredEmailUsers[$user] ?? false,
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
				$filteredEmailUsers[$user] ?? false,
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
				$filteredEmailUsers[$user] ?? false,
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
621
		$accessList['ownerPath'] = '/';
622
		if (isset($sections[3])) {
623
			// Not the case when a file in root is renamed
624
			$accessList['ownerPath'] .= $sections[3];
625
		}
626
627
		return $accessList;
628
	}
629
630
	/**
631
	 * Return the source
632
	 *
633
	 * @param string $path
634
	 * @return array
635
	 */
636
	protected function getSourcePathAndOwner($path) {
637
		$view = Filesystem::getView();
638
		$owner = $view->getOwner($path);
639
		$owner = !is_string($owner) || $owner === '' ? null : $owner;
640
		$fileId = 0;
641
		$currentUser = $this->currentUser->getUID();
642
643
		if ($owner === null || $owner !== $currentUser) {
644
			/** @var \OCP\Files\Storage\IStorage $storage */
645
			list($storage,) = $view->resolvePath($path);
646
647
			if ($owner !== null && !$storage->instanceOfStorage('OCA\Files_Sharing\External\Storage')) {
648
				Filesystem::initMountPoints($owner);
649
			} else {
650
				// Probably a remote user, let's try to at least generate activities
651
				// for the current user
652
				if ($currentUser === null) {
653
					list(,$owner,) = explode('/', $view->getAbsolutePath($path), 3);
654
				} else {
655
					$owner = $currentUser;
656
				}
657
			}
658
		}
659
660
		$info = Filesystem::getFileInfo($path);
661
		if ($info !== false) {
662
			$ownerView = new View('/' . $owner . '/files');
663
			$fileId = (int) $info['fileid'];
664
			$path = $ownerView->getPath($fileId);
665
		}
666
667
		return array($path, $owner, $fileId);
668
	}
669
670
	/**
671
	 * Manage sharing events
672
	 * @param array $params The hook params
673
	 */
674 3
	public function share($params) {
675 3
		if ($params['itemType'] === 'file' || $params['itemType'] === 'folder') {
676 3
			if ((int) $params['shareType'] === Share::SHARE_TYPE_USER) {
677 1
				$this->shareWithUser($params['shareWith'], (int) $params['fileSource'], $params['itemType'], $params['fileTarget']);
678 2
			} else if ((int) $params['shareType'] === Share::SHARE_TYPE_GROUP) {
679 1
				$this->shareWithGroup($params['shareWith'], (int) $params['fileSource'], $params['itemType'], $params['fileTarget'], (int) $params['id']);
680 1
			} else if ((int) $params['shareType'] === Share::SHARE_TYPE_LINK) {
681 1
				$this->shareByLink((int) $params['fileSource'], $params['itemType'], $params['uidOwner']);
682
			}
683
		}
684 3
	}
685
686
	/**
687
	 * Sharing a file or folder with a user
688
	 *
689
	 * @param string $shareWith
690
	 * @param int $fileSource File ID that is being shared
691
	 * @param string $itemType File type that is being shared (file or folder)
692
	 * @param string $fileTarget File path
693
	 */
694 2
	protected function shareWithUser($shareWith, $fileSource, $itemType, $fileTarget) {
695
		// User performing the share
696 2
		$this->shareNotificationForSharer('shared_user_self', $shareWith, $fileSource, $itemType);
697 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...
698 2
			$this->shareNotificationForOriginalOwners($this->currentUser->getUID(), 'reshared_user_by', $shareWith, $fileSource, $itemType);
699
		}
700
701
		// New shared user
702 2
		$this->addNotificationsForUser(
703 2
			$shareWith, 'shared_with_by', [[$fileSource => $fileTarget], $this->currentUser->getUserIdentifier()],
704 2
			(int) $fileSource, $fileTarget, $itemType === 'file',
705 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...
706 2
			$this->userSettings->getUserSetting($shareWith, 'email', Files_Sharing::TYPE_SHARED) ? $this->userSettings->getUserSetting($shareWith, 'setting', 'batchtime') : false
0 ignored issues
show
Bug introduced by
It seems like $this->userSettings->get...', 'batchtime') : false 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...
707
		);
708 2
	}
709
710
	/**
711
	 * Sharing a file or folder with a group
712
	 *
713
	 * @param string $shareWith
714
	 * @param int $fileSource File ID that is being shared
715
	 * @param string $itemType File type that is being shared (file or folder)
716
	 * @param string $fileTarget File path
717
	 * @param int $shareId The Share ID of this share
718
	 */
719 6
	protected function shareWithGroup($shareWith, $fileSource, $itemType, $fileTarget, $shareId) {
720
		// Members of the new group
721 6
		$group = $this->groupManager->get($shareWith);
722 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...
723 1
			return;
724
		}
725
726
		// User performing the share
727 5
		$this->shareNotificationForSharer('shared_group_self', $shareWith, $fileSource, $itemType);
728 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...
729 5
			$this->shareNotificationForOriginalOwners($this->currentUser->getUID(), 'reshared_group_by', $shareWith, $fileSource, $itemType);
730
		}
731
732 5
		$offset = 0;
733 5
		$users = $group->searchUsers('', self::USER_BATCH_SIZE, $offset);
734 5
		while (!empty($users)) {
735 4
			$this->addNotificationsForGroupUsers($users, 'shared_with_by', $fileSource, $itemType, $fileTarget, $shareId);
736 4
			$offset += self::USER_BATCH_SIZE;
737 4
			$users = $group->searchUsers('', self::USER_BATCH_SIZE, $offset);
738
		}
739 5
	}
740
741
	/**
742
	 * Sharing a file or folder via link/public
743
	 *
744
	 * @param int $fileSource File ID that is being shared
745
	 * @param string $itemType File type that is being shared (file or folder)
746
	 * @param string $linkOwner
747
	 */
748 2
	protected function shareByLink($fileSource, $itemType, $linkOwner) {
749 2
		$this->view->chroot('/' . $linkOwner . '/files');
750
751
		try {
752 2
			$path = $this->view->getPath($fileSource);
753 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...
754 1
			return;
755
		}
756
757 1
		$this->shareNotificationForOriginalOwners($linkOwner, 'reshared_link_by', '', $fileSource, $itemType);
758
759 1
		$this->addNotificationsForUser(
760 1
			$linkOwner, 'shared_link_self', [[$fileSource => $path]],
761 1
			(int) $fileSource, $path, $itemType === 'file',
762 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...
763 1
			$this->userSettings->getUserSetting($linkOwner, 'email', Files_Sharing::TYPE_SHARED) ? $this->userSettings->getUserSetting($linkOwner, 'setting', 'batchtime') : false
0 ignored issues
show
Bug introduced by
It seems like $this->userSettings->get...', 'batchtime') : false 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...
764
		);
765 1
	}
766
767
	/**
768
	 * Manage unsharing events
769
	 * @param IShare $share
770
	 * @throws \OCP\Files\NotFoundException
771
	 */
772
	public function unShare(IShare $share) {
773
		if (in_array($share->getNodeType(), ['file', 'folder'], true)) {
774
			if ($share->getShareType() === Share::SHARE_TYPE_USER) {
775
				$this->unshareFromUser($share);
776
			} else if ($share->getShareType() === Share::SHARE_TYPE_GROUP) {
777
				$this->unshareFromGroup($share);
778
			} else if ($share->getShareType() === Share::SHARE_TYPE_LINK) {
779
				$this->unshareLink($share);
780
			}
781
		}
782
	}
783
784
	/**
785
	 * Unharing a file or folder from a user
786
	 *
787
	 * @param IShare $share
788
	 * @throws \OCP\Files\NotFoundException
789
	 */
790
	protected function unshareFromUser(IShare $share) {
791
		// User performing the share
792
		$this->shareNotificationForSharer('unshared_user_self', $share->getSharedWith(), $share->getNodeId(), $share->getNodeType());
793
794
		// Owner
795 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...
796
			$this->shareNotificationForOriginalOwners($this->currentUser->getUID(), 'unshared_user_by', $share->getSharedWith(), $share->getNodeId(), $share->getNodeType());
797
		}
798
799
		// Recipient
800
		$this->addNotificationsForUser(
801
			$share->getSharedWith(), 'unshared_by', [[$share->getNodeId() => $share->getTarget()], $this->currentUser->getUserIdentifier()],
802
			$share->getNodeId(), $share->getTarget(), $share->getNodeType() === 'file',
803
			$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...
804
			$this->userSettings->getUserSetting($share->getSharedWith(), 'email', Files_Sharing::TYPE_SHARED) ? $this->userSettings->getUserSetting($share->getSharedWith(), 'setting', 'batchtime') : false
0 ignored issues
show
Bug introduced by
It seems like $this->userSettings->get...', 'batchtime') : false 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...
805
		);
806
	}
807
808
	/**
809
	 * Unsharing a file or folder from a group
810
	 *
811
	 * @param IShare $share
812
	 * @throws \OCP\Files\NotFoundException
813
	 */
814
	protected function unshareFromGroup(IShare $share) {
815
		// Members of the new group
816
		$group = $this->groupManager->get($share->getSharedWith());
817
		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...
818
			return;
819
		}
820
821
		// User performing the share
822
		$this->shareNotificationForSharer('unshared_group_self', $share->getSharedWith(), $share->getNodeId(), $share->getNodeType());
823 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...
824
			$this->shareNotificationForOriginalOwners($this->currentUser->getUID(), 'unshared_group_by', $share->getSharedWith(), $share->getNodeId(), $share->getNodeType());
825
		}
826
827
		$offset = 0;
828
		$users = $group->searchUsers('', self::USER_BATCH_SIZE, $offset);
829
		while (!empty($users)) {
830
			$this->addNotificationsForGroupUsers($users, 'unshared_by', $share->getNodeId(), $share->getNodeType(), $share->getTarget(), $share->getId());
831
			$offset += self::USER_BATCH_SIZE;
832
			$users = $group->searchUsers('', self::USER_BATCH_SIZE, $offset);
833
		}
834
	}
835
836
	/**
837
	 * Sharing a file or folder via link/public
838
	 *
839
	 * @param IShare $share
840
	 * @throws \OCP\Files\NotFoundException
841
	 */
842
	protected function unshareLink(IShare $share) {
843
		$owner = $share->getSharedBy();
844
		if ($this->currentUser->getUID() === null) {
845
			// Link expired
846
			$actionSharer = 'link_expired';
847
			$actionOwner = 'link_by_expired';
848
		} else {
849
			$actionSharer = 'unshared_link_self';
850
			$actionOwner = 'unshared_link_by';
851
		}
852
853
		$this->addNotificationsForUser(
854
			$owner, $actionSharer, [[$share->getNodeId() => $share->getTarget()]],
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') : false
0 ignored issues
show
Bug introduced by
It seems like $this->userSettings->get...', 'batchtime') : false 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
		if ($share->getSharedBy() !== $share->getShareOwner()) {
861
			$owner = $share->getShareOwner();
862
			$this->addNotificationsForUser(
863
				$owner, $actionOwner, [[$share->getNodeId() => $share->getTarget()], $share->getSharedBy()],
864
				$share->getNodeId(), $share->getTarget(), $share->getNodeType() === 'file',
865
				$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...
866
				$this->userSettings->getUserSetting($owner, 'email', Files_Sharing::TYPE_SHARED) ? $this->userSettings->getUserSetting($owner, 'setting', 'batchtime') : false
0 ignored issues
show
Bug introduced by
It seems like $this->userSettings->get...', 'batchtime') : false 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...
867
			);
868
		}
869
	}
870
871
	/**
872
	 * @param IUser[] $usersInGroup
873
	 * @param string $actionUser
874
	 * @param int $fileSource File ID that is being shared
875
	 * @param string $itemType File type that is being shared (file or folder)
876
	 * @param string $fileTarget File path
877
	 * @param int $shareId The Share ID of this share
878
	 */
879 4
	protected function addNotificationsForGroupUsers(array $usersInGroup, $actionUser, $fileSource, $itemType, $fileTarget, $shareId) {
880 4
		$affectedUsers = [];
881
882 4
		foreach ($usersInGroup as $user) {
883 4
			$affectedUsers[$user->getUID()] = $fileTarget;
884
		}
885
886
		// Remove the triggering user, we already managed his notifications
887 4
		unset($affectedUsers[$this->currentUser->getUID()]);
888
889 4
		if (empty($affectedUsers)) {
890 1
			return;
891
		}
892
893 3
		$userIds = array_keys($affectedUsers);
894 3
		$filteredStreamUsersInGroup = $this->userSettings->filterUsersBySetting($userIds, 'stream', Files_Sharing::TYPE_SHARED);
895 3
		$filteredEmailUsersInGroup = $this->userSettings->filterUsersBySetting($userIds, 'email', Files_Sharing::TYPE_SHARED);
896
897 3
		$affectedUsers = $this->fixPathsForShareExceptions($affectedUsers, $shareId);
898 3
		foreach ($affectedUsers as $user => $path) {
899 3
			if (empty($filteredStreamUsersInGroup[$user]) && empty($filteredEmailUsersInGroup[$user])) {
900 2
				continue;
901
			}
902
903 1
			$this->addNotificationsForUser(
904 1
				$user, $actionUser, [[$fileSource => $path], $this->currentUser->getUserIdentifier()],
905 1
				$fileSource, $path, ($itemType === 'file'),
906 1
				!empty($filteredStreamUsersInGroup[$user]),
907 1
				$filteredEmailUsersInGroup[$user] ?? false
908
			);
909
		}
910 3
	}
911
912
	/**
913
	 * Check when there was a naming conflict and the target is different
914
	 * for some of the users
915
	 *
916
	 * @param array $affectedUsers
917
	 * @param int $shareId
918
	 * @return mixed
919
	 */
920
	protected function fixPathsForShareExceptions(array $affectedUsers, $shareId) {
921
		$queryBuilder = $this->connection->getQueryBuilder();
922
		$queryBuilder->select(['share_with', 'file_target'])
923
			->from('share')
924
			->where($queryBuilder->expr()->eq('parent', $queryBuilder->createParameter('parent')))
925
			->setParameter('parent', (int) $shareId);
926
		$query = $queryBuilder->execute();
927
928
		while ($row = $query->fetch()) {
929
			$affectedUsers[$row['share_with']] = $row['file_target'];
930
		}
931
932
		return $affectedUsers;
933
	}
934
935
	/**
936
	 * Add notifications for the user that shares a file/folder
937
	 *
938
	 * @param string $subject
939
	 * @param string $shareWith
940
	 * @param int $fileSource
941
	 * @param string $itemType
942
	 */
943 2
	protected function shareNotificationForSharer($subject, $shareWith, $fileSource, $itemType) {
944 2
		$sharer = $this->currentUser->getUID();
945 2
		if ($sharer === null) {
946
			return;
947
		}
948
949 2
		$this->view->chroot('/' . $sharer . '/files');
950
951
		try {
952 2
			$path = $this->view->getPath($fileSource);
953 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...
954 1
			return;
955
		}
956
957 1
		$this->addNotificationsForUser(
958 1
			$sharer, $subject, [[$fileSource => $path], $shareWith],
959 1
			$fileSource, $path, ($itemType === 'file'),
960 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...
961 1
			$this->userSettings->getUserSetting($sharer, 'email', Files_Sharing::TYPE_SHARED) ? $this->userSettings->getUserSetting($sharer, 'setting', 'batchtime') : false
0 ignored issues
show
Bug introduced by
It seems like $this->userSettings->get...', 'batchtime') : false 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...
962
		);
963 1
	}
964
965
	/**
966
	 * Add notifications for the user that shares a file/folder
967
	 *
968
	 * @param string $owner
969
	 * @param string $subject
970
	 * @param string $shareWith
971
	 * @param int $fileSource
972
	 * @param string $itemType
973
	 */
974 2
	protected function reshareNotificationForSharer($owner, $subject, $shareWith, $fileSource, $itemType) {
975 2
		$this->view->chroot('/' . $owner . '/files');
976
977
		try {
978 2
			$path = $this->view->getPath($fileSource);
979 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...
980 1
			return;
981
		}
982
983 1
		$this->addNotificationsForUser(
984 1
			$owner, $subject, [[$fileSource => $path], $this->currentUser->getUserIdentifier(), $shareWith],
985 1
			$fileSource, $path, ($itemType === 'file'),
986 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...
987 1
			$this->userSettings->getUserSetting($owner, 'email', Files_Sharing::TYPE_SHARED) ? $this->userSettings->getUserSetting($owner, 'setting', 'batchtime') : false
0 ignored issues
show
Bug introduced by
It seems like $this->userSettings->get...', 'batchtime') : false 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...
988
		);
989 1
	}
990
991
	/**
992
	 * Add notifications for the owners whose files have been reshared
993
	 *
994
	 * @param string $currentOwner
995
	 * @param string $subject
996
	 * @param string $shareWith
997
	 * @param int $fileSource
998
	 * @param string $itemType
999
	 */
1000 10
	protected function shareNotificationForOriginalOwners($currentOwner, $subject, $shareWith, $fileSource, $itemType) {
1001
		// Get the full path of the current user
1002 10
		$this->view->chroot('/' . $currentOwner . '/files');
1003
1004
		try {
1005 10
			$path = $this->view->getPath($fileSource);
1006 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...
1007 1
			return;
1008
		}
1009
1010
		/**
1011
		 * Get the original owner and his path
1012
		 */
1013 9
		$owner = $this->view->getOwner($path);
1014 9
		if ($owner !== $currentOwner) {
1015 7
			$this->reshareNotificationForSharer($owner, $subject, $shareWith, $fileSource, $itemType);
1016
		}
1017
1018
		/**
1019
		 * Get the sharee who shared the item with the currentUser
1020
		 */
1021 9
		$this->view->chroot('/' . $currentOwner . '/files');
1022 9
		$mount = $this->view->getMount($path);
1023 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...
1024 1
			return;
1025
		}
1026
1027 8
		$storage = $mount->getStorage();
1028 8
		if (!$storage->instanceOfStorage('OCA\Files_Sharing\SharedStorage')) {
1029 1
			return;
1030
		}
1031
1032
		/** @var \OCA\Files_Sharing\SharedStorage $storage */
1033 7
		$shareOwner = $storage->getSharedFrom();
1034 7
		if ($shareOwner === '' || $shareOwner === null || $shareOwner === $owner || $shareOwner === $currentOwner) {
1035 5
			return;
1036
		}
1037
1038 2
		$this->reshareNotificationForSharer($shareOwner, $subject, $shareWith, $fileSource, $itemType);
1039 2
	}
1040
1041
	/**
1042
	 * Adds the activity and email for a user when the settings require it
1043
	 *
1044
	 * @param string $user
1045
	 * @param string $subject
1046
	 * @param array $subjectParams
1047
	 * @param int $fileId
1048
	 * @param string $path
1049
	 * @param bool $isFile If the item is a file, we link to the parent directory
1050
	 * @param bool $streamSetting
1051
	 * @param int $emailSetting
1052
	 * @param string $type
1053
	 */
1054 11
	protected function addNotificationsForUser($user, $subject, $subjectParams, $fileId, $path, $isFile, $streamSetting, $emailSetting, $type = Files_Sharing::TYPE_SHARED) {
1055 11
		if (!$streamSetting && !$emailSetting) {
1056 1
			return;
1057
		}
1058
1059 10
		$selfAction = $user === $this->currentUser->getUID();
1060 10
		$app = $type === Files_Sharing::TYPE_SHARED ? 'files_sharing' : 'files';
1061 10
		$link = $this->urlGenerator->linkToRouteAbsolute('files.view.index', array(
1062 10
			'dir' => ($isFile) ? dirname($path) : $path,
1063
		));
1064
1065 10
		$objectType = ($fileId) ? 'files' : '';
1066
1067 10
		$event = $this->manager->generateEvent();
1068
		try {
1069 10
			$event->setApp($app)
1070 10
				->setType($type)
1071 10
				->setAffectedUser($user)
1072 10
				->setTimestamp(time())
1073 10
				->setSubject($subject, $subjectParams)
1074 10
				->setObject($objectType, $fileId, $path)
1075 10
				->setLink($link);
1076
1077 10
			if ($this->currentUser->getUID() !== null) {
1078
				// Allow this to be empty for guests
1079 10
				$event->setAuthor($this->currentUser->getUID());
1080
			}
1081
		} catch (\InvalidArgumentException $e) {
1082
			$this->logger->logException($e);
1083
		}
1084
1085
		// Add activity to stream
1086 10
		if ($streamSetting && (!$selfAction || $this->userSettings->getUserSetting($this->currentUser->getUID(), 'setting', 'self'))) {
1087 3
			$this->activityData->send($event);
1088
		}
1089
1090
		// Add activity to mail queue
1091 10
		if ($emailSetting !== false && (!$selfAction || $this->userSettings->getUserSetting($this->currentUser->getUID(), 'setting', 'selfemail'))) {
1092 5
			$latestSend = time() + $emailSetting;
1093 5
			$this->activityData->storeMail($event, $latestSend);
1094
		}
1095 10
	}
1096
}
1097