Completed
Pull Request — master (#384)
by Tortue
02:35
created

MembersService   F

Complexity

Total Complexity 68

Size/Duplication

Total Lines 637
Duplicated Lines 7.38 %

Coupling/Cohesion

Components 1
Dependencies 14

Importance

Changes 0
Metric Value
wmc 68
lcom 1
cbo 14
dl 47
loc 637
rs 2.923
c 0
b 0
f 0

23 Methods

Rating   Name   Duplication   Size   Complexity  
A addMember() 7 16 3
A __construct() 19 19 1
A addSingleMember() 0 16 1
A addMassiveMembers() 0 12 3
A addMemberBasedOnItsType() 0 5 1
A addLocalMember() 0 12 3
A addEmailAddress() 0 8 2
A addContact() 0 8 2
A verifyIdentWithGroupBackend() 0 16 6
A verifyIdentBasedOnItsType() 0 5 1
A verifyIdentLocalMember() 0 11 3
A verifyIdentEmailAddress() 0 18 4
A verifyIdentContact() 0 19 4
B addGroupMembers() 0 29 10
A addMassiveMails() 0 21 5
A getMember() 0 12 2
A getMemberById() 0 3 1
A levelMember() 7 28 4
A updateMemberLevel() 0 13 3
A editMemberLevel() 0 15 2
A switchOwner() 0 19 2
A removeMember() 14 33 4
A onUserRemoved() 0 3 1

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like MembersService often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use MembersService, and based on these observations, apply Extract Interface, too.

1
<?php
2
/**
3
 * Circles - bring cloud-users closer
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
namespace OCA\Circles\Service;
28
29
30
use Exception;
31
use OC;
32
use OC\User\NoUserException;
33
use OCA\Circles\Circles\FileSharingBroadcaster;
34
use OCA\Circles\Db\CirclesRequest;
35
use OCA\Circles\Db\MembersRequest;
36
use OCA\Circles\Db\SharesRequest;
37
use OCA\Circles\Db\TokensRequest;
38
use OCA\Circles\Exceptions\CircleDoesNotExistException;
39
use OCA\Circles\Exceptions\CircleTypeNotValidException;
40
use OCA\Circles\Exceptions\ConfigNoCircleAvailableException;
41
use OCA\Circles\Exceptions\EmailAccountInvalidFormatException;
42
use OCA\Circles\Exceptions\GroupDoesNotExistException;
43
use OCA\Circles\Exceptions\MemberAlreadyExistsException;
44
use OCA\Circles\Exceptions\MemberDoesNotExistException;
45
use OCA\Circles\Exceptions\MemberIsNotModeratorException;
46
use OCA\Circles\Exceptions\MemberIsOwnerException;
47
use OCA\Circles\Exceptions\MemberTypeCantEditLevelException;
48
use OCA\Circles\Exceptions\ModeratorIsNotHighEnoughException;
49
use OCA\Circles\Model\Circle;
50
use OCA\Circles\Model\Member;
51
use OCP\IGroup;
52
use OCP\IL10N;
53
use OCP\IUser;
54
use OCP\IUserManager;
55
56
57
/**
58
 * Class MembersService
59
 *
60
 * @package OCA\Circles\Service
61
 */
62
class MembersService {
63
64
	/** @var string */
65
	private $userId;
66
67
	/** @var IL10N */
68
	private $l10n;
69
70
	/** @var IUserManager */
71
	private $userManager;
72
73
	/** @var ConfigService */
74
	private $configService;
75
76
	/** @var CirclesRequest */
77
	private $circlesRequest;
78
79
	/** @var MembersRequest */
80
	private $membersRequest;
81
82
	/** @var SharesRequest */
83
	private $sharesRequest;
84
85
	/** @var TokensRequest */
86
	private $tokensRequest;
87
88
	/** @var CirclesService */
89
	private $circlesService;
90
91
	/** @var EventsService */
92
	private $eventsService;
93
94
	/** @var FileSharingBroadcaster */
95
	private $fileSharingBroadcaster;
96
97
	/** @var MiscService */
98
	private $miscService;
99
100
	/**
101
	 * MembersService constructor.
102
	 *
103
	 * @param string $userId
104
	 * @param IL10N $l10n
105
	 * @param IUserManager $userManager
106
	 * @param ConfigService $configService
107
	 * @param CirclesRequest $circlesRequest
108
	 * @param MembersRequest $membersRequest
109
	 * @param SharesRequest $sharesRequest
110
	 * @param TokensRequest $tokensRequest
111
	 * @param CirclesService $circlesService
112
	 * @param EventsService $eventsService
113
	 * @param FileSharingBroadcaster $fileSharingBroadcaster
114
	 * @param MiscService $miscService
115
	 */
116 View Code Duplication
	public function __construct(
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...
117
		$userId, IL10N $l10n, IUserManager $userManager, ConfigService $configService,
118
		CirclesRequest $circlesRequest, MembersRequest $membersRequest, SharesRequest $sharesRequest,
119
		TokensRequest $tokensRequest, CirclesService $circlesService, EventsService $eventsService,
120
		FileSharingBroadcaster $fileSharingBroadcaster, MiscService $miscService
121
	) {
122
		$this->userId = $userId;
123
		$this->l10n = $l10n;
124
		$this->userManager = $userManager;
125
		$this->configService = $configService;
126
		$this->circlesRequest = $circlesRequest;
127
		$this->membersRequest = $membersRequest;
128
		$this->sharesRequest = $sharesRequest;
129
		$this->tokensRequest = $tokensRequest;
130
		$this->circlesService = $circlesService;
131
		$this->eventsService = $eventsService;
132
		$this->fileSharingBroadcaster = $fileSharingBroadcaster;
133
		$this->miscService = $miscService;
134
	}
135
136
137
	/**
138
	 * addMember();
139
	 *
140
	 * add a new member to a circle.
141
	 *
142
	 * @param string $circleUniqueId
143
	 * @param $ident
144
	 * @param int $type
145
	 *
146
	 * @param bool $force
147
	 *
148
	 * @return array
0 ignored issues
show
Documentation introduced by
Consider making the return type a bit more specific; maybe use Member[].

This check looks for the generic type array as a return type and suggests a more specific type. This type is inferred from the actual code.

Loading history...
149
	 * @throws Exception
150
	 */
151
	public function addMember($circleUniqueId, $ident, $type, bool $force = false) {
152
153 View Code Duplication
		if ($force === true) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
154
			$circle = $this->circlesRequest->forceGetCircle($circleUniqueId);
155
		} else {
156
			$circle = $this->circlesRequest->getCircle($circleUniqueId, $this->userId);
157
			$circle->getHigherViewer()
158
				   ->hasToBeModerator();
159
		}
160
161
		if (!$this->addMassiveMembers($circle, $ident, $type)) {
162
			$this->addSingleMember($circle, $ident, $type);
163
		}
164
165
		return $this->membersRequest->getMembers($circle->getUniqueId(), $circle->getHigherViewer(), $force);
166
	}
167
168
169
	/**
170
	 * add a single member to a circle.
171
	 *
172
	 * @param Circle $circle
173
	 * @param string $ident
174
	 * @param int $type
175
	 *
176
	 * @throws MemberAlreadyExistsException
177
	 * @throws Exception
178
	 */
179
	private function addSingleMember(Circle $circle, $ident, $type) {
180
		$this->verifyIdentBasedOnItsType($ident, $type);
181
		$this->verifyIdentWithGroupBackend($circle, $ident, $type);
182
183
		$member = $this->membersRequest->getFreshNewMember($circle->getUniqueId(), $ident, $type);
184
		$member->hasToBeInviteAble();
185
186
		$this->circlesService->checkThatCircleIsNotFull($circle);
187
188
		$this->addMemberBasedOnItsType($circle, $member);
189
190
		$this->membersRequest->updateMember($member);
191
		$this->fileSharingBroadcaster->sendMailAboutExistingShares($circle, $member);
192
193
		$this->eventsService->onMemberNew($circle, $member);
194
	}
195
196
197
	/**
198
	 * add a bunch of users to a circle based on the type of the 'bunch'
199
	 *
200
	 * @param Circle $circle
201
	 * @param string $ident
202
	 * @param int $type
203
	 *
204
	 * @return bool
205
	 * @throws Exception
206
	 */
207
	private function addMassiveMembers(Circle $circle, $ident, $type) {
208
209
		if ($type === Member::TYPE_GROUP) {
210
			return $this->addGroupMembers($circle, $ident);
211
		}
212
213
		if ($type === Member::TYPE_USER) {
214
			return $this->addMassiveMails($circle, $ident);
215
		}
216
217
		return false;
218
	}
219
220
221
	/**
222
	 * add a new member based on its type.
223
	 *
224
	 * @param Circle $circle
225
	 * @param Member $member
226
	 *
227
	 * @throws Exception
228
	 */
229
	private function addMemberBasedOnItsType(Circle $circle, Member &$member) {
230
		$this->addLocalMember($circle, $member);
231
		$this->addEmailAddress($member);
232
		$this->addContact($member);
233
	}
234
235
236
	/**
237
	 * @param Circle $circle
238
	 * @param Member $member
239
	 *
240
	 * @throws Exception
241
	 */
242
	private function addLocalMember(Circle $circle, Member $member) {
243
244
		if ($member->getType() !== Member::TYPE_USER) {
245
			return;
246
		}
247
248
		$member->inviteToCircle($circle->getType());
249
250
		if ($this->configService->isInvitationSkipped()) {
251
			$member->joinCircle($circle->getType());
252
		}
253
	}
254
255
256
	/**
257
	 * add mail address as contact.
258
	 *
259
	 * @param Member $member
260
	 *
261
	 * @throws Exception
262
	 */
263
	private function addEmailAddress(Member $member) {
264
265
		if ($member->getType() !== Member::TYPE_MAIL) {
266
			return;
267
		}
268
269
		$member->addMemberToCircle();
270
	}
271
272
273
	/**
274
	 * Add contact as member.
275
	 *
276
	 * @param Member $member
277
	 *
278
	 * @throws Exception
279
	 */
280
	private function addContact(Member $member) {
281
282
		if ($member->getType() !== Member::TYPE_CONTACT) {
283
			return;
284
		}
285
286
		$member->addMemberToCircle();
287
	}
288
289
290
	/**
291
	 * Verify the availability of an ident when Group Backend is enabled
292
	 *
293
	 * @param Circle $circle
294
	 * @param string $ident
295
	 * @param int $type
296
	 *
297
	 * @throws Exception
298
	 */
299
	private function verifyIdentWithGroupBackend(Circle $circle, $ident, $type) {
0 ignored issues
show
Unused Code introduced by
The parameter $ident is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
300
		if ($this->configService->isGroupsBackend() &&
301
			in_array($type, [Member::TYPE_MAIL, Member::TYPE_CONTACT], true) &&
302
			in_array($circle->getType(), [Circle::CIRCLES_CLOSED, Circle::CIRCLES_PUBLIC], true)
303
		) {
304
			if ($type === Member::TYPE_MAIL) {
305
				$errorMessage = 'You cannot add a mail address as member of your Circle';
306
			}
307
			if ($type === Member::TYPE_CONTACT) {
308
				$errorMessage = 'You cannot add a contact as member of your Circle';
309
			}
310
			throw new EmailAccountInvalidFormatException(
311
				$this->l10n->t($errorMessage)
312
			);
313
		}
314
	}
315
316
317
	/**
318
	 * Verify the availability of an ident, based on its type.
319
	 *
320
	 * @param string $ident
321
	 * @param int $type
322
	 *
323
	 * @throws Exception
324
	 */
325
	private function verifyIdentBasedOnItsType(&$ident, $type) {
326
		$this->verifyIdentLocalMember($ident, $type);
327
		$this->verifyIdentEmailAddress($ident, $type);
328
		$this->verifyIdentContact($ident, $type);
329
	}
330
331
332
	/**
333
	 * Verify if a local account is valid.
334
	 *
335
	 * @param $ident
336
	 * @param $type
337
	 *
338
	 * @throws NoUserException
339
	 */
340
	private function verifyIdentLocalMember(&$ident, $type) {
341
		if ($type !== Member::TYPE_USER) {
342
			return;
343
		}
344
345
		try {
346
			$ident = $this->miscService->getRealUserId($ident);
347
		} catch (NoUserException $e) {
0 ignored issues
show
Bug introduced by
The class OC\User\NoUserException 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...
348
			throw new NoUserException($this->l10n->t("This user does not exist"));
349
		}
350
	}
351
352
353
	/**
354
	 * Verify if a mail have a valid format.
355
	 *
356
	 * @param $ident
357
	 * @param $type
358
	 *
359
	 * @throws EmailAccountInvalidFormatException
360
	 */
361
	private function verifyIdentEmailAddress(&$ident, $type) {
362
363
		if ($type !== Member::TYPE_MAIL) {
364
			return;
365
		}
366
367
		if ($this->configService->isAccountOnly()) {
368
			throw new EmailAccountInvalidFormatException(
369
				$this->l10n->t('You cannot add a mail address as member of your Circle')
370
			);
371
		}
372
373
		if (!filter_var($ident, FILTER_VALIDATE_EMAIL)) {
374
			throw new EmailAccountInvalidFormatException(
375
				$this->l10n->t('Email format is not valid')
376
			);
377
		}
378
	}
379
380
381
	/**
382
	 * Verify if a contact exist in current user address books.
383
	 *
384
	 * @param $ident
385
	 * @param $type
386
	 *
387
	 * @throws NoUserException
388
	 * @throws EmailAccountInvalidFormatException
389
	 */
390
	private function verifyIdentContact(&$ident, $type) {
391
		if ($type !== Member::TYPE_CONTACT) {
392
			return;
393
		}
394
395
		if ($this->configService->isAccountOnly()) {
396
			throw new EmailAccountInvalidFormatException(
397
				$this->l10n->t('You cannot add a contact as member of your Circle')
398
			);
399
		}
400
401
		$tmpContact = $this->userId . ':' . $ident;
402
		$result = MiscService::getContactData($tmpContact);
403
		if (empty($result)) {
404
			throw new NoUserException($this->l10n->t("This contact is not available"));
405
		}
406
407
		$ident = $tmpContact;
408
	}
409
410
411
	/**
412
	 * @param Circle $circle
413
	 * @param string $groupId
414
	 *
415
	 * @return bool
416
	 * @throws Exception
417
	 */
418
	private function addGroupMembers(Circle $circle, $groupId) {
419
420
		$groupManager = OC::$server->getGroupManager();
421
		$group = $groupManager->get($groupId);
422
423
		$user = OC::$server->getUserSession()->getUser();
424
425
		if (!$this->configService->isAddingAnyGroupMembersAllowed() &&
426
			$group instanceof IGroup && $user instanceof IUser &&
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...
Bug introduced by
The class OCP\IUser 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...
427
			!$group->inGroup($user) && !$groupManager->isAdmin($user->getUID())
428
		) {
429
			$group = null;
430
		}
431
432
		if ($group === null) {
433
			throw new GroupDoesNotExistException($this->l10n->t('This group does not exist'));
434
		}
435
436
		foreach ($group->getUsers() as $user) {
437
			try {
438
				$this->addSingleMember($circle, $user->getUID(), Member::TYPE_USER);
439
			} catch (MemberAlreadyExistsException $e) {
440
			} catch (Exception $e) {
441
				throw $e;
442
			}
443
		}
444
445
		return true;
446
	}
447
448
449
	/**
450
	 * @param Circle $circle
451
	 * @param string $mails
452
	 *
453
	 * @return bool
454
	 */
455
	private function addMassiveMails(Circle $circle, $mails) {
456
457
		$mails = trim($mails);
458
		if (substr($mails, 0, 6) !== 'mails:') {
459
			return false;
460
		}
461
462
		$mails = substr($mails, 6);
463
		foreach (explode(' ', $mails) as $mail) {
464
			if (!filter_var($mail, FILTER_VALIDATE_EMAIL)) {
465
				continue;
466
			}
467
468
			try {
469
				$this->addMember($circle->getUniqueId(), $mail, Member::TYPE_MAIL);
470
			} catch (Exception $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
471
			}
472
		}
473
474
		return true;
475
	}
476
477
478
	/**
479
	 * getMember();
480
	 *
481
	 * Will return any data of a user related to a circle (as a Member). User can be a 'non-member'
482
	 * Viewer needs to be at least Member of the Circle
483
	 *
484
	 * @param $circleId
485
	 * @param $userId
486
	 * @param $type
487
	 * @param bool $forceAll
488
	 *
489
	 * @return Member
490
	 * @throws CircleDoesNotExistException
491
	 * @throws ConfigNoCircleAvailableException
492
	 * @throws MemberDoesNotExistException
493
	 */
494
	public function getMember($circleId, $userId, $type, $forceAll = false) {
495
		if (!$forceAll) {
496
			$this->circlesRequest->getCircle($circleId, $this->userId)
497
								 ->getHigherViewer()
498
								 ->hasToBeMember();
499
		}
500
501
		$member = $this->membersRequest->forceGetMember($circleId, $userId, $type);
502
		$member->setNote('');
503
504
		return $member;
505
	}
506
507
508
	/**
509
	 * @param string $memberId
510
	 *
511
	 * @return Member
512
	 * @throws MemberDoesNotExistException
513
	 */
514
	public function getMemberById(string $memberId): Member {
515
		return $this->membersRequest->forceGetMemberById($memberId);
516
	}
517
518
519
	/**
520
	 * @param string $circleUniqueId
521
	 * @param string $name
522
	 * @param int $type
523
	 * @param int $level
524
	 * @param bool $force
525
	 *
526
	 * @return array
0 ignored issues
show
Documentation introduced by
Consider making the return type a bit more specific; maybe use Member[].

This check looks for the generic type array as a return type and suggests a more specific type. This type is inferred from the actual code.

Loading history...
527
	 * @throws CircleDoesNotExistException
528
	 * @throws CircleTypeNotValidException
529
	 * @throws ConfigNoCircleAvailableException
530
	 * @throws MemberDoesNotExistException
531
	 * @throws MemberTypeCantEditLevelException
532
	 * @throws Exception
533
	 */
534
	public function levelMember($circleUniqueId, $name, $type, $level, bool $force = false) {
535
536
		$level = (int)$level;
537
		if ($force === false) {
538
			$circle = $this->circlesRequest->getCircle($circleUniqueId, $this->userId);
539
		} else {
540
			$circle = $this->circlesRequest->forceGetCircle($circleUniqueId);
541
		}
542
543
		if ($circle->getType() === Circle::CIRCLES_PERSONAL) {
544
			throw new CircleTypeNotValidException(
545
				$this->l10n->t('You cannot edit level in a personal circle')
546
			);
547
		}
548
549
		$member = $this->membersRequest->forceGetMember($circle->getUniqueId(), $name, $type);
550
		$member->levelHasToBeEditable();
551
		$this->updateMemberLevel($circle, $member, $level, $force);
552
553 View Code Duplication
		if ($force === false) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
554
			return $this->membersRequest->getMembers(
555
				$circle->getUniqueId(), $circle->getHigherViewer()
556
			);
557
		} else {
558
			return $this->membersRequest->forceGetMembers($circle->getUniqueId());
559
		}
560
561
	}
