Completed
Push — master ( 6ab97c...6a4e84 )
by Maxence
02:33
created

MembersService::addGroupMembers()   B

Complexity

Conditions 5
Paths 13

Size

Total Lines 28
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 28
rs 8.439
cc 5
eloc 18
nc 13
nop 2
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\User\NoUserException;
32
use OCA\Circles\Db\CirclesRequest;
33
use OCA\Circles\Db\MembersRequest;
34
use OCA\Circles\Exceptions\CircleTypeNotValidException;
35
use OCA\Circles\Exceptions\EmailAccountInvalidFormatException;
36
use OCA\Circles\Exceptions\GroupDoesNotExistException;
37
use OCA\Circles\Exceptions\MemberAlreadyExistsException;
38
use OCA\Circles\Model\Circle;
39
use OCA\Circles\Model\Member;
40
use OCP\IL10N;
41
use OCP\IUserManager;
42
43
class MembersService {
44
45
	/** @var string */
46
	private $userId;
47
48
	/** @var IL10N */
49
	private $l10n;
50
51
	/** @var IUserManager */
52
	private $userManager;
53
54
	/** @var ConfigService */
55
	private $configService;
56
57
	/** @var CirclesRequest */
58
	private $circlesRequest;
59
60
	/** @var MembersRequest */
61
	private $membersRequest;
62
63
	/** @var EventsService */
64
	private $eventsService;
65
66
	/** @var MiscService */
67
	private $miscService;
68
69
	/**
70
	 * MembersService constructor.
71
	 *
72
	 * @param $userId
73
	 * @param IL10N $l10n
74
	 * @param IUserManager $userManager
75
	 * @param ConfigService $configService
76
	 * @param CirclesRequest $circlesRequest
77
	 * @param MembersRequest $membersRequest
78
	 * @param EventsService $eventsService
79
	 * @param MiscService $miscService
80
	 */
81 View Code Duplication
	public function __construct(
1 ignored issue
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...
82
		$userId,
83
		IL10N $l10n,
84
		IUserManager $userManager,
85
		ConfigService $configService,
86
		CirclesRequest $circlesRequest,
87
		MembersRequest $membersRequest,
88
		EventsService $eventsService,
89
		MiscService $miscService
90
	) {
91
		$this->userId = $userId;
92
		$this->l10n = $l10n;
93
		$this->userManager = $userManager;
94
		$this->configService = $configService;
95
		$this->circlesRequest = $circlesRequest;
96
		$this->membersRequest = $membersRequest;
97
		$this->eventsService = $eventsService;
98
		$this->miscService = $miscService;
99
	}
100
101
102
	/**
103
	 * addMember();
104
	 *
105
	 * add a new member to a circle.
106
	 *
107
	 * @param string $circleUniqueId
108
	 * @param $ident
109
	 * @param $type
110
	 *
111
	 * @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...
112
	 * @throws \Exception
113
	 */
114
	public function addMember($circleUniqueId, $ident, $type) {
115
116
		try {
117
			$circle = $this->circlesRequest->getCircle($circleUniqueId, $this->userId);
118
			$circle->getHigherViewer()
119
				   ->hasToBeModerator();
120
121
			if (!$this->addMassiveMembers($circle, $ident, $type)) {
122
				$this->addSingleMember($circle, $ident, $type);
123
			}
124
		} catch (\Exception $e) {
125
			throw $e;
126
		}
127
128
		return $this->membersRequest->getMembers($circle->getUniqueId(), $circle->getHigherViewer());
129
	}
130
131
132
	/**
133
	 * add a single member to a circle.
134
	 *
135
	 * @param Circle $circle
136
	 * @param string $ident
137
	 * @param int $type
138
	 */
139
	private function addSingleMember(Circle $circle, $ident, $type) {
140
		$this->verifyIdentBasedOnItsType($ident, $type);
141
142
		$member = $this->membersRequest->getFreshNewMember($circle->getUniqueId(), $ident, $type);
143
		$member->hasToBeInviteAble();
144
145
		$this->addMemberBasedOnItsType($circle, $member);
146
147
		$this->membersRequest->updateMember($member);
148
		$this->eventsService->onMemberNew($circle, $member);
149
	}
150
151
152
	/**
153
	 * add a bunch of users to a circle based on the type of the 'bunch'
154
	 *
155
	 * @param Circle $circle
156
	 * @param string $ident
157
	 * @param int $type
158
	 *
159
	 * @return bool
160
	 */
161
	private function addMassiveMembers(Circle $circle, $ident, $type) {
162
163
		if ($type === Member::TYPE_GROUP) {
164
			return $this->addGroupMembers($circle, $ident);
165
		}
166
167
		return false;
168
	}
169
170
171
	/**
172
	 * add a new member based on its type.
173
	 *
174
	 * @param Circle $circle
175
	 * @param Member $member
176
	 */
177
	private function addMemberBasedOnItsType(Circle $circle, Member &$member) {
178
		$this->addLocalMember($circle, $member);
179
		$this->addEmailAddress($member);
180
		$this->addContact($member);
181
	}
182
183
184
	/**
185
	 * @param Circle $circle
186
	 * @param Member $member
187
	 *
188
	 * @throws \Exception
189
	 */
190
	public function addLocalMember(Circle $circle, Member $member) {
191
192
		if ($member->getType() !== Member::TYPE_USER) {
193
			return;
194
		}
195
196
		$member->inviteToCircle($circle->getType());
197
	}
198
199
200
	/**
201
	 * add mail address as contact.
202
	 *
203
	 * @param Member $member
204
	 *
205
	 * @throws \Exception
206
	 */
207
	private function addEmailAddress(Member $member) {
208
209
		if ($member->getType() !== Member::TYPE_MAIL) {
210
			return;
211
		}
212
213
		$member->addMemberToCircle();
214
	}
215
216
217
	/**
218
	 * Add contact as member.
219
	 *
220
	 * @param Member $member
221
	 *
222
	 * @throws \Exception
223
	 */
224
	private function addContact(Member $member) {
225
226
		if ($member->getType() !== Member::TYPE_CONTACT) {
227
			return;
228
		}
229
230
		$member->addMemberToCircle();
231
	}
232
233
234
	/**
235
	 * Verify the availability of an ident, based on its type.
236
	 *
237
	 * @param string $ident
238
	 * @param int $type
239
	 *
240
	 * @throws Exception
241
	 */
242
	private function verifyIdentBasedOnItsType(&$ident, $type) {
243
		try {
244
			$this->verifyIdentLocalMember($ident, $type);
245
			$this->verifyIdentEmailAddress($ident, $type);
246
			$this->verifyIdentContact($ident, $type);
247
		} catch (Exception $e) {
248
			throw $e;
249
		}
250
	}
251
252
253
	/**
254
	 * Verify if a local account is valid.
255
	 *
256
	 * @param $ident
257
	 * @param $type
258
	 *
259
	 * @throws NoUserException
260
	 */
261
	private function verifyIdentLocalMember(&$ident, $type) {
262
		if ($type !== Member::TYPE_USER) {
263
			return;
264
		}
265
266
		try {
267
			$ident = $this->miscService->getRealUserId($ident);
268
		} 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...
269
			throw new NoUserException($this->l10n->t("This user does not exist"));
270
		}
271
	}
