Completed
Push — stable9 ( 4170cb...e5f189 )
by Joas
7s
created

FilesHooks::fileUpdate()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

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