562
563
564
	/**
565
	 * @param Circle $circle
566
	 * @param Member $member
567
	 * @param $level
568
	 * @param bool $force
569
	 *
570
	 * @throws Exception
571
	 */
572
	private function updateMemberLevel(Circle $circle, Member $member, $level, bool $force = false) {
573
		if ($member->getLevel() === $level) {
574
			return;
575
		}
576
577
		if ($level === Member::LEVEL_OWNER) {
578
			$this->switchOwner($circle, $member, $force);
579
		} else {
580
			$this->editMemberLevel($circle, $member, $level, $force);
581
		}
582
583
		$this->eventsService->onMemberLevel($circle, $member);
584
	}
585
586
587
	/**
588
	 * @param Circle $circle
589
	 * @param Member $member
590
	 * @param $level
591
	 * @param bool $force
592
	 *
593
	 * @throws Exception
594
	 */
595
	private function editMemberLevel(Circle $circle, Member &$member, $level, bool $force = false) {
596
		if ($force === false) {
597
			$isMod = $circle->getHigherViewer();
598
			$isMod->hasToBeModerator();
599
			$isMod->hasToBeHigherLevel($level);
600
601
			$member->hasToBeMember();
602
			$isMod->hasToBeHigherLevel($member->getLevel());
603
		}
604
605
		$member->cantBeOwner();
606
607
		$member->setLevel($level);
608
		$this->membersRequest->updateMember($member);
609
	}
