Completed
Push — master ( 3071ad...3723f6 )
by Maxence
03:35 queued 01:12
created

MembersService   A

Complexity

Total Complexity 33

Size/Duplication

Total Lines 361
Duplicated Lines 9.97 %

Coupling/Cohesion

Components 1
Dependencies 8

Importance

Changes 6
Bugs 0 Features 1
Metric Value
wmc 33
c 6
b 0
f 1
lcom 1
cbo 8
dl 36
loc 361
rs 9.3999

12 Methods

Rating   Name   Duplication   Size   Complexity  
B importMembersFromGroup() 0 32 6
A getMember() 0 15 2
B getFreshNewMember() 0 24 4
A memberAlreadyExist() 0 6 3
B levelMember() 0 28 5
A __construct() 19 19 1
A addMember() 0 23 3
A getRealUserId() 0 8 2
A editMemberLevel() 17 17 2
A switchOwner() 0 16 2
B removeMember() 0 25 2
A onUserRemoved() 0 3 1

How to fix   Duplicated Code   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

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\CircleTypeNotValid;
34
use OCA\Circles\Exceptions\GroupDoesNotExistException;
35
use OCA\Circles\Exceptions\MemberAlreadyExistsException;
36
use OCA\Circles\Exceptions\MemberDoesNotExistException;
37
use OCA\Circles\Model\Circle;
38
use \OCA\Circles\Model\Member;
39
use OCP\IL10N;
40
use OCP\IUserManager;
41
42
class MembersService {
43
44
	/** @var string */
45
	private $userId;
46
47
	/** @var IL10N */
48
	private $l10n;
49
50
	/** @var IUserManager */
51
	private $userManager;
52
53
	/** @var ConfigService */
54
	private $configService;
55
56
	/** @var CirclesRequest */
57
	private $circlesRequest;
58
59
	/** @var MembersRequest */
60
	private $membersRequest;
61
62
	/** @var EventsService */
63
	private $eventsService;
64
65
	/** @var MiscService */
66
	private $miscService;
67
68
	/**
69
	 * MembersService constructor.
70
	 *
71
	 * @param $userId
72
	 * @param IL10N $l10n
73
	 * @param IUserManager $userManager
74
	 * @param ConfigService $configService
75
	 * @param CirclesRequest $circlesRequest
76
	 * @param MembersRequest $membersRequest
77
	 * @param EventsService $eventsService
78
	 * @param MiscService $miscService
79
	 */
80 View Code Duplication
	public function __construct(
81
		$userId,
82
		IL10N $l10n,
83
		IUserManager $userManager,
84
		ConfigService $configService,
85
		CirclesRequest $circlesRequest,
86
		MembersRequest $membersRequest,
87
		EventsService $eventsService,
88
		MiscService $miscService
89
	) {
90
		$this->userId = $userId;
91
		$this->l10n = $l10n;
92
		$this->userManager = $userManager;
93
		$this->configService = $configService;
94
		$this->circlesRequest = $circlesRequest;
95
		$this->membersRequest = $membersRequest;
96
		$this->eventsService = $eventsService;
97
		$this->miscService = $miscService;
98
	}
99
100
101
	/**
102
	 * @param $circleId
103
	 * @param $name
104
	 *
105
	 * @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...
106
	 * @throws \Exception
107
	 */
108
	public function addMember($circleId, $name) {
109
110
		try {
111
			$circle = $this->circlesRequest->getCircle($circleId, $this->userId);
112
			$circle->getHigherViewer()
113
				   ->hasToBeModerator();
114
		} catch (\Exception $e) {
115
			throw $e;
116
		}
117
118
		try {
119
			$member = $this->getFreshNewMember($circleId, $name);
120
		} catch (\Exception $e) {
121
			throw $e;
122
		}
123
124
		$member->inviteToCircle($circle->getType());
125
		$this->membersRequest->updateMember($member);
0 ignored issues
show
Bug introduced by
It seems like $member defined by $this->getFreshNewMember($circleId, $name) on line 119 can be null; however, OCA\Circles\Db\MembersRequest::updateMember() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
126
127
		$this->eventsService->onMemberNew($circle, $member);
0 ignored issues
show
Bug introduced by
It seems like $member defined by $this->getFreshNewMember($circleId, $name) on line 119 can be null; however, OCA\Circles\Service\EventsService::onMemberNew() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
128
129
		return $this->membersRequest->getMembers($circle->getId(), $circle->getHigherViewer());
130
	}
131
132
133
	/**
134
	 * @param int $circleId
135
	 * @param string $groupId
136
	 *
137
	 * @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...
138
	 * @throws \Exception
139
	 */
140
	public function importMembersFromGroup($circleId, $groupId) {
141
142
		try {
143
			$circle = $this->circlesRequest->getCircle($circleId, $this->userId);
144
			$circle->getHigherViewer()
145
				   ->hasToBeModerator();
146
		} catch (\Exception $e) {
147
			throw $e;
148
		}
149
150
		$group = \OC::$server->getGroupManager()
151
							 ->get($groupId);
152
		if ($group === null) {
153
			throw new GroupDoesNotExistException('This group does not exist');
154
		}
155
156
		foreach ($group->getUsers() as $user) {
157
			try {
158
				$member = $this->getFreshNewMember($circleId, $user->getUID());
159
160
				$member->inviteToCircle($circle->getType());
161
				$this->membersRequest->updateMember($member);
0 ignored issues
show
Bug introduced by
It seems like $member defined by $this->getFreshNewMember...cleId, $user->getUID()) on line 158 can be null; however, OCA\Circles\Db\MembersRequest::updateMember() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
162
163
				$this->eventsService->onMemberNew($circle, $member);
0 ignored issues
show
Bug introduced by
It seems like $member defined by $this->getFreshNewMember...cleId, $user->getUID()) on line 158 can be null; however, OCA\Circles\Service\EventsService::onMemberNew() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
164
			} catch (MemberAlreadyExistsException $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
165
			} catch (\Exception $e) {
166
				throw $e;
167
			}
168
		}
