Completed
Pull Request — master (#120)
by Maxence
03:22
created

MembersService::verifyIdentLocalMember()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 11
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

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