610
611
	/**
612
	 * @param Circle $circle
613
	 * @param Member $member
614
	 * @param bool $force
615
	 *
616
	 * @throws Exception
617
	 */
618
	private function switchOwner(Circle $circle, Member &$member, bool $force = false) {
619
		if ($force === false) {
620
			$isMod = $circle->getHigherViewer();
621
622
			// should already be possible from an NCAdmin, but not enabled in the frontend.
623
			$this->circlesService->hasToBeOwner($isMod);
624
		} else {
625
			$isMod = $circle->getOwner();
626
		}
627
628
		$member->hasToBeMember();
629
		$member->cantBeOwner();
630
631
		$member->setLevel(Member::LEVEL_OWNER);
632
		$this->membersRequest->updateMember($member);
633
634
		$isMod->setLevel(Member::LEVEL_ADMIN);
635
		$this->membersRequest->updateMember($isMod);
636
	}
637
638
639
	/**
640
	 * @param string $circleUniqueId
641
	 * @param string $name
642
	 * @param $type
643
	 * @param bool $force
644
	 *
645
	 * @return array
0 ignored issues
show
Documentation introduced by
Consider making the return type a bit more specific; maybe use Member[].

This check looks for the generic type array as a return type and suggests a more specific type. This type is inferred from the actual code.

Loading history...
646
	 * @throws CircleDoesNotExistException
647
	 * @throws ConfigNoCircleAvailableException
648
	 * @throws MemberDoesNotExistException
649
	 * @throws MemberIsNotModeratorException
650
	 * @throws MemberIsOwnerException
651
	 * @throws ModeratorIsNotHighEnoughException
652
	 */
