Completed
Pull Request — master (#270)
by
unknown
02:55
created

FilesHooks::unshareFromCircle()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 4

Duplication

Lines 3
Ratio 37.5 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 0
Metric Value
dl 3
loc 8
ccs 0
cts 5
cp 0
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 4
nc 2
nop 1
crap 6
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
			} else if ((int) $params['shareType'] === Share::SHARE_TYPE_CIRCLE) {
683
				$this->shareWithCircle($params['shareWith'], (int) $params['fileSource'], $params['itemType'], $params['fileTarget'], (int) $params['id']);
684
			}
685
		}
686 3
	}
687
688
	/**
689
	 * Sharing a file or folder with a user
690
	 *
691
	 * @param string $shareWith
692
	 * @param int $fileSource File ID that is being shared
693
	 * @param string $itemType File type that is being shared (file or folder)
694
	 * @param string $fileTarget File path
695
	 */
696 2
	protected function shareWithUser($shareWith, $fileSource, $itemType, $fileTarget) {
697
		// User performing the share
698 2
		$this->shareNotificationForSharer('shared_user_self', $shareWith, $fileSource, $itemType);
699 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...
700 2
			$this->shareNotificationForOriginalOwners($this->currentUser->getUID(), 'reshared_user_by', $shareWith, $fileSource, $itemType);
701
		}
702
703
		// New shared user
704 2
		$this->addNotificationsForUser(
705 2
			$shareWith, 'shared_with_by', [[$fileSource => $fileTarget], $this->currentUser->getUserIdentifier()],
706 2
			(int) $fileSource, $fileTarget, $itemType === 'file',
707 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...
708 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...
709
		);
710 2
	}
711
712
	/**
713
	 * Sharing a file or folder with a group
714
	 *
715
	 * @param string $shareWith
716
	 * @param int $fileSource File ID that is being shared
717
	 * @param string $itemType File type that is being shared (file or folder)
718
	 * @param string $fileTarget File path
719
	 * @param int $shareId The Share ID of this share
720
	 */
721 6
	protected function shareWithGroup($shareWith, $fileSource, $itemType, $fileTarget, $shareId) {
722
		// Members of the new group
723 6
		$group = $this->groupManager->get($shareWith);
724 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...
725 1
			return;
726
		}
727
728
		// User performing the share
729 5
		$this->shareNotificationForSharer('shared_group_self', $shareWith, $fileSource, $itemType);
730 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...
731 5
			$this->shareNotificationForOriginalOwners($this->currentUser->getUID(), 'reshared_group_by', $shareWith, $fileSource, $itemType);
732
		}
733
734 5
		$offset = 0;
735 5
		$users = $group->searchUsers('', self::USER_BATCH_SIZE, $offset);
736 5
		while (!empty($users)) {
737 4
			$this->addNotificationsForGroupUsers($users, 'shared_with_by', $fileSource, $itemType, $fileTarget, $shareId);
738 4
			$offset += self::USER_BATCH_SIZE;
739 4
			$users = $group->searchUsers('', self::USER_BATCH_SIZE, $offset);
740
		}
741 5
	}
742
743
	/**
744
	 * Sharing a file or folder via link/public
745
	 *
746
	 * @param int $fileSource File ID that is being shared
747
	 * @param string $itemType File type that is being shared (file or folder)
748
	 * @param string $linkOwner
749
	 */
750 2
	protected function shareByLink($fileSource, $itemType, $linkOwner) {
751 2
		$this->view->chroot('/' . $linkOwner . '/files');
752
753
		try {
754 2
			$path = $this->view->getPath($fileSource);
755 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...
756 1
			return;
757
		}
758
759 1
		$this->shareNotificationForOriginalOwners($linkOwner, 'reshared_link_by', '', $fileSource, $itemType);
760
761 1
		$this->addNotificationsForUser(
762 1
			$linkOwner, 'shared_link_self', [[$fileSource => $path]],
763 1
			(int) $fileSource, $path, $itemType === 'file',
764 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...
765 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...
766
		);
767 1
	}
