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

CirclesService::hasToBeAdmin()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 8

Duplication

Lines 8
Ratio 100 %

Importance

Changes 0
Metric Value
dl 8
loc 8
rs 10
c 0
b 0
f 0
cc 3
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->setViewer($circle->getViewer());
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->setViewer($circle->getViewer());
403
		$event->setMember($member);
404
		$this->gsUpstreamService->newEvent($event);
405
406
		return $member;
407
	}
408
409
410
	/**
411
	 * destroy a circle.
412
	 *
413
	 * @param string $circleUniqueId
414
	 *
415
	 * @throws CircleDoesNotExistException
416
	 * @throws MemberIsNotModeratorException
417
	 * @throws MemberIsNotOwnerException
418
	 */
419
	public function removeCircle($circleUniqueId) {
420
		$circle = $this->circlesRequest->getCircle($circleUniqueId, $this->userId);
421
422
		$this->hasToBeOwner($circle->getHigherViewer());
423
424
		$this->eventsService->onCircleDestruction($circle);
425
426
		$this->membersRequest->removeAllFromCircle($circleUniqueId);
427
		$this->circlesRequest->destroyCircle($circleUniqueId);
428
	}
429
430
431
	/**
432
	 * @param $circleName
433
	 *
434
	 * @return Circle|null
435
	 * @throws CircleDoesNotExistException
436
	 */
437
	public function infoCircleByName($circleName) {
438
		return $this->circlesRequest->forceGetCircleByName($circleName);
439
	}
440
441
442
	/**
443
	 * When a user is removed.
444
	 * Before deleting a user from the cloud, we assign a new owner to his Circles.
445
	 * Remove the Circle if it has no admin.
446
	 *
447
	 * @param string $userId
448
	 */
449
	public function onUserRemoved($userId) {
450
		$circles = $this->circlesRequest->getCircles($userId, 0, '', Member::LEVEL_OWNER);
451
452
		foreach ($circles as $circle) {
453
454
			$members =
455
				$this->membersRequest->forceGetMembers($circle->getUniqueId(), Member::LEVEL_ADMIN);
456
457
			if (sizeof($members) === 1) {
458
				$this->circlesRequest->destroyCircle($circle->getUniqueId());
459
				continue;
460
			}
461
462
			$this->switchOlderAdminToOwner($circle, $members);
463
		}
464
	}
465
466
467
	/**
468
	 * switchOlderAdminToOwner();
469
	 *
470
	 * @param Circle $circle
471
	 * @param Member[] $members
472
	 */
473
	private function switchOlderAdminToOwner(Circle $circle, $members) {
474
475
		foreach ($members as $member) {
476
			if ($member->getLevel() === Member::LEVEL_ADMIN) {
477
				$member->setLevel(Member::LEVEL_OWNER);
478
				$this->membersRequest->updateMember($member);
479
				$this->eventsService->onMemberOwner($circle, $member);
480
481
				return;
482
			}
483
		}
484
485
	}
486
487
488
	/**
489
	 * Convert a Type in String to its Bit Value
490
	 *
491
	 * @param string $type
492
	 *
493
	 * @return int|mixed
494
	 */
495
	public function convertTypeStringToBitValue($type) {
496
		$strings = [
497
			'personal' => Circle::CIRCLES_PERSONAL,
498
			'secret'   => Circle::CIRCLES_SECRET,
499
			'closed'   => Circle::CIRCLES_CLOSED,
500
			'public'   => Circle::CIRCLES_PUBLIC,
501
			'all'      => Circle::CIRCLES_ALL
502
		];
503
504
		if (!key_exists(strtolower($type), $strings)) {
505
			return $type;
506
		}
507
508
		return $strings[strtolower($type)];
509
	}
510
511
512
	/**
513
	 * getCircleIcon()
514
	 *
515
	 * Return the right imagePath for a type of circle.
516
	 *
517
	 * @param string $type
518
	 * @param bool $png
519
	 *
520
	 * @return string
521
	 */
