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