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

MembersService::addContact()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 8
rs 10
cc 2
nc 2
nop 1
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->miscService->log('__>>  ' . $instance);
184
		$this->verifyIdentBasedOnItsType($ident, $type, $instance);
185
186
		$member = $this->membersRequest->getFreshNewMember($circle->getUniqueId(), $ident, $type, $instance);
187
//		$this->membersRequest->createMember($member);
188
//		$member->hasToBeInviteAble();
189
190
//		$this->circlesService->checkThatCircleIsNotFull($circle);
191
192
//		$this->addMemberBasedOnItsType($circle, $member);
193
194
		$event = new GSevent(GSEvent::MEMBER_ADD);
195
		$event->setCircle($circle);
196
		$event->setViewer($circle->getViewer());
197
		$event->setMember($member);
198
		$this->gsUpstreamService->newEvent($event);
199
200
//		$this->membersRequest->updateMember($member);
201
//		$this->fileSharingBroadcaster->sendMailAboutExistingShares($circle, $member);
202
//		$this->eventsService->onMemberNew($circle, $member);
203
204
	}
205
206
207
	/**
208
	 * add a bunch of users to a circle based on the type of the 'bunch'
209
	 *
210
	 * @param Circle $circle
211
	 * @param string $ident
212
	 * @param int $type
213
	 *
214
	 * @return bool
215
	 * @throws Exception
216
	 */
217
	private function addMassiveMembers(Circle $circle, $ident, $type) {
218
		if ($type === Member::TYPE_GROUP) {
219
			return $this->addGroupMembers($circle, $ident);
220
		}
221
222
		if ($type === Member::TYPE_USER) {
223
			return $this->addMassiveMails($circle, $ident);
224
		}
225
226
		return false;
227
	}
228
229
230
	/**
231
	 * add a new member based on its type.
232
	 *
233
	 * @param Circle $circle
234
	 * @param Member $member
235
	 *
236
	 * @throws CircleTypeNotValidException
237
	 * @throws MemberCantJoinCircleException
238
	 */
239
	public function addMemberBasedOnItsType(Circle $circle, Member &$member) {
240
		$this->addLocalMember($circle, $member);
241
		$this->addEmailAddress($member);
242
		$this->addContact($member);
243
	}
244
245
246
	/**
247
	 * @param Circle $circle
248
	 * @param Member $member
249
	 *
250
	 * @throws CircleTypeNotValidException
251
	 * @throws MemberCantJoinCircleException
252
	 */
253
	private function addLocalMember(Circle $circle, Member $member) {
254
255
		if ($member->getType() !== Member::TYPE_USER) {
256
			return;
257
		}
258
259
		$member->inviteToCircle($circle->getType());
260
261
		if ($this->configService->isInvitationSkipped()) {
262
			$member->joinCircle($circle->getType());
263
		}
264
	}
265
266
267
	/**
268
	 * add mail address as contact.
269
	 *
270
	 * @param Member $member
271
	 */
272
	private function addEmailAddress(Member $member) {
273
274
		if ($member->getType() !== Member::TYPE_MAIL) {
275
			return;
276
		}
277
278
		$member->addMemberToCircle();
279
	}
280
281
282
	/**
283
	 * Add contact as member.
284
	 *
285
	 * @param Member $member
286
	 */
287
	private function addContact(Member $member) {
288
289
		if ($member->getType() !== Member::TYPE_CONTACT) {
290
			return;
291
		}
292
293
		$member->addMemberToCircle();
294
	}
295
296
297
	/**
298
	 * Verify the availability of an ident, based on its type.
299
	 *
300
	 * @param string $ident
301
	 * @param int $type
302
	 * @param string $instance
303
	 *
304
	 * @throws EmailAccountInvalidFormatException
305
	 * @throws NoUserException
306
	 */
307
	public function verifyIdentBasedOnItsType(&$ident, $type, string $instance = '') {
308
		$this->verifyIdentLocalMember($ident, $type, $instance);
309
		$this->verifyIdentEmailAddress($ident, $type);
310
		$this->verifyIdentContact($ident, $type);
311
	}
312
313
314
	/**
315
	 * Verify if a local account is valid.
316
	 *
317
	 * @param $ident
318
	 * @param $type
319
	 *
320
	 * @param string $instance
321
	 *
322
	 * @throws NoUserException
323
	 */
324
	private function verifyIdentLocalMember(&$ident, $type, string $instance = '') {
325
		if ($type !== Member::TYPE_USER) {
326
			return;
327
		}
328
329
		if ($instance === '') {
330
			try {
331
				$ident = $this->miscService->getRealUserId($ident);
332
			} 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...
333
				throw new NoUserException($this->l10n->t("This user does not exist"));
334
			}
335
		}
336
	}
337
338
339
	/**
340
	 * Verify if a mail have a valid format.
341
	 *
342
	 * @param $ident
343
	 * @param $type
344
	 *
345
	 * @throws EmailAccountInvalidFormatException
346
	 */
