Completed
Push — master ( fc6227...6ab97c )
by Maxence
04:01
created

MembersService::addMember()   B

Complexity

Conditions 3
Paths 11

Size

Total Lines 24
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
dl 0
loc 24
rs 8.9713
c 2
b 0
f 0
cc 3
eloc 15
nc 11
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\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
	 * @param string $circleUniqueId
104
	 * @param $ident
105
	 * @param $type
106
	 *
107
	 * @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...
108
	 * @throws \Exception
109
	 */
110
	public function addMember($circleUniqueId, $ident, $type) {
111
112
		try {
113
			$circle = $this->circlesRequest->getCircle($circleUniqueId, $this->userId);
114
			$circle->getHigherViewer()
115
				   ->hasToBeModerator();
116
117
			if (!$this->addMemberMassively($circle, $type, $ident)) {
118
				$this->verifyIdentBasedOnItsType($ident, $type);
119
120
				$member = $this->membersRequest->getFreshNewMember($circleUniqueId, $ident, $type);
121
				$member->hasToBeInviteAble();
122
123
				$this->addMemberBasedOnItsType($circle, $member);
124
125
				$this->membersRequest->updateMember($member);
126
				$this->eventsService->onMemberNew($circle, $member);
127
			}
128
		} catch (\Exception $e) {
129
			throw $e;
130
		}
131
132
		return $this->membersRequest->getMembers($circle->getUniqueId(), $circle->getHigherViewer());
133
	}
134
135
136
	private function addMemberMassively(Circle $circle, $type, $ident) {
137
138
		if ($type === Member::TYPE_GROUP) {
139
			return $this->addGroupMembers($circle, $ident);
140
		}
141
142
		return false;
143
	}
144
145
146
	/**
147
	 * add a new member based on its type.
148
	 *
149
	 * @param Circle $circle
150
	 * @param Member $member
151
	 */
152
	private function addMemberBasedOnItsType(Circle $circle, Member &$member) {
153
		$this->addLocalMember($circle, $member);
154
		$this->addEmailAddress($member);
155
		$this->addContact($member);
156
	}
157
158
159
	/**
160
	 * @param Circle $circle
161
	 * @param Member $member
162
	 *
163
	 * @throws \Exception
164
	 */
165
	public function addLocalMember(Circle $circle, Member $member) {
166
167
		if ($member->getType() !== Member::TYPE_USER) {
168
			return;
169
		}
170
171
		$member->inviteToCircle($circle->getType());
172
	}
173
174
175
	/**
176
	 * add mail address as contact.
177
	 *
178
	 * @param Member $member
179
	 *
180
	 * @throws \Exception
181
	 */
182
	private function addEmailAddress(Member $member) {
183
184
		if ($member->getType() !== Member::TYPE_MAIL) {
185
			return;
186
		}
187
188
		$member->addMemberToCircle();
189
	}
190
191
192
	/**
193
	 * Add contact as member.
194
	 *
195
	 * @param Member $member
196
	 *
197
	 * @throws \Exception
198
	 */
199
	private function addContact(Member $member) {
200
201
		if ($member->getType() !== Member::TYPE_CONTACT) {
202
			return;
203
		}
204
205
		$member->addMemberToCircle();
206
	}
207
208
209
	/**
210
	 * Verify the availability of an ident, based on its type.
211
	 *
212
	 * @param string $ident
213
	 * @param int $type
214
	 *
215
	 * @throws Exception
216
	 */
217
	private function verifyIdentBasedOnItsType(&$ident, $type) {
218
		try {
219
			$this->verifyIdentLocalMember($ident, $type);
220
			$this->verifyIdentEmailAddress($ident, $type);
221
			$this->verifyIdentContact($ident, $type);
222
		} catch (Exception $e) {
223
			throw $e;
224
		}
225
	}
226
227
228
	/**
229
	 * Verify if a local account is valid.
230
	 *
231
	 * @param $ident
232
	 * @param $type
233
	 *
234
	 * @throws NoUserException
235
	 */