768
769
	/**
770
	 * Manage unsharing events
771
	 * @param IShare $share
772
	 * @throws \OCP\Files\NotFoundException
773
	 */
774
	public function unShare(IShare $share) {
775
		if (in_array($share->getNodeType(), ['file', 'folder'], true)) {
776
			if ($share->getShareType() === Share::SHARE_TYPE_USER) {
777
				$this->unshareFromUser($share);
778
			} else if ($share->getShareType() === Share::SHARE_TYPE_GROUP) {
779
				$this->unshareFromGroup($share);
780
			} else if ($share->getShareType() === Share::SHARE_TYPE_LINK) {
781
				$this->unshareLink($share);
782
			} else if ($share->getShareType() === Share::SHARE_TYPE_CIRCLE) {
783
				$this->unshareFromCircle($share);
784
			}
785
		}
786
	}
787
788
	/**
789
	 * Unharing a file or folder from a user
790
	 *
791
	 * @param IShare $share
792
	 * @throws \OCP\Files\NotFoundException
793
	 */
794
	protected function unshareFromUser(IShare $share) {
795
		// User performing the share
796
		$this->shareNotificationForSharer('unshared_user_self', $share->getSharedWith(), $share->getNodeId(), $share->getNodeType());
797
798
		// Owner
799 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...
800
			$this->shareNotificationForOriginalOwners($this->currentUser->getUID(), 'unshared_user_by', $share->getSharedWith(), $share->getNodeId(), $share->getNodeType());
801
		}
802
803
		// Recipient
804
		$this->addNotificationsForUser(
805
			$share->getSharedWith(), 'unshared_by', [[$share->getNodeId() => $share->getTarget()], $this->currentUser->getUserIdentifier()],
806
			$share->getNodeId(), $share->getTarget(), $share->getNodeType() === 'file',
807
			$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...
808
			$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...
809
		);
810
	}
811
812
	/**
813
	 * Unsharing a file or folder from a group
814
	 *
815
	 * @param IShare $share
816
	 * @throws \OCP\Files\NotFoundException
817
	 */
818
	protected function unshareFromGroup(IShare $share) {
819
		// Members of the new group
820
		$group = $this->groupManager->get($share->getSharedWith());
821
		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...
822
			return;
823
		}
824
825
		// User performing the share
826
		$this->shareNotificationForSharer('unshared_group_self', $share->getSharedWith(), $share->getNodeId(), $share->getNodeType());
827 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...
828
			$this->shareNotificationForOriginalOwners($this->currentUser->getUID(), 'unshared_group_by', $share->getSharedWith(), $share->getNodeId(), $share->getNodeType());
829
		}
830
831
		$offset = 0;
832
		$users = $group->searchUsers('', self::USER_BATCH_SIZE, $offset);
833
		while (!empty($users)) {
834
			$this->addNotificationsForGroupUsers($users, 'unshared_by', $share->getNodeId(), $share->getNodeType(), $share->getTarget(), $share->getId());
835
			$offset += self::USER_BATCH_SIZE;
836
			$users = $group->searchUsers('', self::USER_BATCH_SIZE, $offset);
837
		}
838
	}
839
840
	/**
841
	 * Sharing a file or folder via link/public
842
	 *
843
	 * @param IShare $share
844
	 * @throws \OCP\Files\NotFoundException
845
	 */
846
	protected function unshareLink(IShare $share) {
847
		$owner = $share->getSharedBy();
848
		if ($this->currentUser->getUID() === null) {
849
			// Link expired
850
			$actionSharer = 'link_expired';
851
			$actionOwner = 'link_by_expired';
852
		} else {
853
			$actionSharer = 'unshared_link_self';
854
			$actionOwner = 'unshared_link_by';
855
		}
856
857
		$this->addNotificationsForUser(
858
			$owner, $actionSharer, [[$share->getNodeId() => $share->getTarget()]],
859
			$share->getNodeId(), $share->getTarget(), $share->getNodeType() === 'file',
860
			$this->userSettings->getUserSetting($owner, 'stream', Files_Sharing::TYPE_SHARED),
0 ignored issues
show
Bug introduced by
It seems like $this->userSettings->get...s_Sharing::TYPE_SHARED) targeting OCA\Activity\UserSettings::getUserSetting() can also be of type integer; however, OCA\Activity\FilesHooks::addNotificationsForUser() does only seem to accept boolean, maybe add an additional type check?

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

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

An additional type check may prevent trouble.

Loading history...
861
			$this->userSettings->getUserSetting($owner, 'email', Files_Sharing::TYPE_SHARED) ? $this->userSettings->getUserSetting($owner, 'setting', 'batchtime') : 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...
862
		);
