FilesHooks   F
last analyzed

Complexity

Total Complexity 80

Size/Duplication

Total Lines 541
Duplicated Lines 4.07 %

Coupling/Cohesion

Components 1
Dependencies 2

Test Coverage

Coverage 78.99%

Importance

Changes 0
Metric Value
wmc 80
lcom 1
cbo 2
dl 22
loc 541
ccs 188
cts 238
cp 0.7899
rs 2
c 0
b 0
f 0

20 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 10 1
A getCurrentUser() 0 3 1
A fileUpdate() 0 3 1
A fileDelete() 0 3 1
A fileRestore() 0 3 1
A getUserPathsFromPath() 0 3 1
A fileCreate() 0 7 2
B addNotificationsForFileAction() 0 38 8
A getSourcePathAndOwner() 0 24 4
A share() 11 11 6
A unShare() 11 11 6
A shareFileOrFolderWithUser() 0 23 3
A shareFileOrFolderWithGroup() 0 29 4
B addNotificationsForGroupUsers() 0 32 7
A fixPathsForShareExceptions() 0 14 2
A shareFileOrFolderByLink() 0 32 5
A shareNotificationForSharer() 0 16 3
A reshareNotificationForSharer() 0 16 3
B shareNotificationForOriginalOwners() 0 40 9
C addNotificationsForUser() 0 34 12

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like FilesHooks often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use FilesHooks, and based on these observations, apply Extract Interface, too.

1
<?php
2
/**
3
 * @author Frank Karlitschek <[email protected]>
4
 * @author Joas Schilling <[email protected]>
5
 * @author Thomas Müller <[email protected]>
6
 *
7
 * @copyright Copyright (c) 2016, ownCloud, Inc.
8
 * @license AGPL-3.0
9
 *
10
 * This code is free software: you can redistribute it and/or modify
11
 * it under the terms of the GNU Affero General Public License, version 3,
12
 * as published by the Free Software Foundation.
13
 *
14
 * This program is distributed in the hope that it will be useful,
15
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17
 * GNU Affero General Public License for more details.
18
 *
19
 * You should have received a copy of the GNU Affero General Public License, version 3,
20
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
21
 *
22
 */
23
24
namespace OCA\Activity;
25
26
use OC\Files\Filesystem;
27
use OC\Files\View;
28
use OCA\Activity\Extension\Files;
29
use OCA\Activity\Extension\Files_Sharing;
30
use OCP\Activity\IManager;
31
use OCP\Files\Mount\IMountPoint;
32
use OCP\Files\NotFoundException;
33
use OCP\IDBConnection;
34
use OCP\IGroup;
35
use OCP\IGroupManager;
36
use OCP\IURLGenerator;
37
use OCP\IUser;
38
use OCP\Share;
39
40
/**
41
 * The class to handle the filesystem hooks
42
 */