236
	private function verifyIdentLocalMember(&$ident, $type) {
237
		if ($type !== Member::TYPE_USER) {
238
			return;
239
		}
240
241
		try {
242
			$ident = $this->miscService->getRealUserId($ident);
243
		} 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...
244
			throw new NoUserException($this->l10n->t("This user does not exist"));
245
		}
246
	}
247
248
249
	/**
250
	 * Verify if a mail have a valid format.
251
	 *
252
	 * @param $ident
253
	 * @param $type
254
	 *
255
	 * @throws EmailAccountInvalidFormatException
256
	 */
257
	private function verifyIdentEmailAddress(&$ident, $type) {
258
		if ($type !== Member::TYPE_MAIL) {
259
			return;
260
		}
261
262
		if (!filter_var($ident, FILTER_VALIDATE_EMAIL)) {
263
			throw new EmailAccountInvalidFormatException(
264
				$this->l10n->t('Email format is not valid')
265
			);
266
		}
267
	}
268
269
270
	/**
271
	 * Verify if a contact exist in current user address books.
272
	 *
273
	 * @param $ident
274
	 * @param $type
275
	 *
276
	 * @throws NoUserException
277
	 */
278
	private function verifyIdentContact(&$ident, $type) {
279
		if ($type !== Member::TYPE_CONTACT) {
280
			return;
281
		}
282
283
		$tmpContact = $this->userId . ':' . $ident;
284
		try {
285
			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...
286
		} catch (Exception $e) {
287
			throw new NoUserException($this->l10n->t("This contact is not available"));
288
		}
289
290
		$ident = $tmpContact;
291
	}
292
293
294
	/**
295
	 * @param Circle $circle
296
	 * @param string $groupId
297
	 *
298
	 * @return bool
299
	 * @throws \Exception
300
	 */
301
	private function addGroupMembers(Circle $circle, $groupId) {
302
303
		$group = \OC::$server->getGroupManager()
304
							 ->get($groupId);
305
		if ($group === null) {
306
			throw new GroupDoesNotExistException($this->l10n->t('This group does not exist'));
307
		}
308
309
		foreach ($group->getUsers() as $user) {
310
			try {
311
				$member =
312
					$this->membersRequest->getFreshNewMember(
313
						$circle->getUniqueId(), $user->getUID(), Member::TYPE_USER
314
					);
315
				$member->hasToBeInviteAble();
316
317
				$member->inviteToCircle($circle->getType());
318
				$this->membersRequest->updateMember($member);
319
320
				$this->eventsService->onMemberNew($circle, $member);
321
			} catch (MemberAlreadyExistsException $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
322
			} catch (\Exception $e) {
323
				throw $e;
324
			}
325
		}
326
327
		return true;
328
	}
329
330
331
	/**
332
	 * getMember();
333
	 *
334
	 * Will return any data of a user related to a circle (as a Member). User can be a 'non-member'
335
	 * Viewer needs to be at least Member of the Circle
336
	 *
337
	 * @param $circleId
338
	 * @param $userId
339
	 * @param $type
340
	 *
341
	 * @return Member
342
	 * @throws \Exception
343
	 */
344
	public function getMember($circleId, $userId, $type) {
345
346
		try {
347
			$this->circlesRequest->getCircle($circleId, $this->userId)
348
								 ->getHigherViewer()
349
								 ->hasToBeMember();
350
351
			$member = $this->membersRequest->forceGetMember($circleId, $userId, $type);
352
			$member->setNote('');
353
354
			return $member;
355
		} catch (\Exception $e) {
356
			throw $e;
357
		}
358
	}
359
360
361
	/**
362
	 * @param string $circleUniqueId
363
	 * @param string $name
364
	 * @param int $type
365
	 * @param int $level
366
	 *
367
	 * @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...
368
	 * @throws \Exception
369
	 */
