Completed
Pull Request — master (#9919)
by John
17:54
created

Group::canAddUser()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 8
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 5
nc 3
nop 0
dl 0
loc 8
rs 9.4285
c 0
b 0
f 0
1
<?php
2
/**
3
 * @copyright Copyright (c) 2016, ownCloud, Inc.
4
 *
5
 * @author Arthur Schiwon <[email protected]>
6
 * @author Bart Visscher <[email protected]>
7
 * @author Lukas Reschke <[email protected]>
8
 * @author Morris Jobke <[email protected]>
9
 * @author Robin Appelman <[email protected]>
10
 * @author Robin McCorkell <[email protected]>
11
 * @author Roeland Jago Douma <[email protected]>
12
 * @author Vincent Petry <[email protected]>
13
 * @author John Molakvoæ <[email protected]>
14
 *
15
 * @license AGPL-3.0
16
 *
17
 * This code is free software: you can redistribute it and/or modify
18
 * it under the terms of the GNU Affero General Public License, version 3,
19
 * as published by the Free Software Foundation.
20
 *
21
 * This program is distributed in the hope that it will be useful,
22
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
23
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24
 * GNU Affero General Public License for more details.
25
 *
26
 * You should have received a copy of the GNU Affero General Public License, version 3,
27
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
28
 *
29
 */
30
31
namespace OC\Group;
32
33
use OCP\GroupInterface;
34
use OCP\IGroup;
35
use OCP\IUser;
36
use OCP\Group\Backend\ICountDisabledInGroup;
37
38
class Group implements IGroup {
39
	/** @var null|string  */
40
	protected $displayName;
41
42
	/**
43
	 * @var string $id
44
	 */
45
	private $gid;
46
47
	/**
48
	 * @var \OC\User\User[] $users
49
	 */
50
	private $users = array();
51
52
	/**
53
	 * @var bool $usersLoaded
54
	 */
55
	private $usersLoaded;
56
57
	/**
58
	 * @var \OC\Group\Backend[]|\OC\Group\Database[] $backend
59
	 */
60
	private $backends;
61
62
	/**
63
	 * @var \OC\Hooks\PublicEmitter $emitter
64
	 */
65
	private $emitter;
66
67
	/**
68
	 * @var \OC\User\Manager $userManager
69
	 */
70
	private $userManager;
71
72
	/**
73
	 * @param string $gid
74
	 * @param \OC\Group\Backend[] $backends
75
	 * @param \OC\User\Manager $userManager
76
	 * @param \OC\Hooks\PublicEmitter $emitter
77
	 * @param string $displayName
78
	 */
79
	public function __construct($gid, $backends, $userManager, $emitter = null, $displayName = null) {
80
		$this->gid = $gid;
81
		$this->backends = $backends;
82
		$this->userManager = $userManager;
83
		$this->emitter = $emitter;
84
		$this->displayName = $displayName;
85
	}
86
87
	public function getGID() {
88
		return $this->gid;
89
	}
90
91
	public function getDisplayName() {
92
		if (is_null($this->displayName)) {
93
			return $this->gid;
94
		}
95
		return $this->displayName;
96
	}
97
98
	/**
99
	 * get all users in the group
100
	 *
101
	 * @return \OC\User\User[]
102
	 */
103
	public function getUsers() {
104
		if ($this->usersLoaded) {
105
			return $this->users;
106
		}
107
108
		$userIds = array();
109
		foreach ($this->backends as $backend) {
110
			$diff = array_diff(
111
				$backend->usersInGroup($this->gid),
112
				$userIds
113
			);
114
			if ($diff) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $diff of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
115
				$userIds = array_merge($userIds, $diff);
116
			}
117
		}
118
119
		$this->users = $this->getVerifiedUsers($userIds);
120
		$this->usersLoaded = true;
121
		return $this->users;
122
	}
123
124
	/**
125
	 * check if a user is in the group
126
	 *
127
	 * @param IUser $user
128
	 * @return bool
129
	 */
130
	public function inGroup(IUser $user) {
131
		if (isset($this->users[$user->getUID()])) {
132
			return true;
133
		}
134
		foreach ($this->backends as $backend) {
135
			if ($backend->inGroup($user->getUID(), $this->gid)) {
136
				$this->users[$user->getUID()] = $user;
137
				return true;
138
			}
139
		}
140
		return false;
141
	}
142
143
	/**
144
	 * add a user to the group
145
	 *
146
	 * @param IUser $user
147
	 */
148
	public function addUser(IUser $user) {
149
		if ($this->inGroup($user)) {
150
			return;
151
		}
152
153
		if ($this->emitter) {
154
			$this->emitter->emit('\OC\Group', 'preAddUser', array($this, $user));
155
		}
156
		foreach ($this->backends as $backend) {
157
			if ($backend->implementsActions(\OC\Group\Backend::ADD_TO_GROUP)) {
158
				$backend->addToGroup($user->getUID(), $this->gid);
0 ignored issues
show
Bug introduced by
The method addToGroup() does not seem to exist on object<OC\Group\Backend>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
159
				if ($this->users) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->users of type OC\User\User[] is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
160
					$this->users[$user->getUID()] = $user;
161
				}
162
				if ($this->emitter) {
163
					$this->emitter->emit('\OC\Group', 'postAddUser', array($this, $user));
164
				}
165
				return;
166
			}
167
		}
168
	}