43
class FilesHooks {
44
	const USER_BATCH_SIZE = 50;
45
46
	/** @var \OCP\Activity\IManager */
47
	protected $manager;
48
49
	/** @var \OCA\Activity\Data */
50
	protected $activityData;
51
52
	/** @var \OCA\Activity\UserSettings */
53
	protected $userSettings;
54
55
	/** @var \OCP\IGroupManager */
56
	protected $groupManager;
57
58
	/** @var \OCP\IDBConnection */
59
	protected $connection;
60
61
	/** @var \OC\Files\View */
62
	protected $view;
63
64
	/** @var IURLGenerator */
65
	protected $urlGenerator;
66
67
	/** @var string|false */
68
	protected $currentUser;
69
70
	/**
71
	 * Constructor
72
	 *
73
	 * @param IManager $manager
74
	 * @param Data $activityData
75
	 * @param UserSettings $userSettings
76
	 * @param IGroupManager $groupManager
77
	 * @param View $view
78
	 * @param IDBConnection $connection
79
	 * @param IURLGenerator $urlGenerator
80
	 * @param string|false $currentUser
81
	 */
82 49
	public function __construct(IManager $manager, Data $activityData, UserSettings $userSettings, IGroupManager $groupManager, View $view, IDBConnection $connection, IURLGenerator $urlGenerator, $currentUser) {
83 49
		$this->manager = $manager;
84 49
		$this->activityData = $activityData;
85 49
		$this->userSettings = $userSettings;
86 49
		$this->groupManager = $groupManager;
87 49
		$this->view = $view;
88 49
		$this->connection = $connection;
89 49
		$this->urlGenerator = $urlGenerator;
90 49
		$this->currentUser = $currentUser;
91 49
	}
92
93
	/**
94
	 * @return string|false Current UserID if logged in, false otherwise
95
	 */
96 2
	protected function getCurrentUser() {
97 2
		return $this->currentUser;
98
	}
99
100
	/**
101
	 * Store the create hook events
102
	 * @param string $path Path of the file that has been created
103
	 */
104 2
	public function fileCreate($path) {
105 2
		if ($this->getCurrentUser() !== false) {
106 1
			$this->addNotificationsForFileAction($path, Files::TYPE_SHARE_CREATED, 'created_self', 'created_by');
107
		} else {
108 1
			$this->addNotificationsForFileAction($path, Files::TYPE_SHARE_CREATED, '', 'created_public');
109
		}
110 2
	}
111
112
	/**
113
	 * Store the update hook events
114
	 * @param string $path Path of the file that has been modified
115
	 */
116 1
	public function fileUpdate($path) {
117 1
		$this->addNotificationsForFileAction($path, Files::TYPE_SHARE_CHANGED, 'changed_self', 'changed_by');
118 1
	}
119
120
	/**
121
	 * Store the delete hook events
122
	 * @param string $path Path of the file that has been deleted
123
	 */
124 1
	public function fileDelete($path) {
125 1
		$this->addNotificationsForFileAction($path, Files::TYPE_SHARE_DELETED, 'deleted_self', 'deleted_by');
126 1
	}
127
128
	/**
129
	 * Store the restore hook events
130
	 * @param string $path Path of the file that has been restored
131
	 */
132 1
	public function fileRestore($path) {
133 1
		$this->addNotificationsForFileAction($path, Files::TYPE_SHARE_RESTORED, 'restored_self', 'restored_by');
134 1
	}
135
136
	/**
137
	 * Creates the entries for file actions on $file_path
138
	 *
139
	 * @param string $filePath         The file that is being changed
140
	 * @param int    $activityType     The activity type
141
	 * @param string $subject          The subject for the actor
142
	 * @param string $subjectBy        The subject for other users (with "by $actor")
143
	 */
144 3
	protected function addNotificationsForFileAction($filePath, $activityType, $subject, $subjectBy) {
145
		// Do not add activities for .part-files
146 3
		if (\substr($filePath, -5) === '.part') {
147 1
			return;
148
		}
149
150 2
		list($filePath, $uidOwner, $fileId) = $this->getSourcePathAndOwner($filePath);
151 2
		if (!$fileId) {
152
			// no owner, possibly deleted or unknown
153
			// skip notifications
154
			return;
155
		}
156 2
		$affectedUsers = $this->getUserPathsFromPath($filePath, $uidOwner);
157 2
		$filteredStreamUsers = $this->userSettings->filterUsersBySetting(\array_keys($affectedUsers), 'stream', $activityType);
158 2
		$filteredEmailUsers = $this->userSettings->filterUsersBySetting(\array_keys($affectedUsers), 'email', $activityType);
159
160 2
		foreach ($affectedUsers as $user => $path) {
161 2
			if (empty($filteredStreamUsers[$user]) && empty($filteredEmailUsers[$user])) {
162 2
				continue;
163
			}
164
165 2
			if ($user === $this->currentUser) {
166 1
				$userSubject = $subject;
167 1
				$userParams = [[$fileId => $path]];
168
			} else {
169 1
				$userSubject = $subjectBy;
170 1
				$userParams = [[$fileId => $path], $this->currentUser];
171
			}
172
173 2
			$this->addNotificationsForUser(
174 2
				$user, $userSubject, $userParams,
175 2
				$fileId, $path, true,
176 2
				!empty($filteredStreamUsers[$user]),
177 2
				!empty($filteredEmailUsers[$user]) ? $filteredEmailUsers[$user] : 0,
178 2
				$activityType
179
			);
180
		}
181 2
	}
182
183
	/**
184
	 * Returns a "username => path" map for all affected users
185
	 *
186
	 * @param string $path
187
	 * @param string $uidOwner
188
	 * @return array
189
	 */
190
	protected function getUserPathsFromPath($path, $uidOwner) {
191
		return Share::getUsersSharingFile($path, $uidOwner, true, true);
192
	}
193
194
	/**
195
	 * Return the source
196
	 *
197
	 * @param string $path
198
	 * @return array
199
	 */
200
	protected function getSourcePathAndOwner($path) {
201
		$currentUserView = Filesystem::getView();
202
		$uidOwner = $currentUserView->getOwner($path);
203
		$fileId = 0;
204
205
		if ($uidOwner !== $this->currentUser) {
206
			list($storage, $internalPath) = $currentUserView->resolvePath($path);
0 ignored issues
show
Unused Code introduced by
The assignment to $internalPath is unused. Consider omitting it like so list($first,,$third).

This checks looks for assignemnts to variables using the list(...) function, where not all assigned variables are subsequently used.

Consider the following code example.

<?php

function returnThreeValues() {
    return array('a', 'b', 'c');
}

list($a, $b, $c) = returnThreeValues();

print $a . " - " . $c;

Only the variables $a and $c are used. There was no need to assign $b.

Instead, the list call could have been.

list($a,, $c) = returnThreeValues();
Loading history...
207
			if ($storage->instanceOfStorage('OCA\Files_Sharing\External\Storage')) {
208
				// for federated shares we don't have access to the remote user, use the current one
209
				// which will also make it use the matching local "shared::" federated share storage instead
210
				$uidOwner = $this->currentUser;
211
			} else {
212
				Filesystem::initMountPoints($uidOwner);
213
			}
214
		}
215
		$info = Filesystem::getFileInfo($path);
216
		if ($info !== false) {
217
			$ownerView = new View('/' . $uidOwner . '/files');
218
			$fileId = (int) $info['fileid'];
219
			$path = $ownerView->getPath($fileId);
220
		}
221
222
		return [$path, $uidOwner, $fileId];
223
	}
224
225
	/**
226
	 * Manage sharing events
227
	 * @param array $params The hook params
228
	 */
229 3 View Code Duplication
	public function share($params) {
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...
230 3
		if ($params['itemType'] === 'file' || $params['itemType'] === 'folder') {
231 3
			if ((int) $params['shareType'] === Share::SHARE_TYPE_USER) {
232 1
				$this->shareFileOrFolderWithUser($params['shareWith'], (int) $params['fileSource'], $params['itemType'], $params['fileTarget'], true);
233 2
			} elseif ((int) $params['shareType'] === Share::SHARE_TYPE_GROUP) {
234 1
				$this->shareFileOrFolderWithGroup($params['shareWith'], (int) $params['fileSource'], $params['itemType'], $params['fileTarget'], (int) $params['id'], true);
235 1
			} elseif ((int) $params['shareType'] === Share::SHARE_TYPE_LINK) {
236 1
				$this->shareFileOrFolderByLink((int) $params['fileSource'], $params['itemType'], $params['uidOwner'], true);
237
			}
238
		}
239 3
	}
240
241
	/**
242
	 * Manage sharing events
243
	 * @param array $params The hook params
244
	 */
245 View Code Duplication
	public function unShare($params) {
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...
246
		if ($params['itemType'] === 'file' || $params['itemType'] === 'folder') {
247
			if ((int) $params['shareType'] === Share::SHARE_TYPE_USER) {
248
				$this->shareFileOrFolderWithUser($params['shareWith'], (int) $params['fileSource'], $params['itemType'], $params['fileTarget'], false);
249
			} elseif ((int) $params['shareType'] === Share::SHARE_TYPE_GROUP) {
250
				$this->shareFileOrFolderWithGroup($params['shareWith'], (int) $params['fileSource'], $params['itemType'], $params['fileTarget'], (int) $params['id'], false);
251
			} elseif ((int) $params['shareType'] === Share::SHARE_TYPE_LINK) {
252
				$this->shareFileOrFolderByLink((int) $params['fileSource'], $params['itemType'], $params['uidOwner'], false);
253
			}
254
		}
255
	}
256
257
	/**
258
	 * Sharing a file or folder with a user
259
	 *
260
	 * @param string $shareWith
261
	 * @param int $fileSource File ID that is being shared
262
	 * @param string $itemType File type that is being shared (file or folder)
263
	 * @param string $fileTarget File path
264
	 * @param bool $isSharing True if sharing, false if unsharing
265
	 */
266 2
	protected function shareFileOrFolderWithUser($shareWith, $fileSource, $itemType, $fileTarget, $isSharing) {
267 2
		if ($isSharing) {
268 2
			$actionSharer = 'shared_user_self';
269 2
			$actionOwner = 'reshared_user_by';
270 2
			$actionUser = 'shared_with_by';
271
		} else {
272
			$actionSharer = 'unshared_user_self';
273
			$actionOwner = 'unshared_user_by';
274
			$actionUser = 'unshared_by';
275
		}
276
277
		// User performing the share
278 2
		$this->shareNotificationForSharer($actionSharer, $shareWith, $fileSource, $itemType);
279 2
		$this->shareNotificationForOriginalOwners($this->currentUser, $actionOwner, $shareWith, $fileSource, $itemType);
0 ignored issues
show
Security Bug introduced by
It seems like $this->currentUser can also be of type false; however, OCA\Activity\FilesHooks:...tionForOriginalOwners() does only seem to accept string, did you maybe forget to handle an error condition?
Loading history...
280
281
		// New shared user
282 2
		$this->addNotificationsForUser(
283 2
			$shareWith, $actionUser, [[$fileSource => $fileTarget], $this->currentUser],
284 2
			(int) $fileSource, $fileTarget, ($itemType === 'file'),
285 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...
286 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...
287
		);
288 2
	}
289
290
	/**
291
	 * Sharing a file or folder with a group
292
	 *
293
	 * @param string $shareWith
294
	 * @param int $fileSource File ID that is being shared
295
	 * @param string $itemType File type that is being shared (file or folder)
296
	 * @param string $fileTarget File path
297
	 * @param int $shareId The Share ID of this share
298
	 * @param bool $isSharing True if sharing, false if unsharing
299
	 */
300 6
	protected function shareFileOrFolderWithGroup($shareWith, $fileSource, $itemType, $fileTarget, $shareId, $isSharing) {
301 6
		if ($isSharing) {
302 6
			$actionSharer = 'shared_group_self';
303 6
			$actionOwner = 'reshared_group_by';
304 6
			$actionUser = 'shared_with_by';
305
		} else {
306
			$actionSharer = 'unshared_group_self';
307
			$actionOwner = 'unshared_group_by';
308
			$actionUser = 'unshared_by';
309
		}
310
311
		// Members of the new group
312 6
		$group = $this->groupManager->get($shareWith);
313 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...
314 1
			return;
315
		}
316
317
		// User performing the share
318 5
		$this->shareNotificationForSharer($actionSharer, $shareWith, $fileSource, $itemType);
319 5
		$this->shareNotificationForOriginalOwners($this->currentUser, $actionOwner, $shareWith, $fileSource, $itemType);
0 ignored issues
show
Security Bug introduced by
It seems like $this->currentUser can also be of type false; however, OCA\Activity\FilesHooks:...tionForOriginalOwners() does only seem to accept string, did you maybe forget to handle an error condition?
Loading history...
320
321 5
		$offset = 0;
322 5
		$users = $group->searchUsers('', self::USER_BATCH_SIZE, $offset);
323 5
		while (!empty($users)) {
324 4
			$this->addNotificationsForGroupUsers($users, $actionUser, $fileSource, $itemType, $fileTarget, $shareId);
325 4
			$offset += self::USER_BATCH_SIZE;
326 4
			$users = $group->searchUsers('', self::USER_BATCH_SIZE, $offset);
327
		}
328 5
	}
