Completed
Pull Request — master (#551)
by Maxence
02:15
created

CirclesMemberships::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 20

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 20
rs 9.6
c 0
b 0
f 0
cc 1
nc 1
nop 8

How to fix   Many Parameters   

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

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\Command;
33
34
use daita\MySmallPhpTools\Exceptions\InvalidItemException;
35
use daita\MySmallPhpTools\Exceptions\ItemNotFoundException;
36
use daita\MySmallPhpTools\Exceptions\UnknownTypeException;
37
use daita\MySmallPhpTools\Model\Nextcloud\nc22\NC22TreeNode;
38
use daita\MySmallPhpTools\Model\SimpleDataStore;
39
use daita\MySmallPhpTools\Traits\Nextcloud\nc22\TNC22ConsoleTree;
40
use daita\MySmallPhpTools\Traits\TArrayTools;
41
use Exception;
42
use OC\Core\Command\Base;
43
use OCA\Circles\Db\CircleRequest;
44
use OCA\Circles\Db\MemberRequest;
45
use OCA\Circles\Db\MembershipRequest;
46
use OCA\Circles\Exceptions\CircleNotFoundException;
47
use OCA\Circles\Exceptions\FederatedItemException;
48
use OCA\Circles\Exceptions\FederatedUserException;
49
use OCA\Circles\Exceptions\FederatedUserNotFoundException;
50
use OCA\Circles\Exceptions\InitiatorNotFoundException;
51
use OCA\Circles\Exceptions\InvalidIdException;
52
use OCA\Circles\Exceptions\MemberNotFoundException;
53
use OCA\Circles\Exceptions\OwnerNotFoundException;
54
use OCA\Circles\Exceptions\RemoteInstanceException;
55
use OCA\Circles\Exceptions\RemoteNotFoundException;
56
use OCA\Circles\Exceptions\RemoteResourceNotFoundException;
57
use OCA\Circles\Exceptions\SingleCircleNotFoundException;
58
use OCA\Circles\Exceptions\UnknownRemoteException;
59
use OCA\Circles\Exceptions\UserTypeNotFoundException;
60
use OCA\Circles\Model\Circle;
61
use OCA\Circles\Model\FederatedUser;
62
use OCA\Circles\Model\Member;
63
use OCA\Circles\Service\CircleService;
64
use OCA\Circles\Service\ConfigService;
65
use OCA\Circles\Service\FederatedUserService;
66
use OCA\Circles\Service\MembershipService;
67
use OCP\IUserManager;
68
use Symfony\Component\Console\Helper\Table;
69
use Symfony\Component\Console\Input\InputArgument;
70
use Symfony\Component\Console\Input\InputInterface;
71
use Symfony\Component\Console\Input\InputOption;
72
use Symfony\Component\Console\Output\ConsoleOutput;
73
use Symfony\Component\Console\Output\OutputInterface;
74
75
76
/**
77
 * Class CirclesMemberships
78
 *
79
 * @package OCA\Circles\Command
80
 */
81
class CirclesMemberships extends Base {
82
83
84
	use TArrayTools;
85
	use TNC22ConsoleTree;
86
87
88
	/** @var IUserManager */
89
	private $userManager;
90
91
	/** @var MembershipRequest */
92
	private $membershipRequest;
93
94
	/** @var MemberRequest */
95
	private $memberRequest;
96
97
	/** @var CircleRequest */
98
	private $circleRequest;
99
100
	/** @var FederatedUserService */
101
	private $federatedUserService;
102
103
	/** @var CircleService */
104
	private $circleService;
105
106
	/** @var MembershipService */
107
	private $membershipsService;
108
109
	/** @var ConfigService */
110
	private $configService;
111
112
113
	/** @var InputInterface */
114
	private $input;
115
116
117
	/** @var array */
118
	private $memberships = [];
119
120
121
	/**
122
	 * CirclesMemberships constructor.
123
	 *
124
	 * @param IUserManager $userManager
125
	 * @param MembershipRequest $membershipRequest
126
	 * @param MemberRequest $memberRequest
127
	 * @param CircleRequest $circleRequest
128
	 * @param FederatedUserService $federatedUserService
129
	 * @param CircleService $circleService
130
	 * @param MembershipService $membershipsService
131
	 * @param ConfigService $configService
132
	 */
133
	public function __construct(
134
		IUserManager $userManager,
135
		MembershipRequest $membershipRequest,
136
		MemberRequest $memberRequest,
137
		CircleRequest $circleRequest,
138
		FederatedUserService $federatedUserService,
139
		CircleService $circleService,
140
		MembershipService $membershipsService,
141
		ConfigService $configService
142
	) {
143
		parent::__construct();
144
		$this->userManager = $userManager;
145
		$this->memberRequest = $memberRequest;
146
		$this->membershipRequest = $membershipRequest;
147
		$this->circleRequest = $circleRequest;
148
		$this->federatedUserService = $federatedUserService;
149
		$this->circleService = $circleService;
150
		$this->membershipsService = $membershipsService;
151
		$this->configService = $configService;
152
	}
153
154
155
	/**
156
	 *
157
	 */
158
	protected function configure() {
159
		parent::configure();
160
		$this->setName('circles:memberships')
161
			 ->setDescription('index and display memberships for local and federated users')
162
			 ->addArgument('userId', InputArgument::OPTIONAL, 'userId to generate memberships', '')
163
			 ->addOption('display-name', '', InputOption::VALUE_NONE, 'display the displayName')
164
			 ->addOption('reset', '', InputOption::VALUE_NONE, 'reset memberships')
165
			 ->addOption('all', '', InputOption::VALUE_NONE, 'refresh memberships for all entities')
166
			 ->addOption(
167
				 'type', '', InputOption::VALUE_REQUIRED, 'type of the user',
168
				 Member::$TYPE[Member::TYPE_SINGLE]
169
			 );
170
	}
171
172
173
	/**
174
	 * @param InputInterface $input
175
	 * @param OutputInterface $output
176
	 *
177
	 * @return int
178
	 * @throws CircleNotFoundException
179
	 * @throws FederatedUserException
180
	 * @throws FederatedUserNotFoundException
181
	 * @throws MemberNotFoundException
182
	 * @throws OwnerNotFoundException
183
	 * @throws RemoteInstanceException
184
	 * @throws RemoteNotFoundException
185
	 * @throws RemoteResourceNotFoundException
186
	 * @throws UnknownRemoteException
187
	 * @throws UserTypeNotFoundException
188
	 * @throws FederatedItemException
189
	 * @throws InvalidIdException
190
	 * @throws SingleCircleNotFoundException
191
	 * @throws Exception
192
	 */
193
	protected function execute(InputInterface $input, OutputInterface $output): int {
194
		$this->input = $input;
195
196
		if ($input->getOption('all')) {
197
			$this->manageAllMemberships();
198
199
			return 0;
200
		}
201
202
		$userId = $input->getArgument('userId');
203
		if ($userId === '') {
204
			throw new Exception('Not enough arguments (missing: "userId").');
205
		}
206
207
		$type = Member::parseTypeString($input->getOption('type'));
208
		$federatedUser = $this->federatedUserService->getFederatedUser($userId, (int)$type);
209
210
		if ($this->input->getOption('reset')) {
211
			$this->membershipsService->resetMemberships($federatedUser->getSingleId());
212
213
			return 0;
214
		}
215
216
		$output->writeln('Id: <info>' . $federatedUser->getUserId() . '</info>');
217
		$output->writeln('Instance: <info>' . $federatedUser->getInstance() . '</info>');
218
		$output->writeln('Type: <info>' . Member::$TYPE[$federatedUser->getUserType()] . '</info>');
219
		$output->writeln('SingleId: <info>' . $federatedUser->getSingleId() . '</info>');
220
221
		$output->writeln('');
222
		$output->writeln('Memberships:');
223
224
		$count = $this->membershipsService->manageMemberships($federatedUser->getSingleId());
225
		if ($count === 0) {
226
			$output->writeln('(database not updated)');
227
		} else {
228
			$output->writeln('(' . $count . ' entries generated/updated in the database)');
229
		}
230
231
		foreach ($federatedUser->getMemberships() as $membership) {
232
			$this->memberships[$membership->getCircleId()] = $membership;
233
			$output->writeln(
234
				'- <info>' . $membership->getCircleId() . '</info> ('
235
				. Member::$DEF_LEVEL[$membership->getLevel()] . ')'
236
			);
237
		}
238
239
		$output->writeln('');
240
241
		$tree = new NC22TreeNode(null, new SimpleDataStore(['federatedUser' => $federatedUser]));
242
		$this->generateTree($federatedUser->getSingleId(), $tree);
243
244
		$this->drawTree(
245
			$tree, [$this, 'displayLeaf'],
246
			[
247
				'height'       => 3,
248
				'node-spacing' => 0,
249
				'item-spacing' => 1,
250
			]
251
		);
252
253
		return 0;
254
	}
255
256
257
	/**
258
	 * @param string $id
259
	 * @param NC22TreeNode $tree
260
	 * @param array $knownIds
261
	 */
262
	private function generateTree(string $id, NC22TreeNode $tree, array $knownIds = []) {
263
		if (in_array($id, $knownIds)) {
264
			return;
265
		}
266
		$knownIds[] = $id;
267
268
		$members = $this->memberRequest->getMembersBySingleId($id);
269
		foreach ($members as $member) {
270
			if ($member->getCircle()->isConfig(Circle::CFG_SINGLE)) {
271
				continue;
272
			}
273
274
			$item = new NC22TreeNode(
275
				$tree, new SimpleDataStore(
276
						 [
277
							 'member'  => $member,
278
							 'cycling' => in_array($member->getCircleId(), $knownIds)
279
						 ]
280
					 )
281
			);
282
			$this->generateTree($member->getCircleId(), $item, $knownIds);
283
		}
284
	}
285
286
287
	/**
288
	 * @param SimpleDataStore $data
289
	 * @param int $lineNumber
290
	 *
291
	 * @return string
292
	 * @throws OwnerNotFoundException
293
	 */
294
	public function displayLeaf(SimpleDataStore $data, int $lineNumber): string {
295
		if ($lineNumber === 3) {
296
			return ($data->gBool('cycling')) ? '<comment>(loop detected)</comment>' : '';
297
		}
298
299
		try {
300
			$line = '';
301
			if ($data->hasKey('federatedUser')) {
302
				/** @var FederatedUser $federatedUser */
303
				$federatedUser = $data->gObj('federatedUser', FederatedUser::class);
304
305
				if ($lineNumber === 2) {
306
					return '';
307
				}
308
				$line .= '<info>' . $federatedUser->getSingleId() . '</info>';
309
				if (!$this->configService->isLocalInstance($federatedUser->getInstance())) {
310
					$line .= '@' . $federatedUser->getInstance();
311
				}
312
313
				return $line;
314
			}
315
316
			if ($data->hasKey('member')) {
317
				/** @var Member $member */
318
				$member = $data->gObj('member', Member::class);
319
				$circle = $member->getCircle();
320
321
				if ($lineNumber === 1) {
322
					$line .= '<info>' . $circle->getSingleId() . '</info>';
323
					if (!$this->configService->isLocalInstance($circle->getInstance())) {
324
						$line .= '@' . $circle->getInstance();
325
					}
326
					$line .= ' (' . ($this->input->getOption('display-name') ?
327
							$circle->getDisplayName() : $circle->getName()) . ')';
328
					$line .= ' <info>MemberId</info>: ' . $member->getId();
329
					$line .= ' <info>Level</info>: ' . Member::$DEF_LEVEL[$member->getLevel()];
330
331
					$knownMembership = $this->memberships[$member->getCircleId()];
332
					if ($member->getLevel() !== $knownMembership->getLevel()) {
333
						$line .= ' (' . Member::$DEF_LEVEL[$knownMembership->getLevel()] . ')';
334
					}
335
				}
336
				if ($lineNumber === 2) {
337
					$owner = $circle->getOwner();
338
					$line .= '<info>Owner</info>: ' . $owner->getUserId() . '@' . $owner->getInstance();
339
					if ($owner->hasBasedOn()) {
340
						$line .= ' (' . Circle::$DEF_SOURCE[$owner->getBasedOn()->getSource()] . ') ';
341
					}
342
					$type = implode(", ", Circle::getCircleFlags($circle, Circle::FLAGS_LONG));
343
					$line .= ($type === '') ? '' : '<info>Config</info>: ' . $type;
344
				}
345
346
				return $line;
347
			}
348
		} catch (InvalidItemException | ItemNotFoundException | UnknownTypeException $e) {
349
		}
350
351
		return '';
352
	}
353
354
355
	/**
356
	 * @throws CircleNotFoundException
357
	 * @throws FederatedUserNotFoundException
358
	 * @throws InitiatorNotFoundException
359
	 * @throws OwnerNotFoundException
360
	 */
361
	private function manageAllMemberships() {
362
		if ($this->input->getOption('reset')) {
363
			$this->membershipsService->resetMemberships('', true);
364
365
			return;
366
		}
367
368
		$this->federatedUserService->bypassCurrentUserCondition(true);
369
370
		$params = new SimpleDataStore(['includeSystemCircles' => true]);
371
		$circles = $this->circleService->getCircles(null, null, $params);
372
373
		$output = new ConsoleOutput();
374
		$output = $output->section();
375
		$table = new Table($output);
376
		$table->setHeaders(['Circle Id', 'Name', 'Source', 'Owner', 'Instance', 'Updated', 'Memberships']);
377
		$table->render();
378
379
		$count = 0;
380
		$local = $this->configService->getFrontalInstance();
381
		foreach ($circles as $circle) {
382
			$owner = $circle->getOwner();
383
384
			$updated = $this->membershipsService->manageMemberships($circle->getSingleId());
385
			$count += $updated;
386
			$federatedUser = $this->circleRequest->getFederatedUserBySingleId($circle->getSingleId());
387
			$table->appendRow(
388
				[
389
					$circle->getSingleId(),
390
					$circle->getDisplayName(),
391
					($circle->getSource() > 0) ? Circle::$DEF_SOURCE[$circle->getSource()] : '',
392
					$owner->getUserId(),
393
					($owner->getInstance() === $local) ? '' : $owner->getInstance(),
394
					$updated,
395
					sizeof($federatedUser->getMemberships())
396
				]
397
			);
398
		}
399
400
		$output->writeln($count . ' memberships updated');
401
	}
402
403
}
404
405