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

MembersService::addSingleMember()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 16

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 16
rs 9.7333
c 0
b 0
f 0
cc 1
nc 1
nop 3
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\IL10N;
52
use OCP\IUserManager;
53
54
55
/**
56
 * Class MembersService
57
 *
58
 * @package OCA\Circles\Service
59
 */
60
class MembersService {
61
62
	/** @var string */
63
	private $userId;
64
65
	/** @var IL10N */
66
	private $l10n;
67
68
	/** @var IUserManager */
69
	private $userManager;
70
71
	/** @var ConfigService */
72
	private $configService;
73
74
	/** @var CirclesRequest */
75
	private $circlesRequest;
76
77
	/** @var MembersRequest */
78
	private $membersRequest;
79
80
	/** @var SharesRequest */
81
	private $sharesRequest;
82
83
	/** @var TokensRequest */
84
	private $tokensRequest;
85
86
	/** @var CirclesService */
87
	private $circlesService;
88
89
	/** @var EventsService */
90
	private $eventsService;
91
92
	/** @var FileSharingBroadcaster */
93
	private $fileSharingBroadcaster;
94
95
	/** @var MiscService */
96
	private $miscService;
97
98
	/**
99
	 * MembersService constructor.
100
	 *
101
	 * @param string $userId
102
	 * @param IL10N $l10n
103
	 * @param IUserManager $userManager
104
	 * @param ConfigService $configService
105
	 * @param CirclesRequest $circlesRequest
106
	 * @param MembersRequest $membersRequest
107
	 * @param SharesRequest $sharesRequest
108
	 * @param TokensRequest $tokensRequest
109
	 * @param CirclesService $circlesService
110
	 * @param EventsService $eventsService
111
	 * @param FileSharingBroadcaster $fileSharingBroadcaster
112
	 * @param MiscService $miscService
113
	 */
114 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...
115
		$userId, IL10N $l10n, IUserManager $userManager, ConfigService $configService,
116
		CirclesRequest $circlesRequest, MembersRequest $membersRequest, SharesRequest $sharesRequest,
117
		TokensRequest $tokensRequest, CirclesService $circlesService, EventsService $eventsService,
118
		FileSharingBroadcaster $fileSharingBroadcaster, MiscService $miscService
119
	) {
120
		$this->userId = $userId;
121
		$this->l10n = $l10n;
122
		$this->userManager = $userManager;
123
		$this->configService = $configService;
124
		$this->circlesRequest = $circlesRequest;
125
		$this->membersRequest = $membersRequest;
126
		$this->sharesRequest = $sharesRequest;
127
		$this->tokensRequest = $tokensRequest;
128
		$this->circlesService = $circlesService;
129
		$this->eventsService = $eventsService;
130
		$this->fileSharingBroadcaster = $fileSharingBroadcaster;
131
		$this->miscService = $miscService;
132
	}
133
134
135
	/**
136
	 * addMember();
137
	 *
138
	 * add a new member to a circle.
139
	 *
140
	 * @param string $circleUniqueId
141
	 * @param $ident
142
	 * @param int $type
143
	 *
144
	 * @param bool $force
145
	 *
146
	 * @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...
147
	 * @throws Exception
148
	 */
149
	public function addMember($circleUniqueId, $ident, $type, bool $force = false) {
150
151 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...
152
			$circle = $this->circlesRequest->forceGetCircle($circleUniqueId);
153
		} else {
154
			$circle = $this->circlesRequest->getCircle($circleUniqueId, $this->userId);
155
			$circle->getHigherViewer()
156
				   ->hasToBeModerator();
157
		}
158
159
		if (!$this->addMassiveMembers($circle, $ident, $type)) {
160
			$this->addSingleMember($circle, $ident, $type);
161
		}
162
163
		return $this->membersRequest->getMembers($circle->getUniqueId(), $circle->getHigherViewer(), $force);
164
	}
