Completed
Push — master ( 6a3d7a...383329 )
by Maxence
03:05 queued 10s
created

MembershipService::getMembershipsFromList()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

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