Completed
Push — master ( 4a5e09...ab41ba )
by Maxence
02:19
created

MembersService::addEmailAddress()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

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