370
	public function levelMember($circleUniqueId, $name, $type, $level) {
371
372
		$level = (int)$level;
373
		try {
374
			$circle = $this->circlesRequest->getCircle($circleUniqueId, $this->userId);
375
			if ($circle->getType() === Circle::CIRCLES_PERSONAL) {
376
				throw new CircleTypeNotValidException(
377
					$this->l10n->t('You cannot edit level in a personal circle')
378
				);
379
			}
380
381
			$member = $this->membersRequest->forceGetMember($circle->getUniqueId(), $name, $type);
382
			$member->levelHasToBeEditable();
383
			if ($member->getLevel() !== $level) {
384
				if ($level === Member::LEVEL_OWNER) {
385
					$this->switchOwner($circle, $member);
386
				} else {
387
					$this->editMemberLevel($circle, $member, $level);
388
				}
389
390
				$this->eventsService->onMemberLevel($circle, $member);
391
			}
392
393
			return $this->membersRequest->getMembers(
394
				$circle->getUniqueId(), $circle->getHigherViewer()
395
			);
396
		} catch (\Exception $e) {
397
			throw $e;
398
		}
399
400
	}
401
402
403
	/**
404
	 * @param Circle $circle
405
	 * @param Member $member
406
	 * @param $level
407
	 *
408
	 * @throws \Exception
409
	 */
410
	private function editMemberLevel(Circle $circle, Member &$member, $level) {
411
		try {
412
			$isMod = $circle->getHigherViewer();
413
			$isMod->hasToBeModerator();
414
			$isMod->hasToBeHigherLevel($level);
415
416
			$member->hasToBeMember();
417
			$member->cantBeOwner();
418
			$isMod->hasToBeHigherLevel($member->getLevel());
419
420
			$member->setLevel($level);
421
			$this->membersRequest->updateMember($member);
422
		} catch (\Exception $e) {
423
			throw $e;
424
		}
425
426
	}
427
428
	/**
429
	 * @param Circle $circle
430
	 * @param Member $member
431
	 *
432
	 * @throws \Exception
433
	 */
434
	private function switchOwner(Circle $circle, Member &$member) {
435
		try {
436
			$isMod = $circle->getHigherViewer();
437
			$isMod->hasToBeOwner();
438
439
			$member->hasToBeMember();
440
			$member->cantBeOwner();
441
442
			$member->setLevel(Member::LEVEL_OWNER);
443
			$this->membersRequest->updateMember($member);
444
445
			$isMod->setLevel(Member::LEVEL_ADMIN);
446
			$this->membersRequest->updateMember($isMod);
447
448
		} catch (\Exception $e) {
449
			throw $e;
450
		}
451
	}
452
453
454
	/**
455
	 * @param string $circleUniqueId
456
	 * @param string $name
457
	 * @param $type
458
	 *
459
	 * @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...
460
	 * @throws \Exception
461
	 */
462
	public function removeMember($circleUniqueId, $name, $type) {
463
464
		try {
465
			$circle = $this->circlesRequest->getCircle($circleUniqueId, $this->userId);
466
			$circle->getHigherViewer()
467
				   ->hasToBeModerator();
468
469
			$member = $this->membersRequest->forceGetMember($circleUniqueId, $name, $type);
470
			$member->hasToBeMemberOrAlmost();
471
			$member->cantBeOwner();
472
473
			$circle->getHigherViewer()
474
				   ->hasToBeHigherLevel($member->getLevel());
475
		} catch (\Exception $e) {
476
			throw $e;
477
		}
478
479
		$this->eventsService->onMemberLeaving($circle, $member);
480
481
		$member->setStatus(Member::STATUS_NONMEMBER);
482
		$member->setLevel(Member::LEVEL_NONE);
483
		$this->membersRequest->updateMember($member);
484
485
		return $this->membersRequest->getMembers(
486
			$circle->getUniqueId(), $circle->getHigherViewer()
487
		);
488
	}
489
490
491
	/**
492
	 * When a user is removed, remove him from all Circles
493
	 *
494
	 * @param $userId
495
	 */
496
	public function onUserRemoved($userId) {
497
		$this->membersRequest->removeAllFromUser($userId);
498
	}
499
500
501
}