272
273
274
	/**
275
	 * Verify if a mail have a valid format.
276
	 *
277
	 * @param $ident
278
	 * @param $type
279
	 *
280
	 * @throws EmailAccountInvalidFormatException
281
	 */
282
	private function verifyIdentEmailAddress(&$ident, $type) {
283
		if ($type !== Member::TYPE_MAIL) {
284
			return;
285
		}
286
287
		if (!filter_var($ident, FILTER_VALIDATE_EMAIL)) {
288
			throw new EmailAccountInvalidFormatException(
289
				$this->l10n->t('Email format is not valid')
290
			);
291
		}
292
	}
293
294
295
	/**
296
	 * Verify if a contact exist in current user address books.
297
	 *
298
	 * @param $ident
299
	 * @param $type
300
	 *
301
	 * @throws NoUserException
302
	 */
303
	private function verifyIdentContact(&$ident, $type) {
304
		if ($type !== Member::TYPE_CONTACT) {
305
			return;
306
		}
307
308
		$tmpContact = $this->userId . ':' . $ident;
309
		try {
310
			MiscService::getContactData($tmpContact);
0 ignored issues
show
Deprecated Code introduced by
The method OCA\Circles\Service\MiscService::getContactData() has been deprecated with message: - move this somewhere else, no static if possible

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
311
		} catch (Exception $e) {
312
			throw new NoUserException($this->l10n->t("This contact is not available"));
313
		}
314
315
		$ident = $tmpContact;
316
	}
317
318
319
	/**
320
	 * @param Circle $circle
321
	 * @param string $groupId
322
	 *
323
	 * @return bool
324
	 * @throws \Exception
325
	 */
326
	private function addGroupMembers(Circle $circle, $groupId) {
327
328
		$group = \OC::$server->getGroupManager()
329
							 ->get($groupId);
330
		if ($group === null) {
331
			throw new GroupDoesNotExistException($this->l10n->t('This group does not exist'));
332
		}
333
334
		foreach ($group->getUsers() as $user) {
335
			try {
336
				$member =
337
					$this->membersRequest->getFreshNewMember(
338
						$circle->getUniqueId(), $user->getUID(), Member::TYPE_USER
339
					);
340
				$member->hasToBeInviteAble();
341
342
				$member->inviteToCircle($circle->getType());
343
				$this->membersRequest->updateMember($member);
344
345
				$this->eventsService->onMemberNew($circle, $member);
346
			} catch (MemberAlreadyExistsException $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
347
			} catch (\Exception $e) {
348
				throw $e;
349
			}
350
		}
351
352
		return true;
353
	}
354
355
356
	/**
357
	 * getMember();
358
	 *
359
	 * Will return any data of a user related to a circle (as a Member). User can be a 'non-member'
360
	 * Viewer needs to be at least Member of the Circle
361
	 *
362
	 * @param $circleId
363
	 * @param $userId
364
	 * @param $type
365
	 *
366
	 * @return Member
367
	 * @throws \Exception
368
	 */