653
	public function removeMember($circleUniqueId, $name, $type, bool $force = false) {
654
655 View Code Duplication
		if ($force === false) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
656
			$circle = $this->circlesRequest->getCircle($circleUniqueId, $this->userId);
657
			$circle->getHigherViewer()
658
				   ->hasToBeModerator();
659
		} else {
660
			$circle = $this->circlesRequest->forceGetCircle($circleUniqueId);
661
		}
662
663
		$member = $this->membersRequest->forceGetMember($circleUniqueId, $name, $type);
664
		$member->hasToBeMemberOrAlmost();
665
		$member->cantBeOwner();
666
667
		if ($force === false) {
668
			$circle->getHigherViewer()
669
				   ->hasToBeHigherLevel($member->getLevel());
670
		}
671
672
		$this->eventsService->onMemberLeaving($circle, $member);
673
674
		$this->membersRequest->removeMember($member);
675
		$this->sharesRequest->removeSharesFromMember($member);
676
		$this->tokensRequest->removeTokensFromMember($member);
677
678 View Code Duplication
		if ($force === false) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
679
			return $this->membersRequest->getMembers(
680
				$circle->getUniqueId(), $circle->getHigherViewer()
681
			);
682
		} else {
683
			return $this->membersRequest->forceGetMembers($circle->getUniqueId());
684
		}
685
	}
686
687
688
	/**
689
	 * When a user is removed, remove him from all Circles
690
	 *
691
	 * @param $userId
692
	 */
693
	public function onUserRemoved($userId) {
694
		$this->membersRequest->removeAllMembershipsFromUser($userId);
695
	}
696
697
698
}
699