329
330
	/**
331
	 * @param IUser[] $usersInGroup
332
	 * @param string $actionUser
333
	 * @param int $fileSource File ID that is being shared
334
	 * @param string $itemType File type that is being shared (file or folder)
335
	 * @param string $fileTarget File path
336
	 * @param int $shareId The Share ID of this share
337
	 */
338 4
	protected function addNotificationsForGroupUsers(array $usersInGroup, $actionUser, $fileSource, $itemType, $fileTarget, $shareId) {
339 4
		$affectedUsers = [];
340
341 4
		foreach ($usersInGroup as $user) {
342 4
			$affectedUsers[$user->getUID()] = $fileTarget;
343
		}
344
345
		// Remove the triggering user, we already managed his notifications
346 4
		unset($affectedUsers[$this->currentUser]);
347
348 4
		if (empty($affectedUsers)) {
349 1
			return;
350
		}
351
352 3
		$userIds = \array_keys($affectedUsers);
353 3
		$filteredStreamUsersInGroup = $this->userSettings->filterUsersBySetting($userIds, 'stream', Files_Sharing::TYPE_SHARED);
354 3
		$filteredEmailUsersInGroup = $this->userSettings->filterUsersBySetting($userIds, 'email', Files_Sharing::TYPE_SHARED);
355
356 3
		$affectedUsers = $this->fixPathsForShareExceptions($affectedUsers, $shareId);
357 3
		foreach ($affectedUsers as $user => $path) {
358 3
			if (empty($filteredStreamUsersInGroup[$user]) && empty($filteredEmailUsersInGroup[$user])) {
359 2
				continue;
360
			}
361
362 1
			$this->addNotificationsForUser(
363 1
				$user, $actionUser, [[$fileSource => $path], $this->currentUser],
364 1
				$fileSource, $path, ($itemType === 'file'),
365 1
				!empty($filteredStreamUsersInGroup[$user]),
366 1
				!empty($filteredEmailUsersInGroup[$user]) ? $filteredEmailUsersInGroup[$user] : 0
367
			);
368
		}
369 3
	}
