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