Completed
Pull Request — stable9 (#58)
by Joas
02:24
created

FilesHooks::fileCreate()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 7
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 2

Importance

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