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

CirclesService::detailsCircleLinkedGroups()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 11
rs 9.9
cc 2
nc 2
nop 1
1
<?php
2
/**
3
 * Circles - Bring cloud-users closer together.
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
 * @author Vinicius Cubas Brand <[email protected]>
10
 * @author Daniel Tygel <[email protected]>
11
 *
12
 * @copyright 2017
13
 * @license GNU AGPL version 3 or any later version
14
 *
15
 * This program is free software: you can redistribute it and/or modify
16
 * it under the terms of the GNU Affero General Public License as
17
 * published by the Free Software Foundation, either version 3 of the
18
 * License, or (at your option) any later version.
19
 *
20
 * This program is distributed in the hope that it will be useful,
21
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
23
 * GNU Affero General Public License for more details.
24
 *
25
 * You should have received a copy of the GNU Affero General Public License
26
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
27
 *
28
 */
29
30
namespace OCA\Circles\Service;
31
32
33
use Exception;
34
use OC;
35
use OCA\Circles\AppInfo\Application;
36
use OCA\Circles\Db\CircleProviderRequest;
37
use OCA\Circles\Db\CirclesRequest;
38
use OCA\Circles\Db\FederatedLinksRequest;
39
use OCA\Circles\Db\MembersRequest;
40
use OCA\Circles\Db\SharesRequest;
41
use OCA\Circles\Exceptions\CircleAlreadyExistsException;
42
use OCA\Circles\Exceptions\CircleDoesNotExistException;
43
use OCA\Circles\Exceptions\CircleTypeDisabledException;
44
use OCA\Circles\Exceptions\ConfigNoCircleAvailableException;
45
use OCA\Circles\Exceptions\FederatedCircleNotAllowedException;
46
use OCA\Circles\Exceptions\MemberAlreadyExistsException;
47
use OCA\Circles\Exceptions\MemberDoesNotExistException;
48
use OCA\Circles\Exceptions\MemberIsNotModeratorException;
49
use OCA\Circles\Exceptions\MemberIsNotOwnerException;
50
use OCA\Circles\Exceptions\MembersLimitException;
51
use OCA\Circles\Model\Circle;
52
use OCA\Circles\Model\GlobalScale\GSEvent;
53
use OCA\Circles\Model\Member;
54
use OCP\IGroupManager;
55
use OCP\IL10N;
56
57
class CirclesService {
58
59
	/** @var string */
60
	private $userId;
61
62
	/** @var IL10N */
63
	private $l10n;
64
65
	/** @var IGroupManager */
66
	private $groupManager;
67
68
	/** @var ConfigService */
69
	private $configService;
70
71
	/** @var CirclesRequest */
72
	private $circlesRequest;
73
74
	/** @var MembersRequest */
75
	private $membersRequest;
76
77
	/** @var SharesRequest */
78
	private $sharesRequest;
79
80
	/** @var FederatedLinksRequest */
81
	private $federatedLinksRequest;
82
83
	/** @var GSUpstreamService */
84
	private $gsUpstreamService;
85
86
	/** @var EventsService */
87
	private $eventsService;
88
89
	/** @var CircleProviderRequest */
90
	private $circleProviderRequest;
91
92
	/** @var MiscService */
93
	private $miscService;
94
95
96
	/**
97
	 * CirclesService constructor.
98
	 *
99
	 * @param string $userId
100
	 * @param IL10N $l10n
101
	 * @param IGroupManager $groupManager
102
	 * @param ConfigService $configService
103
	 * @param CirclesRequest $circlesRequest
104
	 * @param MembersRequest $membersRequest
105
	 * @param SharesRequest $sharesRequest
106
	 * @param FederatedLinksRequest $federatedLinksRequest
107
	 * @param GSUpstreamService $gsUpstreamService
108
	 * @param EventsService $eventsService
109
	 * @param CircleProviderRequest $circleProviderRequest
110
	 * @param MiscService $miscService
111
	 */
112 View Code Duplication
	public function __construct(
113
		$userId,
114
		IL10N $l10n,
115
		IGroupManager $groupManager,
116
		ConfigService $configService,
117
		CirclesRequest $circlesRequest,
118
		MembersRequest $membersRequest,
119
		SharesRequest $sharesRequest,
120
		FederatedLinksRequest $federatedLinksRequest,
121
		GSUpstreamService $gsUpstreamService,
122
		EventsService $eventsService,
123
		CircleProviderRequest $circleProviderRequest,
124
		MiscService $miscService
125
	) {
126
		$this->userId = $userId;
127
		$this->l10n = $l10n;
128
		$this->groupManager = $groupManager;
129
		$this->configService = $configService;
130
		$this->circlesRequest = $circlesRequest;
131
		$this->membersRequest = $membersRequest;
132
		$this->sharesRequest = $sharesRequest;
133
		$this->federatedLinksRequest = $federatedLinksRequest;
134
		$this->gsUpstreamService = $gsUpstreamService;
135
		$this->eventsService = $eventsService;
136
		$this->circleProviderRequest = $circleProviderRequest;
137
		$this->miscService = $miscService;
138
	}
139
140
141
	/**
142
	 * Create circle using this->userId as owner
143
	 *
144
	 * @param int|string $type
145
	 * @param string $name
146
	 *
147
	 * @param string $ownerId
148
	 *
149
	 * @return Circle
150
	 * @throws CircleAlreadyExistsException
151
	 * @throws CircleTypeDisabledException
152
	 * @throws MemberAlreadyExistsException
153
	 * @throws Exception
154
	 */
155
	public function createCircle($type, $name, string $ownerId = '') {
156
		$type = $this->convertTypeStringToBitValue($type);
157
		$type = (int)$type;
158
159
		if ($type === '') {
0 ignored issues
show
Unused Code Bug introduced by
The strict comparison === seems to always evaluate to false as the types of $type (integer) and '' (string) can never be identical. Maybe you want to use a loose comparison == instead?
Loading history...
160
			throw new CircleTypeDisabledException(
161
				$this->l10n->t('You need a specify a type of circle')
162
			);
163
		}
164
165
		if (!$this->configService->isCircleAllowed($type)) {
166
			throw new CircleTypeDisabledException(
167
				$this->l10n->t('You cannot create this type of circle')
168
			);
169
		}
170
171
		$circle = new Circle($type, $name);
172
173
		if ($ownerId === '') {
174
			$ownerId = $this->userId;
175
		}
176
177
//		try {
178
//			$this->circlesRequest->createCircle($circle, $ownerId);
179
//			$this->membersRequest->createMember($circle->getOwner());
180
//		} catch (CircleAlreadyExistsException $e) {
181
//			throw $e;
182
//		}
183
184
		$circle->generateUniqueId();
185
		// TODO: check uniqueness !!
186
		// TODO: check uniqueness !!
187
		// TODO: check uniqueness !!
188
		// TODO: check uniqueness !!
189
190
		$owner = new Member($ownerId, Member::TYPE_USER);
191
		$owner->setCircleId($circle->getUniqueId())
192
			  ->setLevel(Member::LEVEL_OWNER)
193
			  ->setStatus(Member::STATUS_MEMBER);
194
		$circle->setOwner($owner)
195
			   ->setViewer($owner);
196
197
//			$this->membersRequest->createMember($circle->getOwner());
198
199
		$event = new GSevent(GSEvent::CIRCLE_CREATE, true);
200
		$event->setCircle($circle);
201
		$this->gsUpstreamService->newEvent($event);
202
203
//		} catch (CircleAlreadyExistsException $e) {
204
//			throw $e;
205
//		}
206
207
//		$this->eventsService->onCircleCreation($circle);
208
209
		return $circle;
210
	}
211
212
213
	/**
214
	 * list Circles depends on type (or all) and name (parts) and minimum level.
215
	 *
216
	 * @param string $userId
217
	 * @param mixed $type
218
	 * @param string $name
219
	 * @param int $level
220
	 *
221
	 * @param bool $forceAll
222
	 *
223
	 * @return Circle[]
224
	 * @throws CircleTypeDisabledException
225
	 * @throws Exception
226
	 */
227
	public function listCircles($userId, $type, $name = '', $level = 0, $forceAll = false) {
228
		$type = $this->convertTypeStringToBitValue($type);
229
230
		if ($userId === '') {
231
			throw new Exception('UserID cannot be null');
232
		}
233
234
		if (!$this->configService->isCircleAllowed((int)$type)) {
235
			throw new CircleTypeDisabledException(
236
				$this->l10n->t('You cannot display this type of circle')
237
			);
238
		}
239
240
		$data = [];
241
		$result = $this->circlesRequest->getCircles($userId, $type, $name, $level, $forceAll);
242
		foreach ($result as $item) {
243
			$data[] = $item;
244
		}
245
246
		return $data;
247
	}
248
249
250
	/**
251
	 * returns details on circle and its members if this->userId is a member itself.
252
	 *
253
	 * @param string $circleUniqueId
254
	 * @param bool $forceAll
255
	 *
256
	 * @return Circle
257
	 * @throws Exception
258
	 */
259
	public function detailsCircle($circleUniqueId, $forceAll = false) {
260
261
		try {
262
			$circle = $this->circlesRequest->getCircle($circleUniqueId, $this->userId, '', $forceAll);
263
			if ($this->viewerIsAdmin()
264
				|| $circle->getHigherViewer()
265
						  ->isLevel(Member::LEVEL_MEMBER)
266
				|| $forceAll === true
267
			) {
268
				$this->detailsCircleMembers($circle);
269
				$this->detailsCircleLinkedGroups($circle);
270
				$this->detailsCircleFederatedCircles($circle);
271
			}
272
		} catch (Exception $e) {
273
			throw $e;
274
		}
275
276
		return $circle;
277
	}
278
279
280
	/**
281
	 * get the Members list and add the result to the Circle.
282
	 *
283
	 * @param Circle $circle
284
	 *
285
	 * @throws Exception
286
	 */
287
	private function detailsCircleMembers(Circle &$circle) {
288
		if ($this->viewerIsAdmin()) {
289
			$members = $this->membersRequest->forceGetMembers($circle->getUniqueId(), 0);
290
		} else {
291
			$members = $this->membersRequest->getMembers(
292
				$circle->getUniqueId(), $circle->getHigherViewer()
293
			);
294
		}
295
296
		$circle->setMembers($members);
297
	}
298
299
300
	/**
301
	 * get the Linked Group list and add the result to the Circle.
302
	 *
303
	 * @param Circle $circle
304
	 *
305
	 * @throws MemberDoesNotExistException
306
	 */
307
	private function detailsCircleLinkedGroups(Circle &$circle) {
308
		$groups = [];
309
		if ($this->configService->isLinkedGroupsAllowed()) {
310
			$groups =
311
				$this->membersRequest->getGroupsFromCircle(
312
					$circle->getUniqueId(), $circle->getHigherViewer()
313
				);
314
		}
315
316
		$circle->setGroups($groups);
317
	}
318
319
320
	/**
321
	 * get the Federated Circles list and add the result to the Circle.
322
	 *
323
	 * @param Circle $circle
324
	 */
325
	private function detailsCircleFederatedCircles(Circle &$circle) {
326
		$links = [];
327
328
		try {
329
			if ($this->configService->isFederatedCirclesAllowed()) {
330
				$circle->hasToBeFederated();
331
				$links = $this->federatedLinksRequest->getLinksFromCircle($circle->getUniqueId());
332
			}
333
		} catch (FederatedCircleNotAllowedException $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
334
		}
335
336
		$circle->setLinks($links);
337
	}
338
339
340
	/**
341
	 * save new settings if current user is admin.
342
	 *
343
	 * @param string $circleUniqueId
344
	 * @param array $settings
345
	 *
346
	 * @return Circle
347
	 * @throws Exception
348
	 */
349
	public function settingsCircle($circleUniqueId, $settings) {
350
351
		try {
352
			$circle = $this->circlesRequest->getCircle($circleUniqueId, $this->userId);
353
			$this->hasToBeOwner($circle->getHigherViewer());
354
355
			if (!$this->viewerIsAdmin()) {
356
				$settings['members_limit'] = $circle->getSetting('members_limit');
357
			}
358
359
			$ak = array_keys($settings);
360
			foreach ($ak AS $k) {
361
				$circle->setSetting($k, $settings[$k]);
362
			}
363
364
			$this->circlesRequest->updateCircle($circle, $this->userId);
365
366
			$this->eventsService->onSettingsChange($circle);
367
		} catch (Exception $e) {
368
			throw $e;
369
		}
370
371
		return $circle;
372
	}
373
374
375
	/**
376
	 * Join a circle.
377
	 *
378
	 * @param string $circleUniqueId
379
	 *
380
	 * @return null|Member
381
	 * @throws Exception
382
	 */
383
	public function joinCircle($circleUniqueId) {
384
		try {
385
			$circle = $this->circlesRequest->getCircle($circleUniqueId, $this->userId);
386
			$member = $this->membersRequest->getFreshNewMember(
387
				$circleUniqueId, $this->userId, Member::TYPE_USER, ''
388
			);
389
390
			$event = new GSevent(GSEvent::MEMBER_JOIN);
391
			$event->setCircle($circle);
392
			$event->setMember($member);
393
			$this->gsUpstreamService->newEvent($event);
394
		} catch (Exception $e) {
395
			throw $e;
396
		}
397
398
		return $member;
399
	}
400
401
402
	/**
403
	 * Leave a circle.
404
	 *
405
	 * @param string $circleUniqueId
406
	 *
407
	 * @return null|Member
408
	 * @throws Exception
409
	 */
410
	public function leaveCircle($circleUniqueId) {
411
		$circle = $this->circlesRequest->getCircle($circleUniqueId, $this->userId);
412
		$member = $circle->getViewer();
413
414
		$event = new GSevent(GSEvent::MEMBER_LEAVE);
415
		$event->setCircle($circle);
416
		$event->setMember($member);
417
		$this->gsUpstreamService->newEvent($event);
418
419
		return $member;
420
	}
421
422
423
	/**
424
	 * destroy a circle.
425
	 *
426
	 * @param string $circleUniqueId
427
	 *
428
	 * @param bool $force
429
	 *
430
	 * @throws CircleDoesNotExistException
431
	 * @throws MemberIsNotOwnerException
432
	 * @throws ConfigNoCircleAvailableException
433
	 */
434
	public function removeCircle($circleUniqueId, bool $force = false) {
435
436 View Code Duplication
		if ($force) {
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...
437
			$circle = $this->circlesRequest->forceGetCircle($circleUniqueId);
438
		} else {
439
			$circle = $this->circlesRequest->getCircle($circleUniqueId, $this->userId);
440
			$this->hasToBeOwner($circle->getHigherViewer());
441
		}
442
443
		$this->eventsService->onCircleDestruction($circle);
444
445
		$this->membersRequest->removeAllFromCircle($circleUniqueId);
446
		$this->circlesRequest->destroyCircle($circleUniqueId);
447
	}
448
449
450
	/**
451
	 * @param $circleName
452
	 *
453
	 * @return Circle|null
454
	 * @throws CircleDoesNotExistException
455
	 */
456
	public function infoCircleByName($circleName) {
457
		return $this->circlesRequest->forceGetCircleByName($circleName);
458
	}
459
460
461
	/**
462
	 * When a user is removed.
463
	 * Before deleting a user from the cloud, we assign a new owner to his Circles.
464
	 * Remove the Circle if it has no admin.
465
	 *
466
	 * @param string $userId
467
	 */
468
	public function onUserRemoved($userId) {
469
		$circles = $this->circlesRequest->getCircles($userId, 0, '', Member::LEVEL_OWNER);
470
471
		foreach ($circles as $circle) {
472
473
			$members =
474
				$this->membersRequest->forceGetMembers($circle->getUniqueId(), Member::LEVEL_ADMIN);
475
476
			if (sizeof($members) === 1) {
477
				$this->circlesRequest->destroyCircle($circle->getUniqueId());
478
				continue;
479
			}
480
481
			$this->switchOlderAdminToOwner($circle, $members);
482
		}
483
	}
484
485
486
	/**
487
	 * switchOlderAdminToOwner();
488
	 *
489
	 * @param Circle $circle
490
	 * @param Member[] $members
491
	 */
492
	private function switchOlderAdminToOwner(Circle $circle, $members) {
493
494
		foreach ($members as $member) {
495
			if ($member->getLevel() === Member::LEVEL_ADMIN) {
496
				$member->setLevel(Member::LEVEL_OWNER);
497
				$this->membersRequest->updateMember($member);
498
				$this->eventsService->onMemberOwner($circle, $member);
499
500
				return;
501
			}
502
		}
503
504
	}
505
506
507
	/**
508
	 * Convert a Type in String to its Bit Value
509
	 *
510
	 * @param string $type
511
	 *
512
	 * @return int|mixed
513
	 */
514
	public function convertTypeStringToBitValue($type) {
515
		$strings = [
516
			'personal' => Circle::CIRCLES_PERSONAL,
517
			'secret'   => Circle::CIRCLES_SECRET,
518
			'closed'   => Circle::CIRCLES_CLOSED,
519
			'public'   => Circle::CIRCLES_PUBLIC,
520
			'all'      => Circle::CIRCLES_ALL
521
		];
522
523
		if (!key_exists(strtolower($type), $strings)) {
524
			return $type;
525
		}
526
527
		return $strings[strtolower($type)];
528
	}
529
530
531
	/**
532
	 * getCircleIcon()
533
	 *
534
	 * Return the right imagePath for a type of circle.
535
	 *
536
	 * @param string $type
537
	 * @param bool $png
538
	 *
539
	 * @return string
540
	 */
541
	public static function getCircleIcon($type, $png = false) {
542
543
		$ext = '.svg';
544
		if ($png === true) {
545
			$ext = '.png';
546
		}
547
548
		$urlGen = OC::$server->getURLGenerator();
549
		switch ($type) {
550
			case Circle::CIRCLES_PERSONAL:
551
				return $urlGen->getAbsoluteURL(
552
					$urlGen->imagePath(Application::APP_NAME, 'personal' . $ext)
553
				);
554
			case Circle::CIRCLES_CLOSED:
555
				return $urlGen->getAbsoluteURL(
556
					$urlGen->imagePath(Application::APP_NAME, 'closed' . $ext)
557
				);
558
			case Circle::CIRCLES_SECRET:
559
				return $urlGen->getAbsoluteURL(
560
					$urlGen->imagePath(Application::APP_NAME, 'secret' . $ext)
561
				);
562
			case Circle::CIRCLES_PUBLIC:
563
				return $urlGen->getAbsoluteURL(
564
					$urlGen->imagePath(Application::APP_NAME, 'black_circle' . $ext)
565
				);
566
		}
567
568
		return $urlGen->getAbsoluteURL(
569
			$urlGen->imagePath(Application::APP_NAME, 'black_circle' . $ext)
570
		);
571
	}
572
573
574
	/**
575
	 * @param string $circleUniqueIds
576
	 * @param int $limit
577
	 * @param int $offset
578
	 *
579
	 * @return array
580
	 */
581
	public function getFilesForCircles($circleUniqueIds, $limit = -1, $offset = 0) {
582
		if (!is_array($circleUniqueIds)) {
583
			$circleUniqueIds = [$circleUniqueIds];
584
		}
585
586
		$objectIds = $this->circleProviderRequest->getFilesForCircles(
587
			$this->userId, $circleUniqueIds, $limit, $offset
588
		);
589
590
		return $objectIds;
591
	}
592
593
594
	/**
595
	 * @param Circle $circle
596
	 *
597
	 * @throws MembersLimitException
598
	 */
599
	public function checkThatCircleIsNotFull(Circle $circle) {
600
601
		$members = $this->membersRequest->forceGetMembers(
602
			$circle->getUniqueId(), Member::LEVEL_MEMBER, true
603
		);
604
605
		$limit = $circle->getSetting('members_limit');
606
		if ($limit === -1) {
607
			return;
608
		}
609
		if ($limit === 0 || $limit === '' || $limit === null) {
610
			$limit = $this->configService->getAppValue(ConfigService::CIRCLES_MEMBERS_LIMIT);
611
		}
612
613
		if (sizeof($members) >= $limit) {
614
			throw new MembersLimitException(
615
				'This circle already reach its limit on the number of members'
616
			);
617
		}
618
619
	}
620
621
	/**
622
	 * @return bool
623
	 */
624
	public function viewerIsAdmin() {
625
		if ($this->userId === '') {
626
			return false;
627
		}
628
629
		return ($this->groupManager->isAdmin($this->userId));
630
	}
631
632
633
	/**
634
	 * should be moved.
635
	 *
636
	 * @param Member $member
637
	 *
638
	 * @throws MemberIsNotOwnerException
639
	 */
640 View Code Duplication
	public function hasToBeOwner(Member $member) {
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...
641
		if (!$this->groupManager->isAdmin($this->userId)
642
			&& $member->getLevel() < Member::LEVEL_OWNER) {
643
			throw new MemberIsNotOwnerException(
644
				$this->l10n->t('This member is not the owner of the circle')
645
			);
646
		}
647
	}
648
649
650
	/**
651
	 * should be moved.
652
	 *
653
	 * @param Member $member
654
	 *
655
	 * @throws MemberIsNotOwnerException
656
	 */
657 View Code Duplication
	public function hasToBeAdmin(Member $member) {
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...
658
		if (!$this->groupManager->isAdmin($member->getUserId())
659
			&& $member->getLevel() < Member::LEVEL_ADMIN) {
660
			throw new MemberIsNotOwnerException(
661
				$this->l10n->t('This member is not an admin of the circle')
662
			);
663
		}
664
	}
665
}
666