Completed
Pull Request — master (#362)
by Maxence
01:50
created

MembersService::addLocalMember()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 12
rs 9.8666
c 0
b 0
f 0
cc 3
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;
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\Exceptions\MemberIsNotModeratorException;
47
use OCA\Circles\Model\Circle;
48
use OCA\Circles\Model\GlobalScale\GSEvent;
49
use OCA\Circles\Model\Member;
50
use OCP\IL10N;
51
use OCP\IUserManager;
52
53
54
/**
55
 * Class MembersService
56
 *
57
 * @package OCA\Circles\Service
58
 */
59
class MembersService {
60
61
	/** @var string */
62
	private $userId;
63
64
	/** @var IL10N */
65
	private $l10n;
66
67
	/** @var IUserManager */
68
	private $userManager;
69
70
	/** @var ConfigService */
71
	private $configService;
72
73
	/** @var CirclesRequest */
74
	private $circlesRequest;
75
76
	/** @var MembersRequest */
77
	private $membersRequest;
78
79
	/** @var SharesRequest */
80
	private $sharesRequest;
81
82
	/** @var TokensRequest */
83
	private $tokensRequest;
84
85
	/** @var CirclesService */
86
	private $circlesService;
87
88
	/** @var EventsService */
89
	private $eventsService;
90
91
	/** @var GSUpstreamService */
92
	private $gsUpstreamService;
93
94
	/** @var FileSharingBroadcaster */
95
	private $fileSharingBroadcaster;
96
97
	/** @var MiscService */
98
	private $miscService;
99
100
	/**
101
	 * MembersService constructor.
102
	 *
103
	 * @param string $userId
104
	 * @param IL10N $l10n
105
	 * @param IUserManager $userManager
106
	 * @param ConfigService $configService
107
	 * @param CirclesRequest $circlesRequest
108
	 * @param MembersRequest $membersRequest
109
	 * @param SharesRequest $sharesRequest
110
	 * @param TokensRequest $tokensRequest
111
	 * @param CirclesService $circlesService
112
	 * @param EventsService $eventsService
113
	 * @param FileSharingBroadcaster $fileSharingBroadcaster
114
	 * @param MiscService $miscService
115
	 */
116 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...
117
		$userId, IL10N $l10n, IUserManager $userManager, ConfigService $configService,
118
		CirclesRequest $circlesRequest, MembersRequest $membersRequest, SharesRequest $sharesRequest,
119
		TokensRequest $tokensRequest, CirclesService $circlesService, EventsService $eventsService,
120
		GSUpstreamService $gsUpstreamService, FileSharingBroadcaster $fileSharingBroadcaster,
121
		MiscService $miscService
122
	) {
123
		$this->userId = $userId;
124
		$this->l10n = $l10n;
125
		$this->userManager = $userManager;
126
		$this->configService = $configService;
127
		$this->circlesRequest = $circlesRequest;
128
		$this->membersRequest = $membersRequest;
129
		$this->sharesRequest = $sharesRequest;
130
		$this->tokensRequest = $tokensRequest;
131
		$this->circlesService = $circlesService;
132
		$this->eventsService = $eventsService;
133
		$this->gsUpstreamService = $gsUpstreamService;
134
		$this->fileSharingBroadcaster = $fileSharingBroadcaster;
135
		$this->miscService = $miscService;
136
	}
137
138
139
	/**
140
	 * addMember();
141
	 *
142
	 * add a new member to a circle.
143
	 *
144
	 * @param string $circleUniqueId
145
	 * @param $ident
146
	 * @param int $type
147
	 * @param string $instance
148
	 *
149
	 * @param bool $force
150
	 *
151
	 * @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...
152
	 * @throws Exception
153
	 */
154
	public function addMember($circleUniqueId, $ident, $type, string $instance, bool $force = false) {
155 View Code Duplication
		if ($force === true) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
156
			$circle = $this->circlesRequest->forceGetCircle($circleUniqueId);
157
		} else {
158
			$circle = $this->circlesRequest->getCircle($circleUniqueId, $this->userId);
159
			$circle->getHigherViewer()
160
				   ->hasToBeModerator();
161
		}
162
163
		if (!$this->addMassiveMembers($circle, $ident, $type)) {
164
			$this->addSingleMember($circle, $ident, $type, $instance, $force);
165
		}
166
167
		return $this->membersRequest->getMembers($circle->getUniqueId(), $circle->getHigherViewer(), $force);
168
	}
169
170
171
	/**
172
	 * add a single member to a circle.
173
	 *
174
	 * @param Circle $circle
175
	 * @param string $ident
176
	 * @param int $type
177
	 *
178
	 * @param string $instance
179
	 * @param bool $force
180
	 *
181
	 * @throws EmailAccountInvalidFormatException
182
	 * @throws NoUserException
183
	 */
