Completed
Pull Request — master (#247)
by Blizzz
02:10
created

FilesHooks::shareWithUser()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 15
Code Lines 9

Duplication

Lines 3
Ratio 20 %

Code Coverage

Tests 10
CRAP Score 3

Importance

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