Completed
Push — master ( bd517f...a37793 )
by Maxence
02:29
created

CirclesService::detailsCircle()   A

Complexity

Conditions 4
Paths 7

Size

Total Lines 18

Duplication

Lines 0
Ratio 0 %

Importance

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