169
170
		return $this->membersRequest->getMembers($circle->getId(), $circle->getHigherViewer());
171
	}
172
173
174
	/**
175
	 * getMember();
176
	 *
177
	 * Will return any data of a user related to a circle (as a Member). User can be a 'non-member'
178
	 * Viewer needs to be at least Member of the Circle
179
	 *
180
	 * @param $circleId
181
	 * @param $userId
182
	 *
183
	 * @return Member
184
	 * @throws \Exception
185
	 */
186
	public function getMember($circleId, $userId) {
187
188
		try {
189
			$this->circlesRequest->getCircle($circleId, $this->userId)
190
								 ->getHigherViewer()
191
								 ->hasToBeMember();
192
193
			$member = $this->membersRequest->forceGetMember($circleId, $userId);
194
			$member->setNote('');
195
196
			return $member;
197
		} catch (\Exception $e) {
198
			throw $e;
199
		}
200
	}
201
202
203
	/**
204
	 * Check if a fresh member can be generated (by addMember)
205
	 *
206
	 * @param $circleId
207
	 * @param $name
208
	 *
209
	 * @return null|Member
210
	 * @throws MemberAlreadyExistsException
211
	 * @throws \Exception
212
	 */
213
	private function getFreshNewMember($circleId, $name) {
214
215
		try {
216
			$userId = $this->getRealUserId($name);
217
		} catch (\Exception $e) {
218
			throw $e;
219
		}
220
221
		try {
222
			$member = $this->membersRequest->forceGetMember($circleId, $userId);
223
224
		} catch (MemberDoesNotExistException $e) {
225
			$member = new Member($this->l10n, $userId, $circleId);
226
			$this->membersRequest->createMember($member);
227
		}
228
229
		if ($this->memberAlreadyExist($member)) {
230
			throw new MemberAlreadyExistsException(
231
				$this->l10n->t('This user is already a member of the circle')
232
			);
233
		}
234
235
		return $member;
236
	}
237
238
239
	/**
240
	 * return the real userId, with its real case
241
	 *
242
	 * @param $userId
243
	 *
244
	 * @return string
245
	 * @throws NoUserException
246
	 */
247
	private function getRealUserId($userId) {
248
		if (!$this->userManager->userExists($userId)) {
249
			throw new NoUserException($this->l10n->t("This user does not exist"));
250
		}
251
252
		return $this->userManager->get($userId)
253
								 ->getUID();
254
	}
255
256
	/**
257
	 * return if member already exists
258
	 *
259
	 * @param Member $member
260
	 *
261
	 * @return bool
262
	 */