370
371
	/**
372
	 * Check when there was a naming conflict and the target is different
373
	 * for some of the users
374
	 *
375
	 * @param array $affectedUsers
376
	 * @param int $shareId
377
	 * @return mixed
378
	 */
379
	protected function fixPathsForShareExceptions(array $affectedUsers, $shareId) {
380
		$queryBuilder = $this->connection->getQueryBuilder();
381
		$queryBuilder->select(['share_with', 'file_target'])
382
			->from('share')
383
			->where($queryBuilder->expr()->eq('parent', $queryBuilder->createParameter('parent')))
384
			->setParameter('parent', (int) $shareId);
385
		$query = $queryBuilder->execute();
386
387
		while ($row = $query->fetch()) {
388
			$affectedUsers[$row['share_with']] = $row['file_target'];
389
		}
390
391
		return $affectedUsers;
392
	}
393
394
	/**
395
	 * Sharing a file or folder via link/public
396
	 *
397
	 * @param int $fileSource File ID that is being shared
398
	 * @param string $itemType File type that is being shared (file or folder)
399
	 * @param string $linkOwner
400
	 * @param bool $isSharing True if sharing, false if unsharing
401
	 */
402 2
	protected function shareFileOrFolderByLink($fileSource, $itemType, $linkOwner, $isSharing) {
403 2
		if ($isSharing) {
404 2
			$actionSharer = 'shared_link_self';
405 2
			$actionOwner = 'reshared_link_by';
406
		} elseif ($this->currentUser !== $linkOwner) {
407
			// Link expired
408
			$actionSharer = 'link_expired';
409
			$actionOwner = 'link_by_expired';
410
			$this->currentUser = $linkOwner;
411
			\OC::$server->getUserFolder($linkOwner);
412
		} else {
413
			$actionSharer = 'unshared_link_self';
414
			$actionOwner = 'unshared_link_by';
415
		}
416
417 2
		$this->view->chroot('/' . $this->currentUser . '/files');
418
419
		try {
420 2
			$path = $this->view->getPath($fileSource);
421 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...
422 1
			return;
423
		}
424
425 1
		$this->shareNotificationForOriginalOwners($this->currentUser, $actionOwner, '', $fileSource, $itemType);
0 ignored issues
show
Security Bug introduced by
It seems like $this->currentUser can also be of type false; however, OCA\Activity\FilesHooks:...tionForOriginalOwners() does only seem to accept string, did you maybe forget to handle an error condition?
Loading history...
426
427 1
		$this->addNotificationsForUser(
428 1
			$this->currentUser, $actionSharer, [[$fileSource => $path]],
0 ignored issues
show
Security Bug introduced by
It seems like $this->currentUser can also be of type false; however, OCA\Activity\FilesHooks::addNotificationsForUser() does only seem to accept string, did you maybe forget to handle an error condition?
Loading history...
429 1
			(int) $fileSource, $path, ($itemType === 'file'),
430 1
			$this->userSettings->getUserSetting($this->currentUser, 'stream', Files_Sharing::TYPE_SHARED),
0 ignored issues
show
Security Bug introduced by
It seems like $this->currentUser can also be of type false; however, OCA\Activity\UserSettings::getUserSetting() does only seem to accept string, did you maybe forget to handle an error condition?
Loading history...
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...
431 1
			$this->userSettings->getUserSetting($this->currentUser, 'email', Files_Sharing::TYPE_SHARED) ? $this->userSettings->getUserSetting($this->currentUser, 'setting', 'batchtime') : 0
0 ignored issues
show
Security Bug introduced by
It seems like $this->currentUser can also be of type false; however, OCA\Activity\UserSettings::getUserSetting() does only seem to accept string, did you maybe forget to handle an error condition?
Loading history...
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...
432
		);
433 1
	}
