Completed
Push — master ( 70edb2...6362ed )
by Maxence
02:18
created

MembersRequest::avoidDuplicateMembers()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 10
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 10
rs 9.2
cc 4
eloc 7
nc 4
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
	 *
49
	 * @return Member
50
	 * @throws MemberDoesNotExistException
51
	 */
52 View Code Duplication
	public function forceGetMember($circleUniqueId, $userId) {
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...
53
		$qb = $this->getMembersSelectSql();
54
55
		$this->limitToUserId($qb, $userId);
56
		$this->limitToCircleId($qb, $circleUniqueId);
57
58
		$cursor = $qb->execute();
59
		$data = $cursor->fetch();
60
		$cursor->closeCursor();
61
62
		if ($data === false) {
63
			throw new MemberDoesNotExistException($this->l10n->t('This member does not exist'));
64
		}
65
66
		$member = $this->parseMembersSelectSql($data);
67
68
		return $member;
69
	}
70
71
72
	/**
73
	 * Returns members list of a circle, based on their level.
74
	 *
75
	 * WARNING: This function does not filters data regarding the current user/viewer.
76
	 *          In case of interaction with users, Please use getMembers() instead.
77
	 *
78
	 * @param string $circleUniqueId
79
	 * @param int $level
80
	 * @param bool $includeGroupMembers
81
	 *
82
	 * @return Member[]
83
	 */
84
	public function forceGetMembers(
85
		$circleUniqueId, $level = Member::LEVEL_MEMBER, $includeGroupMembers = false
86
	) {
87
88
		$qb = $this->getMembersSelectSql();
89
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() && $includeGroupMembers === 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_MEMBER);
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
	 * Returns members list of all Group Members of a Circle. The Level of the linked group will be
228
	 * assigned to each entry
229
	 *
230
	 * NOTE: Can contains duplicate.
231
	 *
232
	 * WARNING: This function does not filters data regarding the current user/viewer.
233
	 *          Do not use in case of direct interaction with users.
234
	 *
235
	 * @param string $circleUniqueId
236
	 * @param int $level
237
	 *
238
	 * @return Member[]
239
	 */
240
	public function forceGetGroupMembers($circleUniqueId, $level = Member::LEVEL_MEMBER) {
241
		$qb = $this->getGroupsSelectSql();
242
243
		$this->limitToLevel($qb, $level);
244
		$this->limitToCircleId($qb, $circleUniqueId);
245
		$this->limitToNCGroupUser($qb);
246
247
		$members = [];
248
		$cursor = $qb->execute();
249
		while ($data = $cursor->fetch()) {
250
			$members[] = $this->parseGroupsSelectSql($data);
251
		}
252
		$cursor->closeCursor();
253
254
		return $members;
255
	}
256
257
258
	/**
259
	 * returns all users from a Group as a list of Members.
260
	 *
261
	 * @param Member $group
262
	 *
263
	 * @return Member[]
264
	 */
265
	public function getGroupMemberMembers(Member $group) {
266
		/** @var IGroup $grp */
267
		$grp = $this->groupManager->get($group->getGroupId());
268
		if ($grp === null) {
269
			return [];
270
		}
271
272
		$members = [];
273
		$users = $grp->getUsers();
274
		foreach ($users as $user) {
275
			$member = clone $group;
276
			//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...
277
			$member->setUserId($user->getUID());
278
			$members[] = $member;
279
		}
280
281
		return $members;
282
	}
283
284
285
	/**
286
	 * return the higher level group linked to a circle, that include the userId.
287
	 *
288
	 * WARNING: This function does not filters data regarding the current user/viewer.
289
	 *          In case of direct interaction with users, Please don't use this.
290
	 *
291
	 * @param string $circleUniqueId
292
	 * @param string $userId
293
	 *
294
	 * @return Member
295
	 */
296
	public function forceGetHigherLevelGroupFromUser($circleUniqueId, $userId) {
297
		$qb = $this->getGroupsSelectSql();
298
299
		$this->limitToCircleId($qb, $circleUniqueId);
300
		$this->limitToNCGroupUser($qb, $userId);
301
302
		/** @var Member $group */
303
		$group = null;
304
305
		$cursor = $qb->execute();
306
		while ($data = $cursor->fetch()) {
307
			$entry = $this->parseGroupsSelectSql($data);
308
			if ($group === null || $entry->getLevel() > $group->getLevel()) {
309
				$group = $entry;
310
			}
311
		}
312
		$cursor->closeCursor();
313
314
		return $group;
315
	}
316
317
318
	/**
319
	 * Insert Member into database.
320
	 *
321
	 * @param Member $member
322
	 *
323
	 * @throws MemberAlreadyExistsException
324
	 */
