Completed
Push — master ( 1f8246...effc67 )
by Maxence
02:37
created

MembershipService::generateMemberships()   B

Complexity

Conditions 6
Paths 8

Size

Total Lines 34

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 34
rs 8.7537
c 0
b 0
f 0
cc 6
nc 8
nop 5
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 daita\MySmallPhpTools\Exceptions\ItemNotFoundException;
36
use daita\MySmallPhpTools\Traits\Nextcloud\nc22\TNC22Logger;
37
use OCA\Circles\Db\CircleRequest;
38
use OCA\Circles\Db\MemberRequest;
39
use OCA\Circles\Db\MembershipRequest;
40
use OCA\Circles\Exceptions\CircleNotFoundException;
41
use OCA\Circles\Exceptions\OwnerNotFoundException;
42
use OCA\Circles\Exceptions\RequestBuilderException;
43
use OCA\Circles\Model\FederatedUser;
44
use OCA\Circles\Model\Member;
45
use OCA\Circles\Model\Membership;
46
47
48
/**
49
 * Class MembershipService
50
 *
51
 * @package OCA\Circles\Service
52
 */
53
class MembershipService {
54
55
56
	use TNC22Logger;
57
58
59
	/** @var MembershipRequest */
60
	private $membershipRequest;
61
62
	/** @var CircleRequest */
63
	private $circleRequest;
64
65
	/** @var MemberRequest */
66
	private $memberRequest;
67
68
	/** @var EventService */
69
	private $eventService;
70
71
72
	/**
73
	 * MembershipService constructor.
74
	 *
75
	 * @param MembershipRequest $membershipRequest
76
	 * @param CircleRequest $circleRequest
77
	 * @param MemberRequest $memberRequest
78
	 * @param EventService $eventService
79
	 */
80
	public function __construct(
81
		MembershipRequest $membershipRequest,
82
		CircleRequest $circleRequest,
83
		MemberRequest $memberRequest,
84
		EventService $eventService
85
	) {
86
		$this->membershipRequest = $membershipRequest;
87
		$this->circleRequest = $circleRequest;
88
		$this->memberRequest = $memberRequest;
89
		$this->eventService = $eventService;
90
	}
91
92
93
	/**
94
	 * @param string $singleId
95
	 *
96
	 * @throws RequestBuilderException
97
	 */
98
	public function onUpdate(string $singleId): void {
99
		if ($singleId === '') {
100
			return;
101
		}
102
103
		try {
104
			$this->circleRequest->getFederatedUserBySingleId($singleId);
105
		} catch (CircleNotFoundException | OwnerNotFoundException $e) {
106
			$this->membershipRequest->removeBySingleId($singleId);
107
		}
108
109
		$children = array_unique(
110
			array_merge(
111
				[$singleId],
112
				$this->getChildrenMembers($singleId),
113
				$this->getChildrenMemberships($singleId)
114
			)
115
		);
116
117
		foreach ($children as $singleId) {
118
			$this->manageMemberships($singleId);
119
		}
120
	}
121
122
123
	/**
124
	 * @param string $singleId
125
	 *
126
	 * @return int
127
	 */
128
	public function manageMemberships(string $singleId): int {
129
		$memberships = $this->generateMemberships($singleId);
130
131
		return $this->updateMembershipsDatabase($singleId, $memberships);
132
	}
133
134
135
	/**
136
	 * @param string $id
137
	 * @param array $knownIds
138
	 *
139
	 * @return array
140
	 * @throws RequestBuilderException
141
	 */
142
	private function getChildrenMembers(string $id, array &$knownIds = []): array {
143
		$singleIds = array_map(
144
			function(Member $item): string {
145
				return $item->getSingleId();
146
			}, $this->memberRequest->getMembers($id)
147
		);
148
149
		foreach ($singleIds as $singleId) {
150
			if ($singleId !== $id && !in_array($singleId, $knownIds)) {
151
				$knownIds[] = $singleId;
152
				$singleIds = array_merge($singleIds, $this->getChildrenMembers($singleId, $knownIds));
153
			}
154
		}
155
156
		return array_unique($singleIds);
157
	}
158
159
160
	/**
161
	 * @param string $id
162
	 * @param array $knownIds
163
	 *
164
	 * @return array
165
	 */
166
	private function getChildrenMemberships(string $id, array &$knownIds = []): array {
167
		$singleIds = array_map(
168
			function(Membership $item): string {
169
				return $item->getSingleId();
170
			}, $this->membershipRequest->getChildren($id)
171
		);
172
173
		foreach ($singleIds as $singleId) {
174
			if ($singleId !== $id && !in_array($singleId, $knownIds)) {
175
				$knownIds[] = $singleId;
176
				$singleIds = array_merge($singleIds, $this->getChildrenMemberships($singleId, $knownIds));
177
			}
178
		}
179
180
		return array_unique($singleIds);
181
	}
182
183
184
	/**
185
	 * @param string $singleId
186
	 * @param string $circleId
187
	 * @param array $memberships
188
	 * @param array $knownIds
189
	 * @param array $path
190
	 *
191
	 * @return array
192
	 */
193
	public function generateMemberships(
194
		string $singleId,
195
		string $circleId = '',
196
		array &$memberships = [],
197
		array $knownIds = [],
198
		array $path = []
199
	): array {
200
		$circleId = ($circleId === '') ? $singleId : $circleId;
201
		$path[] = $circleId;
202
		$knownIds[] = $circleId;
203
204
		$members = $this->memberRequest->getMembersBySingleId($circleId);
205
		foreach ($members as $member) {
206
			if ($member->getLevel() < Member::LEVEL_MEMBER) {
207
				continue;
208
			}
209
210
			$membership = new Membership($singleId, count($path) > 1 ? $path[1] : '', $member);
211
			$membership->setInheritancePath(array_reverse($path))
212
					   ->setInheritanceDepth(sizeof($path));
213
			$this->fillMemberships($membership, $memberships);
214
			if (!in_array($member->getCircleId(), $knownIds)) {
215
				$this->generateMemberships(
216
					$singleId,
217
					$member->getCircleId(),
218
					$memberships,
219
					$knownIds,
220
					$path
221
				);
222
			}
223
		}
224
225
		return $memberships;
226
	}
227
228
229
	/**
230
	 * @param Membership $membership
231
	 * @param Membership[] $memberships
232
	 */
233
	private function fillMemberships(Membership $membership, array &$memberships) {
234
		foreach ($memberships as $known) {
235
			if ($known->getCircleId() === $membership->getCircleId()) {
236
				if ($known->getLevel() < $membership->getLevel()) {
237
					$known->setLevel($membership->getLevel());
238
//					$known->setMemberId($membership->getMemberId());
239
					$known->setSingleId($membership->getSingleId());
240
					$known->setInheritanceLast($membership->getInheritanceLast());
241
				}
242
243
				return;
244
			}
245
		}
246
247
		$memberships[$membership->getCircleId()] = $membership;
248
	}
249
250
251
	/**
252
	 * @param string $singleId
253
	 * @param Membership[] $memberships
254
	 *
255
	 * @return int
256
	 */
257
	public function updateMembershipsDatabase(string $singleId, array $memberships): int {
258
		$known = $this->membershipRequest->getMemberships($singleId);
259
260
		$deprecated = $this->removeDeprecatedMemberships($memberships, $known);
261
		if (!empty($deprecated)) {
262
			$this->eventService->membershipsRemoved($deprecated);
263
		}
264
265
		$new = $this->createNewMemberships($memberships, $known);
266
		if (!empty($new)) {
267
			$this->eventService->membershipsCreated($new);
268
		}
269
270
		return count($deprecated) + count($new);
271
	}
272
273
274
	/**
275
	 * @param Membership[] $memberships
276
	 * @param Membership[] $known
277
	 *
278
	 * @return Membership[]
279
	 */
280
	private function removeDeprecatedMemberships(array $memberships, array $known): array {
281
		$circleIds = array_map(
282
			function(Membership $membership): string {
283
				return $membership->getCircleId();
284
			}, $memberships
285
		);
286
287
		$deprecated = [];
288
		foreach ($known as $item) {
289
			if (!in_array($item->getCircleId(), $circleIds)) {
290
				$deprecated[] = $item;
291
				$this->membershipRequest->delete($item);
292
			}
293
		}
294
295
		return $deprecated;
296
	}
297
298
299
	/**
300
	 * @param Membership[] $memberships
301
	 * @param Membership[] $known
302
	 *
303
	 * @return Membership[]
304
	 */
305
	private function createNewMemberships(array $memberships, array $known): array {
306
		$new = [];
307
		foreach ($memberships as $membership) {
308
			try {
309
				$item = $this->getMembershipsFromList($known, $membership->getCircleId());
310
				if ($item->getLevel() !== $membership->getLevel()) {
311
					$this->membershipRequest->update($membership);
312
					$new[] = $item;
313
				}
314
			} catch (ItemNotFoundException $e) {
315
				$this->membershipRequest->insert($membership);
316
				$new[] = $membership;
317
			}
318
		}
319
320
		return $new;
321
	}
322
323
324
	/**
325
	 * @param Membership[] $list
326
	 * @param string $circleId
327
	 *
328
	 * @return Membership
329
	 * @throws ItemNotFoundException
330
	 */
331
	private function getMembershipsFromList(array $list, string $circleId): Membership {
332
		foreach ($list as $item) {
333
			if ($item->getCircleId() === $circleId) {
334
				return $item;
335
			}
336
		}
337
338
		throw new ItemNotFoundException();
339
	}
340
341
342
	/**
343
	 * @param string $singleId
344
	 * @param bool $all
345
	 */
346
	public function resetMemberships(string $singleId = '', bool $all = false) {
347
		$this->membershipRequest->removeBySingleId($singleId, $all);
348
	}
349
350
351
	/**
352
	 * @param FederatedUser $federatedUser
353
	 */
354
	public function deleteFederatedUser(FederatedUser $federatedUser) {
355
		$this->membershipRequest->deleteFederatedUser($federatedUser);
356
	}
357
358
}
359
360