169
170
	/**
171
	 * remove a user from the group
172
	 *
173
	 * @param \OC\User\User $user
174
	 */
175
	public function removeUser($user) {
176
		$result = false;
177
		if ($this->emitter) {
178
			$this->emitter->emit('\OC\Group', 'preRemoveUser', array($this, $user));
179
		}
180
		foreach ($this->backends as $backend) {
181
			if ($backend->implementsActions(\OC\Group\Backend::REMOVE_FROM_GOUP) and $backend->inGroup($user->getUID(), $this->gid)) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
Using logical operators such as and instead of && is generally not recommended.

PHP has two types of connecting operators (logical operators, and boolean operators):

  Logical Operators Boolean Operator
AND - meaning and &&
OR - meaning or ||

The difference between these is the order in which they are executed. In most cases, you would want to use a boolean operator like &&, or ||.

Let’s take a look at a few examples:

// Logical operators have lower precedence:
$f = false or true;

// is executed like this:
($f = false) or true;


// Boolean operators have higher precedence:
$f = false || true;

// is executed like this:
$f = (false || true);

Logical Operators are used for Control-Flow

One case where you explicitly want to use logical operators is for control-flow such as this:

$x === 5
    or die('$x must be 5.');

// Instead of
if ($x !== 5) {
    die('$x must be 5.');
}

Since die introduces problems of its own, f.e. it makes our code hardly testable, and prevents any kind of more sophisticated error handling; you probably do not want to use this in real-world code. Unfortunately, logical operators cannot be combined with throw at this point:

// The following is currently a parse error.
$x === 5
    or throw new RuntimeException('$x must be 5.');

These limitations lead to logical operators rarely being of use in current PHP code.

Loading history...
182
				$backend->removeFromGroup($user->getUID(), $this->gid);
0 ignored issues
show
Bug introduced by
The method removeFromGroup() does not seem to exist on object<OC\Group\Backend>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
183
				$result = true;
184
			}
185
		}
186
		if ($result) {
187
			if ($this->emitter) {
188
				$this->emitter->emit('\OC\Group', 'postRemoveUser', array($this, $user));
189
			}
190
			if ($this->users) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->users of type OC\User\User[] is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
191
				foreach ($this->users as $index => $groupUser) {
192
					if ($groupUser->getUID() === $user->getUID()) {
193
						unset($this->users[$index]);
194
						return;
195
					}
196
				}
197
			}
198
		}
199
	}
200
201
	/**
202
	 * search for users in the group by userid
203
	 *
204
	 * @param string $search
205
	 * @param int $limit
206
	 * @param int $offset
207
	 * @return \OC\User\User[]
208
	 */
