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

MembersService::addSingleMember()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 23

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 23
rs 9.552
c 0
b 0
f 0
cc 1
nc 1
nop 4
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->setSeverity(GSEvent::SEVERITY_HIGH);
195
		$event->setCircle($circle);
196
		$event->setMember($member);
197
		$this->gsUpstreamService->newEvent($event);
198
199
//		$this->membersRequest->updateMember($member);
200
//		$this->fileSharingBroadcaster->sendMailAboutExistingShares($circle, $member);
201
//		$this->eventsService->onMemberNew($circle, $member);
202
203
	}
204
205
206
	/**
207
	 * add a bunch of users to a circle based on the type of the 'bunch'
208
	 *
209
	 * @param Circle $circle
210
	 * @param string $ident
211
	 * @param int $type
212
	 *
213
	 * @return bool
214
	 * @throws Exception
215
	 */
216
	private function addMassiveMembers(Circle $circle, $ident, $type) {
217
		if ($type === Member::TYPE_GROUP) {
218
			return $this->addGroupMembers($circle, $ident);
219
		}
220
221
		if ($type === Member::TYPE_USER) {
222
			return $this->addMassiveMails($circle, $ident);
223
		}
224
225
		return false;
226
	}
227
228
229
	/**
230
	 * add a new member based on its type.
231
	 *
232
	 * @param Circle $circle
233
	 * @param Member $member
234
	 *
235
	 * @throws CircleTypeNotValidException
236
	 * @throws MemberCantJoinCircleException
237
	 */
238
	public function addMemberBasedOnItsType(Circle $circle, Member &$member) {
239
		$this->addLocalMember($circle, $member);
240
		$this->addEmailAddress($member);
241
		$this->addContact($member);
242
	}
243
244
245
	/**
246
	 * @param Circle $circle
247
	 * @param Member $member
248
	 *
249
	 * @throws CircleTypeNotValidException
250
	 * @throws MemberCantJoinCircleException
251
	 */
252
	private function addLocalMember(Circle $circle, Member $member) {
253
254
		if ($member->getType() !== Member::TYPE_USER) {
255
			return;
256
		}
257
258
		$member->inviteToCircle($circle->getType());
259
260
		if ($this->configService->isInvitationSkipped()) {
261
			$member->joinCircle($circle->getType());
262
		}
263
	}
264
265
266
	/**
267
	 * add mail address as contact.
268
	 *
269
	 * @param Member $member
270
	 */
271
	private function addEmailAddress(Member $member) {
272
273
		if ($member->getType() !== Member::TYPE_MAIL) {
274
			return;
275
		}
276
277
		$member->addMemberToCircle();
278
	}
279
280
281
	/**
282
	 * Add contact as member.
283
	 *
284
	 * @param Member $member
285
	 */
286
	private function addContact(Member $member) {
287
288
		if ($member->getType() !== Member::TYPE_CONTACT) {
289
			return;
290
		}
291
292
		$member->addMemberToCircle();
293
	}
294
295
296
	/**
297
	 * Verify the availability of an ident, based on its type.
298
	 *
299
	 * @param string $ident
300
	 * @param int $type
301
	 * @param string $instance
302
	 *
303
	 * @throws EmailAccountInvalidFormatException
304
	 * @throws NoUserException
305
	 */
306
	public function verifyIdentBasedOnItsType(&$ident, $type, string $instance = '') {
307
		$this->verifyIdentLocalMember($ident, $type, $instance);
308
		$this->verifyIdentEmailAddress($ident, $type);
309
		$this->verifyIdentContact($ident, $type);
310
	}
311
312
313
	/**
314
	 * Verify if a local account is valid.
315
	 *
316
	 * @param $ident
317
	 * @param $type
318
	 *
319
	 * @param string $instance
320
	 *
321
	 * @throws NoUserException
322
	 */
323
	private function verifyIdentLocalMember(&$ident, $type, string $instance = '') {
324
		if ($type !== Member::TYPE_USER) {
325
			return;
326
		}
327
328
		if ($instance === '') {
329
			try {
330
				$ident = $this->miscService->getRealUserId($ident);
331
			} 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...
332
				throw new NoUserException($this->l10n->t("This user does not exist"));
333
			}
334
		}
335
	}
336
337
338
	/**
339
	 * Verify if a mail have a valid format.
340
	 *
341
	 * @param $ident
342
	 * @param $type
343
	 *
344
	 * @throws EmailAccountInvalidFormatException
345
	 */
346
	private function verifyIdentEmailAddress(&$ident, $type) {
347
348
		if ($type !== Member::TYPE_MAIL) {
349
			return;
350
		}
351
352
		if ($this->configService->isAccountOnly()) {
353
			throw new EmailAccountInvalidFormatException(
354
				$this->l10n->t('You cannot add a mail address as member of your Circle')
355
			);
356
		}
357
358
		if (!filter_var($ident, FILTER_VALIDATE_EMAIL)) {
359
			throw new EmailAccountInvalidFormatException(
360
				$this->l10n->t('Email format is not valid')
361
			);
362
		}
363
	}