325
	public function createMember(Member $member) {
326
327
		try {
328
			$qb = $this->getMembersInsertSql();
329
			$qb->setValue('circle_id', $qb->createNamedParameter($member->getCircleId()))
330
			   ->setValue('user_id', $qb->createNamedParameter($member->getUserId()))
331
			   ->setValue('level', $qb->createNamedParameter($member->getLevel()))
332
			   ->setValue('status', $qb->createNamedParameter($member->getStatus()))
333
			   ->setValue('note', $qb->createNamedParameter($member->getNote()));
334
335
			$qb->execute();
336
		} 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...
337
			throw new MemberAlreadyExistsException(
338
				$this->l10n->t('This user is already a member of the circle')
339
			);
340
		}
341
	}
342
343
344
	/**
345
	 * @param string $circleUniqueId
346
	 * @param Member $viewer
347
	 *
348
	 * @return Member[]
349
	 * @throws MemberDoesNotExistException
350
	 */
351
	public function getGroupsFromCircle($circleUniqueId, Member $viewer) {
352
353
		if ($viewer->getLevel() < Member::LEVEL_MEMBER) {
354
			return [];
355
		}
356
357
		$qb = $this->getGroupsSelectSql();
358
		$this->limitToCircleId($qb, $circleUniqueId);
359
		$this->limitToLevel($qb, Member::LEVEL_MEMBER);
360
361
		$cursor = $qb->execute();
362
		$groups = [];
363
		while ($data = $cursor->fetch()) {
364
			if ($viewer->getLevel() < Member::LEVEL_MODERATOR) {
365
				$data['note'] = '';
366
			}
367
			$groups[] = $this->parseGroupsSelectSql($data);
368
		}
369
		$cursor->closeCursor();
370
371
		return $groups;
372
	}
373
374
375
	/**
376
	 * Insert Member into database.
377
	 *
378
	 * @param Member $member
379
	 *
380
	 * @throws MemberAlreadyExistsException
381
	 */
382
	public function insertGroup(Member $member) {
383
		try {
384
			$qb = $this->getGroupsInsertSql();
385
			$qb->setValue('circle_id', $qb->createNamedParameter($member->getCircleId()))
386
			   ->setValue('group_id', $qb->createNamedParameter($member->getGroupId()))
387
			   ->setValue('level', $qb->createNamedParameter($member->getLevel()))
388
			   ->setValue('note', $qb->createNamedParameter($member->getNote()));
389
390
			$qb->execute();
391
		} catch (UniqueConstraintViolationException $e) {
392
			throw new MemberAlreadyExistsException(
393
				$this->l10n->t('This user is already a member of the circle')
394
			);
395
		}
396
	}
397
398
399
	/**
400
	 * update database entry for a specific Member.
401
	 *
402
	 * @param Member $member
403
	 */
404
	public function updateMember(Member $member) {
405
		$qb = $this->getMembersUpdateSql($member->getCircleId(), $member->getUserId());
406
		$qb->set('level', $qb->createNamedParameter($member->getLevel()))
407
		   ->set('status', $qb->createNamedParameter($member->getStatus()));
408
409
		$qb->execute();
410
	}
411
412
413
	/**
414
	 * removeAllFromCircle();
415
	 *
416
	 * Remove All members from a Circle. Used when deleting a Circle.
417
	 *
418
	 * @param string $uniqueCircleId
419
	 */
420
	public function removeAllFromCircle($uniqueCircleId) {
421
		$qb = $this->getMembersDeleteSql($uniqueCircleId, '');
422
		$qb->execute();
423
	}
424
425
426
	/**
427
	 * removeAllFromUser();
428
	 *
429
	 * remove All membership from a User. Used when removing a User from the Cloud.
430
	 *
431
	 * @param $userId
432
	 */
433
	public function removeAllFromUser($userId) {
434
		if ($userId === '') {
435
			return;
436
		}
437
438
		$qb = $this->getMembersDeleteSql(0, $userId);
439
		$qb->execute();
440
	}
441
442
443
	/**
444
	 * update database entry for a specific Group.
445
	 *
446
	 * @param Member $member
447
	 *
448
	 * @return bool
449
	 */
450
	public function updateGroup(Member $member) {
451
452
		$qb = $this->getGroupsUpdateSql($member->getCircleId(), $member->getGroupId());
453
		$qb->set('level', $qb->createNamedParameter($member->getLevel()));
454
		$qb->execute();
455
456
		return true;
457
	}
458
459
460
	public function unlinkAllFromGroup($groupId) {
461
		$qb = $this->getGroupsDeleteSql($groupId);
462
		$qb->execute();
463
	}
464
465
}