863
864
		if ($share->getSharedBy() !== $share->getShareOwner()) {
865
			$owner = $share->getShareOwner();
866
			$this->addNotificationsForUser(
867
				$owner, $actionOwner, [[$share->getNodeId() => $share->getTarget()], $share->getSharedBy()],
868
				$share->getNodeId(), $share->getTarget(), $share->getNodeType() === 'file',
869
				$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...
870
				$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...
871
			);
872
		}
873
	}
874
875
	/**
876
	 * @param IUser[] $usersInGroup
877
	 * @param string $actionUser
878
	 * @param int $fileSource File ID that is being shared
879
	 * @param string $itemType File type that is being shared (file or folder)
880
	 * @param string $fileTarget File path
881
	 * @param int $shareId The Share ID of this share
882
	 */
883 4
	protected function addNotificationsForGroupUsers(array $usersInGroup, $actionUser, $fileSource, $itemType, $fileTarget, $shareId) {
884 4
		$affectedUsers = [];
885
886 4
		foreach ($usersInGroup as $user) {
887 4
			$affectedUsers[$user->getUID()] = $fileTarget;
888
		}
889
890
		// Remove the triggering user, we already managed his notifications
891 4
		unset($affectedUsers[$this->currentUser->getUID()]);
892
893 4
		if (empty($affectedUsers)) {
894 1
			return;
895
		}
896
897 3
		$userIds = array_keys($affectedUsers);
898 3
		$filteredStreamUsersInGroup = $this->userSettings->filterUsersBySetting($userIds, 'stream', Files_Sharing::TYPE_SHARED);
899 3
		$filteredEmailUsersInGroup = $this->userSettings->filterUsersBySetting($userIds, 'email', Files_Sharing::TYPE_SHARED);
900
901 3
		$affectedUsers = $this->fixPathsForShareExceptions($affectedUsers, $shareId);
902 3
		foreach ($affectedUsers as $user => $path) {
903 3
			if (empty($filteredStreamUsersInGroup[$user]) && empty($filteredEmailUsersInGroup[$user])) {
904 2
				continue;
905
			}
906
907 1
			$this->addNotificationsForUser(
908 1
				$user, $actionUser, [[$fileSource => $path], $this->currentUser->getUserIdentifier()],
909 1
				$fileSource, $path, ($itemType === 'file'),
910 1
				!empty($filteredStreamUsersInGroup[$user]),
911 1
				$filteredEmailUsersInGroup[$user] ?? false
912
			);
913
		}
914 3
	}
915
916
	/**
917
	 * Check when there was a naming conflict and the target is different
918
	 * for some of the users
919
	 *
920
	 * @param array $affectedUsers
921
	 * @param int $shareId
922
	 * @return mixed
923
	 */
924
	protected function fixPathsForShareExceptions(array $affectedUsers, $shareId) {
925
		$queryBuilder = $this->connection->getQueryBuilder();
926
		$queryBuilder->select(['share_with', 'file_target'])
927
			->from('share')
928
			->where($queryBuilder->expr()->eq('parent', $queryBuilder->createParameter('parent')))
929
			->setParameter('parent', (int) $shareId);
930
		$query = $queryBuilder->execute();
931
932
		while ($row = $query->fetch()) {
933
			$affectedUsers[$row['share_with']] = $row['file_target'];
934
		}
935
936
		return $affectedUsers;
937
	}
