Completed
Push — master ( 2493d7...51beeb )
by Maxence
03:12 queued 12s
created

MembershipService::manageAll()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 22

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 22
rs 9.568
c 0
b 0
f 0
cc 2
nc 2
nop 0
1
<?php
2
3
declare(strict_types=1);
4
5
6
/**
7
 * Circles - Bring cloud-users closer together.
8
 *
9
 * This file is licensed under the Affero General Public License version 3 or
10
 * later. See the COPYING file.
11
 *
12
 * @author Maxence Lange <[email protected]>
13
 * @copyright 2021
14
 * @license GNU AGPL version 3 or any later version
15
 *
16
 * This program is free software: you can redistribute it and/or modify
17
 * it under the terms of the GNU Affero General Public License as
18
 * published by the Free Software Foundation, either version 3 of the
19
 * License, or (at your option) any later version.
20
 *
21
 * This program is distributed in the hope that it will be useful,
22
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
23
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
24
 * GNU Affero General Public License for more details.
25
 *
26
 * You should have received a copy of the GNU Affero General Public License
27
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
28
 *
29
 */
30
31
32
namespace OCA\Circles\Service;
33
34
35
use ArtificialOwl\MySmallPhpTools\Exceptions\ItemNotFoundException;
36
use ArtificialOwl\MySmallPhpTools\Model\SimpleDataStore;
37
use ArtificialOwl\MySmallPhpTools\Traits\Nextcloud\nc22\TNC22Logger;
38
use OCA\Circles\Db\CircleRequest;
39
use OCA\Circles\Db\MemberRequest;
40
use OCA\Circles\Db\MembershipRequest;
41
use OCA\Circles\Exceptions\FederatedUserNotFoundException;
42
use OCA\Circles\Exceptions\MembershipNotFoundException;
43
use OCA\Circles\Exceptions\OwnerNotFoundException;
44
use OCA\Circles\Exceptions\RequestBuilderException;
45
use OCA\Circles\Model\FederatedUser;
46
use OCA\Circles\Model\Member;
47
use OCA\Circles\Model\Membership;
48
49
50
/**
51
 * Class MembershipService
52
 *
53
 * @package OCA\Circles\Service
54
 */