165
166
167
	/**
168
	 * add a single member to a circle.
169
	 *
170
	 * @param Circle $circle
171
	 * @param string $ident
172
	 * @param int $type
173
	 *
174
	 * @throws MemberAlreadyExistsException
175
	 * @throws Exception
176
	 */
177
	private function addSingleMember(Circle $circle, $ident, $type) {
178
		$this->verifyIdentBasedOnItsType($ident, $type);
179
		$this->verifyIdentWithGroupBackend($circle, $ident, $type);
180
181
		$member = $this->membersRequest->getFreshNewMember($circle->getUniqueId(), $ident, $type);
182
		$member->hasToBeInviteAble();
183
184
		$this->circlesService->checkThatCircleIsNotFull($circle);
185
186
		$this->addMemberBasedOnItsType($circle, $member);
187
188
		$this->membersRequest->updateMember($member);
189
		$this->fileSharingBroadcaster->sendMailAboutExistingShares($circle, $member);
190
191
		$this->eventsService->onMemberNew($circle, $member);
192
	}
193
194
195
	/**
196
	 * add a bunch of users to a circle based on the type of the 'bunch'
197
	 *
198
	 * @param Circle $circle
199
	 * @param string $ident
200
	 * @param int $type
201
	 *
202
	 * @return bool
203
	 * @throws Exception
204
	 */
205
	private function addMassiveMembers(Circle $circle, $ident, $type) {
206
207
		if ($type === Member::TYPE_GROUP) {
208
			return $this->addGroupMembers($circle, $ident);
209
		}
210
211
		if ($type === Member::TYPE_USER) {
212
			return $this->addMassiveMails($circle, $ident);
213
		}
214
215
		return false;
216
	}
217
218
219
	/**
220
	 * add a new member based on its type.
221
	 *
222
	 * @param Circle $circle
223
	 * @param Member $member
224
	 *
225
	 * @throws Exception
226
	 */
227
	private function addMemberBasedOnItsType(Circle $circle, Member &$member) {
228
		$this->addLocalMember($circle, $member);
229
		$this->addEmailAddress($member);
230
		$this->addContact($member);
231
	}
232
233
234
	/**
235
	 * @param Circle $circle
236
	 * @param Member $member
237
	 *
238
	 * @throws Exception
239
	 */
240
	private function addLocalMember(Circle $circle, Member $member) {
241
242
		if ($member->getType() !== Member::TYPE_USER) {
243
			return;
244
		}
245
246
		$member->inviteToCircle($circle->getType());
247
248
		if ($this->configService->isInvitationSkipped()) {
249
			$member->joinCircle($circle->getType());
250
		}
251
	}
252
253
254
	/**
255
	 * add mail address as contact.
256
	 *
257
	 * @param Member $member
258
	 *
259
	 * @throws Exception
260
	 */
261
	private function addEmailAddress(Member $member) {
262
263
		if ($member->getType() !== Member::TYPE_MAIL) {
264
			return;
265
		}
266
267
		$member->addMemberToCircle();
268
	}
269
270
271
	/**
272
	 * Add contact as member.
273
	 *
274
	 * @param Member $member
275
	 *
276
	 * @throws Exception
277
	 */
278
	private function addContact(Member $member) {
279
280
		if ($member->getType() !== Member::TYPE_CONTACT) {
281
			return;
282
		}
283
284
		$member->addMemberToCircle();
285
	}
286
287
288
	/**
289
	 * Verify the availability of an ident when Group Backend is enabled
290
	 *
291
	 * @param Circle $circle
292
	 * @param string $ident
293
	 * @param int $type
294
	 *
295
	 * @throws Exception
296
	 */
297
	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...
298
		if ($this->configService->isGroupsBackend() &&
299
			in_array($type, [Member::TYPE_MAIL, Member::TYPE_CONTACT], true) &&
300
			in_array($circle->getType(), [Circle::CIRCLES_CLOSED, Circle::CIRCLES_PUBLIC], true)
301
		) {
302
			if ($type === Member::TYPE_MAIL) {
303
				$errorMessage = 'You cannot add a mail address as member of your Circle';
304
			}
305
			if ($type === Member::TYPE_CONTACT) {
306
				$errorMessage = 'You cannot add a contact as member of your Circle';
307
			}
308
			throw new EmailAccountInvalidFormatException(
309
				$this->l10n->t($errorMessage)
310
			);
311
		}
