Completed
Pull Request — master (#479)
by Maxence
02:08
created

CirclesService::settingsCircle()   B

Complexity

Conditions 7
Paths 8

Size

Total Lines 35

Duplication

Lines 0
Ratio 0 %

Importance

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