Completed
Pull Request — master (#362)
by Maxence
02:13
created

MembersService::editMemberLevel()   A

Complexity

Conditions 2
Paths 9

Size

Total Lines 17

Duplication

Lines 17
Ratio 100 %

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 17
loc 17
rs 9.7
cc 2
nc 9
nop 3
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;
32
use OC\User\NoUserException;
33
use OCA\Circles\Circles\FileSharingBroadcaster;
34
use OCA\Circles\Db\CirclesRequest;
35
use OCA\Circles\Db\MembersRequest;
36
use OCA\Circles\Db\SharesRequest;
37
use OCA\Circles\Db\TokensRequest;
38
use OCA\Circles\Exceptions\CircleDoesNotExistException;
39
use OCA\Circles\Exceptions\CircleTypeNotValidException;
40
use OCA\Circles\Exceptions\ConfigNoCircleAvailableException;
41
use OCA\Circles\Exceptions\EmailAccountInvalidFormatException;
42
use OCA\Circles\Exceptions\GroupDoesNotExistException;
43
use OCA\Circles\Exceptions\MemberAlreadyExistsException;
44
use OCA\Circles\Exceptions\MemberCantJoinCircleException;
45
use OCA\Circles\Exceptions\MemberDoesNotExistException;
46
use OCA\Circles\Model\Circle;
47
use OCA\Circles\Model\GlobalScale\GSEvent;
48
use OCA\Circles\Model\Member;
49
use OCP\IL10N;
50
use OCP\IUserManager;
51
52
53
/**
54
 * Class MembersService
55
 *
56
 * @package OCA\Circles\Service
57
 */
58
class MembersService {
59
60
	/** @var string */
61
	private $userId;
62
63
	/** @var IL10N */
64
	private $l10n;
65
66
	/** @var IUserManager */
67
	private $userManager;
68
69
	/** @var ConfigService */
70
	private $configService;
71
72
	/** @var CirclesRequest */
73
	private $circlesRequest;
74
75
	/** @var MembersRequest */
76
	private $membersRequest;
77
78
	/** @var SharesRequest */
79
	private $sharesRequest;
80
81
	/** @var TokensRequest */
82
	private $tokensRequest;
83
84
	/** @var CirclesService */
85
	private $circlesService;
86
87
	/** @var EventsService */
88
	private $eventsService;
89
90
	/** @var GSUpstreamService */
91
	private $gsUpstreamService;
92
93
	/** @var FileSharingBroadcaster */
94
	private $fileSharingBroadcaster;
95
96
	/** @var MiscService */
97
	private $miscService;
98
99
	/**
100
	 * MembersService constructor.
101
	 *
102
	 * @param string $userId
103
	 * @param IL10N $l10n
104
	 * @param IUserManager $userManager
105
	 * @param ConfigService $configService
106
	 * @param CirclesRequest $circlesRequest
107
	 * @param MembersRequest $membersRequest
108
	 * @param SharesRequest $sharesRequest
109
	 * @param TokensRequest $tokensRequest
110
	 * @param CirclesService $circlesService
111
	 * @param EventsService $eventsService
112
	 * @param FileSharingBroadcaster $fileSharingBroadcaster
113
	 * @param MiscService $miscService
114
	 */
115 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...
116
		$userId, IL10N $l10n, IUserManager $userManager, ConfigService $configService,
117
		CirclesRequest $circlesRequest, MembersRequest $membersRequest, SharesRequest $sharesRequest,
118
		TokensRequest $tokensRequest, CirclesService $circlesService, EventsService $eventsService,
119
		GSUpstreamService $gsUpstreamService, FileSharingBroadcaster $fileSharingBroadcaster,
120
		MiscService $miscService
121
	) {
122
		$this->userId = $userId;
123
		$this->l10n = $l10n;
124
		$this->userManager = $userManager;
125
		$this->configService = $configService;
126
		$this->circlesRequest = $circlesRequest;
127
		$this->membersRequest = $membersRequest;
128
		$this->sharesRequest = $sharesRequest;
129
		$this->tokensRequest = $tokensRequest;
130
		$this->circlesService = $circlesService;
131
		$this->eventsService = $eventsService;
132
		$this->gsUpstreamService = $gsUpstreamService;
133
		$this->fileSharingBroadcaster = $fileSharingBroadcaster;
134
		$this->miscService = $miscService;
135
	}
136
137
138
	/**
139
	 * addMember();
140
	 *
141
	 * add a new member to a circle.
142
	 *
143
	 * @param string $circleUniqueId
144
	 * @param $ident
145
	 * @param int $type
146
	 * @param string $instance
147
	 *
148
	 * @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...
149
	 * @throws Exception
150
	 */
151
	public function addMember($circleUniqueId, $ident, $type, string $instance) {
152
		try {
153
			$circle = $this->circlesRequest->getCircle($circleUniqueId, $this->userId);
154
			$circle->getHigherViewer()
155
				   ->hasToBeModerator();
156
157
			if (!$this->addMassiveMembers($circle, $ident, $type)) {
158
				$this->addSingleMember($circle, $ident, $type, $instance);
159
			}
160
		} catch (Exception $e) {
161
			throw $e;
162
		}
163
164
		return $this->membersRequest->getMembers(
165
			$circle->getUniqueId(), $circle->getHigherViewer()
166
		);
167
	}
168
169
170
	/**
171
	 * add a single member to a circle.
172
	 *
173
	 * @param Circle $circle
174
	 * @param string $ident
175
	 * @param int $type
176
	 *
177
	 * @param string $instance
178
	 *
179
	 * @throws Exception
180
	 */
181
	private function addSingleMember(Circle $circle, $ident, $type, $instance = '') {
182
183
		$this->verifyIdentBasedOnItsType($ident, $type, $instance);
184
185
		$member = $this->membersRequest->getFreshNewMember($circle->getUniqueId(), $ident, $type, $instance);
186
//		$this->membersRequest->createMember($member);
187
//		$member->hasToBeInviteAble();
188
189
//		$this->circlesService->checkThatCircleIsNotFull($circle);
190
191
//		$this->addMemberBasedOnItsType($circle, $member);
192
193
		$event = new GSevent(GSEvent::MEMBER_ADD);
194
		$event->setCircle($circle);
195
		$event->setMember($member);
196
		$this->gsUpstreamService->newEvent($event);
197
198
//		$this->membersRequest->updateMember($member);
199
//		$this->fileSharingBroadcaster->sendMailAboutExistingShares($circle, $member);
200
//		$this->eventsService->onMemberNew($circle, $member);
201
202
	}
203
204
205
	/**
206
	 * add a bunch of users to a circle based on the type of the 'bunch'
207
	 *
208
	 * @param Circle $circle
209
	 * @param string $ident
210
	 * @param int $type
211
	 *
212
	 * @return bool
213
	 * @throws Exception
214
	 */
215
	private function addMassiveMembers(Circle $circle, $ident, $type) {
216
		if ($type === Member::TYPE_GROUP) {
217
			return $this->addGroupMembers($circle, $ident);
218
		}
219
220
		if ($type === Member::TYPE_USER) {
221
			return $this->addMassiveMails($circle, $ident);
222
		}
223
224
		return false;
225
	}
226
227
228
	/**
229
	 * add a new member based on its type.
230
	 *
231
	 * @param Circle $circle
232
	 * @param Member $member
233
	 *
234
	 * @throws CircleTypeNotValidException
235
	 * @throws MemberCantJoinCircleException
236
	 */
237
	public function addMemberBasedOnItsType(Circle $circle, Member &$member) {
238
		$this->addLocalMember($circle, $member);
239
		$this->addEmailAddress($member);
240
		$this->addContact($member);
241
	}
242
243
244
	/**
245
	 * @param Circle $circle
246
	 * @param Member $member
247
	 *
248
	 * @throws CircleTypeNotValidException
249
	 * @throws MemberCantJoinCircleException
250
	 */
251
	private function addLocalMember(Circle $circle, Member $member) {
252
253
		if ($member->getType() !== Member::TYPE_USER) {
254
			return;
255
		}
256
257
		$member->inviteToCircle($circle->getType());
258
259
		if ($this->configService->isInvitationSkipped()) {
260
			$member->joinCircle($circle->getType());
261
		}
262
	}
263
264
265
	/**
266
	 * add mail address as contact.
267
	 *
268
	 * @param Member $member
269
	 */
270
	private function addEmailAddress(Member $member) {
271
272
		if ($member->getType() !== Member::TYPE_MAIL) {
273
			return;
274
		}
275
276
		$member->addMemberToCircle();
277
	}
278
279
280
	/**
281
	 * Add contact as member.
282
	 *
283
	 * @param Member $member
284
	 */
285
	private function addContact(Member $member) {
286
287
		if ($member->getType() !== Member::TYPE_CONTACT) {
288
			return;
289
		}
290
291
		$member->addMemberToCircle();
292
	}
293
294
295
	/**
296
	 * Verify the availability of an ident, based on its type.
297
	 *
298
	 * @param string $ident
299
	 * @param int $type
300
	 * @param string $instance
301
	 *
302
	 * @throws EmailAccountInvalidFormatException
303
	 * @throws NoUserException
304
	 */
305
	public function verifyIdentBasedOnItsType(&$ident, $type, string $instance = '') {
306
		$this->verifyIdentLocalMember($ident, $type, $instance);
307
		$this->verifyIdentEmailAddress($ident, $type);
308
		$this->verifyIdentContact($ident, $type);
309
	}
310
311
312
	/**
313
	 * Verify if a local account is valid.
314
	 *
315
	 * @param $ident
316
	 * @param $type
317
	 *
318
	 * @param string $instance
319
	 *
320
	 * @throws NoUserException
321
	 */
322
	private function verifyIdentLocalMember(&$ident, $type, string $instance = '') {
323
		if ($type !== Member::TYPE_USER) {
324
			return;
325
		}
326
327
		if ($instance === '') {
328
			try {
329
				$ident = $this->miscService->getRealUserId($ident);
330
			} 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...
331
				throw new NoUserException($this->l10n->t("This user does not exist"));
332
			}
333
		}
334
	}