312
	}
313
314
315
	/**
316
	 * Verify the availability of an ident, based on its type.
317
	 *
318
	 * @param string $ident
319
	 * @param int $type
320
	 *
321
	 * @throws Exception
322
	 */
323
	private function verifyIdentBasedOnItsType(&$ident, $type) {
324
		$this->verifyIdentLocalMember($ident, $type);
325
		$this->verifyIdentEmailAddress($ident, $type);
326
		$this->verifyIdentContact($ident, $type);
327
	}
328
329
330
	/**
331
	 * Verify if a local account is valid.
332
	 *
333
	 * @param $ident
334
	 * @param $type
335
	 *
336
	 * @throws NoUserException
337
	 */
338
	private function verifyIdentLocalMember(&$ident, $type) {
339
		if ($type !== Member::TYPE_USER) {
340
			return;
341
		}
342
343
		try {
344
			$ident = $this->miscService->getRealUserId($ident);
345
		} 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...
346
			throw new NoUserException($this->l10n->t("This user does not exist"));
347
		}
348
	}
349
350
351
	/**
352
	 * Verify if a mail have a valid format.
353
	 *
354
	 * @param $ident
355
	 * @param $type
356
	 *
357
	 * @throws EmailAccountInvalidFormatException
358
	 */
359
	private function verifyIdentEmailAddress(&$ident, $type) {
360
361
		if ($type !== Member::TYPE_MAIL) {
362
			return;
363
		}
364
365
		if ($this->configService->isAccountOnly()) {
366
			throw new EmailAccountInvalidFormatException(
367
				$this->l10n->t('You cannot add a mail address as member of your Circle')
368
			);
369
		}
370
371
		if (!filter_var($ident, FILTER_VALIDATE_EMAIL)) {
372
			throw new EmailAccountInvalidFormatException(
373
				$this->l10n->t('Email format is not valid')
374
			);
375
		}
376
	}
377
378
379
	/**
380
	 * Verify if a contact exist in current user address books.
381
	 *
382
	 * @param $ident
383
	 * @param $type
384
	 *
385
	 * @throws NoUserException
386
	 * @throws EmailAccountInvalidFormatException
387
	 */
388
	private function verifyIdentContact(&$ident, $type) {
389
		if ($type !== Member::TYPE_CONTACT) {
390
			return;
391
		}
392
393
		if ($this->configService->isAccountOnly()) {
394
			throw new EmailAccountInvalidFormatException(
395
				$this->l10n->t('You cannot add a contact as member of your Circle')
396
			);
397
		}
398
399
		$tmpContact = $this->userId . ':' . $ident;
400
		$result = MiscService::getContactData($tmpContact);
401
		if (empty($result)) {
402
			throw new NoUserException($this->l10n->t("This contact is not available"));
403
		}
404
405
		$ident = $tmpContact;
406
	}
407
408
409
	/**
410
	 * @param Circle $circle
411
	 * @param string $groupId
412
	 *
413
	 * @return bool
414
	 * @throws Exception
415
	 */
416
	private function addGroupMembers(Circle $circle, $groupId) {
417
418
		$group = OC::$server->getGroupManager()
419
							->get($groupId);
420
		if ($group === null) {
421
			throw new GroupDoesNotExistException($this->l10n->t('This group does not exist'));
422
		}
423
424
		foreach ($group->getUsers() as $user) {
425
			try {
426
				$this->addSingleMember($circle, $user->getUID(), Member::TYPE_USER);
427
			} catch (MemberAlreadyExistsException $e) {
428
			} catch (Exception $e) {
429
				throw $e;
430
			}
431
		}
432
433
		return true;
434
	}
435
436
437
	/**
438
	 * @param Circle $circle
439
	 * @param string $mails
440
	 *
441
	 * @return bool
442
	 */
