Completed
Push — master ( b4b3e8...fb5007 )
by Maxence
02:30 queued 42s
created

CirclesService::viewerIsAdmin()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

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