434
435
	/**
436
	 * Add notifications for the user that shares a file/folder
437
	 *
438
	 * @param string $subject
439
	 * @param string $shareWith
440
	 * @param int $fileSource
441
	 * @param string $itemType
442
	 */
443 2
	protected function shareNotificationForSharer($subject, $shareWith, $fileSource, $itemType) {
444 2
		$this->view->chroot('/' . $this->currentUser . '/files');
445
446
		try {
447 2
			$path = $this->view->getPath($fileSource);
448 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...
449 1
			return;
450
		}
451
452 1
		$this->addNotificationsForUser(
453 1
			$this->currentUser, $subject, [[$fileSource => $path], $shareWith],
0 ignored issues
show
Security Bug introduced by
It seems like $this->currentUser can also be of type false; however, OCA\Activity\FilesHooks::addNotificationsForUser() does only seem to accept string, did you maybe forget to handle an error condition?
Loading history...
454 1
			$fileSource, $path, ($itemType === 'file'),
455 1
			$this->userSettings->getUserSetting($this->currentUser, 'stream', Files_Sharing::TYPE_SHARED),
0 ignored issues
show
Security Bug introduced by
It seems like $this->currentUser can also be of type false; however, OCA\Activity\UserSettings::getUserSetting() does only seem to accept string, did you maybe forget to handle an error condition?
Loading history...
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...
456 1
			$this->userSettings->getUserSetting($this->currentUser, 'email', Files_Sharing::TYPE_SHARED) ? $this->userSettings->getUserSetting($this->currentUser, 'setting', 'batchtime') : 0
0 ignored issues
show
Security Bug introduced by
It seems like $this->currentUser can also be of type false; however, OCA\Activity\UserSettings::getUserSetting() does only seem to accept string, did you maybe forget to handle an error condition?
Loading history...
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...
457
		);
458 1
	}