209
	public function searchUsers($search, $limit = null, $offset = null) {
210
		$users = array();
211
		foreach ($this->backends as $backend) {
212
			$userIds = $backend->usersInGroup($this->gid, $search, $limit, $offset);
213
			$users += $this->getVerifiedUsers($userIds);
214
			if (!is_null($limit) and $limit <= 0) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
Using logical operators such as and instead of && is generally not recommended.

PHP has two types of connecting operators (logical operators, and boolean operators):

  Logical Operators Boolean Operator
AND - meaning and &&
OR - meaning or ||

The difference between these is the order in which they are executed. In most cases, you would want to use a boolean operator like &&, or ||.

Let’s take a look at a few examples:

// Logical operators have lower precedence:
$f = false or true;

// is executed like this:
($f = false) or true;


// Boolean operators have higher precedence:
$f = false || true;

// is executed like this:
$f = (false || true);

Logical Operators are used for Control-Flow

One case where you explicitly want to use logical operators is for control-flow such as this:

$x === 5
    or die('$x must be 5.');

// Instead of
if ($x !== 5) {
    die('$x must be 5.');
}

Since die introduces problems of its own, f.e. it makes our code hardly testable, and prevents any kind of more sophisticated error handling; you probably do not want to use this in real-world code. Unfortunately, logical operators cannot be combined with throw at this point:

// The following is currently a parse error.
$x === 5
    or throw new RuntimeException('$x must be 5.');

These limitations lead to logical operators rarely being of use in current PHP code.

Loading history...
215
				return $users;
216
			}
217
		}
218
		return $users;
219
	}
220
221
	/**
222
	 * returns the number of users matching the search string
223
	 *
224
	 * @param string $search
225
	 * @return int|bool
226
	 */
227
	public function count($search = '') {
228
		$users = false;
229
		foreach ($this->backends as $backend) {
230
			if($backend->implementsActions(\OC\Group\Backend::COUNT_USERS)) {
231
				if($users === false) {
232
					//we could directly add to a bool variable, but this would
233
					//be ugly
234
					$users = 0;
235
				}
236
				$users += $backend->countUsersInGroup($this->gid, $search);
0 ignored issues
show
Bug introduced by
The method countUsersInGroup() does not exist on OC\Group\Backend. Did you maybe mean inGroup()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
237
			}
238
		}
239
		return $users;
0 ignored issues
show
Bug Compatibility introduced by
The expression return $users; of type integer|double|false is incompatible with the return type declared by the interface OCP\IGroup::count of type integer|boolean as it can also be of type double which is not included in this return type.
Loading history...
240
	}
241
242
	/**
243
	 * returns the number of disabled users
244
	 *
245
	 * @return int|bool
246
	 */
247 View Code Duplication
	public function countDisabled() {
248
		$users = false;
249
		foreach ($this->backends as $backend) {
250
			if($backend instanceOf ICountDisabledInGroup) {
251
				if($users === false) {
252
					//we could directly add to a bool variable, but this would
253
					//be ugly
254
					$users = 0;
255
				}
256
				$users += $backend->countDisabledInGroup($this->gid);
257
			}
258
		}
259
		return $users;
0 ignored issues
show
Bug Compatibility introduced by
The expression return $users; of type integer|double|false is incompatible with the return type declared by the interface OCP\IGroup::countDisabled of type integer|boolean as it can also be of type double which is not included in this return type.
Loading history...
260
	}
261
262
	/**
263
	 * search for users in the group by displayname
264
	 *
265
	 * @param string $search
266
	 * @param int $limit
267
	 * @param int $offset
268
	 * @return \OC\User\User[]
269
	 */
270
	public function searchDisplayName($search, $limit = null, $offset = null) {
271
		$users = array();
272
		foreach ($this->backends as $backend) {
273
			$userIds = $backend->usersInGroup($this->gid, $search, $limit, $offset);
274
			$users = $this->getVerifiedUsers($userIds);
275
			if (!is_null($limit) and $limit <= 0) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
Using logical operators such as and instead of && is generally not recommended.

PHP has two types of connecting operators (logical operators, and boolean operators):

  Logical Operators Boolean Operator
AND - meaning and &&
OR - meaning or ||

The difference between these is the order in which they are executed. In most cases, you would want to use a boolean operator like &&, or ||.

Let’s take a look at a few examples:

// Logical operators have lower precedence:
$f = false or true;

// is executed like this:
($f = false) or true;


// Boolean operators have higher precedence:
$f = false || true;

// is executed like this:
$f = (false || true);

Logical Operators are used for Control-Flow

One case where you explicitly want to use logical operators is for control-flow such as this:

$x === 5
    or die('$x must be 5.');

// Instead of
if ($x !== 5) {
    die('$x must be 5.');
}

Since die introduces problems of its own, f.e. it makes our code hardly testable, and prevents any kind of more sophisticated error handling; you probably do not want to use this in real-world code. Unfortunately, logical operators cannot be combined with throw at this point:

// The following is currently a parse error.
$x === 5
    or throw new RuntimeException('$x must be 5.');

These limitations lead to logical operators rarely being of use in current PHP code.

Loading history...
276
				return array_values($users);
277
			}
278
		}
