Cancelled
Push — stable9 ( 48dd99...f67948 )
by Joas
685:53 queued 685:53
created

FilesHooks::shareFileOrFolderByLink()   B

Complexity

Conditions 5
Paths 6

Size

Total Lines 32
Code Lines 23

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 17
CRAP Score 5.6205

Importance

Changes 1
Bugs 0 Features 1
Metric Value
c 1
b 0
f 1
dl 0
loc 32
ccs 17
cts 24
cp 0.7083
rs 8.439
cc 5
eloc 23
nc 6
nop 4
crap 5.6205
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 1
		} 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
		if (Files::TYPE_SHARE_CREATED) {
151
			try {
152 2
				list($filePath, $uidOwner, $fileId) = $this->getSourcePathAndOwner($filePath);
153 2
			} catch (\OCP\Files\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...
154
				// File not found? Sounds weird, but this happens before 9.1:
155
				// https://github.com/owncloud/core/issues/23212
156
				// Chunk assembling triggered the exact same hooks twice.
157
				// The first call however is before the file is in the database.
158
				// So when trying to get the owner, the file can not be found.
159
				// But since the second hook will come along, we simply ignore this.
160
				return;
161
			}
162 2
		} else {
163
			list($filePath, $uidOwner, $fileId) = $this->getSourcePathAndOwner($filePath);
164
		}
165 2
		$affectedUsers = $this->getUserPathsFromPath($filePath, $uidOwner);
166 2
		$filteredStreamUsers = $this->userSettings->filterUsersBySetting(array_keys($affectedUsers), 'stream', $activityType);
167 2
		$filteredEmailUsers = $this->userSettings->filterUsersBySetting(array_keys($affectedUsers), 'email', $activityType);
168
169 2
		foreach ($affectedUsers as $user => $path) {
170 2
			if (empty($filteredStreamUsers[$user]) && empty($filteredEmailUsers[$user])) {
171 2
				continue;
172
			}
173
174 2
			if ($user === $this->currentUser) {
175 1
				$userSubject = $subject;
176 1
				$userParams = [[$fileId => $path]];
177 1
			} else {
178 1
				$userSubject = $subjectBy;
179 1
				$userParams = [[$fileId => $path], $this->currentUser];
180
			}
181
182 2
			$this->addNotificationsForUser(
183 2
				$user, $userSubject, $userParams,
184 2
				$fileId, $path, true,
185 2
				!empty($filteredStreamUsers[$user]),
186 2
				!empty($filteredEmailUsers[$user]) ? $filteredEmailUsers[$user] : 0,
187
				$activityType
188 2
			);
189 2
		}
190 2
	}
191
192
	/**
193
	 * Returns a "username => path" map for all affected users
194
	 *
195
	 * @param string $path
196
	 * @param string $uidOwner
197
	 * @return array
198
	 */
199
	protected function getUserPathsFromPath($path, $uidOwner) {
200
		return Share::getUsersSharingFile($path, $uidOwner, true, true);
201
	}
202
203
	/**
204
	 * Return the source
205
	 *
206
	 * @param string $path
207
	 * @return array
208
	 */
209
	protected function getSourcePathAndOwner($path) {
210
		$uidOwner = Filesystem::getOwner($path);
211
		$fileId = 0;
212
213
		if ($uidOwner !== $this->currentUser) {
214
			Filesystem::initMountPoints($uidOwner);
215
		}
216
		$info = Filesystem::getFileInfo($path);
217
		if ($info !== false) {
218
			$ownerView = new View('/' . $uidOwner . '/files');
219
			$fileId = (int) $info['fileid'];
220
			$path = $ownerView->getPath($fileId);
221
		}
222
223
		return array($path, $uidOwner, $fileId);
224
	}
225
226
	/**
227
	 * Manage sharing events
228
	 * @param array $params The hook params
229
	 */
230 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...
231 3
		if ($params['itemType'] === 'file' || $params['itemType'] === 'folder') {
232 3
			if ((int) $params['shareType'] === Share::SHARE_TYPE_USER) {
233 1
				$this->shareFileOrFolderWithUser($params['shareWith'], (int) $params['fileSource'], $params['itemType'], $params['fileTarget'], true);
234 3
			} else if ((int) $params['shareType'] === Share::SHARE_TYPE_GROUP) {
235 1
				$this->shareFileOrFolderWithGroup($params['shareWith'], (int) $params['fileSource'], $params['itemType'], $params['fileTarget'], (int) $params['id'], true);
236 2
			} else if ((int) $params['shareType'] === Share::SHARE_TYPE_LINK) {
237 1
				$this->shareFileOrFolderByLink((int) $params['fileSource'], $params['itemType'], $params['uidOwner'], true);
238 1
			}
239 3
		}