263
	private function memberAlreadyExist($member) {
264
		return ($member->getLevel() > Member::LEVEL_NONE
265
				|| ($member->getStatus() !== Member::STATUS_NONMEMBER
266
					&& $member->getStatus() !== Member::STATUS_REQUEST)
267
		);
268
	}
269
270
271
	/**
272
	 * @param int $circleId
273
	 * @param string $name
274
	 * @param int $level
275
	 *
276
	 * @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...
277
	 * @throws \Exception
278
	 */
279
	public function levelMember($circleId, $name, $level) {
280
281
		$level = (int)$level;
282
		try {
283
			$circle = $this->circlesRequest->getCircle($circleId, $this->userId);
284
			if ($circle->getType() === Circle::CIRCLES_PERSONAL) {
285
				throw new CircleTypeNotValid(
286
					$this->l10n->t('You cannot edit level in a personal circle')
287
				);
288
			}
289
290
			$member = $this->membersRequest->forceGetMember($circle->getId(), $name);
291
			if ($member->getLevel() !== $level) {
292
				if ($level === Member::LEVEL_OWNER) {
293
					$this->switchOwner($circle, $member);
294
				} else {
295
					$this->editMemberLevel($circle, $member, $level);
296
				}
297
298
				$this->eventsService->onMemberLevel($circle, $member);
299
			}
300
301
			return $this->membersRequest->getMembers($circle->getId(), $circle->getHigherViewer());
302
		} catch (\Exception $e) {
303
			throw $e;
304
		}
305
306
	}
307
308
309
	/**
310
	 * @param Circle $circle
311
	 * @param Member $member
312
	 * @param $level
313
	 *
314
	 * @throws \Exception
315
	 */
316 View Code Duplication
	private function editMemberLevel(Circle $circle, Member &$member, $level) {
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...
317
		try {
318
			$isMod = $this->membersRequest->forceGetMember($circle->getId(), $this->userId);
319
			$isMod->hasToBeModerator();
320
			$isMod->hasToBeHigherLevel($level);
321
322
			$member->hasToBeMember();
323
			$member->cantBeOwner();
324
			$isMod->hasToBeHigherLevel($member->getLevel());
325
326
			$member->setLevel($level);
327
			$this->membersRequest->updateMember($member);
328
		} catch (\Exception $e) {
329
			throw $e;
330
		}
331
332
	}
333
334
	/**
335
	 * @param Circle $circle
336
	 * @param Member $member
337
	 *
338
	 * @throws \Exception
339
	 */
340
	public function switchOwner(Circle $circle, Member &$member) {
341
		try {
342
			$isMod = $this->membersRequest->forceGetMember($circle->getId(), $this->userId);
343
			$isMod->hasToBeOwner();
344
345
			$member->cantBeOwner();
346
			$member->setLevel(Member::LEVEL_OWNER);
347
			$this->membersRequest->updateMember($member);
348
349
			$isMod->setLevel(Member::LEVEL_ADMIN);
350
			$this->membersRequest->updateMember($isMod);
351
352
		} catch (\Exception $e) {
353
			throw $e;
354
		}
355
	}
356
357
358
	/**
359
	 * @param $circleId
360
	 * @param $name
361
	 *
362
	 * @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...
363
	 * @throws \Exception
364
	 */
365
	public function removeMember($circleId, $name) {
366
367
		try {
368
			$circle = $this->circlesRequest->getCircle($circleId, $this->userId);
369
			$circle->getHigherViewer()
370
				   ->hasToBeModerator();
371
372
			$member = $this->membersRequest->forceGetMember($circleId, $name);
373
			$member->cantBeOwner();
374
			$member->hasToBeMember();
375
376
			$circle->getHigherViewer()
377
				   ->hasToBeHigherLevel($member->getLevel());
378
		} catch (\Exception $e) {
379
			throw $e;
380
		}
381
382
		$member->setStatus(Member::STATUS_NONMEMBER);
383
		$member->setLevel(Member::LEVEL_NONE);
384
		$this->membersRequest->updateMember($member);
385
386
		$this->eventsService->onMemberLeaving($circle, $member);
387
388
		return $this->membersRequest->getMembers($circle->getId(), $circle->getHigherViewer());
389
	}
390
391
392
	/**
393
	 * When a user is removed, remove him from all Circles
394
	 *
395
	 * @param $userId
396
	 */
397
	public function onUserRemoved($userId) {
398
		$this->membersRequest->removeAllFromUser($userId);
399
	}
400
401
402
}