938
939
	/**
940
	 * Add notifications for the user that shares a file/folder
941
	 *
942
	 * @param string $subject
943
	 * @param string $shareWith
944
	 * @param int $fileSource
945
	 * @param string $itemType
946
	 */
947 2
	protected function shareNotificationForSharer($subject, $shareWith, $fileSource, $itemType) {
948 2
		$sharer = $this->currentUser->getUID();
949 2
		if ($sharer === null) {
950
			return;
951
		}
952
953 2
		$this->view->chroot('/' . $sharer . '/files');
954
955
		try {
956 2
			$path = $this->view->getPath($fileSource);
957 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...
958 1
			return;
959
		}
960
961 1
		$this->addNotificationsForUser(
962 1
			$sharer, $subject, [[$fileSource => $path], $shareWith],
963 1
			$fileSource, $path, ($itemType === 'file'),
964 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...
965 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...
966
		);
967 1
	}
968
969
	/**
970
	 * Add notifications for the user that shares a file/folder
971
	 *
972
	 * @param string $owner
973
	 * @param string $subject
974
	 * @param string $shareWith
975
	 * @param int $fileSource
976
	 * @param string $itemType
977
	 */
978 2
	protected function reshareNotificationForSharer($owner, $subject, $shareWith, $fileSource, $itemType) {
979 2
		$this->view->chroot('/' . $owner . '/files');
980
981
		try {
982 2
			$path = $this->view->getPath($fileSource);
983 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...
984 1
			return;
985
		}
986
987 1
		$this->addNotificationsForUser(
988 1
			$owner, $subject, [[$fileSource => $path], $this->currentUser->getUserIdentifier(), $shareWith],
989 1
			$fileSource, $path, ($itemType === 'file'),
990 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...
991 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...
992
		);
993 1
	}
994
995
	/**
996
	 * Add notifications for the owners whose files have been reshared
997
	 *
998
	 * @param string $currentOwner
999
	 * @param string $subject
1000
	 * @param string $shareWith
1001
	 * @param int $fileSource
1002
	 * @param string $itemType
1003
	 */
1004 10
	protected function shareNotificationForOriginalOwners($currentOwner, $subject, $shareWith, $fileSource, $itemType) {
1005
		// Get the full path of the current user
1006 10
		$this->view->chroot('/' . $currentOwner . '/files');
1007
1008
		try {
1009 10
			$path = $this->view->getPath($fileSource);
1010 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...
1011 1
			return;
1012
		}
1013
1014
		/**
1015
		 * Get the original owner and his path
1016
		 */
1017 9
		$owner = $this->view->getOwner($path);
1018 9
		if ($owner !== $currentOwner) {
1019 7
			$this->reshareNotificationForSharer($owner, $subject, $shareWith, $fileSource, $itemType);
1020
		}
1021
1022
		/**
1023
		 * Get the sharee who shared the item with the currentUser
1024
		 */
1025 9
		$this->view->chroot('/' . $currentOwner . '/files');
1026 9
		$mount = $this->view->getMount($path);
1027 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...
1028 1
			return;
1029
		}
1030
1031 8
		$storage = $mount->getStorage();
1032 8
		if (!$storage->instanceOfStorage('OCA\Files_Sharing\SharedStorage')) {
1033 1
			return;
1034
		}
1035
1036
		/** @var \OCA\Files_Sharing\SharedStorage $storage */
1037 7
		$shareOwner = $storage->getSharedFrom();
1038 7
		if ($shareOwner === '' || $shareOwner === null || $shareOwner === $owner || $shareOwner === $currentOwner) {
1039 5
			return;
1040
		}
1041
1042 2
		$this->reshareNotificationForSharer($shareOwner, $subject, $shareWith, $fileSource, $itemType);
1043 2
	}
1044
1045
	/**
1046
	 * Adds the activity and email for a user when the settings require it
1047
	 *
1048
	 * @param string $user
1049
	 * @param string $subject
1050
	 * @param array $subjectParams
1051
	 * @param int $fileId
1052
	 * @param string $path
1053
	 * @param bool $isFile If the item is a file, we link to the parent directory
1054
	 * @param bool $streamSetting
1055
	 * @param int $emailSetting
1056
	 * @param string $type
1057
	 */