184
	private function addSingleMember(Circle $circle, $ident, $type, $instance = '', bool $force = false) {
185
186
		$this->verifyIdentBasedOnItsType($ident, $type, $instance);
187
188
		$member = $this->membersRequest->getFreshNewMember($circle->getUniqueId(), $ident, $type, $instance);
189
//		$this->membersRequest->createMember($member);
190
//		$member->hasToBeInviteAble();
191
192
//		$this->circlesService->checkThatCircleIsNotFull($circle);
193
194
//		$this->addMemberBasedOnItsType($circle, $member);
195
196
		$event = new GSEvent(GSEvent::MEMBER_ADD, false, $force);
197
		$event->setSeverity(GSEvent::SEVERITY_HIGH);
198
199
		$event->setCircle($circle);
200
		$event->setMember($member);
201
		$this->gsUpstreamService->newEvent($event);
202
203
//		$this->membersRequest->updateMember($member);
204
//		$this->fileSharingBroadcaster->sendMailAboutExistingShares($circle, $member);
205
//		$this->eventsService->onMemberNew($circle, $member);
206
207
	}
208
209
210
	/**
211
	 * add a bunch of users to a circle based on the type of the 'bunch'
212
	 *
213
	 * @param Circle $circle
214
	 * @param string $ident
215
	 * @param int $type
216
	 *
217
	 * @return bool
218
	 * @throws Exception
219
	 */
220
	private function addMassiveMembers(Circle $circle, $ident, $type) {
221
		if ($type === Member::TYPE_GROUP) {
222
			return $this->addGroupMembers($circle, $ident);
223
		}
224
225
		if ($type === Member::TYPE_USER) {
226
			return $this->addMassiveMails($circle, $ident);
227
		}
228
229
		return false;
230
	}
231
232
233
	/**
234
	 * add a new member based on its type.
235
	 *
236
	 * @param Circle $circle
237
	 * @param Member $member
238
	 *
239
	 * @throws CircleTypeNotValidException
240
	 * @throws MemberCantJoinCircleException
241
	 */
242
	public function addMemberBasedOnItsType(Circle $circle, Member &$member) {
243
		$this->addLocalMember($circle, $member);
244
		$this->addEmailAddress($member);
245
		$this->addContact($member);
246
	}
247
248
249
	/**
250
	 * @param Circle $circle
251
	 * @param Member $member
252
	 *
253
	 * @throws CircleTypeNotValidException
254
	 * @throws MemberCantJoinCircleException
255
	 */
256
	private function addLocalMember(Circle $circle, Member $member) {
257
258
		if ($member->getType() !== Member::TYPE_USER) {
259
			return;
260
		}
261
262
		$member->inviteToCircle($circle->getType());
263
264
		if ($this->configService->isInvitationSkipped()) {
265
			$member->joinCircle($circle->getType());
266
		}
267
	}
268
269
270
	/**
271
	 * add mail address as contact.
272
	 *
273
	 * @param Member $member
274
	 */
275
	private function addEmailAddress(Member $member) {
276
277
		if ($member->getType() !== Member::TYPE_MAIL) {
278
			return;
279
		}
280
281
		$member->addMemberToCircle();
282
	}
283
284
285
	/**
286
	 * Add contact as member.
287
	 *
288
	 * @param Member $member
289
	 */
290
	private function addContact(Member $member) {
291
292
		if ($member->getType() !== Member::TYPE_CONTACT) {
293
			return;
294
		}
295
296
		$member->addMemberToCircle();
297
	}
298
299
300
	/**
301
	 * Verify the availability of an ident, based on its type.
302
	 *
303
	 * @param string $ident
304
	 * @param int $type
305
	 * @param string $instance
306
	 *
307
	 * @throws EmailAccountInvalidFormatException
308
	 * @throws NoUserException
309
	 */
310
	public function verifyIdentBasedOnItsType(&$ident, $type, string $instance = '') {
311
		if ($instance === $this->configService->getLocalCloudId()) {
312
			$instance = '';
313
		}
314
315
		$this->verifyIdentLocalMember($ident, $type, $instance);
316
		$this->verifyIdentEmailAddress($ident, $type);
317
		$this->verifyIdentContact($ident, $type);
318
	}
319
320
321
	/**
322
	 * Verify if a local account is valid.
323
	 *
324
	 * @param $ident
325
	 * @param $type
326
	 *
327
	 * @param string $instance
328
	 *
329
	 * @throws NoUserException
330
	 */