443
	private function addMassiveMails(Circle $circle, $mails) {
444
445
		$mails = trim($mails);
446
		if (substr($mails, 0, 6) !== 'mails:') {
447
			return false;
448
		}
449
450
		$mails = substr($mails, 6);
451
		foreach (explode(' ', $mails) as $mail) {
452
			if (!filter_var($mail, FILTER_VALIDATE_EMAIL)) {
453
				continue;
454
			}
455
456
			try {
457
				$this->addMember($circle->getUniqueId(), $mail, Member::TYPE_MAIL);
458
			} catch (Exception $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
459
			}
460
		}
461
462
		return true;
463
	}
464
465
466
	/**
467
	 * getMember();
468
	 *
469
	 * Will return any data of a user related to a circle (as a Member). User can be a 'non-member'
470
	 * Viewer needs to be at least Member of the Circle
471
	 *
472
	 * @param $circleId
473
	 * @param $userId
474
	 * @param $type
475
	 * @param bool $forceAll
476
	 *
477
	 * @return Member
478
	 * @throws CircleDoesNotExistException
479
	 * @throws ConfigNoCircleAvailableException
480
	 * @throws MemberDoesNotExistException
481
	 */
482
	public function getMember($circleId, $userId, $type, $forceAll = false) {
483
		if (!$forceAll) {
484
			$this->circlesRequest->getCircle($circleId, $this->userId)
485
								 ->getHigherViewer()
486
								 ->hasToBeMember();
487
		}
488
489
		$member = $this->membersRequest->forceGetMember($circleId, $userId, $type);
490
		$member->setNote('');
491
492
		return $member;
493
	}
494
495
496
	/**
497
	 * @param string $memberId
498
	 *
499
	 * @return Member
500
	 * @throws MemberDoesNotExistException
501
	 */
502
	public function getMemberById(string $memberId): Member {
503
		return $this->membersRequest->forceGetMemberById($memberId);
504
	}
505
506
507
	/**
508
	 * @param string $circleUniqueId
509
	 * @param string $name
510
	 * @param int $type
511
	 * @param int $level
512
	 * @param bool $force
513
	 *
514
	 * @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...
515
	 * @throws CircleDoesNotExistException
516
	 * @throws CircleTypeNotValidException
517
	 * @throws ConfigNoCircleAvailableException
518
	 * @throws MemberDoesNotExistException
519
	 * @throws MemberTypeCantEditLevelException
520
	 * @throws Exception
521
	 */
522
	public function levelMember($circleUniqueId, $name, $type, $level, bool $force = false) {
523
524
		$level = (int)$level;
525
		if ($force === false) {
526
			$circle = $this->circlesRequest->getCircle($circleUniqueId, $this->userId);
527
		} else {
528
			$circle = $this->circlesRequest->forceGetCircle($circleUniqueId);
529
		}
530
531
		if ($circle->getType() === Circle::CIRCLES_PERSONAL) {
532
			throw new CircleTypeNotValidException(
533
				$this->l10n->t('You cannot edit level in a personal circle')
534
			);
535
		}
536
537
		$member = $this->membersRequest->forceGetMember($circle->getUniqueId(), $name, $type);
538
		$member->levelHasToBeEditable();
539
		$this->updateMemberLevel($circle, $member, $level, $force);
540
541 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...
542
			return $this->membersRequest->getMembers(
543
				$circle->getUniqueId(), $circle->getHigherViewer()
544
			);
545
		} else {
546
			return $this->membersRequest->forceGetMembers($circle->getUniqueId());
547
		}
548
549
	}
550
551
552
	/**
553
	 * @param Circle $circle
554
	 * @param Member $member
555
	 * @param $level
556
	 * @param bool $force
557
	 *
558
	 * @throws Exception
559
	 */