1058 11
	protected function addNotificationsForUser($user, $subject, $subjectParams, $fileId, $path, $isFile, $streamSetting, $emailSetting, $type = Files_Sharing::TYPE_SHARED) {
1059 11
		if (!$streamSetting && !$emailSetting) {
1060 1
			return;
1061
		}
1062
1063 10
		$user = (string)$user;
1064 10
		$selfAction = $user === $this->currentUser->getUID();
1065 10
		$app = $type === Files_Sharing::TYPE_SHARED ? 'files_sharing' : 'files';
1066 10
		$link = $this->urlGenerator->linkToRouteAbsolute('files.view.index', array(
1067 10
			'dir' => ($isFile) ? dirname($path) : $path,
1068
		));
1069
1070 10
		$objectType = ($fileId) ? 'files' : '';
1071
1072 10
		$event = $this->manager->generateEvent();
1073
		try {
1074 10
			$event->setApp($app)
1075 10
				->setType($type)
1076 10
				->setAffectedUser($user)
1077 10
				->setTimestamp(time())
1078 10
				->setSubject($subject, $subjectParams)
1079 10
				->setObject($objectType, $fileId, $path)
1080 10
				->setLink($link);
1081
1082 10
			if ($this->currentUser->getUID() !== null) {
1083
				// Allow this to be empty for guests
1084 10
				$event->setAuthor($this->currentUser->getUID());
1085
			}
1086
		} catch (\InvalidArgumentException $e) {
1087
			$this->logger->logException($e);
1088
		}
1089
1090
		// Add activity to stream
1091 10
		if ($streamSetting && (!$selfAction || $this->userSettings->getUserSetting($this->currentUser->getUID(), 'setting', 'self'))) {
1092 3
			$this->activityData->send($event);
1093
		}
1094
1095
		// Add activity to mail queue
1096 10
		if ($emailSetting !== false && (!$selfAction || $this->userSettings->getUserSetting($this->currentUser->getUID(), 'setting', 'selfemail'))) {
1097 5
			$latestSend = time() + $emailSetting;
1098 5
			$this->activityData->storeMail($event, $latestSend);
1099
		}
1100 10
	}
1101
	
1102
1103
	/**
1104
	 * Sharing a file or folder with a circle
1105
	 *
1106
	 * @param string $shareWith
1107
	 * @param int $fileSource File ID that is being shared
1108
	 * @param string $itemType File type that is being shared (file or folder)
1109
	 * @param string $fileTarget File path
1110
	 * @param int $shareId The Share ID of this share
1111
	 */
1112
	protected function shareWithCircle($shareWith, $fileSource, $itemType, $fileTarget, $shareId) {
0 ignored issues
show
Unused Code introduced by
The parameter $fileTarget is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $shareId is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
1113
		// User performing the share
1114
		$this->shareNotificationForSharer('shared_circle_self', $shareWith, $fileSource, $itemType);
1115
		if ($this->currentUser->getUID() !== null) {
1116
			$this->shareNotificationForOriginalOwners($this->currentUser->getUID(), 'reshared_circle_by', $shareWith, $fileSource, $itemType);
1117
		}
1118
	}
1119
1120
	/**
1121
	 * Unsharing a file or folder from a circle
1122
	 *
1123
	 * @param IShare $share
1124
	 * @throws \OCP\Files\NotFoundException
1125
	 */
1126
	protected function unshareFromCircle(IShare $share) {
1127
	
1128
		// User performing the share
1129
		$this->shareNotificationForSharer('unshared_circle_self', $share->getSharedWith(), $share->getNodeId(), $share->getNodeType());
1130 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...
1131
			$this->shareNotificationForOriginalOwners($this->currentUser->getUID(), 'unshared_circle_by', $share->getSharedWith(), $share->getNodeId(), $share->getNodeType());
1132
		}
1133
	}
1134
}
1135