522
	public static function getCircleIcon($type, $png = false) {
523
524
		$ext = '.svg';
525
		if ($png === true) {
526
			$ext = '.png';
527
		}
528
529
		$urlGen = OC::$server->getURLGenerator();
530
		switch ($type) {
531
			case Circle::CIRCLES_PERSONAL:
532
				return $urlGen->getAbsoluteURL(
533
					$urlGen->imagePath(Application::APP_NAME, 'personal' . $ext)
534
				);
535
			case Circle::CIRCLES_CLOSED:
536
				return $urlGen->getAbsoluteURL(
537
					$urlGen->imagePath(Application::APP_NAME, 'closed' . $ext)
538
				);
539
			case Circle::CIRCLES_SECRET:
540
				return $urlGen->getAbsoluteURL(
541
					$urlGen->imagePath(Application::APP_NAME, 'secret' . $ext)
542
				);
543
			case Circle::CIRCLES_PUBLIC:
544
				return $urlGen->getAbsoluteURL(
545
					$urlGen->imagePath(Application::APP_NAME, 'black_circle' . $ext)
546
				);
547
		}
548
549
		return $urlGen->getAbsoluteURL(
550
			$urlGen->imagePath(Application::APP_NAME, 'black_circle' . $ext)
551
		);
552
	}
553
554
555
	/**
556
	 * @param string $circleUniqueIds
557
	 * @param int $limit
558
	 * @param int $offset
559
	 *
560
	 * @return array
561
	 */
562
	public function getFilesForCircles($circleUniqueIds, $limit = -1, $offset = 0) {
563
		if (!is_array($circleUniqueIds)) {
564
			$circleUniqueIds = [$circleUniqueIds];
565
		}
566
567
		$objectIds = $this->circleProviderRequest->getFilesForCircles(
568
			$this->userId, $circleUniqueIds, $limit, $offset
569
		);
570
571
		return $objectIds;
572
	}
573
574
575
	/**
576
	 * @param Circle $circle
577
	 *
578
	 * @throws MembersLimitException
579
	 */
580
	public function checkThatCircleIsNotFull(Circle $circle) {
581
582
		$members = $this->membersRequest->forceGetMembers(
583
			$circle->getUniqueId(), Member::LEVEL_MEMBER, true
584
		);
585
586
		$limit = $circle->getSetting('members_limit');
587
		if ($limit === -1) {
588
			return;
589
		}
590
		if ($limit === 0 || $limit === '' || $limit === null) {
591
			$limit = $this->configService->getAppValue(ConfigService::CIRCLES_MEMBERS_LIMIT);
592
		}
593
594
		if (sizeof($members) >= $limit) {
595
			throw new MembersLimitException(
596
				'This circle already reach its limit on the number of members'
597
			);
598
		}
599
600
	}
601
602
	/**
603
	 * @return bool
604
	 */
605
	public function viewerIsAdmin() {
606
		if ($this->userId === '') {
607
			return false;
608
		}
609
610
		return ($this->groupManager->isAdmin($this->userId));
611
	}
612
613
614
	/**
615
	 * should be moved.
616
	 *
617
	 * @param Member $member
618
	 *
619
	 * @throws MemberIsNotOwnerException
620
	 */
621 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...
622
		if (!$this->groupManager->isAdmin($this->userId)
623
			&& $member->getLevel() < Member::LEVEL_OWNER) {
624
			throw new MemberIsNotOwnerException(
625
				$this->l10n->t('This member is not the owner of the circle')
626
			);
627
		}
628
	}
629
630
631
	/**
632
	 * should be moved.
633
	 *
634
	 * @param Member $member
635
	 *
636
	 * @throws MemberIsNotOwnerException
637
	 */
638 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...
639
		if (!$this->groupManager->isAdmin($member->getUserId())
640
			&& $member->getLevel() < Member::LEVEL_ADMIN) {
641
			throw new MemberIsNotOwnerException(
642
				$this->l10n->t('This member is not an admin of the circle')
643
			);
644
		}
645
	}
646
}
647