331
	private function verifyIdentLocalMember(&$ident, $type, string $instance = '') {
332
		if ($type !== Member::TYPE_USER) {
333
			return;
334
		}
335
336
		if ($instance === '') {
337
			try {
338
				$ident = $this->miscService->getRealUserId($ident);
339
			} 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...
340
				throw new NoUserException($this->l10n->t("This user does not exist"));
341
			}
342
		}
343
	}
344
345
346
	/**
347
	 * Verify if a mail have a valid format.
348
	 *
349
	 * @param $ident
350
	 * @param $type
351
	 *
352
	 * @throws EmailAccountInvalidFormatException
353
	 */
354
	private function verifyIdentEmailAddress(&$ident, $type) {
355
356
		if ($type !== Member::TYPE_MAIL) {
357
			return;
358
		}
359
360
		if ($this->configService->isAccountOnly()) {
361
			throw new EmailAccountInvalidFormatException(
362
				$this->l10n->t('You cannot add a mail address as member of your Circle')
363
			);
364
		}
365
366
		if (!filter_var($ident, FILTER_VALIDATE_EMAIL)) {
367
			throw new EmailAccountInvalidFormatException(
368
				$this->l10n->t('Email format is not valid')
369
			);
370
		}
371
	}
372
373
374
	/**
375
	 * Verify if a contact exist in current user address books.
376
	 *
377
	 * @param $ident
378
	 * @param $type
379
	 *
380
	 * @throws NoUserException
381
	 * @throws EmailAccountInvalidFormatException
382
	 */
383
	private function verifyIdentContact(&$ident, $type) {
384
		if ($type !== Member::TYPE_CONTACT) {
385
			return;
386
		}
387
388
		if ($this->configService->isAccountOnly()) {
389
			throw new EmailAccountInvalidFormatException(
390
				$this->l10n->t('You cannot add a contact as member of your Circle')
391
			);
392
		}
393
394
		$tmpContact = $this->userId . ':' . $ident;
395
		$result = MiscService::getContactData($tmpContact);
396
		if (empty($result)) {
397
			throw new NoUserException($this->l10n->t("This contact is not available"));
398
		}
399
400
		$ident = $tmpContact;
401
	}
402
403
404
	/**
405
	 * @param Circle $circle
406
	 * @param string $groupId
407
	 *
408
	 * @return bool
409
	 * @throws Exception
410
	 */
411
	private function addGroupMembers(Circle $circle, $groupId) {
412
413
		$group = OC::$server->getGroupManager()
414
							->get($groupId);
415
		if ($group === null) {
416
			throw new GroupDoesNotExistException($this->l10n->t('This group does not exist'));
417
		}
418
419
		foreach ($group->getUsers() as $user) {
420
			try {
421
				$this->addSingleMember($circle, $user->getUID(), Member::TYPE_USER);
422
			} catch (MemberAlreadyExistsException $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
423
			} catch (Exception $e) {
424
				throw $e;
425
			}
426
		}
427
428
		return true;
429
	}
430
431
432
	/**
433
	 * @param Circle $circle
434
	 * @param string $mails
435
	 *
436
	 * @return bool
437
	 */
438
	private function addMassiveMails(Circle $circle, $mails) {
439
440
		$mails = trim($mails);
441
		if (substr($mails, 0, 6) !== 'mails:') {
442
			return false;
443
		}
444
445
		$mails = substr($mails, 6);
446
		foreach (explode(' ', $mails) as $mail) {
447
			if (!filter_var($mail, FILTER_VALIDATE_EMAIL)) {
448
				continue;
449
			}
450
451
			try {
452
				$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...
453
			} catch (Exception $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
454
			}
455
		}
456
457
		return true;
458
	}
459
460
461
	/**
462
	 * getMember();
463
	 *
464
	 * Will return any data of a user related to a circle (as a Member). User can be a 'non-member'
465
	 * Viewer needs to be at least Member of the Circle
466
	 *
467
	 * @param $circleId
468
	 * @param $userId
469
	 * @param $type
470
	 * @param bool $forceAll
471
	 *
472
	 * @return Member
473
	 * @throws CircleDoesNotExistException
474
	 * @throws ConfigNoCircleAvailableException
475
	 * @throws MemberDoesNotExistException
476
	 */
477
	public function getMember($circleId, $userId, $type, $forceAll = false) {
478
		if (!$forceAll) {
479
			$this->circlesRequest->getCircle($circleId, $this->userId)
480
								 ->getHigherViewer()
481
								 ->hasToBeMember();
482
		}
483
484
		$member = $this->membersRequest->forceGetMember($circleId, $userId, $type);
485
		$member->setNote('');
486
487
		return $member;
488
	}
489
490
491
	/**
492
	 * @param string $memberId
493
	 *
494
	 * @return Member
495
	 * @throws MemberDoesNotExistException
496
	 */