240 3
	}
241
242
	/**
243
	 * Manage sharing events
244
	 * @param array $params The hook params
245
	 */
246 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...
247
		if ($params['itemType'] === 'file' || $params['itemType'] === 'folder') {
248
			if ((int) $params['shareType'] === Share::SHARE_TYPE_USER) {
249
				$this->shareFileOrFolderWithUser($params['shareWith'], (int) $params['fileSource'], $params['itemType'], $params['fileTarget'], false);
250
			} else if ((int) $params['shareType'] === Share::SHARE_TYPE_GROUP) {
251
				$this->shareFileOrFolderWithGroup($params['shareWith'], (int) $params['fileSource'], $params['itemType'], $params['fileTarget'], (int) $params['id'], false);
252
			} else if ((int) $params['shareType'] === Share::SHARE_TYPE_LINK) {
253
				$this->shareFileOrFolderByLink((int) $params['fileSource'], $params['itemType'], $params['uidOwner'], false);
254
			}
255
		}
256
	}
257
258
	/**
259
	 * Sharing a file or folder with a user
260
	 *
261
	 * @param string $shareWith
262
	 * @param int $fileSource File ID that is being shared
263
	 * @param string $itemType File type that is being shared (file or folder)
264
	 * @param string $fileTarget File path
265
	 * @param bool $isSharing True if sharing, false if unsharing
266
	 */
267 2
	protected function shareFileOrFolderWithUser($shareWith, $fileSource, $itemType, $fileTarget, $isSharing) {
268 2
		if ($isSharing) {
269 2
			$actionSharer = 'shared_user_self';
270 2
			$actionOwner = 'reshared_user_by';
271 2
			$actionUser = 'shared_with_by';
272 2
		} else {
273
			$actionSharer = 'unshared_user_self';
274
			$actionOwner = 'unshared_user_by';
275
			$actionUser = 'unshared_by';
276
		}
277
278
		// User performing the share
279 2
		$this->shareNotificationForSharer($actionSharer, $shareWith, $fileSource, $itemType);
280 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...
281
282
		// New shared user
283 2
		$this->addNotificationsForUser(
284 2
			$shareWith, $actionUser, [[$fileSource => $fileTarget], $this->currentUser],
285 2
			(int) $fileSource, $fileTarget, ($itemType === 'file'),
286 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...
287 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...
288 2
		);
289 2
	}
290
291
	/**
292
	 * Sharing a file or folder with a group
293
	 *
294
	 * @param string $shareWith
295
	 * @param int $fileSource File ID that is being shared
296
	 * @param string $itemType File type that is being shared (file or folder)
297
	 * @param string $fileTarget File path
298
	 * @param int $shareId The Share ID of this share
299
	 * @param bool $isSharing True if sharing, false if unsharing
300
	 */
301 6
	protected function shareFileOrFolderWithGroup($shareWith, $fileSource, $itemType, $fileTarget, $shareId, $isSharing) {
302 6
		if ($isSharing) {
303 6
			$actionSharer = 'shared_group_self';
304 6
			$actionOwner = 'reshared_group_by';
305 6
			$actionUser = 'shared_with_by';
306 6
		} else {
307
			$actionSharer = 'unshared_group_self';
308
			$actionOwner = 'unshared_group_by';
309
			$actionUser = 'unshared_by';
310
		}
311
312
		// Members of the new group
313 6
		$group = $this->groupManager->get($shareWith);
314 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...
315 1
			return;
316
		}
317
318
		// User performing the share
319 5
		$this->shareNotificationForSharer($actionSharer, $shareWith, $fileSource, $itemType);
320 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...
321
322 5
		$offset = 0;
323 5
		$users = $group->searchUsers('', self::USER_BATCH_SIZE, $offset);
324 5
		while (!empty($users)) {
325 4
			$this->addNotificationsForGroupUsers($users, $actionUser, $fileSource, $itemType, $fileTarget, $shareId);
326 4
			$offset += self::USER_BATCH_SIZE;
327 4
			$users = $group->searchUsers('', self::USER_BATCH_SIZE, $offset);
328 4
		}
329 5
	}
330
331
	/**
332
	 * @param IUser[] $usersInGroup
333
	 * @param string $actionUser
334
	 * @param int $fileSource File ID that is being shared
335
	 * @param string $itemType File type that is being shared (file or folder)
336
	 * @param string $fileTarget File path
337
	 * @param int $shareId The Share ID of this share
338
	 */
