Completed
Push — master ( 8dcd63...86807f )
by Maxence
02:48
created

MembersService::importMembersFromGroup()   B

Complexity

Conditions 6
Paths 13

Size

Total Lines 34
Code Lines 22

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 1
Metric Value
c 2
b 0
f 1
dl 0
loc 34
rs 8.439
cc 6
eloc 22
nc 13
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\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(
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...
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 string $circleUniqueId
103
	 * @param string $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($circleUniqueId, $name) {
109
110
		try {
111
			$circle = $this->circlesRequest->getCircle($circleUniqueId, $this->userId);
112
			$circle->getHigherViewer()
113
				   ->hasToBeModerator();
114
		} catch (\Exception $e) {
115
			throw $e;
116
		}
117
118
		try {
119
			$member = $this->getFreshNewMember($circleUniqueId, $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($circleUniqueId, $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($circleUniqueId, $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(
130
			$circle->getUniqueId(), $circle->getHigherViewer()
131
		);
132
	}
133
134
135
	/**
136
	 * @param string $circleUniqueId
137
	 * @param string $groupId
138
	 *
139
	 * @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...
140
	 * @throws \Exception
141
	 */
142
	public function importMembersFromGroup($circleUniqueId, $groupId) {
143
144
		try {
145
			$circle = $this->circlesRequest->getCircle($circleUniqueId, $this->userId);
146
			$circle->getHigherViewer()
147
				   ->hasToBeModerator();
148
		} catch (\Exception $e) {
149
			throw $e;
150
		}
151
152
		$group = \OC::$server->getGroupManager()
153
							 ->get($groupId);
154
		if ($group === null) {
155
			throw new GroupDoesNotExistException('This group does not exist');
156
		}
157
158
		foreach ($group->getUsers() as $user) {
159
			try {
160
				$member = $this->getFreshNewMember($circleUniqueId, $user->getUID());
161
162
				$member->inviteToCircle($circle->getType());
163
				$this->membersRequest->updateMember($member);
0 ignored issues
show
Bug introduced by
It seems like $member defined by $this->getFreshNewMember...queId, $user->getUID()) on line 160 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...
164
165
				$this->eventsService->onMemberNew($circle, $member);
0 ignored issues
show
Bug introduced by
It seems like $member defined by $this->getFreshNewMember...queId, $user->getUID()) on line 160 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...
166
			} catch (MemberAlreadyExistsException $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
167
			} catch (\Exception $e) {
168
				throw $e;
169
			}
170
		}
171
172
		return $this->membersRequest->getMembers(
173
			$circle->getUniqueId(), $circle->getHigherViewer()
174
		);
175
	}
176
177
178
	/**
179
	 * getMember();
180
	 *
181
	 * Will return any data of a user related to a circle (as a Member). User can be a 'non-member'
182
	 * Viewer needs to be at least Member of the Circle
183
	 *
184
	 * @param $circleId
185
	 * @param $userId
186
	 *
187
	 * @return Member
188
	 * @throws \Exception
189
	 */
190
	public function getMember($circleId, $userId) {
191
192
		try {
193
			$this->circlesRequest->getCircle($circleId, $this->userId)
194
								 ->getHigherViewer()
195
								 ->hasToBeMember();
196
197
			$member = $this->membersRequest->forceGetMember($circleId, $userId);
198
			$member->setNote('');
199
200
			return $member;
201
		} catch (\Exception $e) {
202
			throw $e;
203
		}
204
	}
205
206
207
	/**
208
	 * Check if a fresh member can be generated (by addMember)
209
	 *
210
	 * @param string $circleUniqueId
211
	 * @param string $name
212
	 *
213
	 * @return null|Member
214
	 * @throws MemberAlreadyExistsException
215
	 * @throws \Exception
216
	 */
217
	private function getFreshNewMember($circleUniqueId, $name) {
218
219
		try {
220
			$userId = $this->getRealUserId($name);
221
		} catch (\Exception $e) {
222
			throw $e;
223
		}
224
225
		try {
226
			$member = $this->membersRequest->forceGetMember($circleUniqueId, $userId);
227
228
		} catch (MemberDoesNotExistException $e) {
229
			$member = new Member($this->l10n, $userId, $circleUniqueId);
230
			$this->membersRequest->createMember($member);
231
		}
232
233
		if ($this->memberAlreadyExist($member)) {
234
			throw new MemberAlreadyExistsException(
235
				$this->l10n->t('This user is already a member of the circle')
236
			);
237
		}
238
239
		return $member;
240
	}
241
242
243
	/**
244
	 * return the real userId, with its real case
245
	 *
246
	 * @param $userId
247
	 *
248
	 * @return string
249
	 * @throws NoUserException
250
	 */
251
	private function getRealUserId($userId) {
252
		if (!$this->userManager->userExists($userId)) {
253
			throw new NoUserException($this->l10n->t("This user does not exist"));
254
		}
255
256
		return $this->userManager->get($userId)
257
								 ->getUID();
258
	}
259
260
	/**
261
	 * return if member already exists
262
	 *
263
	 * @param Member $member
264
	 *
265
	 * @return bool
266
	 */
267
	private function memberAlreadyExist($member) {
268
		return ($member->getLevel() > Member::LEVEL_NONE
269
				|| ($member->getStatus() !== Member::STATUS_NONMEMBER
270
					&& $member->getStatus() !== Member::STATUS_REQUEST)
271
		);
272
	}
273
274
275
	/**
276
	 * @param string $circleUniqueId
277
	 * @param string $name
278
	 * @param int $level
279
	 *
280
	 * @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...
281
	 * @throws \Exception
282
	 */
283 View Code Duplication
	public function levelMember($circleUniqueId, $name, $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...
284
285
		$level = (int)$level;
286
		try {
287
			$circle = $this->circlesRequest->getCircle($circleUniqueId, $this->userId);
288
			if ($circle->getType() === Circle::CIRCLES_PERSONAL) {
289
				throw new CircleTypeNotValidException(
290
					$this->l10n->t('You cannot edit level in a personal circle')
291
				);
292
			}
293
294
			$member = $this->membersRequest->forceGetMember($circle->getUniqueId(), $name);
295
			if ($member->getLevel() !== $level) {
296
				if ($level === Member::LEVEL_OWNER) {
297
					$this->switchOwner($circle, $member);
298
				} else {
299
					$this->editMemberLevel($circle, $member, $level);
300
				}
301
302
				$this->eventsService->onMemberLevel($circle, $member);
303
			}
304
305
			return $this->membersRequest->getMembers(
306
				$circle->getUniqueId(), $circle->getHigherViewer()
307
			);
308
		} catch (\Exception $e) {
309
			throw $e;
310
		}
311
312
	}
313
314
315
	/**
316
	 * @param Circle $circle
317
	 * @param Member $member
318
	 * @param $level
319
	 *
320
	 * @throws \Exception
321
	 */
322
	private function editMemberLevel(Circle $circle, Member &$member, $level) {
323
		try {
324
			$isMod = $circle->getHigherViewer();
325
			$isMod->hasToBeModerator();
326
			$isMod->hasToBeHigherLevel($level);
327
328
			$member->hasToBeMember();
329
			$member->cantBeOwner();
330
			$isMod->hasToBeHigherLevel($member->getLevel());
331
332
			$member->setLevel($level);
333
			$this->membersRequest->updateMember($member);
334
		} catch (\Exception $e) {
335
			throw $e;
336
		}
337
338
	}
339
340
	/**
341
	 * @param Circle $circle
342
	 * @param Member $member
343
	 *
344
	 * @throws \Exception
345
	 */
346
	private function switchOwner(Circle $circle, Member &$member) {
347
		try {
348
			$isMod = $circle->getHigherViewer();
349
			$isMod->hasToBeOwner();
350
351
			$member->cantBeOwner();
352
			$member->setLevel(Member::LEVEL_OWNER);
353
			$this->membersRequest->updateMember($member);
354
355
			$isMod->setLevel(Member::LEVEL_ADMIN);
356
			$this->membersRequest->updateMember($isMod);
357
358
		} catch (\Exception $e) {
359
			throw $e;
360
		}
361
	}
362
363
364
	/**
365
	 * @param string $circleUniqueId
366
	 * @param string $name
367
	 *
368
	 * @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...
369
	 * @throws \Exception
370
	 */
371
	public function removeMember($circleUniqueId, $name) {
372
373
		try {
374
			$circle = $this->circlesRequest->getCircle($circleUniqueId, $this->userId);
375
			$circle->getHigherViewer()
376
				   ->hasToBeModerator();
377
378
			$member = $this->membersRequest->forceGetMember($circleUniqueId, $name);
379
			$member->hasToBeMemberOrAlmost();
380
			$member->cantBeOwner();
381
382
			$circle->getHigherViewer()
383
				   ->hasToBeHigherLevel($member->getLevel());
384
		} catch (\Exception $e) {
385
			throw $e;
386
		}
387
388
		$this->eventsService->onMemberLeaving($circle, $member);
389
390
		$member->setStatus(Member::STATUS_NONMEMBER);
391
		$member->setLevel(Member::LEVEL_NONE);
392
		$this->membersRequest->updateMember($member);
393
394
		return $this->membersRequest->getMembers(
395
			$circle->getUniqueId(), $circle->getHigherViewer()
396
		);
397
	}
398
399
400
	/**
401
	 * When a user is removed, remove him from all Circles
402
	 *
403
	 * @param $userId
404
	 */
405
	public function onUserRemoved($userId) {
406
		$this->membersRequest->removeAllFromUser($userId);
407
	}
408
409
410
}