459
460
	/**
461
	 * Add notifications for the user that shares a file/folder
462
	 *
463
	 * @param string $owner
464
	 * @param string $subject
465
	 * @param string $shareWith
466
	 * @param int $fileSource
467
	 * @param string $itemType
468
	 */
469 2
	protected function reshareNotificationForSharer($owner, $subject, $shareWith, $fileSource, $itemType) {
470 2
		$this->view->chroot('/' . $owner . '/files');
471
472
		try {
473 2
			$path = $this->view->getPath($fileSource);
474 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...
475 1
			return;
476
		}
477
478 1
		$this->addNotificationsForUser(
479 1
			$owner, $subject, [[$fileSource => $path], $this->currentUser, $shareWith],
480 1
			$fileSource, $path, ($itemType === 'file'),
481 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...
482 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...
483
		);
484 1
	}
485
486
	/**
487
	 * Add notifications for the owners whose files have been reshared
488
	 *
489
	 * @param string $currentOwner
490
	 * @param string $subject
491
	 * @param string $shareWith
492
	 * @param int $fileSource
493
	 * @param string $itemType
494
	 */
495 10
	protected function shareNotificationForOriginalOwners($currentOwner, $subject, $shareWith, $fileSource, $itemType) {
496
		// Get the full path of the current user
497 10
		$this->view->chroot('/' . $currentOwner . '/files');
498
499
		try {
500 10
			$path = $this->view->getPath($fileSource);
501 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...
502 1
			return;
503
		}
504
505
		/**
506
		 * Get the original owner and his path
507
		 */
508 9
		$owner = $this->view->getOwner($path);
509 9
		if ($owner !== $currentOwner) {
510 7
			$this->reshareNotificationForSharer($owner, $subject, $shareWith, $fileSource, $itemType);
511
		}
512
513
		/**
514
		 * Get the sharee who shared the item with the currentUser
515
		 */
516 9
		$this->view->chroot('/' . $currentOwner . '/files');
517 9
		$mount = $this->view->getMount($path);
518 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...
519 1
			return;
520
		}