364
365
366
	/**
367
	 * Verify if a contact exist in current user address books.
368
	 *
369
	 * @param $ident
370
	 * @param $type
371
	 *
372
	 * @throws NoUserException
373
	 * @throws EmailAccountInvalidFormatException
374
	 */
375
	private function verifyIdentContact(&$ident, $type) {
376
		if ($type !== Member::TYPE_CONTACT) {
377
			return;
378
		}
379
380
		if ($this->configService->isAccountOnly()) {
381
			throw new EmailAccountInvalidFormatException(
382
				$this->l10n->t('You cannot add a contact as member of your Circle')
383
			);
384
		}
385
386
		$tmpContact = $this->userId . ':' . $ident;
387
		$result = MiscService::getContactData($tmpContact);
388
		if (empty($result)) {
389
			throw new NoUserException($this->l10n->t("This contact is not available"));
390
		}
391
392
		$ident = $tmpContact;
393
	}
394
395
396
	/**
397
	 * @param Circle $circle
398
	 * @param string $groupId
399
	 *
400
	 * @return bool
401
	 * @throws Exception
402
	 */
403
	private function addGroupMembers(Circle $circle, $groupId) {
404
405
		$group = OC::$server->getGroupManager()
406
							->get($groupId);
407
		if ($group === null) {
408
			throw new GroupDoesNotExistException($this->l10n->t('This group does not exist'));
409
		}
410
411
		foreach ($group->getUsers() as $user) {
412
			try {
413
				$this->addSingleMember($circle, $user->getUID(), Member::TYPE_USER);
414
			} catch (MemberAlreadyExistsException $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
415
			} catch (Exception $e) {
416
				throw $e;
417
			}
418
		}
419
420
		return true;
421
	}
422
423
424
	/**
425
	 * @param Circle $circle
426
	 * @param string $mails
427
	 *
428
	 * @return bool
429
	 */
430
	private function addMassiveMails(Circle $circle, $mails) {
431
432
		$mails = trim($mails);
433
		if (substr($mails, 0, 6) !== 'mails:') {
434
			return false;
435
		}
436
437
		$mails = substr($mails, 6);
438
		foreach (explode(' ', $mails) as $mail) {
439
			if (!filter_var($mail, FILTER_VALIDATE_EMAIL)) {
440
				continue;
441
			}
442
443
			try {
444
				$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...
445
			} catch (Exception $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
446
			}
447
		}
448
449
		return true;
450
	}
451
452
453
	/**
454
	 * getMember();
455
	 *
456
	 * Will return any data of a user related to a circle (as a Member). User can be a 'non-member'
457
	 * Viewer needs to be at least Member of the Circle
458
	 *
459
	 * @param $circleId
460
	 * @param $userId
461
	 * @param $type
462
	 * @param bool $forceAll
463
	 *
464
	 * @return Member
465
	 * @throws CircleDoesNotExistException
466
	 * @throws ConfigNoCircleAvailableException
467
	 * @throws MemberDoesNotExistException
468
	 */
469
	public function getMember($circleId, $userId, $type, $forceAll = false) {
470
		if (!$forceAll) {
471
			$this->circlesRequest->getCircle($circleId, $this->userId)
472
								 ->getHigherViewer()
473
								 ->hasToBeMember();
474
		}
475
476
		$member = $this->membersRequest->forceGetMember($circleId, $userId, $type);
477
		$member->setNote('');
478
479
		return $member;
480
	}
481
482
483
	/**
484
	 * @param string $circleUniqueId
485
	 * @param string $name
486
	 * @param int $type
487
	 * @param int $level
488
	 *
489
	 * @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...
490
	 * @throws Exception
491
	 */
492
	public function levelMember($circleUniqueId, $name, $type, $instance, $level) {
493
494
		$level = (int)$level;
495
		try {
496
			$circle = $this->circlesRequest->getCircle($circleUniqueId, $this->userId);
497
			if ($circle->getType() === Circle::CIRCLES_PERSONAL) {
498
				throw new CircleTypeNotValidException(
499
					$this->l10n->t('You cannot edit level in a personal circle')
500
				);
501
			}
502
503
			$member = $this->membersRequest->forceGetMember($circle->getUniqueId(), $name, $type, $instance);
504
			if ($member->getLevel() !== $level) {
505
				$event = new GSevent(GSEvent::MEMBER_LEVEL);
506
				$event->setCircle($circle);
507
508
				$event->getData()
509
					  ->sInt('level', $level);
510
				$event->setMember($member);
511
				$this->gsUpstreamService->newEvent($event);
512
			}
513
514
			return $this->membersRequest->getMembers(
515
				$circle->getUniqueId(), $circle->getHigherViewer()
516
			);
517
		} catch (Exception $e) {
518
			throw $e;
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