347
	private function verifyIdentEmailAddress(&$ident, $type) {
348
349
		if ($type !== Member::TYPE_MAIL) {
350
			return;
351
		}
352
353
		if ($this->configService->isAccountOnly()) {
354
			throw new EmailAccountInvalidFormatException(
355
				$this->l10n->t('You cannot add a mail address as member of your Circle')
356
			);
357
		}
358
359
		if (!filter_var($ident, FILTER_VALIDATE_EMAIL)) {
360
			throw new EmailAccountInvalidFormatException(
361
				$this->l10n->t('Email format is not valid')
362
			);
363
		}
364
	}
365
366
367
	/**
368
	 * Verify if a contact exist in current user address books.
369
	 *
370
	 * @param $ident
371
	 * @param $type
372
	 *
373
	 * @throws NoUserException
374
	 * @throws EmailAccountInvalidFormatException
375
	 */
376
	private function verifyIdentContact(&$ident, $type) {
377
		if ($type !== Member::TYPE_CONTACT) {
378
			return;
379
		}
380
381
		if ($this->configService->isAccountOnly()) {
382
			throw new EmailAccountInvalidFormatException(
383
				$this->l10n->t('You cannot add a contact as member of your Circle')
384
			);
385
		}
386
387
		$tmpContact = $this->userId . ':' . $ident;
388
		$result = MiscService::getContactData($tmpContact);
389
		if (empty($result)) {
390
			throw new NoUserException($this->l10n->t("This contact is not available"));
391
		}
392
393
		$ident = $tmpContact;
394
	}
395
396
397
	/**
398
	 * @param Circle $circle
399
	 * @param string $groupId
400
	 *
401
	 * @return bool
402
	 * @throws Exception
403
	 */
404
	private function addGroupMembers(Circle $circle, $groupId) {
405
406
		$group = OC::$server->getGroupManager()
407
							->get($groupId);
408
		if ($group === null) {
409
			throw new GroupDoesNotExistException($this->l10n->t('This group does not exist'));
410
		}
411
412
		foreach ($group->getUsers() as $user) {
413
			try {
414
				$this->addSingleMember($circle, $user->getUID(), Member::TYPE_USER);
415
			} catch (MemberAlreadyExistsException $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
416
			} catch (Exception $e) {
417
				throw $e;
418
			}
419
		}
420
421
		return true;
422
	}
423
424
425
	/**
426
	 * @param Circle $circle
427
	 * @param string $mails
428
	 *
429
	 * @return bool
430
	 */
431
	private function addMassiveMails(Circle $circle, $mails) {
432
433
		$mails = trim($mails);
434
		if (substr($mails, 0, 6) !== 'mails:') {
435
			return false;
436
		}
437
438
		$mails = substr($mails, 6);
439
		foreach (explode(' ', $mails) as $mail) {
440
			if (!filter_var($mail, FILTER_VALIDATE_EMAIL)) {
441
				continue;
442
			}
443
444
			try {
445
				$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...
446
			} catch (Exception $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
447
			}
448
		}
449
450
		return true;
451
	}
452
453
454
	/**
455
	 * getMember();
456
	 *
457
	 * Will return any data of a user related to a circle (as a Member). User can be a 'non-member'
458
	 * Viewer needs to be at least Member of the Circle
459
	 *
460
	 * @param $circleId
461
	 * @param $userId
462
	 * @param $type
463
	 * @param bool $forceAll
464
	 *
465
	 * @return Member
466
	 * @throws CircleDoesNotExistException
467
	 * @throws ConfigNoCircleAvailableException
468
	 * @throws MemberDoesNotExistException
469
	 */
470
	public function getMember($circleId, $userId, $type, $forceAll = false) {
471
		if (!$forceAll) {
472
			$this->circlesRequest->getCircle($circleId, $this->userId)
473
								 ->getHigherViewer()
474
								 ->hasToBeMember();
475
		}
476
477
		$member = $this->membersRequest->forceGetMember($circleId, $userId, $type);
478
		$member->setNote('');
479
480
		return $member;
481
	}
482
483
484
	/**
485
	 * @param string $circleUniqueId
486
	 * @param string $name
487
	 * @param int $type
488
	 * @param int $level
489
	 *
490
	 * @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...
491
	 * @throws Exception
492
	 */
493
	public function levelMember($circleUniqueId, $name, $type, $instance, $level) {
494
495
		$level = (int)$level;
496
		try {
497
			$circle = $this->circlesRequest->getCircle($circleUniqueId, $this->userId);
498
			if ($circle->getType() === Circle::CIRCLES_PERSONAL) {
499
				throw new CircleTypeNotValidException(
500
					$this->l10n->t('You cannot edit level in a personal circle')
501
				);
502
			}
503
504
			$member = $this->membersRequest->forceGetMember($circle->getUniqueId(), $name, $type, $instance);
505
506
			$event = new GSevent(GSEvent::MEMBER_LEVEL);
507
			$event->setCircle($circle);
508
			$event->setViewer($circle->getViewer());
509
			$event->getData()
510
				  ->sInt('level', $level);
511
			$event->setMember($member);
512
			$this->gsUpstreamService->newEvent($event);
513
514
//			$member->levelHasToBeEditable();
515
//			$this->updateMemberLevel($circle, $member, $level);
516
//
517
518
			return $this->membersRequest->getMembers(
519
				$circle->getUniqueId(), $circle->getHigherViewer()
520
			);
521
		} catch (Exception $e) {
522
			throw $e;
523
		}
524
525
	}
