Completed
Push — master ( 7588c2...f404fc )
by Maxence
03:40
created

MembersService::addMember()   B

Complexity

Conditions 3
Paths 4

Size

Total Lines 25
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
c 2
b 0
f 0
dl 0
loc 25
rs 8.8571
cc 3
eloc 16
nc 4
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 OC\User\NoUserException;
31
use OCA\Circles\Db\CirclesRequest;
32
use OCA\Circles\Db\MembersRequest;
33
use OCA\Circles\Exceptions\CircleTypeNotValidException;
34
use OCA\Circles\Exceptions\EmailAccountInvalidFormatException;
35
use OCA\Circles\Exceptions\GroupDoesNotExistException;
36
use OCA\Circles\Exceptions\MemberAlreadyExistsException;
37
use OCA\Circles\Exceptions\MemberDoesNotExistException;
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 string $name
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
	public function addLocalMember($circleUniqueId, $name) {
110
111
		try {
112
			$circle = $this->circlesRequest->getCircle($circleUniqueId, $this->userId);
113
			$circle->getHigherViewer()
114
				   ->hasToBeModerator();
115
		} catch (\Exception $e) {
116
			throw $e;
117
		}
118
119
		try {
120
			$member = $this->getFreshNewMember($circleUniqueId, $name, Member::TYPE_USER);
121
		} catch (\Exception $e) {
122
			throw $e;
123
		}
124
125
		$member->inviteToCircle($circle->getType());
126
		$this->membersRequest->updateMember($member);
127
128
		$this->eventsService->onMemberNew($circle, $member);
129
130
		return $this->membersRequest->getMembers(
131
			$circle->getUniqueId(), $circle->getHigherViewer()
132
		);
133
	}
134
135
136
	/**
137
	 * @param string $circleUniqueId
138
	 * @param string $email
139
	 *
140
	 * @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...
141
	 * @throws \Exception
142
	 */
143
	public function addEmailAddress($circleUniqueId, $email) {
144
145
		$this->miscService->log('___' . $email);
146
		if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
147
			throw new EmailAccountInvalidFormatException(
148
				$this->l10n->t('Email format is not valid')
149
			);
150
		}
151
152
		try {
153
			$circle = $this->circlesRequest->getCircle($circleUniqueId, $this->userId);
154
			$circle->getHigherViewer()
155
				   ->hasToBeModerator();
156
		} catch (\Exception $e) {
157
			throw $e;
158
		}
159
160
		try {
161
			$member = $this->getFreshNewMember($circleUniqueId, $email, Member::TYPE_MAIL);
162
		} catch (\Exception $e) {
163
			throw $e;
164
		}
165
166
		$this->miscService->log('___' . json_encode($member));
167
168
//
0 ignored issues
show
Unused Code Comprehensibility introduced by
65% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
169
//		$member->inviteToCircle($circle->getType());
170
//		$this->membersRequest->updateMember($member);
171
//
172
//		$this->eventsService->onMemberNew($circle, $member);
173
//
174
		return $this->membersRequest->getMembers(
175
			$circle->getUniqueId(), $circle->getHigherViewer()
176
		);
177
	}
178
179
180
	/**
181
	 * @param string $circleUniqueId
182
	 * @param string $groupId
183
	 *
184
	 * @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...
185
	 * @throws \Exception
186
	 */
187
	public function importMembersFromGroup($circleUniqueId, $groupId) {
188
189
		try {
190
			$circle = $this->circlesRequest->getCircle($circleUniqueId, $this->userId);
191
			$circle->getHigherViewer()
192
				   ->hasToBeModerator();
193
		} catch (\Exception $e) {
194
			throw $e;
195
		}
196
197
		$group = \OC::$server->getGroupManager()
198
							 ->get($groupId);
199
		if ($group === null) {
200
			throw new GroupDoesNotExistException($this->l10n->t('This group does not exist'));
201
		}
202
203
		foreach ($group->getUsers() as $user) {
204
			try {
205
				$member =
206
					$this->getFreshNewMember($circleUniqueId, $user->getUID(), Member::TYPE_USER);
207
208
				$member->inviteToCircle($circle->getType());
209
				$this->membersRequest->updateMember($member);
210
211
				$this->eventsService->onMemberNew($circle, $member);
212
			} catch (MemberAlreadyExistsException $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
213
			} catch (\Exception $e) {
214
				throw $e;
215
			}
216
		}
217
218
		return $this->membersRequest->getMembers(
219
			$circle->getUniqueId(), $circle->getHigherViewer()
220
		);
221
	}