369
	public function getMember($circleId, $userId, $type) {
370
371
		try {
372
			$this->circlesRequest->getCircle($circleId, $this->userId)
373
								 ->getHigherViewer()
374
								 ->hasToBeMember();
375
376
			$member = $this->membersRequest->forceGetMember($circleId, $userId, $type);
377
			$member->setNote('');
378
379
			return $member;
380
		} catch (\Exception $e) {
381
			throw $e;
382
		}
383
	}
384
385
386
	/**
387
	 * @param string $circleUniqueId
388
	 * @param string $name
389
	 * @param int $type
390
	 * @param int $level
391
	 *
392
	 * @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...
393
	 * @throws \Exception
394
	 */
395
	public function levelMember($circleUniqueId, $name, $type, $level) {
396
397
		$level = (int)$level;
398
		try {
399
			$circle = $this->circlesRequest->getCircle($circleUniqueId, $this->userId);
400
			if ($circle->getType() === Circle::CIRCLES_PERSONAL) {
401
				throw new CircleTypeNotValidException(
402
					$this->l10n->t('You cannot edit level in a personal circle')
403
				);
404
			}
405
406
			$member = $this->membersRequest->forceGetMember($circle->getUniqueId(), $name, $type);
407
			$member->levelHasToBeEditable();
408
			if ($member->getLevel() !== $level) {
409
				if ($level === Member::LEVEL_OWNER) {
410
					$this->switchOwner($circle, $member);
411
				} else {
412
					$this->editMemberLevel($circle, $member, $level);
413
				}
414
415
				$this->eventsService->onMemberLevel($circle, $member);
416
			}
417
418
			return $this->membersRequest->getMembers(
419
				$circle->getUniqueId(), $circle->getHigherViewer()
420
			);
421
		} catch (\Exception $e) {
422
			throw $e;
423
		}
424
425
	}
426
427
428
	/**
429
	 * @param Circle $circle
430
	 * @param Member $member
431
	 * @param $level
432
	 *
433
	 * @throws \Exception
434
	 */
435
	private function editMemberLevel(Circle $circle, Member &$member, $level) {
436
		try {
437
			$isMod = $circle->getHigherViewer();
438
			$isMod->hasToBeModerator();
439
			$isMod->hasToBeHigherLevel($level);
440
441
			$member->hasToBeMember();
442
			$member->cantBeOwner();
443
			$isMod->hasToBeHigherLevel($member->getLevel());
444
445
			$member->setLevel($level);
446
			$this->membersRequest->updateMember($member);
447
		} catch (\Exception $e) {
448
			throw $e;
449
		}
450
451
	}
452
453
	/**
454
	 * @param Circle $circle
455
	 * @param Member $member
456
	 *
457
	 * @throws \Exception
458
	 */
459
	private function switchOwner(Circle $circle, Member &$member) {
460
		try {
461
			$isMod = $circle->getHigherViewer();
462
			$isMod->hasToBeOwner();
463
464
			$member->hasToBeMember();
465
			$member->cantBeOwner();
466
467
			$member->setLevel(Member::LEVEL_OWNER);
468
			$this->membersRequest->updateMember($member);
469
470
			$isMod->setLevel(Member::LEVEL_ADMIN);
471
			$this->membersRequest->updateMember($isMod);
472
473
		} catch (\Exception $e) {
474
			throw $e;
475
		}
476
	}
477
478
479
	/**
480
	 * @param string $circleUniqueId
481
	 * @param string $name
482
	 * @param $type
483
	 *
484
	 * @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...
485
	 * @throws \Exception
486
	 */
487
	public function removeMember($circleUniqueId, $name, $type) {
488
489
		try {
490
			$circle = $this->circlesRequest->getCircle($circleUniqueId, $this->userId);
491
			$circle->getHigherViewer()
492
				   ->hasToBeModerator();
493
494
			$member = $this->membersRequest->forceGetMember($circleUniqueId, $name, $type);
495
			$member->hasToBeMemberOrAlmost();
496
			$member->cantBeOwner();
497
498
			$circle->getHigherViewer()
499
				   ->hasToBeHigherLevel($member->getLevel());
500
		} catch (\Exception $e) {
501
			throw $e;
502
		}
503
504
		$this->eventsService->onMemberLeaving($circle, $member);
505
506
		$member->setStatus(Member::STATUS_NONMEMBER);
507
		$member->setLevel(Member::LEVEL_NONE);
508
		$this->membersRequest->updateMember($member);
509
510
		return $this->membersRequest->getMembers(
511
			$circle->getUniqueId(), $circle->getHigherViewer()
512
		);
513
	}
514
515
516
	/**
517
	 * When a user is removed, remove him from all Circles
518
	 *
519
	 * @param $userId
520
	 */
521
	public function onUserRemoved($userId) {
522
		$this->membersRequest->removeAllFromUser($userId);
523
	}
524
525
526
}