339 4
	protected function addNotificationsForGroupUsers(array $usersInGroup, $actionUser, $fileSource, $itemType, $fileTarget, $shareId) {
340 4
		$affectedUsers = [];
341
342 4
		foreach ($usersInGroup as $user) {
343 4
			$affectedUsers[$user->getUID()] = $fileTarget;
344 4
		}
345
346
		// Remove the triggering user, we already managed his notifications
347 4
		unset($affectedUsers[$this->currentUser]);
348
349 4
		if (empty($affectedUsers)) {
350 1
			return;
351
		}
352
353 3
		$userIds = array_keys($affectedUsers);
354 3
		$filteredStreamUsersInGroup = $this->userSettings->filterUsersBySetting($userIds, 'stream', Files_Sharing::TYPE_SHARED);
355 3
		$filteredEmailUsersInGroup = $this->userSettings->filterUsersBySetting($userIds, 'email', Files_Sharing::TYPE_SHARED);
356
357 3
		$affectedUsers = $this->fixPathsForShareExceptions($affectedUsers, $shareId);
358 3
		foreach ($affectedUsers as $user => $path) {
359 3
			if (empty($filteredStreamUsersInGroup[$user]) && empty($filteredEmailUsersInGroup[$user])) {
360 2
				continue;
361
			}
362
363 1
			$this->addNotificationsForUser(
364 1
				$user, $actionUser, [[$fileSource => $path], $this->currentUser],
365 1
				$fileSource, $path, ($itemType === 'file'),
366 1
				!empty($filteredStreamUsersInGroup[$user]),
367 1
				!empty($filteredEmailUsersInGroup[$user]) ? $filteredEmailUsersInGroup[$user] : 0
368 1
			);
369 3
		}
370 3
	}
371
372
	/**
373
	 * Check when there was a naming conflict and the target is different
374
	 * for some of the users
375
	 *
376
	 * @param array $affectedUsers
377
	 * @param int $shareId
378
	 * @return mixed
379
	 */
380
	protected function fixPathsForShareExceptions(array $affectedUsers, $shareId) {
381
		$queryBuilder = $this->connection->getQueryBuilder();
382
		$queryBuilder->select(['share_with', 'file_target'])
383
			->from('share')
384
			->where($queryBuilder->expr()->eq('parent', $queryBuilder->createParameter('parent')))
385
			->setParameter('parent', (int) $shareId);
386
		$query = $queryBuilder->execute();
387
388
		while ($row = $query->fetch()) {
389
			$affectedUsers[$row['share_with']] = $row['file_target'];
390
		}
391
392
		return $affectedUsers;
393
	}
394
395
	/**
396
	 * Sharing a file or folder via link/public
397
	 *
398
	 * @param int $fileSource File ID that is being shared
399
	 * @param string $itemType File type that is being shared (file or folder)
400
	 * @param string $linkOwner
401
	 * @param bool $isSharing True if sharing, false if unsharing
402
	 */
403 2
	protected function shareFileOrFolderByLink($fileSource, $itemType, $linkOwner, $isSharing) {
404 2
		if ($isSharing) {
405 2
			$actionSharer = 'shared_link_self';
406 2
			$actionOwner = 'reshared_link_by';
407 2
		} else if ($this->currentUser !== $linkOwner) {
408
			// Link expired
409
			$actionSharer = 'link_expired';
410
			$actionOwner = 'link_by_expired';
411
			$this->currentUser = $linkOwner;
412
			\OC::$server->getUserFolder($linkOwner);
413
		} else {
414
			$actionSharer = 'unshared_link_self';
415
			$actionOwner = 'unshared_link_by';
416
		}
417
418 2
		$this->view->chroot('/' . $this->currentUser . '/files');
419
420
		try {
421 2
			$path = $this->view->getPath($fileSource);
422 2
		} 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...
423 1
			return;
424
		}
425
426 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...
427
428 1
		$this->addNotificationsForUser(
429 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...
430 1
			(int) $fileSource, $path, ($itemType === 'file'),
431 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...
432 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...
433 1
		);
434 1
	}
435
436
	/**
437
	 * Add notifications for the user that shares a file/folder
438
	 *
439
	 * @param string $subject
440
	 * @param string $shareWith
441
	 * @param int $fileSource
442
	 * @param string $itemType
443
	 */
444 2
	protected function shareNotificationForSharer($subject, $shareWith, $fileSource, $itemType) {
445 2
		$this->view->chroot('/' . $this->currentUser . '/files');
446
447
		try {
448 2
			$path = $this->view->getPath($fileSource);
449 2
		} 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...
450 1
			return;
451
		}
452
453 1
		$this->addNotificationsForUser(
454 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...
455 1
			$fileSource, $path, ($itemType === 'file'),
456 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...
457 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...
458 1
		);
459 1
	}
460
461
	/**
462
	 * Add notifications for the user that shares a file/folder
463
	 *
464
	 * @param string $owner
465
	 * @param string $subject
466
	 * @param string $shareWith
467
	 * @param int $fileSource
468
	 * @param string $itemType
469
	 */