521
522 8
		$storage = $mount->getStorage();
523 8
		if (!$storage->instanceOfStorage('OC\Files\Storage\Shared')) {
524 1
			return;
525
		}
526
527
		/** @var \OC\Files\Storage\Shared $storage */
528 7
		$shareOwner = $storage->getSharedFrom();
529 7
		if ($shareOwner === '' || $shareOwner === null || $shareOwner === $owner || $shareOwner === $currentOwner) {
530 5
			return;
531
		}
532
533 2
		$this->reshareNotificationForSharer($shareOwner, $subject, $shareWith, $fileSource, $itemType);
534 2
	}
535
536
	/**
537
	 * Adds the activity and email for a user when the settings require it
538
	 *
539
	 * @param string $user
540
	 * @param string $subject
541
	 * @param array $subjectParams
542
	 * @param int $fileId
543
	 * @param string $path
544
	 * @param bool $isFile If the item is a file, we link to the parent directory
545
	 * @param bool $streamSetting
546
	 * @param int $emailSetting
547
	 * @param string $type
548
	 */
549 11
	protected function addNotificationsForUser($user, $subject, $subjectParams, $fileId, $path, $isFile, $streamSetting, $emailSetting, $type = Files_Sharing::TYPE_SHARED) {
550 11
		if (!$streamSetting && !$emailSetting) {
551 1
			return;
552
		}
553
554 10
		$selfAction = $user === $this->currentUser;
555 10
		$app = $type === Files_Sharing::TYPE_SHARED ? 'files_sharing' : 'files';
556 10
		$link = $this->urlGenerator->linkToRouteAbsolute('files.view.index', [
557 10
			'dir' => ($isFile) ? \dirname($path) : $path,
558
		]);
559
560 10
		$objectType = ($fileId) ? 'files' : '';
561
562 10
		$event = $this->manager->generateEvent();
563 10
		$event->setApp($app)
564 10
			->setType($type)
565 10
			->setAffectedUser($user)
566 10
			->setAuthor($this->currentUser)
567 10
			->setTimestamp(\time())
568 10
			->setSubject($subject, $subjectParams)
569 10
			->setObject($objectType, $fileId, $path)
570 10
			->setLink($link);
571
572
		// Add activity to stream
573 10
		if ($streamSetting && (!$selfAction || $this->userSettings->getUserSetting($this->currentUser, 'setting', 'self'))) {
574 3
			$this->activityData->send($event);
575
		}
576
577
		// Add activity to mail queue
578 10
		if ($emailSetting && (!$selfAction || $this->userSettings->getUserSetting($this->currentUser, 'setting', 'selfemail'))) {
579 5
			$latestSend = \time() + $emailSetting;
580 5
			$this->activityData->storeMail($event, $latestSend);
581
		}
582 10
	}
583
}
584