Completed
Push — master ( 041121...2f1837 )
by Maxence
02:46
created

MembersRequest::indexOfMember()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 10
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 10
rs 9.4285
cc 3
eloc 5
nc 3
nop 2
1
<?php
2
/**
3
 * Circles - Bring cloud-users closer together.
4
 *
5
 * This file is licensed under the Affero General Public License version 3 or
6
 * later. See the COPYING file.
7
 *
8
 * @author Maxence Lange <[email protected]>
9
 * @copyright 2017
10
 * @license GNU AGPL version 3 or any later version
11
 *
12
 * This program is free software: you can redistribute it and/or modify
13
 * it under the terms of the GNU Affero General Public License as
14
 * published by the Free Software Foundation, either version 3 of the
15
 * License, or (at your option) any later version.
16
 *
17
 * This program is distributed in the hope that it will be useful,
18
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20
 * GNU Affero General Public License for more details.
21
 *
22
 * You should have received a copy of the GNU Affero General Public License
23
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
24
 *
25
 */
26
27
28
namespace OCA\Circles\Db;
29
30
31
use Doctrine\DBAL\Exception\UniqueConstraintViolationException;
32
use OCA\Circles\Exceptions\MemberAlreadyExistsException;
33
use OCA\Circles\Exceptions\MemberDoesNotExistException;
34
use OCA\Circles\Model\Member;
35
use OCP\IGroup;
36
37
class MembersRequest extends MembersRequestBuilder {
38
39
40
	/**
41
	 * Returns information about a member.
42
	 *
43
	 * WARNING: This function does not filters data regarding the current user/viewer.
44
	 *          In case of interaction with users, Please use MembersService->getMember() instead.
45
	 *
46
	 * @param string $circleUniqueId
47
	 * @param string $userId
48
	 * @param $type
49
	 *
50
	 * @return Member
51
	 * @throws MemberDoesNotExistException
52
	 */
53 View Code Duplication
	public function forceGetMember($circleUniqueId, $userId, $type) {
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...
54
		$qb = $this->getMembersSelectSql();
55
56
		$this->limitToUserId($qb, $userId);
57
		$this->limitToUserType($qb, $type);
58
		$this->limitToCircleId($qb, $circleUniqueId);
59
60
		$cursor = $qb->execute();
61
		$data = $cursor->fetch();
62
		$cursor->closeCursor();
63
64
		if ($data === false) {
65
			throw new MemberDoesNotExistException($this->l10n->t('This member does not exist'));
66
		}
67
68
		$member = $this->parseMembersSelectSql($data);
69
70
		return $member;
71
	}
72
73
74
	/**
75
	 * Returns members list of a circle, based on their level.
76
	 *
77
	 * WARNING: This function does not filters data regarding the current user/viewer.
78
	 *          In case of interaction with users, Please use getMembers() instead.
79
	 *
80
	 * @param string $circleUniqueId
81
	 * @param int $level
82
	 * @param bool $incGroup
83
	 *
84
	 * @return Member[]
85
	 */
86
	public function forceGetMembers($circleUniqueId, $level = Member::LEVEL_MEMBER, $incGroup = false) {
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 100 characters; contains 101 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
87
88
		$qb = $this->getMembersSelectSql();
89
		$this->limitToMembersAndAlmost($qb);
90
		$this->limitToLevel($qb, $level);
91
		$this->limitToCircleId($qb, $circleUniqueId);
92
93
		$members = [];
94
		$cursor = $qb->execute();
95
		while ($data = $cursor->fetch()) {
96
			$members[] = $this->parseMembersSelectSql($data);
97
		}
98
		$cursor->closeCursor();
99
100
		if ($this->configService->isLinkedGroupsAllowed() && $incGroup === true) {
101
			$this->includeGroupMembers($members, $circleUniqueId, $level);
102
		}
103
104
		return $members;
105
	}
106
107
108
	/**
109
	 * @param string $circleUniqueId
110
	 * @param Member $viewer
111
	 *
112
	 * @return Member[]
113
	 * @throws \Exception
114
	 */
115
	public function getMembers($circleUniqueId, Member $viewer) {
116
		try {
117
			$viewer->hasToBeMember();
118
119
			$members = $this->forceGetMembers($circleUniqueId, Member::LEVEL_NONE);
120
			if (!$viewer->isLevel(Member::LEVEL_MODERATOR)) {
121
				array_map(
122
					function(Member $m) {
123
						$m->setNote('');
124
					}, $members
125
				);
126
			}
127
128
			return $members;
129
		} catch (\Exception $e) {
130
			return [];
131
		}
132
	}
133
134
135
	/**
136
	 * forceGetGroup();
137
	 *
138
	 * returns group information as a member within a Circle.
139
	 *
140
	 * WARNING: This function does not filters data regarding the current user/viewer.
141
	 *          In case of interaction with users, Please use getGroup() instead.
142
	 *
143
	 * @param string $circleUniqueId
144
	 * @param string $groupId
145
	 *
146
	 * @return Member
147
	 * @throws MemberDoesNotExistException
148
	 */
149 View Code Duplication
	public function forceGetGroup($circleUniqueId, $groupId) {
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...
150
		$qb = $this->getGroupsSelectSql();
151
152
		$this->limitToGroupId($qb, $groupId);
153
		$this->limitToCircleId($qb, $circleUniqueId);
154
155
		$cursor = $qb->execute();
156
		$data = $cursor->fetch();
157
		if ($data === false) {
158
			throw new MemberDoesNotExistException($this->l10n->t('This member does not exist'));
159
		}
160
161
		$group = $this->parseGroupsSelectSql($data);
162
		$cursor->closeCursor();
163
164
		return $group;
165
	}
166
167
168
	/**
169
	 * includeGroupMembers();
170
	 *
171
	 * This function will get members of a circle throw NCGroups and fill the result an existing
172
	 * Members List. In case of duplicate, higher level will be kept.
173
	 *
174
	 * @param Member[] $members
175
	 * @param string $circleUniqueId
176
	 * @param int $level
177
	 */
178
	private function includeGroupMembers(array &$members, $circleUniqueId, $level) {
179
180
		$groupMembers = $this->forceGetGroupMembers($circleUniqueId, $level);
181
		$this->avoidDuplicateMembers($members, $groupMembers);
182
	}
183
184
185
	/**
186
	 * avoidDuplicateMembers();
187
	 *
188
	 * Use this function to add members to the list (1st argument), keeping the higher level in case
189
	 * of duplicate
190
	 *
191
	 * @param Member[] $members
192
	 * @param Member[] $groupMembers
193
	 */
194
	public function avoidDuplicateMembers(array &$members, array $groupMembers) {
195
		foreach ($groupMembers as $member) {
196
			$index = $this->indexOfMember($members, $member->getUserId());
197
			if ($index === -1) {
198
				array_push($members, $member);
199
			} else if ($members[$index]->getLevel() < $member->getLevel()) {
200
				$members[$index] = $member;
201
			}
202
		}
203
	}
204
205
206
	/**
207
	 * returns the index of a specific UserID in a Members List
208
	 *
209
	 * @param array $members
210
	 * @param $userId
211
	 *
212
	 * @return int
213
	 */
214
	private function indexOfMember(array $members, $userId) {
215
216
		foreach ($members as $k => $member) {
217
			if ($member->getUserId() === $userId) {
218
				return intval($k);
219
			}
220
		}
221
222
		return -1;
223
	}
224
225
226
	/**
227
	 * Check if a fresh member can be generated (by addMember/joinCircle)
228
	 *
229
	 * @param string $circleUniqueId
230
	 * @param string $name
231
	 * @param $type
232
	 *
233
	 * @return Member
234
	 * @throws MemberAlreadyExistsException
235
	 * @throws \Exception
236
	 */
237
	public function getFreshNewMember($circleUniqueId, $name, $type) {
238
239
		try {
240
241
			$member = $this->forceGetMember($circleUniqueId, $name, $type);
242
243
		} catch (MemberDoesNotExistException $e) {
244
			$member = new Member($name, $type, $circleUniqueId);
245
			$this->createMember($member);
246
		}
247
248
//		if ($member->alreadyExistOrJoining()) {
0 ignored issues
show
Unused Code Comprehensibility introduced by
55% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
249
//			throw new MemberAlreadyExistsException(
250
//				$this->l10n->t('This user is already a member of the circle')
251
//			);
252
//		}
253
254
		return $member;
255
	}
256
257
258
	/**
259
	 * Returns members list of all Group Members of a Circle. The Level of the linked group will be
260
	 * assigned to each entry
261
	 *
262
	 * NOTE: Can contains duplicate.
263
	 *
264
	 * WARNING: This function does not filters data regarding the current user/viewer.
265
	 *          Do not use in case of direct interaction with users.
266
	 *
267
	 * @param string $circleUniqueId
268
	 * @param int $level
269
	 *
270
	 * @return Member[]
271
	 */
272 View Code Duplication
	public function forceGetGroupMembers($circleUniqueId, $level = Member::LEVEL_MEMBER) {
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...
273
		$qb = $this->getGroupsSelectSql();
274
275
		$this->limitToLevel($qb, $level);
276
		$this->limitToCircleId($qb, $circleUniqueId);
277
		$this->limitToNCGroupUser($qb);
278
279
		$members = [];
280
		$cursor = $qb->execute();
281
		while ($data = $cursor->fetch()) {
282
			$members[] = $this->parseGroupsSelectSql($data);
283
		}
284
		$cursor->closeCursor();
285
286
		return $members;
287
	}
288
289
290
	/**
291
	 * returns all users from a Group as a list of Members.
292
	 *
293
	 * @param Member $group
294
	 *
295
	 * @return Member[]
296
	 */
297
	public function getGroupMemberMembers(Member $group) {
298
		/** @var IGroup $grp */
299
		$grp = $this->groupManager->get($group->getUserId());
300
		if ($grp === null) {
301
			return [];
302
		}
303
304
		$members = [];
305
		$users = $grp->getUsers();
306
		foreach ($users as $user) {
307
			$member = clone $group;
308
			//Member::fromJSON($this->l10n, json_encode($group));
0 ignored issues
show
Unused Code Comprehensibility introduced by
67% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
309
			$member->setType(Member::TYPE_USER);
310
			$member->setUserId($user->getUID());
311
			$members[] = $member;
312
		}
313
314
		return $members;
315
	}
316
317
318
	/**
319
	 * return the higher level group linked to a circle, that include the userId.
320
	 *
321
	 * WARNING: This function does not filters data regarding the current user/viewer.
322
	 *          In case of direct interaction with users, Please don't use this.
323
	 *
324
	 * @param string $circleUniqueId
325
	 * @param string $userId
326
	 *
327
	 * @return Member
328
	 */
329
	public function forceGetHigherLevelGroupFromUser($circleUniqueId, $userId) {
330
		$qb = $this->getGroupsSelectSql();
331
332
		$this->limitToCircleId($qb, $circleUniqueId);
333
		$this->limitToNCGroupUser($qb, $userId);
334
335
		/** @var Member $group */
336
		$group = null;
337
338
		$cursor = $qb->execute();
339
		while ($data = $cursor->fetch()) {
340
			$entry = $this->parseGroupsSelectSql($data);
341
			if ($group === null || $entry->getLevel() > $group->getLevel()) {
342
				$group = $entry;
343
			}
344
		}
345
		$cursor->closeCursor();
346
347
		return $group;
348
	}
349
350
351
	/**
352
	 * Insert Member into database.
353
	 *
354
	 * @param Member $member
355
	 *
356
	 * @throws MemberAlreadyExistsException
357
	 */
358
	public function createMember(Member $member) {
359
360
		try {
361
			$qb = $this->getMembersInsertSql();
362
			$qb->setValue('circle_id', $qb->createNamedParameter($member->getCircleId()))
363
			   ->setValue('user_id', $qb->createNamedParameter($member->getUserId()))
364
			   ->setValue('user_type', $qb->createNamedParameter($member->getType()))
365
			   ->setValue('level', $qb->createNamedParameter($member->getLevel()))
366
			   ->setValue('status', $qb->createNamedParameter($member->getStatus()))
367
			   ->setValue('note', $qb->createNamedParameter($member->getNote()));
368
369
			$qb->execute();
370
		} catch (UniqueConstraintViolationException $e) {
0 ignored issues
show
Bug introduced by
The class Doctrine\DBAL\Exception\...raintViolationException 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...
371
			throw new MemberAlreadyExistsException(
372
				$this->l10n->t('This user is already a member of the circle')
373
			);
374
		}
375
	}
376
377
378
	/**
379
	 * @param string $circleUniqueId
380
	 * @param Member $viewer
381
	 *
382
	 * @return Member[]
383
	 * @throws MemberDoesNotExistException
384
	 */
385
	public function getGroupsFromCircle($circleUniqueId, Member $viewer) {
386
387
		if ($viewer->getLevel() < Member::LEVEL_MEMBER) {
388
			return [];
389
		}
390
391
		$qb = $this->getGroupsSelectSql();
392
		$this->limitToCircleId($qb, $circleUniqueId);
393
		$this->limitToLevel($qb, Member::LEVEL_MEMBER);
394
395
		$cursor = $qb->execute();
396
		$groups = [];
397
		while ($data = $cursor->fetch()) {
398
			if ($viewer->getLevel() < Member::LEVEL_MODERATOR) {
399
				$data['note'] = '';
400
			}
401
			$groups[] = $this->parseGroupsSelectSql($data);
402
		}
403
		$cursor->closeCursor();
404
405
		return $groups;
406
	}
407
408
409
	/**
410
	 * Insert Member into database.
411
	 *
412
	 * @param Member $member
413
	 *
414
	 * @throws MemberAlreadyExistsException
415
	 */
416
	public function insertGroup(Member $member) {
417
		try {
418
			$qb = $this->getGroupsInsertSql();
419
			$qb->setValue('circle_id', $qb->createNamedParameter($member->getCircleId()))
420
			   ->setValue('group_id', $qb->createNamedParameter($member->getUserId()))
421
			   ->setValue('level', $qb->createNamedParameter($member->getLevel()))
422
			   ->setValue('note', $qb->createNamedParameter($member->getNote()));
423
424
			$qb->execute();
425
		} catch (UniqueConstraintViolationException $e) {
426
			throw new MemberAlreadyExistsException(
427
				$this->l10n->t('This user is already a member of the circle')
428
			);
429
		}
430
	}
431
432
433
	/**
434
	 * update database entry for a specific Member.
435
	 *
436
	 * @param Member $member
437
	 */
438
	public function updateMember(Member $member) {
439
		$qb = $this->getMembersUpdateSql($member->getCircleId(), $member);
440
		$qb->set('level', $qb->createNamedParameter($member->getLevel()))
441
		   ->set('status', $qb->createNamedParameter($member->getStatus()));
442
443
		$qb->execute();
444
	}
445
446
447
	/**
448
	 * removeAllFromCircle();
449
	 *
450
	 * Remove All members from a Circle. Used when deleting a Circle.
451
	 *
452
	 * @param string $uniqueCircleId
453
	 */
454
	public function removeAllFromCircle($uniqueCircleId) {
455
		$qb = $this->getMembersDeleteSql();
456
		$expr = $qb->expr();
457
458
		$qb->where($expr->eq('circle_id', $qb->createNamedParameter($uniqueCircleId)));
459
		$qb->execute();
460
	}
461
462
463
	/**
464
	 * removeAllMembershipsFromUser();
465
	 *
466
	 * remove All membership from a User. Used when removing a User from the Cloud.
467
	 *
468
	 * @param string $userId
469
	 */
470
	public function removeAllMembershipsFromUser($userId) {
471
		if ($userId === '') {
472
			return;
473
		}
474
475
		$qb = $this->getMembersDeleteSql();
476
		$expr = $qb->expr();
477
478
		/** @noinspection PhpMethodParametersCountMismatchInspection */
479
		$qb->where(
480
			$expr->andX(
481
				$expr->eq('user_id', $qb->createNamedParameter($userId)),
482
				$expr->eq('user_type', $qb->createNamedParameter(Member::TYPE_USER))
483
			)
484
		);
485
486
		$qb->execute();
487
	}
488
489
490
491
492
	/**
493
	 * update database entry for a specific Group.
494
	 *
495
	 * @param Member $member
496
	 *
497
	 * @return bool
498
	 */
499
	public function updateGroup(Member $member) {
500
501
		$qb = $this->getGroupsUpdateSql($member->getCircleId(), $member->getUserId());
502
		$qb->set('level', $qb->createNamedParameter($member->getLevel()));
503
		$qb->execute();
504
505
		return true;
506
	}
507
508
509
	public function unlinkAllFromGroup($groupId) {
510
		$qb = $this->getGroupsDeleteSql($groupId);
511
		$qb->execute();
512
	}
513
514
}