55
class MembershipService {
56
57
58
	use TNC22Logger;
59
60
61
	/** @var MembershipRequest */
62
	private $membershipRequest;
63
64
	/** @var CircleRequest */
65
	private $circleRequest;
66
67
	/** @var MemberRequest */
68
	private $memberRequest;
69
70
	/** @var EventService */
71
	private $eventService;
72
73
	/** @var OutputService */
74
	private $outputService;
75
76
77
	/**
78
	 * MembershipService constructor.
79
	 *
80
	 * @param MembershipRequest $membershipRequest
81
	 * @param CircleRequest $circleRequest
82
	 * @param MemberRequest $memberRequest
83
	 * @param EventService $eventService
84
	 * @param OutputService $outputService
85
	 */
86
	public function __construct(
87
		MembershipRequest $membershipRequest,
88
		CircleRequest $circleRequest,
89
		MemberRequest $memberRequest,
90
		EventService $eventService,
91
		OutputService $outputService
92
	) {
93
		$this->membershipRequest = $membershipRequest;
94
		$this->circleRequest = $circleRequest;
95
		$this->memberRequest = $memberRequest;
96
		$this->eventService = $eventService;
97
		$this->outputService = $outputService;
98
	}
99
100
101
	/**
102
	 * @param string $singleId
103
	 *
104
	 * @throws RequestBuilderException
105
	 */
106
	public function onUpdate(string $singleId): void {
107
		if ($singleId === '') {
108
			return;
109
		}
110
111
		try {
112
			$this->circleRequest->getFederatedUserBySingleId($singleId);
113
		} catch (FederatedUserNotFoundException | OwnerNotFoundException $e) {
114
			$this->membershipRequest->removeBySingleId($singleId);
115
		}
116
117
		$children = array_unique(
118
			array_merge(
119
				[$singleId],
120
				$this->getChildrenMembers($singleId),
121
				$this->getChildrenMemberships($singleId)
122
			)
123
		);
124
125
		foreach ($children as $singleId) {
126
			$this->manageMemberships($singleId);
127
		}
128
	}
129
130
131
	/**
132
	 *
133
	 */
134
	public function manageAll(): void {
135
		$params = new SimpleDataStore(['includeSystemCircles' => true]);
136
		$circles = $this->circleRequest->getCircles(
137
			null,
138
			null,
139
			null,
140
			null,
141
			$params
142
		);
143
144
		$this->outputService->startMigrationProgress(sizeof($circles));
145
146
		foreach ($circles as $circle) {
147
			$this->outputService->output(
148
				'Caching memberships for \'' . $circle->getDisplayName() . '\'',
149
				true
150
			);
151
			$this->manageMemberships($circle->getSingleId());
152
		}
153
154
		$this->outputService->finishMigrationProgress();
155
	}
156
157
158
	/**
159
	 * @param string $singleId
160
	 *
161
	 * @return int
162
	 */
163
	public function manageMemberships(string $singleId): int {
164
		$memberships = $this->generateMemberships($singleId);
165
166
		return $this->updateMembershipsDatabase($singleId, $memberships);
167
	}
168
169
170
	/**
171
	 * @param string $circleId
172
	 * @param string $singleId
173
	 *
174
	 * @return Membership
175
	 * @throws MembershipNotFoundException
176
	 * @throws RequestBuilderException
177
	 */
178
	public function getMembership(string $circleId, string $singleId): Membership {
179
		$membership = $this->membershipRequest->getMembership($circleId, $singleId);
180
		$details = $this->circleRequest->getCirclesByIds($membership->getInheritancePath());
181
		$membership->setInheritanceDetails($details);
182
183
		return $membership;
184
	}
185
186
187
	/**
188
	 * @param string $singleId
189
	 * @param bool $all
190
	 */
191
	public function resetMemberships(string $singleId = '', bool $all = false) {
192
		$this->membershipRequest->removeBySingleId($singleId, $all);
193
	}
194
195
196
	/**
197
	 * @param FederatedUser $federatedUser
198
	 */
199
	public function deleteFederatedUser(FederatedUser $federatedUser) {
200
		$this->membershipRequest->deleteFederatedUser($federatedUser);
201
	}
202
203
204
	/**
205
	 * @param string $singleId
206
	 * @param string $circleId
207
	 * @param array $memberships
208
	 * @param array $knownIds
209
	 * @param array $path
210
	 *
211
	 * @return array
212
	 * @throws RequestBuilderException
213
	 */
214
	private function generateMemberships(
215
		string $singleId,
216
		string $circleId = '',
217
		array &$memberships = [],
218
		array $knownIds = [],
219
		array $path = []
220
	): array {
221
		$circleId = ($circleId === '') ? $singleId : $circleId;
222
		$path[] = $circleId;
223
		$knownIds[] = $circleId;
224
225
		$members = $this->memberRequest->getMembersBySingleId($circleId);
226
		foreach ($members as $member) {
227
			if ($member->getLevel() < Member::LEVEL_MEMBER) {
228
				continue;
229
			}
230
231
			$membership = new Membership($singleId, count($path) > 1 ? $path[1] : '', $member);
232
			$membership->setInheritancePath(array_reverse($path))
233
					   ->setInheritanceDepth(sizeof($path));
234
			$this->fillMemberships($membership, $memberships);
235
			if (!in_array($member->getCircleId(), $knownIds)) {
236
				$this->generateMemberships(
237
					$singleId,
238
					$member->getCircleId(),
239
					$memberships,
240
					$knownIds,
241
					$path
242
				);
243
			}
244
		}
245
246
		return $memberships;
247
	}
248
249
250
	/**
251
	 * @param string $singleId
252
	 * @param Membership[] $memberships
253
	 *
254
	 * @return int
255
	 */
256
	private function updateMembershipsDatabase(string $singleId, array $memberships): int {
257
		$known = $this->membershipRequest->getMemberships($singleId);
258
259
		$deprecated = $this->removeDeprecatedMemberships($memberships, $known);
260
		if (!empty($deprecated)) {
261
			$this->eventService->membershipsRemoved($deprecated);
262
		}
263
264
		$new = $this->createNewMemberships($memberships, $known);
265
		if (!empty($new)) {
266
			$this->eventService->membershipsCreated($new);
267
		}
268
269
		return count($deprecated) + count($new);
270
	}
271
272
273
	/**
274
	 * @param string $id
275
	 * @param array $knownIds
276
	 *
277
	 * @return array
278
	 * @throws RequestBuilderException
279
	 */
280
	private function getChildrenMembers(string $id, array &$knownIds = []): array {
281
		$singleIds = array_map(
282
			function(Member $item): string {
283
				return $item->getSingleId();
284
			}, $this->memberRequest->getMembers($id)
285
		);
286
287
		foreach ($singleIds as $singleId) {
288
			if ($singleId !== $id && !in_array($singleId, $knownIds)) {
289
				$knownIds[] = $singleId;
290
				$singleIds = array_merge($singleIds, $this->getChildrenMembers($singleId, $knownIds));
291
			}
292
		}
293
294
		return array_unique($singleIds);
295
	}
296
297
298
	/**
299
	 * @param string $id
300
	 * @param array $knownIds
301
	 *
302
	 * @return array
303
	 */
304
	private function getChildrenMemberships(string $id, array &$knownIds = []): array {
305
		$singleIds = array_map(
306
			function(Membership $item): string {
307
				return $item->getSingleId();
308
			}, $this->membershipRequest->getInherited($id)
309
		);
310
311
		foreach ($singleIds as $singleId) {
312
			if ($singleId !== $id && !in_array($singleId, $knownIds)) {
313
				$knownIds[] = $singleId;
314
				$singleIds = array_merge($singleIds, $this->getChildrenMemberships($singleId, $knownIds));
315
			}
316
		}
317
318
		return array_unique($singleIds);
319
	}
320
321
322
	/**
323
	 * @param Membership $membership
324
	 * @param Membership[] $memberships
325
	 */
326
	private function fillMemberships(Membership $membership, array &$memberships) {
327
		foreach ($memberships as $known) {
328
			if ($known->getCircleId() === $membership->getCircleId()) {
329
				if ($known->getLevel() < $membership->getLevel()) {
330
					$known->setLevel($membership->getLevel());
331
//					$known->setMemberId($membership->getMemberId());
332
					$known->setSingleId($membership->getSingleId());
333
					$known->setInheritanceLast($membership->getInheritanceLast());
334
				}
335
336
				return;
337
			}
338
		}
339
340
		$memberships[$membership->getCircleId()] = $membership;
341
	}
342
343
344
	/**
345
	 * @param Membership[] $memberships
346
	 * @param Membership[] $known
347
	 *
348
	 * @return Membership[]
349
	 */
350
	private function removeDeprecatedMemberships(array $memberships, array $known): array {
351
		$circleIds = array_map(
352
			function(Membership $membership): string {
353
				return $membership->getCircleId();
354
			}, $memberships
355
		);
356
357
		$deprecated = [];
358
		foreach ($known as $item) {
359
			if (!in_array($item->getCircleId(), $circleIds)) {
360
				$deprecated[] = $item;
361
				$this->membershipRequest->delete($item);
362
			}
363
		}
364
365
		return $deprecated;
366
	}
367
368
369
	/**
370
	 * @param Membership[] $memberships
371
	 * @param Membership[] $known
372
	 *
373
	 * @return Membership[]
374
	 */
375
	private function createNewMemberships(array $memberships, array $known): array {
376
		$new = [];
377
		foreach ($memberships as $membership) {
378
			try {
379
				$item = $this->getMembershipsFromList($known, $membership->getCircleId());
380
				if ($item->getLevel() !== $membership->getLevel()) {
381
					$this->membershipRequest->update($membership);
382
					$new[] = $item;
383
				}
384
			} catch (ItemNotFoundException $e) {
385
				$this->membershipRequest->insert($membership);
386
				$new[] = $membership;
387
			}
388
		}
389
390
		return $new;
391
	}
392
393
394
	/**
395
	 * @param Membership[] $list
396
	 * @param string $circleId
397
	 *
398
	 * @return Membership
399
	 * @throws ItemNotFoundException
400
	 */
401
	private function getMembershipsFromList(array $list, string $circleId): Membership {
402
		foreach ($list as $item) {
403
			if ($item->getCircleId() === $circleId) {
404
				return $item;
405
			}
406
		}
407
408
		throw new ItemNotFoundException();
409
	}
410
411
}
412
413