560
	private function updateMemberLevel(Circle $circle, Member $member, $level, bool $force = false) {
561
		if ($member->getLevel() === $level) {
562
			return;
563
		}
564
565
		if ($level === Member::LEVEL_OWNER) {
566
			$this->switchOwner($circle, $member, $force);
567
		} else {
568
			$this->editMemberLevel($circle, $member, $level, $force);
569
		}
570
571
		$this->eventsService->onMemberLevel($circle, $member);
572
	}
573
574
575
	/**
576
	 * @param Circle $circle
577
	 * @param Member $member
578
	 * @param $level
579
	 * @param bool $force
580
	 *
581
	 * @throws Exception
582
	 */
583
	private function editMemberLevel(Circle $circle, Member &$member, $level, bool $force = false) {
584
		if ($force === false) {
585
			$isMod = $circle->getHigherViewer();
586
			$isMod->hasToBeModerator();
587
			$isMod->hasToBeHigherLevel($level);
588
589
			$member->hasToBeMember();
590
			$isMod->hasToBeHigherLevel($member->getLevel());
591
		}
592
593
		$member->cantBeOwner();
594
595
		$member->setLevel($level);
596
		$this->membersRequest->updateMember($member);
597
	}
598
599
	/**
600
	 * @param Circle $circle
601
	 * @param Member $member
602
	 * @param bool $force
603
	 *
604
	 * @throws Exception
605
	 */
606
	private function switchOwner(Circle $circle, Member &$member, bool $force = false) {
607
		if ($force === false) {
608
			$isMod = $circle->getHigherViewer();
609
610
			// should already be possible from an NCAdmin, but not enabled in the frontend.
611
			$this->circlesService->hasToBeOwner($isMod);
612
		} else {
613
			$isMod = $circle->getOwner();
614
		}
615
616
		$member->hasToBeMember();
617
		$member->cantBeOwner();
618
619
		$member->setLevel(Member::LEVEL_OWNER);
620
		$this->membersRequest->updateMember($member);
621
622
		$isMod->setLevel(Member::LEVEL_ADMIN);
623
		$this->membersRequest->updateMember($isMod);
624
	}
625
626
627
	/**
628
	 * @param string $circleUniqueId
629
	 * @param string $name
630
	 * @param $type
631
	 * @param bool $force
632
	 *
633
	 * @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...
634
	 * @throws CircleDoesNotExistException
635
	 * @throws ConfigNoCircleAvailableException
636
	 * @throws MemberDoesNotExistException
637
	 * @throws MemberIsNotModeratorException
638
	 * @throws MemberIsOwnerException
639
	 * @throws ModeratorIsNotHighEnoughException
640
	 */
641
	public function removeMember($circleUniqueId, $name, $type, bool $force = false) {
642
643 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...
644
			$circle = $this->circlesRequest->getCircle($circleUniqueId, $this->userId);
645
			$circle->getHigherViewer()
646
				   ->hasToBeModerator();
647
		} else {
648
			$circle = $this->circlesRequest->forceGetCircle($circleUniqueId);
649
		}
650
651
		$member = $this->membersRequest->forceGetMember($circleUniqueId, $name, $type);
652
		$member->hasToBeMemberOrAlmost();
653
		$member->cantBeOwner();
654
655
		if ($force === false) {
656
			$circle->getHigherViewer()
657
				   ->hasToBeHigherLevel($member->getLevel());
658
		}
659
660
		$this->eventsService->onMemberLeaving($circle, $member);
661
662
		$this->membersRequest->removeMember($member);
663
		$this->sharesRequest->removeSharesFromMember($member);
664
		$this->tokensRequest->removeTokensFromMember($member);
665
666 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...
667
			return $this->membersRequest->getMembers(
668
				$circle->getUniqueId(), $circle->getHigherViewer()
669
			);
670
		} else {
671
			return $this->membersRequest->forceGetMembers($circle->getUniqueId());
672
		}
673
	}
674
675
676
	/**
677
	 * When a user is removed, remove him from all Circles
678
	 *
679
	 * @param $userId
680
	 */
681
	public function onUserRemoved($userId) {
682
		$this->membersRequest->removeAllMembershipsFromUser($userId);
683
	}
684
685
686
}
687