470 2
	protected function reshareNotificationForSharer($owner, $subject, $shareWith, $fileSource, $itemType) {
471 2
		$this->view->chroot('/' . $owner . '/files');
472
473
		try {
474 2
			$path = $this->view->getPath($fileSource);
475 2
		} 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...
476 1
			return;
477
		}
478
479 1
		$this->addNotificationsForUser(
480 1
			$owner, $subject, [[$fileSource => $path], $this->currentUser, $shareWith],
481 1
			$fileSource, $path, ($itemType === 'file'),
482 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...
483 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...
484 1
		);
485 1
	}
486
487
	/**
488
	 * Add notifications for the owners whose files have been reshared
489
	 *
490
	 * @param string $currentOwner
491
	 * @param string $subject
492
	 * @param string $shareWith
493
	 * @param int $fileSource
494
	 * @param string $itemType
495
	 */
496 10
	protected function shareNotificationForOriginalOwners($currentOwner, $subject, $shareWith, $fileSource, $itemType) {
497
		// Get the full path of the current user
498 10
		$this->view->chroot('/' . $currentOwner . '/files');
499
500
		try {
501 10
			$path = $this->view->getPath($fileSource);
502 10
		} 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...
503 1
			return;
504
		}
505
506
		/**
507
		 * Get the original owner and his path
508
		 */
509 9
		$owner = $this->view->getOwner($path);
510 9
		if ($owner !== $currentOwner) {
511 7
			$this->reshareNotificationForSharer($owner, $subject, $shareWith, $fileSource, $itemType);
512 7
		}
513
514
		/**
515
		 * Get the sharee who shared the item with the currentUser
516
		 */
517 9
		$this->view->chroot('/' . $currentOwner . '/files');
518 9
		$mount = $this->view->getMount($path);
519 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...
520 1
			return;
521
		}
522
523 8
		$storage = $mount->getStorage();
524 8
		if (!$storage->instanceOfStorage('OC\Files\Storage\Shared')) {
525 1
			return;
526
		}
527
528
		/** @var \OC\Files\Storage\Shared $storage */
529 7
		$shareOwner = $storage->getSharedFrom();
530 7
		if ($shareOwner === '' || $shareOwner === null || $shareOwner === $owner || $shareOwner === $currentOwner) {
531 5
			return;
532
		}
533
534 2
		$this->reshareNotificationForSharer($shareOwner, $subject, $shareWith, $fileSource, $itemType);
535 2
	}
536
537
	/**
538
	 * Adds the activity and email for a user when the settings require it
539
	 *
540
	 * @param string $user
541
	 * @param string $subject
542
	 * @param array $subjectParams
543
	 * @param int $fileId
544
	 * @param string $path
545
	 * @param bool $isFile If the item is a file, we link to the parent directory
546
	 * @param bool $streamSetting
547
	 * @param int $emailSetting
548
	 * @param string $type
549
	 */
550 11
	protected function addNotificationsForUser($user, $subject, $subjectParams, $fileId, $path, $isFile, $streamSetting, $emailSetting, $type = Files_Sharing::TYPE_SHARED) {
551 11
		if (!$streamSetting && !$emailSetting) {
552 1
			return;
553
		}
554
555 10
		$selfAction = $user === $this->currentUser;
556 10
		$app = $type === Files_Sharing::TYPE_SHARED ? 'files_sharing' : 'files';
557 10
		$link = $this->urlGenerator->linkToRouteAbsolute('files.view.index', array(
558 10
			'dir' => ($isFile) ? dirname($path) : $path,
559 10
		));
560
561 10
		$objectType = ($fileId) ? 'files' : '';
562
563 10
		$event = $this->manager->generateEvent();
564 10
		$event->setApp($app)
565 10
			->setType($type)
566 10
			->setAffectedUser($user)
567 10
			->setAuthor($this->currentUser)
568 10
			->setTimestamp(time())
569 10
			->setSubject($subject, $subjectParams)
570 10
			->setObject($objectType, $fileId, $path)
571 10
			->setLink($link);
572
573
		// Add activity to stream
574 10
		if ($streamSetting && (!$selfAction || $this->userSettings->getUserSetting($this->currentUser, 'setting', 'self'))) {
575 3
			$this->activityData->send($event);
576 3
		}
577
578
		// Add activity to mail queue
579 10
		if ($emailSetting && (!$selfAction || $this->userSettings->getUserSetting($this->currentUser, 'setting', 'selfemail'))) {
580 5
			$latestSend = time() + $emailSetting;
581 5
			$this->activityData->storeMail($event, $latestSend);
582 5
		}
583 10
	}
584
}
585