497
	public function getMemberById(string $memberId): Member {
498
		return $this->membersRequest->forceGetMemberById($memberId);
499
	}
500
501
502
	/**
503
	 * @param string $circleUniqueId
504
	 * @param string $name
505
	 * @param int $type
506
	 * @param string $instance
507
	 * @param int $level
508
	 * @param bool $force
509
	 *
510
	 * @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...
511
	 * @throws CircleDoesNotExistException
512
	 * @throws CircleTypeNotValidException
513
	 * @throws ConfigNoCircleAvailableException
514
	 * @throws MemberDoesNotExistException
515
	 * @throws Exception
516
	 */
517
	public function levelMember(
518
		string $circleUniqueId, string $name, int $type, string $instance, int $level, bool $force = false
519
	) {
520
		$level = (int)$level;
521
		if ($force === false) {
522
			$circle = $this->circlesRequest->getCircle($circleUniqueId, $this->userId);
523
		} else {
524
			$circle = $this->circlesRequest->forceGetCircle($circleUniqueId);
525
		}
526
527
		if ($circle->getType() === Circle::CIRCLES_PERSONAL) {
528
			throw new CircleTypeNotValidException(
529
				$this->l10n->t('You cannot edit level in a personal circle')
530
			);
531
		}
532
533
//<<<<<<< master
534
//		$member = $this->membersRequest->forceGetMember($circle->getUniqueId(), $name, $type);
535
//		$member->levelHasToBeEditable();
536
//		$this->updateMemberLevel($circle, $member, $level, $force);
537
//=======
538
539
		$member = $this->membersRequest->forceGetMember($circle->getUniqueId(), $name, $type, $instance);
540
		if ($member->getLevel() !== $level) {
541
			$event = new GSEvent(GSEvent::MEMBER_LEVEL, false, $force);
542
			$event->setCircle($circle);
543
544
			$event->getData()
545
				  ->sInt('level', $level);
546
			$event->setMember($member);
547
			$this->gsUpstreamService->newEvent($event);
548
		}
549
550 View Code Duplication
		if ($force === false) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
551
			return $this->membersRequest->getMembers(
552
				$circle->getUniqueId(), $circle->getHigherViewer()
553
			);
554
		} else {
555
			return $this->membersRequest->forceGetMembers($circle->getUniqueId());
556
		}
557
558
	}
559
560
561
	/**
562
	 * @param string $circleUniqueId
563
	 * @param string $name
564
	 * @param int $type
565
	 * @param string $instance
566
	 * @param bool $force
567
	 *
568
	 * @return Member[]
569
	 * @throws CircleDoesNotExistException
570
	 * @throws ConfigNoCircleAvailableException
571
	 * @throws MemberDoesNotExistException
572
	 * @throws MemberIsNotModeratorException
573
	 * @throws Exception
574
	 */
575
	public function removeMember(
576
		string $circleUniqueId, string $name, int $type, string $instance, bool $force = false
577
	): array {
578
579 View Code Duplication
		if ($force === false) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
580
			$circle = $this->circlesRequest->getCircle($circleUniqueId, $this->userId);
581
			$circle->getHigherViewer()
582
				   ->hasToBeModerator();
583
		} else {
584
			$circle = $this->circlesRequest->forceGetCircle($circleUniqueId);
585
		}
586
587
		$member = $this->membersRequest->forceGetMember($circleUniqueId, $name, $type, $instance);
588
589
		$event = new GSEvent(GSEvent::MEMBER_REMOVE, false, $force);
590
		$event->setCircle($circle);
591
		$event->setMember($member);
592
		$this->gsUpstreamService->newEvent($event);
593
594
//			$member->hasToBeMemberOrAlmost();
595
//			$member->cantBeOwner();
596
//
597
//			$circle->getHigherViewer()
598
//				   ->hasToBeHigherLevel($member->getLevel());
599
600
//		$this->eventsService->onMemberLeaving($circle, $member);
601
//
602
//		$this->membersRequest->removeMember($member);
603
//		$this->sharesRequest->removeSharesFromMember($member);
604
//		$this->tokensRequest->removeTokensFromMember($member);
605
606 View Code Duplication
		if ($force === false) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
607
			return $this->membersRequest->getMembers(
608
				$circle->getUniqueId(), $circle->getHigherViewer()
609
			);
610
		} else {
611
			return $this->membersRequest->forceGetMembers($circle->getUniqueId());
612
		}
613
	}
614
615
616
	/**
617
	 * When a user is removed, remove him from all Circles
618
	 *
619
	 * @param $userId
620
	 */
621
	public function onUserRemoved($userId) {
622
		// TODO: broadcast the event to all instances
623
		$this->membersRequest->removeAllMembershipsFromUser($userId);
624
	}
625
626
627
}
628
629