526
527
528
//	/**
529
//	 * @param Circle $circle
530
//	 * @param Member $member
531
//	 * @param $level
532
//	 *
533
//	 * @throws Exception
534
//	 */
535
//	private function updateMemberLevel(Circle $circle, Member $member, $level) {
536
//		if ($member->getLevel() === $level) {
537
//			return;
538
//		}
539
//
540
//		if ($level === Member::LEVEL_OWNER) {
541
//			$this->switchOwner($circle, $member);
542
//		} else {
543
//			$this->editMemberLevel($circle, $member, $level);
544
//		}
545
//
546
//		$this->eventsService->onMemberLevel($circle, $member);
547
//	}
548
549
//
550
//	/**
551
//	 * @param Circle $circle
552
//	 * @param Member $member
553
//	 * @param $level
554
//	 *
555
//	 * @throws Exception
556
//	 */
557
//	private function editMemberLevel(Circle $circle, Member &$member, $level) {
558
//		try {
559
//			$isMod = $circle->getHigherViewer();
560
//			$isMod->hasToBeModerator();
561
//			$isMod->hasToBeHigherLevel($level);
562
//
563
//			$member->hasToBeMember();
564
//			$member->cantBeOwner();
565
//			$isMod->hasToBeHigherLevel($member->getLevel());
566
//
567
//			$member->setLevel($level);
568
//			$this->membersRequest->updateMember($member);
569
//		} catch (Exception $e) {
570
//			throw $e;
571
//		}
572
//
573
//	}
574
//
575
//	/**
576
//	 * @param Circle $circle
577
//	 * @param Member $member
578
//	 *
579
//	 * @throws Exception
580
//	 */
581
//	private function switchOwner(Circle $circle, Member &$member) {
582
//		try {
583
//			$isMod = $circle->getHigherViewer();
584
//
585
//			// should already be possible from an NCAdmin, but not enabled in the frontend.
586
//			$this->circlesService->hasToBeOwner($isMod);
587
//
588
//			$member->hasToBeMember();
589
//			$member->cantBeOwner();
590
//
591
//			$member->setLevel(Member::LEVEL_OWNER);
592
//			$this->membersRequest->updateMember($member);
593
//
594
//			$isMod->setLevel(Member::LEVEL_ADMIN);
595
//			$this->membersRequest->updateMember($isMod);
596
//
597
//		} catch (Exception $e) {
598
//			throw $e;
599
//		}
600
//	}
601
//
602
603
	/**
604
	 * @param string $circleUniqueId
605
	 * @param string $name
606
	 * @param int $type
607
	 *
608
	 * @param string $instance
609
	 *
610
	 * @return Member[]
611
	 * @throws Exception
612
	 */
613
	public function removeMember(string $circleUniqueId, string $name, int $type, string $instance) {
614
		try {
615
			$circle = $this->circlesRequest->getCircle($circleUniqueId, $this->userId);
616
//			$circle->getHigherViewer()
617
//				   ->hasToBeModerator();
618
//
619
			$member = $this->membersRequest->forceGetMember($circleUniqueId, $name, $type, $instance);
620
621
			$event = new GSevent(GSEvent::MEMBER_REMOVE);
622
			$event->setCircle($circle);
623
			$event->setViewer($circle->getViewer());
624
			$event->setMember($member);
625
			$this->gsUpstreamService->newEvent($event);
626
627
//			$member->hasToBeMemberOrAlmost();
628
//			$member->cantBeOwner();
629
//
630
//			$circle->getHigherViewer()
631
//				   ->hasToBeHigherLevel($member->getLevel());
632
		} catch (Exception $e) {
633
			throw $e;
634
		}
635
636
//		$this->eventsService->onMemberLeaving($circle, $member);
637
//
638
//		$this->membersRequest->removeMember($member);
639
//		$this->sharesRequest->removeSharesFromMember($member);
640
//		$this->tokensRequest->removeTokensFromMember($member);
641
642
		return $this->membersRequest->getMembers(
643
			$circle->getUniqueId(), $circle->getHigherViewer()
644
		);
645
	}
646
647
648
	/**
649
	 * When a user is removed, remove him from all Circles
650
	 *
651
	 * @param $userId
652
	 */
653
	public function onUserRemoved($userId) {
654
		// TODO: broadcast the event to all instances
655
		$this->membersRequest->removeAllMembershipsFromUser($userId);
656
	}
657
658
}
659
660