Completed
Pull Request — master (#449)
by Maxence
01:59
created

MembersRequest::getFreshNewMember()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 18

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 18
rs 9.6666
c 0
b 0
f 0
cc 2
nc 2
nop 4
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 daita\MySmallPhpTools\Traits\TStringTools;
32
use Doctrine\DBAL\Exception\UniqueConstraintViolationException;
33
use Exception;
34
use OCA\Circles\Exceptions\MemberAlreadyExistsException;
35
use OCA\Circles\Exceptions\MemberDoesNotExistException;
36
use OCA\Circles\Model\Member;
37
use OCP\IGroup;
38
39
class MembersRequest extends MembersRequestBuilder {
40
41
42
	use TStringTools;
43
44
45
	/**
46
	 * Returns information about a member.
47
	 *
48
	 * WARNING: This function does not filters data regarding the current user/viewer.
49
	 *          In case of interaction with users, Please use MembersService->getMember() instead.
50
	 *
51
	 * @param string $circleUniqueId
52
	 * @param string $userId
53
	 * @param $type
54
	 *
55
	 * @param string $instance
56
	 *
57
	 * @return Member
58
	 * @throws MemberDoesNotExistException
59
	 */
60
	public function forceGetMember($circleUniqueId, $userId, $type, string $instance = '') {
61
		$qb = $this->getMembersSelectSql();
62
63
		if ($instance === $this->configService->getLocalCloudId()) {
64
			$instance = '';
65
		}
66
67
		$this->limitToUserId($qb, $userId);
68
		$this->limitToUserType($qb, $type);
69
		$this->limitToInstance($qb, $instance);
70
		$this->limitToCircleId($qb, $circleUniqueId);
71
72
		$cursor = $qb->execute();
73
		$data = $cursor->fetch();
74
		$cursor->closeCursor();
75
76
		if ($data === false) {
77
			throw new MemberDoesNotExistException($this->l10n->t('This member does not exist'));
78
		}
79
80
		return $this->parseMembersSelectSql($data);
81
	}
82
83
84
	/**
85
	 * @param string $memberId
86
	 *
87
	 * @return Member
88
	 * @throws MemberDoesNotExistException
89
	 */
90
	public function forceGetMemberById(string $memberId): Member {
91
		$qb = $this->getMembersSelectSql();
92
93
		$this->limitToMemberId($qb, $memberId);
94
95
		$cursor = $qb->execute();
96
		$data = $cursor->fetch();
97
		$cursor->closeCursor();
98
99
		if ($data === false) {
100
			throw new MemberDoesNotExistException($this->l10n->t('This member does not exist'));
101
		}
102
103
		return $this->parseMembersSelectSql($data);
104
	}
105
106
107
	/**
108
	 * Returns members list of a circle, based on their level.
109
	 *
110
	 * WARNING: This function does not filters data regarding the current user/viewer.
111
	 *          In case of interaction with users, Please use getMembers() instead.
112
	 *
113
	 * @param string $circleUniqueId
114
	 * @param int $level
115
	 * @param int $type
116
	 * @param bool $incGroup
117
	 *
118
	 * @return Member[]
119
	 */
120
	public function forceGetMembers(
121
		$circleUniqueId, $level = Member::LEVEL_MEMBER, int $type = 0, $incGroup = false
122
	) {
123
		$qb = $this->getMembersSelectSql();
124
		$this->limitToMembersAndAlmost($qb);
125
		$this->limitToLevel($qb, $level);
126
127
		if ($type > 0) {
128
			$this->limitToUserType($qb, $type);
129
		}
130
131
		$this->limitToCircleId($qb, $circleUniqueId);
132
133
		$members = [];
134
		$cursor = $qb->execute();
135
		while ($data = $cursor->fetch()) {
136
			$members[] = $this->parseMembersSelectSql($data);
137
		}
138
		$cursor->closeCursor();
139
140
		if ($this->configService->isLinkedGroupsAllowed() && $incGroup === true) {
141
			$this->includeGroupMembers($members, $circleUniqueId, $level);
142
		}
143
144
		return $members;
145
	}
146
147
148
	/**
149
	 * Returns all members.
150
	 *
151
	 * WARNING: This function does not filters data regarding the current user/viewer.
152
	 *          In case of interaction with users, Please use getMembers() instead.
153
	 *
154
	 *
155
	 * @return Member[]
156
	 */
157
	public function forceGetAllMembers() {
158
159
		$qb = $this->getMembersSelectSql();
160
161
		$members = [];
162
		$cursor = $qb->execute();
163
		while ($data = $cursor->fetch()) {
164
			$members[] = $this->parseMembersSelectSql($data);
165
		}
166
		$cursor->closeCursor();
167
168
		return $members;
169
	}
170
171
172
	/**
173
	 * Returns members generated from Contacts that are not 'checked' (as not sent existing shares).
174
	 *
175
	 *
176
	 * @return Member[]
177
	 */
178
	public function forceGetAllRecentContactEdit() {
179
		$qb = $this->getMembersSelectSql();
180
		$this->limitToUserType($qb, Member::TYPE_CONTACT);
181
182
		$expr = $qb->expr();
183
		$orX = $expr->orX();
184
		$orX->add($expr->isNull('contact_checked'));
185
		$orX->add($expr->neq('contact_checked', $qb->createNamedParameter('1')));
186
		$qb->andWhere($orX);
187
188
		$members = [];
189
		$cursor = $qb->execute();
190
		while ($data = $cursor->fetch()) {
191
			$members[] = $this->parseMembersSelectSql($data);
192
		}
193
		$cursor->closeCursor();
194
195
		return $members;
196
	}
197
198
199
	/**
200
	 * @param Member $member
201
	 * @param bool $check
202
	 */
203
	public function checkMember(Member $member, bool $check) {
204
		$qb = $this->getMembersUpdateSql(
205
			$member->getCircleId(), $member->getUserId(), $member->getInstance(), $member->getType()
206
		);
207
		$qb->set('contact_checked', $qb->createNamedParameter(($check) ? 1 : 0));
208
209
		$qb->execute();
210
	}
211
212
213
	/**
214
	 * @param string $circleUniqueId
215
	 * @param Member $viewer
216
	 * @param bool $force
217
	 *
218
	 * @return Member[]
219
	 */
220
	public function getMembers($circleUniqueId, ?Member $viewer, bool $force = false) {
221
		try {
222
			if ($force === false) {
223
				$viewer->hasToBeMember();
224
			}
225
226
			$members = $this->forceGetMembers($circleUniqueId, Member::LEVEL_NONE);
227
			if ($force === false) {
228
				if (!$viewer->isLevel(Member::LEVEL_MODERATOR)) {
229
					array_map(
230
						function(Member $m) {
231
							$m->setNote('');
232
						}, $members
233
					);
234
				}
235
			}
236
237
			return $members;
238
		} catch (Exception $e) {
239
			return [];
240
		}
241
	}
242
243
244
	/**
245
	 * forceGetGroup();
246
	 *
247
	 * returns group information as a member within a Circle.
248
	 *
249
	 * WARNING: This function does not filters data regarding the current user/viewer.
250
	 *          In case of interaction with users, Please use getGroup() instead.
251
	 *
252
	 * @param string $circleUniqueId
253
	 * @param string $groupId
254
	 * @param string $instance
255
	 *
256
	 * @return Member
257
	 * @throws MemberDoesNotExistException
258
	 */
259
	public function forceGetGroup(string $circleUniqueId, string $groupId, string $instance) {
260
		$qb = $this->getMembersSelectSql();
261
262
		$this->limitToUserId($qb, $groupId);
263
		$this->limitToUserType($qb, Member::TYPE_GROUP);
264
		$this->limitToInstance($qb, $instance);
265
		$this->limitToCircleId($qb, $circleUniqueId);
266
267
		$cursor = $qb->execute();
268
		$data = $cursor->fetch();
269
		$cursor->closeCursor();
270
		if ($data === false) {
271
			throw new MemberDoesNotExistException($this->l10n->t('This member does not exist'));
272
		}
273
274
		return $this->parseMembersSelectSql($data);
275
	}
276
277
278
	/**
279
	 * includeGroupMembers();
280
	 *
281
	 * This function will get members of a circle throw NCGroups and fill the result an existing
282
	 * Members List. In case of duplicate, higher level will be kept.
283
	 *
284
	 * @param Member[] $members
285
	 * @param string $circleUniqueId
286
	 * @param int $level
287
	 */
288
	private function includeGroupMembers(array &$members, $circleUniqueId, $level) {
289
		$groupMembers = $this->forceGetGroupMembers($circleUniqueId, $level);
290
		$this->avoidDuplicateMembers($members, $groupMembers);
291
	}
292
293
294
	/**
295
	 * avoidDuplicateMembers();
296
	 *
297
	 * Use this function to add members to the list (1st argument), keeping the higher level in case
298
	 * of duplicate
299
	 *
300
	 * @param Member[] $members
301
	 * @param Member[] $groupMembers
302
	 */
303
	public function avoidDuplicateMembers(array &$members, array $groupMembers) {
304
		foreach ($groupMembers as $member) {
305
			$index = $this->indexOfMember($members, $member->getUserId());
306
			if ($index === -1) {
307
				array_push($members, $member);
308
			} else if ($members[$index]->getLevel() < $member->getLevel()) {
309
				$members[$index] = $member;
310
			}
311
		}
312
	}
313
314
315
	/**
316
	 * returns the index of a specific UserID in a Members List
317
	 *
318
	 * @param array $members
319
	 * @param $userId
320
	 *
321
	 * @return int
322
	 */
323
	private function indexOfMember(array $members, $userId) {
324
325
		foreach ($members as $k => $member) {
326
			if ($member->getUserId() === $userId) {
327
				return intval($k);
328
			}
329
		}
330
331
		return -1;
332
	}
333
334
335
	/**
336
	 * Check if a fresh member can be generated (by addMember/joinCircle)
337
	 *
338
	 * @param string $circleUniqueId
339
	 * @param string $name
340
	 * @param int $type
341
	 *
342
	 * @param string $instance
343
	 *
344
	 * @return Member
345
	 */
346
	public function getFreshNewMember($circleUniqueId, string $name, int $type, string $instance) {
347
348
		try {
349
			$member = $this->forceGetMember($circleUniqueId, $name, $type, $instance);
350
		} catch (MemberDoesNotExistException $e) {
351
			$member = new Member($name, $type, $circleUniqueId);
352
			$member->setInstance($instance);
353
//			$member->setMemberId($this->token(14));
354
		}
355
356
//		if ($member->alreadyExistOrJoining()) {
357
//			throw new MemberAlreadyExistsException(
358
//				$this->l10n->t('This user is already a member of the circle')
359
//			);
360
//		}
361
362
		return $member;
363
	}
364
365
366
	/**
367
	 * Returns members list of all Group Members of a Circle. The Level of the linked group will be
368
	 * assigned to each entry
369
	 *
370
	 * NOTE: Can contains duplicate.
371
	 *
372
	 * WARNING: This function does not filters data regarding the current user/viewer.
373
	 *          Do not use in case of direct interaction with users.
374
	 *
375
	 * @param string $circleUniqueId
376
	 * @param int $level
377
	 *
378
	 * @return Member[]
379
	 */
380
	public function forceGetGroupMembers($circleUniqueId, $level = Member::LEVEL_MEMBER) {
381
		$qb = $this->getMembersSelectSql();
382
383
		$this->limitToUserType($qb, Member::TYPE_GROUP);
384
		$this->limitToLevel($qb, $level);
385
		$this->limitToCircleId($qb, $circleUniqueId);
386
		$this->limitToNCGroupUser($qb);
387
388
		$members = [];
389
		$cursor = $qb->execute();
390
		while ($data = $cursor->fetch()) {
391
			$members[] = $this->parseGroupsSelectSql($data);
392
		}
393
		$cursor->closeCursor();
394
395
		return $members;
396
	}
397
398
399
	/**
400
	 * returns all users from a Group as a list of Members.
401
	 *
402
	 * @param Member $group
403
	 *
404
	 * @return Member[]
405
	 */
406
	public function getGroupMemberMembers(Member $group) {
407
		/** @var IGroup $grp */
408
		$grp = $this->groupManager->get($group->getUserId());
409
		if ($grp === null) {
410
			return [];
411
		}
412
413
		$members = [];
414
		$users = $grp->getUsers();
415
		foreach ($users as $user) {
416
			$member = clone $group;
417
			//Member::fromJSON($this->l10n, json_encode($group));
418
			$member->setType(Member::TYPE_USER);
419
			$member->setUserId($user->getUID());
420
			$members[] = $member;
421
		}
422
423
		return $members;
424
	}
425
426
427
	/**
428
	 * return the higher level group linked to a circle, that include the userId.
429
	 *
430
	 * WARNING: This function does not filters data regarding the current user/viewer.
431
	 *          In case of direct interaction with users, Please don't use this.
432
	 *
433
	 * @param string $circleUniqueId
434
	 * @param string $userId
435
	 *
436
	 * @return Member
437
	 */
438
	public function forceGetHigherLevelGroupFromUser($circleUniqueId, $userId) {
439
		$qb = $this->getMembersSelectSql();
440
441
		$this->limitToUserType($qb, Member::TYPE_GROUP);
442
		$this->limitToInstance($qb, '');
443
		$this->limitToCircleId($qb, $circleUniqueId);
444
		$this->limitToNCGroupUser($qb);
445
446
		$this->limitToNCGroupUser($qb, $userId);
447
448
		/** @var Member $group */
449
		$group = null;
450
451
		$cursor = $qb->execute();
452
		while ($data = $cursor->fetch()) {
453
			$entry = $this->parseGroupsSelectSql($data);
454
			if ($group === null || $entry->getLevel() > $group->getLevel()) {
455
				$group = $entry;
456
			}
457
		}
458
		$cursor->closeCursor();
459
460
		return $group;
461
	}
462
463
464
	/**
465
	 * Insert Member into database.
466
	 *
467
	 * @param Member $member
468
	 *
469
	 * @throws MemberAlreadyExistsException
470
	 */
471
	public function createMember(Member $member) {
472
473
		if ($member->getMemberId() === '') {
474
			$member->setMemberId($this->token(14));
475
		}
476
477
		$instance = $member->getInstance();
478
		if ($instance === $this->configService->getLocalCloudId()) {
479
			$instance = '';
480
		}
481
482
		try {
483
			$qb = $this->getMembersInsertSql();
484
			$qb->setValue('circle_id', $qb->createNamedParameter($member->getCircleId()))
485
			   ->setValue('user_id', $qb->createNamedParameter($member->getUserId()))
486
			   ->setValue('member_id', $qb->createNamedParameter($member->getMemberId()))
487
			   ->setValue('user_type', $qb->createNamedParameter($member->getType()))
488
			   ->setValue('instance', $qb->createNamedParameter($instance))
489
			   ->setValue('level', $qb->createNamedParameter($member->getLevel()))
490
			   ->setValue('status', $qb->createNamedParameter($member->getStatus()))
491
			   ->setValue('contact_id', $qb->createNamedParameter($member->getContactId()))
492
			   ->setValue('note', $qb->createNamedParameter($member->getNote()));
493
494
			$qb->execute();
495
		} 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...
496
			throw new MemberAlreadyExistsException(
497
				$this->l10n->t('This user is already a member of the circle')
498
			);
499
		}
500
	}
501
502
503
	/**
504
	 * @param string $circleUniqueId
505
	 * @param Member $viewer
506
	 *
507
	 * @return Member[]
508
	 */
509
	public function getGroupsFromCircle($circleUniqueId, Member $viewer) {
510
		if ($viewer->getLevel() < Member::LEVEL_MEMBER) {
511
			return [];
512
		}
513
514
		$qb = $this->getMembersSelectSql();
515
516
		$this->limitToUserType($qb, Member::TYPE_GROUP);
517
		$this->limitToLevel($qb, Member::LEVEL_MEMBER);
518
		$this->limitToInstance($qb, '');
519
		$this->limitToCircleId($qb, $circleUniqueId);
520
521
		$cursor = $qb->execute();
522
		$groups = [];
523
		while ($data = $cursor->fetch()) {
524
			if ($viewer->getLevel() < Member::LEVEL_MODERATOR) {
525
				$data['note'] = '';
526
			}
527
			$groups[] = $this->parseGroupsSelectSql($data);
528
		}
529
		$cursor->closeCursor();
530
531
		return $groups;
532
	}
533
534
535
	/**
536
	 * update database entry for a specific Member.
537
	 *
538
	 * @param Member $member
539
	 */
540
	public function updateMember(Member $member) {
541
		$instance = $member->getInstance();
542
		if ($instance === $this->configService->getLocalCloudId()) {
543
			$instance = '';
544
		}
545
546
		$qb = $this->getMembersUpdateSql(
547
			$member->getCircleId(), $member->getUserId(), $instance, $member->getType()
548
		);
549
		$qb->set('level', $qb->createNamedParameter($member->getLevel()))
550
		   ->set('status', $qb->createNamedParameter($member->getStatus()));
551
552
		$qb->execute();
553
	}
554
555
	/**
556
	 * update database entry for a specific Member.
557
	 *
558
	 * @param Member $member
559
	 */
560
	public function updateContactMeta(Member $member) {
561
		$qb = $this->getMembersUpdateSql(
562
			$member->getCircleId(), $member->getUserId(), $member->getInstance(), $member->getType()
563
		);
564
		$qb->set('contact_meta', $qb->createNamedParameter(json_encode($member->getContactMeta())));
565
566
		$qb->execute();
567
	}
568
569
570
	/**
571
	 * removeAllFromCircle();
572
	 *
573
	 * Remove All members from a Circle. Used when deleting a Circle.
574
	 *
575
	 * @param string $uniqueCircleId
576
	 */
577
	public function removeAllFromCircle($uniqueCircleId) {
578
		$qb = $this->getMembersDeleteSql();
579
		$expr = $qb->expr();
580
581
		$qb->where($expr->eq('circle_id', $qb->createNamedParameter($uniqueCircleId)));
582
		$qb->execute();
583
	}
584
585
586
	/**
587
	 * removeAllMembershipsFromUser();
588
	 *
589
	 * remove All membership from a User. Used when removing a User from the Cloud.
590
	 *
591
	 * @param Member $member
592
	 */
593
	public function removeAllMembershipsFromUser(Member $member) {
594
		if ($member->getUserId() === '') {
595
			return;
596
		}
597
598
		$instance = $member->getInstance();
599
		if ($instance === $this->configService->getLocalCloudId()) {
600
			$instance = '';
601
		}
602
603
		$qb = $this->getMembersDeleteSql();
604
		$expr = $qb->expr();
605
606
		$qb->where(
607
			$expr->andX(
608
				$expr->eq('user_id', $qb->createNamedParameter($member->getUserId())),
609
				$expr->eq('instance', $qb->createNamedParameter($instance)),
610
				$expr->eq('user_type', $qb->createNamedParameter(Member::TYPE_USER))
611
			)
612
		);
613
614
		$qb->execute();
615
	}
616
617
618
	/**
619
	 * remove member, identified by its id, type and circleId
620
	 *
621
	 * @param Member $member
622
	 */
623
	public function removeMember(Member $member) {
624
		$instance = $member->getInstance();
625
		if ($instance === $this->configService->getLocalCloudId()) {
626
			$instance = '';
627
		}
628
629
		$qb = $this->getMembersDeleteSql();
630
		$this->limitToCircleId($qb, $member->getCircleId());
631
		$this->limitToUserId($qb, $member->getUserId());
632
		$this->limitToInstance($qb, $instance);
633
		$this->limitToUserType($qb, $member->getType());
634
		if ($member->getContactId() !== '') {
635
			$this->limitToContactId($qb, $member->getContactId());
636
		}
637
638
		$qb->execute();
639
	}
640
641
	/**
642
	 * update database entry for a specific Group.
643
	 *
644
	 * @param Member $member
645
	 *
646
	 * @return bool
647
	 */
648
	public function updateGroup(Member $member) {
649
		$qb = $this->getMembersUpdateSql(
650
			$member->getCircleId(), $member->getUserId(), $member->getInstance(), $member->getType()
651
		);
652
		$qb->set('level', $qb->createNamedParameter($member->getLevel()));
653
		$qb->execute();
654
655
		return true;
656
	}
657
658
659
	public function unlinkAllFromGroup($groupId) {
660
		$qb = $this->getMembersDeleteSql();
661
662
		$this->limitToUserId($qb, $groupId);
663
		$this->limitToUserType($qb, Member::TYPE_GROUP);
664
		$this->limitToInstance($qb, '');
665
666
		$qb->execute();
667
	}
668
669
670
	/**
671
	 * @param string $contactId
672
	 *
673
	 * @return Member[]
674
	 */
675
	public function getMembersByContactId(string $contactId = ''): array {
676
		$qb = $this->getMembersSelectSql();
677
		if ($contactId === '') {
678
			$expr = $qb->expr();
679
			$qb->andWhere($expr->neq('contact_id', $qb->createNamedParameter('')));
680
		} else {
681
			$this->limitToContactId($qb, $contactId);
682
		}
683
684
		$members = [];
685
		$cursor = $qb->execute();
686
		while ($data = $cursor->fetch()) {
687
			$member = $this->parseMembersSelectSql($data);
688
			$members[] = $member;
689
		}
690
		$cursor->closeCursor();
691
692
		return $members;
693
	}
694
695
696
	/**
697
	 * @param string $circleId
698
	 * @param string $contactId
699
	 *
700
	 * @return Member
701
	 * @throws MemberDoesNotExistException
702
	 */
703
	public function getContactMember(string $circleId, string $contactId): Member {
704
		$qb = $this->getMembersSelectSql();
705
		$this->limitToContactId($qb, $contactId);
706
		$this->limitToCircleId($qb, $circleId);
707
708
		$cursor = $qb->execute();
709
		$data = $cursor->fetch();
710
		$cursor->closeCursor();
711
712
		if ($data === false) {
713
			throw new MemberDoesNotExistException($this->l10n->t('This member does not exist'));
714
		}
715
716
		return $this->parseMembersSelectSql($data);
717
	}
718
719
720
	/**
721
	 * @param string $contactId
722
	 *
723
	 * @return Member[]
724
	 */
725
	public function getLocalContactMembers(string $contactId): array {
726
		$qb = $this->getMembersSelectSql();
727
		$this->limitToContactId($qb, $contactId);
728
		$this->limitToUserType($qb, Member::TYPE_USER);
729
730
		$members = [];
731
		$cursor = $qb->execute();
732
		while ($data = $cursor->fetch()) {
733
			$members[] = $this->parseMembersSelectSql($data);
734
		}
735
		$cursor->closeCursor();
736
737
		return $members;
738
	}
739
740
741
	/**
742
	 * @param string $contactId
743
	 * @param int $type
744
	 */
745
	public function removeMembersByContactId(string $contactId, int $type = 0) {
746
		$this->miscService->log($contactId);
747
		if ($contactId === '') {
748
			return;
749
		}
750
751
		$qb = $this->getMembersDeleteSql();
752
		$this->limitToContactId($qb, $contactId);
753
		if ($type > 0) {
754
			$this->limitToUserType($qb, $type);
755
		}
756
757
		$qb->execute();
758
	}
759
760
761
}
762