222
223
224
	/**
225
	 * getMember();
226
	 *
227
	 * Will return any data of a user related to a circle (as a Member). User can be a 'non-member'
228
	 * Viewer needs to be at least Member of the Circle
229
	 *
230
	 * @param $circleId
231
	 * @param $userId
232
	 * @param $type
233
	 *
234
	 * @return Member
235
	 * @throws \Exception
236
	 */
237
	public function getMember($circleId, $userId, $type) {
238
239
		try {
240
			$this->circlesRequest->getCircle($circleId, $this->userId)
241
								 ->getHigherViewer()
242
								 ->hasToBeMember();
243
244
			$member = $this->membersRequest->forceGetMember($circleId, $userId, $type);
245
			$member->setNote('');
246
247
			return $member;
248
		} catch (\Exception $e) {
249
			throw $e;
250
		}
251
	}
252
253
254
	/**
255
	 * Check if a fresh member can be generated (by addMember)
256
	 *
257
	 * @param string $circleUniqueId
258
	 * @param string $name
259
	 * @param $type
260
	 *
261
	 * @return Member
262
	 * @throws MemberAlreadyExistsException
263
	 * @throws \Exception
264
	 */
265
	private function getFreshNewMember($circleUniqueId, $name, $type) {
266
267
		if ($type === Member::TYPE_USER) {
268
			try {
269
				$name = $this->getRealUserId($name);
270
			} catch (\Exception $e) {
271
				throw $e;
272
			}
273
		}
274
275
		try {
276
			$member = $this->membersRequest->forceGetMember($circleUniqueId, $name, $type);
277
278
		} catch (MemberDoesNotExistException $e) {
279
			$member = new Member($name, $type, $circleUniqueId);
280
			$this->membersRequest->createMember($member);
281
		}
282
283
		if ($this->memberAlreadyExist($member)) {
284
			throw new MemberAlreadyExistsException(
285
				$this->l10n->t('This user is already a member of the circle')
286
			);
287
		}
288
289
		return $member;
290
	}
291
292
293
	/**
294
	 * return the real userId, with its real case
295
	 *
296
	 * @param $userId
297
	 *
298
	 * @return string
299
	 * @throws NoUserException
300
	 */
301
	private function getRealUserId($userId) {
302
		if (!$this->userManager->userExists($userId)) {
303
			throw new NoUserException($this->l10n->t("This user does not exist"));
304
		}
305
306
		return $this->userManager->get($userId)
307
								 ->getUID();
308
	}
309
310
	/**
311
	 * return if member already exists
312
	 *
313
	 * @param Member $member
314
	 *
315
	 * @return bool
316
	 */
317
	private function memberAlreadyExist($member) {
318
		return ($member->getLevel() > Member::LEVEL_NONE
319
				|| ($member->getStatus() !== Member::STATUS_NONMEMBER
320
					&& $member->getStatus() !== Member::STATUS_REQUEST)
321
		);
322
	}
323
324
325
	/**
326
	 * @param string $circleUniqueId
327
	 * @param string $name
328
	 * @param int $level
329
	 *
330
	 * @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...
331
	 * @throws \Exception
332
	 */