335
336
337
	/**
338
	 * Verify if a mail have a valid format.
339
	 *
340
	 * @param $ident
341
	 * @param $type
342
	 *
343
	 * @throws EmailAccountInvalidFormatException
344
	 */
345
	private function verifyIdentEmailAddress(&$ident, $type) {
346
347
		if ($type !== Member::TYPE_MAIL) {
348
			return;
349
		}
350
351
		if ($this->configService->isAccountOnly()) {
352
			throw new EmailAccountInvalidFormatException(
353
				$this->l10n->t('You cannot add a mail address as member of your Circle')
354
			);
355
		}
356
357
		if (!filter_var($ident, FILTER_VALIDATE_EMAIL)) {
358
			throw new EmailAccountInvalidFormatException(
359
				$this->l10n->t('Email format is not valid')
360
			);
361
		}
362
	}
363
364
365
	/**
366
	 * Verify if a contact exist in current user address books.
367
	 *
368
	 * @param $ident
369
	 * @param $type
370
	 *
371
	 * @throws NoUserException
372
	 * @throws EmailAccountInvalidFormatException
373
	 */
374
	private function verifyIdentContact(&$ident, $type) {
375
		if ($type !== Member::TYPE_CONTACT) {
376
			return;
377
		}
378
379
		if ($this->configService->isAccountOnly()) {
380
			throw new EmailAccountInvalidFormatException(
381
				$this->l10n->t('You cannot add a contact as member of your Circle')
382
			);
383
		}
384
385
		$tmpContact = $this->userId . ':' . $ident;
386
		$result = MiscService::getContactData($tmpContact);
387
		if (empty($result)) {
388
			throw new NoUserException($this->l10n->t("This contact is not available"));
389
		}
390
391
		$ident = $tmpContact;
392
	}
