Completed
Pull Request — master (#362)
by Maxence
02:54
created

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