333
	public function levelLocalMember($circleUniqueId, $name, $level) {
334
335
		$level = (int)$level;
336
		try {
337
			$circle = $this->circlesRequest->getCircle($circleUniqueId, $this->userId);
338
			if ($circle->getType() === Circle::CIRCLES_PERSONAL) {
339
				throw new CircleTypeNotValidException(
340
					$this->l10n->t('You cannot edit level in a personal circle')
341
				);
342
			}
343
344
			$member = $this->membersRequest->forceGetMember(
345
				$circle->getUniqueId(), $name, Member::TYPE_USER
346
			);
347
			if ($member->getLevel() !== $level) {
348
				if ($level === Member::LEVEL_OWNER) {
349
					$this->switchOwner($circle, $member);
350
				} else {
351
					$this->editMemberLevel($circle, $member, $level);
352
				}
353
354
				$this->eventsService->onMemberLevel($circle, $member);
355
			}
356
357
			return $this->membersRequest->getMembers(
358
				$circle->getUniqueId(), $circle->getHigherViewer()
359
			);
360
		} catch (\Exception $e) {
361
			throw $e;
362
		}
363
364
	}
365
366
367
	/**
368
	 * @param Circle $circle
369
	 * @param Member $member
370
	 * @param $level
371
	 *
372
	 * @throws \Exception
373
	 */
374
	private function editMemberLevel(Circle $circle, Member &$member, $level) {
375
		try {
376
			$isMod = $circle->getHigherViewer();
377
			$isMod->hasToBeModerator();
378
			$isMod->hasToBeHigherLevel($level);
379
380
			$member->hasToBeMember();
381
			$member->cantBeOwner();
382
			$isMod->hasToBeHigherLevel($member->getLevel());
383
384
			$member->setLevel($level);
385
			$this->membersRequest->updateMember($member);
386
		} catch (\Exception $e) {
387
			throw $e;
388
		}
389
390
	}
391
392
	/**
393
	 * @param Circle $circle
394
	 * @param Member $member
395
	 *
396
	 * @throws \Exception
397
	 */
398
	private function switchOwner(Circle $circle, Member &$member) {
399
		try {
400
			$isMod = $circle->getHigherViewer();
401
			$isMod->hasToBeOwner();
402
403
			$member->hasToBeMember();
404
			$member->cantBeOwner();
405
			
406
			$member->setLevel(Member::LEVEL_OWNER);
407
			$this->membersRequest->updateMember($member);
408
409
			$isMod->setLevel(Member::LEVEL_ADMIN);
410
			$this->membersRequest->updateMember($isMod);
411
412
		} catch (\Exception $e) {
413
			throw $e;
414
		}
415
	}
416
417
418
	/**
419
	 * @param string $circleUniqueId
420
	 * @param string $name
421
	 * @param $type
422
	 *
423
	 * @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...
424
	 * @throws \Exception
425
	 */
426
	public function removeMember($circleUniqueId, $name, $type) {
427
428
		try {
429
			$circle = $this->circlesRequest->getCircle($circleUniqueId, $this->userId);
430
			$circle->getHigherViewer()
431
				   ->hasToBeModerator();
432
433
			$member = $this->membersRequest->forceGetMember($circleUniqueId, $name, $type);
434
			$member->hasToBeMemberOrAlmost();
435
			$member->cantBeOwner();
436
437
			$circle->getHigherViewer()
438
				   ->hasToBeHigherLevel($member->getLevel());
439
		} catch (\Exception $e) {
440
			throw $e;
441
		}
442
443
		$this->eventsService->onMemberLeaving($circle, $member);
444
445
		$member->setStatus(Member::STATUS_NONMEMBER);
446
		$member->setLevel(Member::LEVEL_NONE);
447
		$this->membersRequest->updateMember($member);
448
449
		return $this->membersRequest->getMembers(
450
			$circle->getUniqueId(), $circle->getHigherViewer()
451
		);
452
	}
453
454
455
	/**
456
	 * When a user is removed, remove him from all Circles
457
	 *
458
	 * @param $userId
459
	 */
460
	public function onUserRemoved($userId) {
461
		$this->membersRequest->removeAllFromUser($userId);
462
	}
463
464
465
}