393
394
395
	/**
396
	 * @param Circle $circle
397
	 * @param string $groupId
398
	 *
399
	 * @return bool
400
	 * @throws Exception
401
	 */
402
	private function addGroupMembers(Circle $circle, $groupId) {
403
404
		$group = OC::$server->getGroupManager()
405
							->get($groupId);
406
		if ($group === null) {
407
			throw new GroupDoesNotExistException($this->l10n->t('This group does not exist'));
408
		}
409
410
		foreach ($group->getUsers() as $user) {
411
			try {
412
				$this->addSingleMember($circle, $user->getUID(), Member::TYPE_USER);
413
			} catch (MemberAlreadyExistsException $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
414
			} catch (Exception $e) {
415
				throw $e;
416
			}
417
		}
418
419
		return true;
420
	}
421
422
423
	/**
424
	 * @param Circle $circle
425
	 * @param string $mails
426
	 *
427
	 * @return bool
428
	 */
429
	private function addMassiveMails(Circle $circle, $mails) {
430
431
		$mails = trim($mails);
432
		if (substr($mails, 0, 6) !== 'mails:') {
433
			return false;
434
		}
435
436
		$mails = substr($mails, 6);
437
		foreach (explode(' ', $mails) as $mail) {
438
			if (!filter_var($mail, FILTER_VALIDATE_EMAIL)) {
439
				continue;
440
			}
441
442
			try {
443
				$this->addMember($circle->getUniqueId(), $mail, Member::TYPE_MAIL);
0 ignored issues
show
Bug introduced by
The call to addMember() misses a required argument $instance.

This check looks for function calls that miss required arguments.

Loading history...
444
			} catch (Exception $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
445
			}
446
		}
447
448
		return true;
449
	}