279
		return array_values($users);
280
	}
281
282
	/**
283
	 * delete the group
284
	 *
285
	 * @return bool
286
	 */
287
	public function delete() {
288
		// Prevent users from deleting group admin
289
		if ($this->getGID() === 'admin') {
290
			return false;
291
		}
292
293
		$result = false;
294
		if ($this->emitter) {
295
			$this->emitter->emit('\OC\Group', 'preDelete', array($this));
296
		}
297
		foreach ($this->backends as $backend) {
298
			if ($backend->implementsActions(\OC\Group\Backend::DELETE_GROUP)) {
299
				$result = true;
300
				$backend->deleteGroup($this->gid);
0 ignored issues
show
Bug introduced by
The method deleteGroup() does not seem to exist on object<OC\Group\Backend>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
301
			}
302
		}
303
		if ($result and $this->emitter) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
Using logical operators such as and instead of && is generally not recommended.

PHP has two types of connecting operators (logical operators, and boolean operators):

  Logical Operators Boolean Operator
AND - meaning and &&
OR - meaning or ||

The difference between these is the order in which they are executed. In most cases, you would want to use a boolean operator like &&, or ||.

Let’s take a look at a few examples:

// Logical operators have lower precedence:
$f = false or true;

// is executed like this:
($f = false) or true;


// Boolean operators have higher precedence:
$f = false || true;

// is executed like this:
$f = (false || true);

Logical Operators are used for Control-Flow

One case where you explicitly want to use logical operators is for control-flow such as this:

$x === 5
    or die('$x must be 5.');

// Instead of
if ($x !== 5) {
    die('$x must be 5.');
}

Since die introduces problems of its own, f.e. it makes our code hardly testable, and prevents any kind of more sophisticated error handling; you probably do not want to use this in real-world code. Unfortunately, logical operators cannot be combined with throw at this point:

// The following is currently a parse error.
$x === 5
    or throw new RuntimeException('$x must be 5.');

These limitations lead to logical operators rarely being of use in current PHP code.

Loading history...
304
			$this->emitter->emit('\OC\Group', 'postDelete', array($this));
305
		}
306
		return $result;
307
	}
308
309
	/**
310
	 * returns all the Users from an array that really exists
311
	 * @param string[] $userIds an array containing user IDs
312
	 * @return \OC\User\User[] an Array with the userId as Key and \OC\User\User as value
313
	 */
314
	private function getVerifiedUsers($userIds) {
315
		if (!is_array($userIds)) {
316
			return array();
317
		}
318
		$users = array();
319
		foreach ($userIds as $userId) {
320
			$user = $this->userManager->get($userId);
321
			if (!is_null($user)) {
322
				$users[$userId] = $user;
323
			}
324
		}
325
		return $users;
326
	}
327
328
	/**
329
	 * @return bool
330
	 * @since 14.0.0
331
	 */
332
	public function canRemoveUser() {
333
		foreach ($this->backends as $backend) {
334
			if ($backend->implementsActions(GroupInterface::REMOVE_FROM_GOUP)) {
335
				return true;
336
			}
337
		}
338
		return false;
339
	}
340
341
	/**
342
	 * @return bool
343
	 * @since 14.0.0
344
	 */
345
	public function canAddUser() {
346
		foreach ($this->backends as $backend) {
347
			if ($backend->implementsActions(GroupInterface::ADD_TO_GROUP)) {
348
				return true;
349
			}
350
		}
351
		return false;
352
	}
353
}
354