450
451
452
	/**
453
	 * getMember();
454
	 *
455
	 * Will return any data of a user related to a circle (as a Member). User can be a 'non-member'
456
	 * Viewer needs to be at least Member of the Circle
457
	 *
458
	 * @param $circleId
459
	 * @param $userId
460
	 * @param $type
461
	 * @param bool $forceAll
462
	 *
463
	 * @return Member
464
	 * @throws CircleDoesNotExistException
465
	 * @throws ConfigNoCircleAvailableException
466
	 * @throws MemberDoesNotExistException
467
	 */
468
	public function getMember($circleId, $userId, $type, $forceAll = false) {
469
		if (!$forceAll) {
470
			$this->circlesRequest->getCircle($circleId, $this->userId)
471
								 ->getHigherViewer()
472
								 ->hasToBeMember();
473
		}
474
475
		$member = $this->membersRequest->forceGetMember($circleId, $userId, $type);
476
		$member->setNote('');
477
478
		return $member;
479
	}
480
481
482
	/**
483
	 * @param string $circleUniqueId
484
	 * @param string $name
485
	 * @param int $type
486
	 * @param int $level
487
	 *
488
	 * @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...
489
	 * @throws Exception
490
	 */
491
	public function levelMember($circleUniqueId, $name, $type, $instance, $level) {
492
493
		$level = (int)$level;
494
		try {
495
			$circle = $this->circlesRequest->getCircle($circleUniqueId, $this->userId);
496
			if ($circle->getType() === Circle::CIRCLES_PERSONAL) {
497
				throw new CircleTypeNotValidException(
498
					$this->l10n->t('You cannot edit level in a personal circle')
499
				);
500
			}
501
502
			$member = $this->membersRequest->forceGetMember($circle->getUniqueId(), $name, $type, $instance);
503
			if ($member->getLevel() !== $level) {
504
				$event = new GSevent(GSEvent::MEMBER_LEVEL);
505
				$event->setCircle($circle);
506
507
				$event->getData()
508
					  ->sInt('level', $level);
509
				$event->setMember($member);
510
				$this->gsUpstreamService->newEvent($event);
511
			}
512
513
			return $this->membersRequest->getMembers(
514
				$circle->getUniqueId(), $circle->getHigherViewer()
515
			);
516
		} catch (Exception $e) {
517
			throw $e;
518
		}
519
520
	}
521
522
523
524
	/**
525
	 * @param string $circleUniqueId
526
	 * @param string $name
527
	 * @param int $type
528
	 *
529
	 * @param string $instance
530
	 *
531
	 * @return Member[]
532
	 * @throws Exception
533
	 */
534
	public function removeMember(string $circleUniqueId, string $name, int $type, string $instance) {
535
		try {
536
			$circle = $this->circlesRequest->getCircle($circleUniqueId, $this->userId);
537
//			$circle->getHigherViewer()
538
//				   ->hasToBeModerator();
539
//
540
			$member = $this->membersRequest->forceGetMember($circleUniqueId, $name, $type, $instance);
541
542
			$event = new GSevent(GSEvent::MEMBER_REMOVE);
543
			$event->setCircle($circle);
544
			$event->setMember($member);
545
			$this->gsUpstreamService->newEvent($event);
546
547
//			$member->hasToBeMemberOrAlmost();
548
//			$member->cantBeOwner();
549
//
550
//			$circle->getHigherViewer()
551
//				   ->hasToBeHigherLevel($member->getLevel());
552
		} catch (Exception $e) {
553
			throw $e;
554
		}
555
556
//		$this->eventsService->onMemberLeaving($circle, $member);
557
//
558
//		$this->membersRequest->removeMember($member);
559
//		$this->sharesRequest->removeSharesFromMember($member);
560
//		$this->tokensRequest->removeTokensFromMember($member);
561
562
		return $this->membersRequest->getMembers(
563
			$circle->getUniqueId(), $circle->getHigherViewer()
564
		);
565
	}
566
567
568
	/**
569
	 * When a user is removed, remove him from all Circles
570
	 *
571
	 * @param $userId
572
	 */
573
	public function onUserRemoved($userId) {
574
		// TODO: broadcast the event to all instances
575
		$this->membersRequest->removeAllMembershipsFromUser($userId);
576
	}
577
578
}
579
580