Completed
Pull Request — master (#637)
by Maxence
03:00
created

CoreQueryBuilder   F

Complexity

Total Complexity 124

Size/Duplication

Total Lines 1512
Duplicated Lines 0 %

Coupling/Cohesion

Components 5
Dependencies 11

Importance

Changes 0
Metric Value
wmc 124
lcom 5
cbo 11
dl 0
loc 1512
rs 0.8
c 0
b 0
f 0

54 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 5 1
A getInstance() 0 5 2
A limitToCircleId() 0 3 1
A limitToName() 0 3 1
A limitToDisplayName() 0 3 1
A limitToSanitizedName() 0 3 1
A limitToConfig() 0 3 1
A limitToSource() 0 3 1
A limitToConfigFlag() 0 3 1
A limitToSingleId() 0 3 1
A limitToItemId() 0 3 1
A limitToInstance() 0 3 1
A limitToUserType() 0 3 1
A limitToShareType() 0 3 1
A limitToShareWith() 0 3 1
A limitToFileSource() 0 3 1
A limitToFileSourceArray() 0 3 1
A limitToShareParent() 0 3 1
A filterCircle() 0 12 4
A leftJoinRemoteInstance() 0 13 2
A limitToRemoteInstance() 0 16 2
A leftJoinRemoteInstanceIncomingRequest() 0 15 2
A leftJoinMemberFromInstance() 0 23 2
A leftJoinMemberFromRemoteCircle() 0 36 2
A limitRemoteVisibility() 0 49 3
A limitToDirectMembership() 0 19 3
B filterDirectMembership() 0 41 6
A countMembers() 0 26 5
A leftJoinCircle() 0 32 5
A leftJoinInvitedBy() 0 20 3
A leftJoinBasedOn() 0 26 4
A leftJoinOwner() 0 27 3
A leftJoinMember() 0 33 3
A leftJoinInheritedMembers() 0 33 3
A limitToInheritedMemberships() 0 16 2
A limitToMembersByInheritance() 0 15 2
A leftJoinMembersByInheritance() 0 28 3
A limitToInitiator() 0 16 2
C leftJoinInitiator() 0 84 9
B limitInitiatorVisibility() 0 69 6
A filterCircles() 0 18 4
A limitRemoteVisibility_Sensitive_Members() 0 26 1
A leftJoinFileCache() 0 27 1
A leftJoinShareChild() 0 25 2
A limitToShareOwner() 0 11 2
A leftJoinMountpoint() 0 19 2
A generateCircleSelectAlias() 0 10 1
A generateMemberSelectAlias() 0 10 1
A generateMembershipSelectAlias() 0 14 2
A generateRemoteInstanceSelectAlias() 0 10 1
A setOptions() 0 8 2
A generateAlias() 0 24 5
A getAvailablePath() 0 16 3
A orXCheckLevel() 0 9 2

How to fix   Complexity   

Complex Class

Complex classes like CoreQueryBuilder often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use CoreQueryBuilder, and based on these observations, apply Extract Interface, too.

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\Db;
33
34
35
use ArtificialOwl\MySmallPhpTools\Db\Nextcloud\nc22\NC22ExtendedQueryBuilder;
36
use ArtificialOwl\MySmallPhpTools\Traits\TArrayTools;
37
use Doctrine\DBAL\Query\QueryBuilder;
38
use OC;
39
use OCA\Circles\Exceptions\RequestBuilderException;
40
use OCA\Circles\IFederatedModel;
41
use OCA\Circles\IFederatedUser;
42
use OCA\Circles\Model\Circle;
43
use OCA\Circles\Model\Federated\RemoteInstance;
44
use OCA\Circles\Model\FederatedUser;
45
use OCA\Circles\Model\Member;
46
use OCA\Circles\Service\ConfigService;
47
use OCP\DB\QueryBuilder\ICompositeExpression;
48
use OCP\DB\QueryBuilder\IQueryBuilder;
49
50
51
/**
52
 * Class CoreQueryBuilder
53
 *
54
 * @package OCA\Circles\Db
55
 */
56
class CoreQueryBuilder extends NC22ExtendedQueryBuilder {
57
58
59
	use TArrayTools;
60
61
62
	const SINGLE = 'single';
63
	const CIRCLE = 'circle';
64
	const MEMBER = 'member';
65
	const MEMBER_COUNT = 'membercount';
66
	const OWNER = 'owner';
67
	const FEDERATED_EVENT = 'federatedevent';
68
	const REMOTE = 'remote';
69
	const BASED_ON = 'basedon';
70
	const INITIATOR = 'initiator';
71
	const DIRECT_INITIATOR = 'initiatordirect';
72
	const MEMBERSHIPS = 'memberships';
73
	const UPSTREAM_MEMBERSHIPS = 'upstreammemberships';
74
	const INHERITANCE_FROM = 'inheritancefrom';
75
	const INHERITED_BY = 'inheritedby';
76
	const INVITED_BY = 'invitedby';
77
	const MOUNT = 'mount';
78
	const MOUNTPOINT = 'mountpoint';
79
	const SHARE = 'share';
80
	const FILE_CACHE = 'filecache';
81
	const STORAGES = 'storages';
82
	const OPTIONS = 'options';
83
	const HELPER = 'circleshelper';
84
85
86
	public static $SQL_PATH = [
87
		self::SINGLE => [
88
			self::MEMBER
89
		],
90
		self::CIRCLE => [
91
			self::OPTIONS          => [
92
				'getPersonalCircle' => true
93
			],
94
			self::MEMBER,
95
			self::MEMBER_COUNT,
96
			self::OWNER            => [
97
				self::BASED_ON
98
			],
99
			self::MEMBERSHIPS,
100
			self::DIRECT_INITIATOR => [
101
				self::BASED_ON
102
			],
103
			self::INITIATOR        => [
104
				self::BASED_ON,
105
				self::INHERITED_BY => [
106
					self::MEMBERSHIPS
107
				]
108
			],
109
			self::REMOTE           => [
110
				self::MEMBER,
111
				self::CIRCLE => [
112
					self::OWNER
113
				]
114
			]
115
		],
116
		self::MEMBER => [
117
			self::MEMBERSHIPS,
118
			self::INHERITANCE_FROM,
119
			self::CIRCLE     => [
120
				self::OPTIONS   => [
121
					'getData' => true
122
				],
123
				self::OWNER,
124
				self::MEMBERSHIPS,
125
				self::INITIATOR => [
126
					self::OPTIONS      => [
127
						'mustBeMember' => true,
128
						'canBeVisitor' => false
129
					],
130
					self::BASED_ON,
131
					self::INHERITED_BY => [
132
						self::MEMBERSHIPS
133
					],
134
					self::INVITED_BY   => [
135
						self::OWNER,
136
						self::BASED_ON
137
					]
138
				]
139
			],
140
			self::BASED_ON   => [
141
				self::OWNER,
142
				self::MEMBERSHIPS,
143
				self::INITIATOR => [
144
					self::BASED_ON,
145
					self::INHERITED_BY => [
146
						self::MEMBERSHIPS
147
					]
148
				]
149
			],
150
			self::REMOTE     => [
151
				self::MEMBER,
152
				self::CIRCLE => [
153
					self::OWNER
154
				]
155
			],
156
			self::INVITED_BY => [
157
				self::OWNER,
158
				self::BASED_ON
159
			]
160
		],
161
		self::SHARE  => [
162
			self::SHARE,
163
			self::FILE_CACHE           => [
164
				self::STORAGES
165
			],
166
			self::UPSTREAM_MEMBERSHIPS => [
167
				self::MEMBERSHIPS,
168
				self::INHERITED_BY => [
169
					self::BASED_ON
170
				],
171
				self::SHARE,
172
			],
173
			self::MEMBERSHIPS,
174
			self::INHERITANCE_FROM,
175
			self::INHERITED_BY         => [
176
				self::BASED_ON
177
			],
178
			self::CIRCLE               => [
179
				self::OWNER
180
			],
181
			self::INITIATOR            => [
182
				self::BASED_ON,
183
				self::INHERITED_BY => [
184
					self::MEMBERSHIPS
185
				]
186
			]
187
		],
188
		self::REMOTE => [
189
			self::MEMBER
190
		],
191
		self::MOUNT  => [
192
			self::MEMBER    => [
193
				self::REMOTE
194
			],
195
			self::INITIATOR => [
196
				self::INHERITED_BY => [
197
					self::MEMBERSHIPS
198
				]
199
			],
200
			self::MOUNTPOINT,
201
			self::MEMBERSHIPS
202
		],
203
		self::HELPER => [
204
			self::MEMBERSHIPS,
205
			self::INITIATOR => [
206
				self::INHERITED_BY => [
207
					self::MEMBERSHIPS
208
				]
209
			],
210
			self::CIRCLE    => [
211
				self::OPTIONS => [
212
					'getPersonalCircle' => true
213
				],
214
				self::MEMBER,
215
				self::OWNER   => [
216
					self::BASED_ON
217
				]
218
			]
219
		]
220
	];
221
222
223
	/** @var ConfigService */
224
	private $configService;
225
226
227
	/** @var array */
228
	private $options = [];
229
230
231
	/**
232
	 * CoreQueryBuilder constructor.
233
	 */
234
	public function __construct() {
235
		parent::__construct();
236
237
		$this->configService = OC::$server->get(ConfigService::class);
238
	}
239
240
241
	/**
242
	 * @param IFederatedModel $federatedModel
243
	 *
244
	 * @return string
245
	 */
246
	public function getInstance(IFederatedModel $federatedModel): string {
247
		$instance = $federatedModel->getInstance();
248
249
		return ($this->configService->isLocalInstance($instance)) ? '' : $instance;
250
	}
251
252
253
	/**
254
	 * @param string $id
255
	 */
256
	public function limitToCircleId(string $id): void {
257
		$this->limitToDBField('circle_id', $id, true);
0 ignored issues
show
Deprecated Code introduced by
The method ArtificialOwl\MySmallPhp...ilder::limitToDBField() has been deprecated with message: - use limit();

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
258
	}
259
260
	/**
261
	 * @param string $name
262
	 */
263
	public function limitToName(string $name): void {
264
		$this->limit('name', $name);
265
	}
266
267
	/**
268
	 * @param string $name
269
	 */
270
	public function limitToDisplayName(string $name): void {
271
		$this->limit('display_name', $name, '', false);
272
	}
273
274
	/**
275
	 * @param string $name
276
	 */
277
	public function limitToSanitizedName(string $name): void {
278
		$this->limit('sanitized_name', $name, '', false);
279
	}
280
281
	/**
282
	 * @param int $config
283
	 */
284
	public function limitToConfig(int $config): void {
285
		$this->limitToDBFieldInt('config', $config);
0 ignored issues
show
Deprecated Code introduced by
The method ArtificialOwl\MySmallPhp...er::limitToDBFieldInt() has been deprecated with message: - use limitInt()

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
286
	}
287
288
	/**
289
	 * @param int $source
290
	 */
291
	public function limitToSource(int $source): void {
292
		$this->limitToDBFieldInt('source', $source);
0 ignored issues
show
Deprecated Code introduced by
The method ArtificialOwl\MySmallPhp...er::limitToDBFieldInt() has been deprecated with message: - use limitInt()

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
293
	}
294
295
	/**
296
	 * @param int $config
297
	 * @param string $alias
298
	 */
299
	public function limitToConfigFlag(int $config, string $alias = ''): void {
300
		$this->limitBitwise('config', $config, $alias);
301
	}
302
303
304
	/**
305
	 * @param string $singleId
306
	 */
307
	public function limitToSingleId(string $singleId): void {
308
		$this->limitToDBField('single_id', $singleId, true);
0 ignored issues
show
Deprecated Code introduced by
The method ArtificialOwl\MySmallPhp...ilder::limitToDBField() has been deprecated with message: - use limit();

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
309
	}
310
311
312
	/**
313
	 * @param string $itemId
314
	 */
315
	public function limitToItemId(string $itemId): void {
316
		$this->limitToDBField('item_id', $itemId, true);
0 ignored issues
show
Deprecated Code introduced by
The method ArtificialOwl\MySmallPhp...ilder::limitToDBField() has been deprecated with message: - use limit();

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
317
	}
318
319
320
	/**
321
	 * @param string $host
322
	 */
323
	public function limitToInstance(string $host): void {
324
		$this->limitToDBField('instance', $host, false);
0 ignored issues
show
Deprecated Code introduced by
The method ArtificialOwl\MySmallPhp...ilder::limitToDBField() has been deprecated with message: - use limit();

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
325
	}
326
327
328
	/**
329
	 * @param int $userType
330
	 */
331
	public function limitToUserType(int $userType): void {
332
		$this->limitToDBFieldInt('user_type', $userType);
0 ignored issues
show
Deprecated Code introduced by
The method ArtificialOwl\MySmallPhp...er::limitToDBFieldInt() has been deprecated with message: - use limitInt()

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
333
	}
334
335
336
	/**
337
	 * @param int $shareType
338
	 */
339
	public function limitToShareType(int $shareType): void {
340
		$this->limitToDBFieldInt('share_type', $shareType);
0 ignored issues
show
Deprecated Code introduced by
The method ArtificialOwl\MySmallPhp...er::limitToDBFieldInt() has been deprecated with message: - use limitInt()

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
341
	}
342
343
344
	/**
345
	 * @param string $shareWith
346
	 */
347
	public function limitToShareWith(string $shareWith): void {
348
		$this->limitToDBField('share_with', $shareWith);
0 ignored issues
show
Deprecated Code introduced by
The method ArtificialOwl\MySmallPhp...ilder::limitToDBField() has been deprecated with message: - use limit();

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
349
	}
350
351
352
	/**
353
	 * @param int $nodeId
354
	 */
355
	public function limitToFileSource(int $nodeId): void {
356
		$this->limitToDBFieldInt('file_source', $nodeId);
0 ignored issues
show
Deprecated Code introduced by
The method ArtificialOwl\MySmallPhp...er::limitToDBFieldInt() has been deprecated with message: - use limitInt()

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
357
	}
358
359
	/**
360
	 * @param array $files
361
	 */
362
	public function limitToFileSourceArray(array $files): void {
363
		$this->limitToDBFieldInArray('file_source', $files);
0 ignored issues
show
Deprecated Code introduced by
The method ArtificialOwl\MySmallPhp...limitToDBFieldInArray() has been deprecated with message: - use limitInArray()

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
364
	}
365
366
367
	/**
368
	 * @param int $shareId
369
	 */
370
	public function limitToShareParent(int $shareId): void {
371
		$this->limitToDBFieldInt('parent', $shareId);
0 ignored issues
show
Deprecated Code introduced by
The method ArtificialOwl\MySmallPhp...er::limitToDBFieldInt() has been deprecated with message: - use limitInt()

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
372
	}
373
374
375
	/**
376
	 * @param Circle $circle
377
	 */
378
	public function filterCircle(Circle $circle): void {
379
		if ($this->getType() !== QueryBuilder::SELECT) {
380
			return;
381
		}
382
383
		if ($circle->getDisplayName() !== '') {
384
			$this->searchInDBField('display_name', '%' . $circle->getDisplayName() . '%');
385
		}
386
		if ($circle->getConfig() > 0) {
387
			$this->limitBitwise('config', $circle->getConfig());
388
		}
389
	}
390
391
392
	/**
393
	 * left join RemoteInstance based on a Member
394
	 */
395
	public function leftJoinRemoteInstance(string $alias): void {
396
		$expr = $this->expr();
397
398
		try {
399
			$aliasRemoteInstance = $this->generateAlias($alias, self::REMOTE);
400
			$this->generateRemoteInstanceSelectAlias($aliasRemoteInstance)
401
				 ->leftJoin(
402
					 $alias, CoreRequestBuilder::TABLE_REMOTE, $aliasRemoteInstance,
403
					 $expr->eq($alias . '.instance', $aliasRemoteInstance . '.instance')
404
				 );
405
		} catch (RequestBuilderException $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
406
		}
407
	}
408
409
410
	/**
411
	 * @param string $alias
412
	 * @param RemoteInstance $remoteInstance
413
	 * @param bool $filterSensitiveData
414
	 * @param string $aliasCircle
415
	 *
416
	 * @throws RequestBuilderException
417
	 */
418
	public function limitToRemoteInstance(
419
		string $alias,
420
		RemoteInstance $remoteInstance,
421
		bool $filterSensitiveData = true,
422
		string $aliasCircle = ''
423
	): void {
424
425
		if ($aliasCircle === '') {
426
			$aliasCircle = $alias;
427
		}
428
429
		$this->leftJoinRemoteInstanceIncomingRequest($alias, $remoteInstance);
430
		$this->leftJoinMemberFromInstance($alias, $remoteInstance, $aliasCircle);
431
		$this->leftJoinMemberFromRemoteCircle($alias, $remoteInstance, $aliasCircle);
432
		$this->limitRemoteVisibility($alias, $filterSensitiveData, $aliasCircle);
433
	}
434
435
436
	/**
437
	 * Left join RemoteInstance based on an incoming request
438
	 *
439
	 * @param string $alias
440
	 * @param RemoteInstance $remoteInstance
441
	 *
442
	 * @throws RequestBuilderException
443
	 */
444
	public function leftJoinRemoteInstanceIncomingRequest(
445
		string $alias,
446
		RemoteInstance $remoteInstance
447
	): void {
448
		if ($this->getType() !== QueryBuilder::SELECT) {
449
			return;
450
		}
451
452
		$aliasRemote = $this->generateAlias($alias, self::REMOTE);
453
		$expr = $this->expr();
454
		$this->leftJoin(
455
			$this->getDefaultSelectAlias(), CoreRequestBuilder::TABLE_REMOTE, $aliasRemote,
456
			$expr->eq($aliasRemote . '.instance', $this->createNamedParameter($remoteInstance->getInstance()))
457
		);
458
	}
459
460
461
	/**
462
	 * left join members to check memberships of someone from instance
463
	 *
464
	 * @param string $alias
465
	 * @param RemoteInstance $remoteInstance
466
	 * @param string $aliasCircle
467
	 *
468
	 * @throws RequestBuilderException
469
	 */
470
	private function leftJoinMemberFromInstance(
471
		string $alias, RemoteInstance $remoteInstance, string $aliasCircle
472
	) {
473
		if ($this->getType() !== QueryBuilder::SELECT) {
474
			return;
475
		}
476
477
		$aliasRemote = $this->generateAlias($alias, self::REMOTE);
478
		$aliasRemoteMember = $this->generateAlias($aliasRemote, self::MEMBER);
479
480
		$expr = $this->expr();
481
		$this->leftJoin(
482
			$this->getDefaultSelectAlias(), CoreRequestBuilder::TABLE_MEMBER, $aliasRemoteMember,
483
			$expr->andX(
484
				$expr->eq($aliasRemoteMember . '.circle_id', $aliasCircle . '.unique_id'),
485
				$expr->eq(
486
					$aliasRemoteMember . '.instance',
487
					$this->createNamedParameter($remoteInstance->getInstance())
488
				),
489
				$expr->gte($aliasRemoteMember . '.level', $this->createNamedParameter(Member::LEVEL_MEMBER))
490
			)
491
		);
492
	}
493
494
495
	/**
496
	 * left join circle is member of a circle from remote instance
497
	 *
498
	 * @param string $alias
499
	 * @param RemoteInstance $remoteInstance
500
	 * @param string $aliasCircle
501
	 *
502
	 * @throws RequestBuilderException
503
	 */
504
	private function leftJoinMemberFromRemoteCircle(
505
		string $alias,
506
		RemoteInstance $remoteInstance,
507
		string $aliasCircle
508
	) {
509
		if ($this->getType() !== QueryBuilder::SELECT) {
510
			return;
511
		}
512
513
		$aliasRemote = $this->generateAlias($alias, self::REMOTE);
514
		$aliasRemoteCircle = $this->generateAlias($aliasRemote, self::CIRCLE);
515
		$aliasRemoteCircleOwner = $this->generateAlias($aliasRemoteCircle, self::OWNER);
516
517
		$expr = $this->expr();
518
		$this->leftJoin(
519
			$this->getDefaultSelectAlias(), CoreRequestBuilder::TABLE_MEMBER, $aliasRemoteCircle,
520
			$expr->andX(
521
				$expr->eq($aliasRemoteCircle . '.single_id', $aliasCircle . '.unique_id'),
522
				$expr->emptyString($aliasRemoteCircle . '.instance'),
523
				$expr->gte($aliasRemoteCircle . '.level', $this->createNamedParameter(Member::LEVEL_MEMBER))
524
			)
525
		);
526
		$this->leftJoin(
527
			$this->getDefaultSelectAlias(), CoreRequestBuilder::TABLE_MEMBER, $aliasRemoteCircleOwner,
528
			$expr->andX(
529
				$expr->eq($aliasRemoteCircle . '.circle_id', $aliasRemoteCircleOwner . '.circle_id'),
530
				$expr->eq(
531
					$aliasRemoteCircleOwner . '.instance',
532
					$this->createNamedParameter($remoteInstance->getInstance())
533
				),
534
				$expr->eq(
535
					$aliasRemoteCircleOwner . '.level', $this->createNamedParameter(Member::LEVEL_OWNER)
536
				)
537
			)
538
		);
539
	}
540
541
542
	/**
543
	 * - global_scale: visibility on all Circles
544
	 * - trusted: visibility on all FEDERATED Circle if owner is local
545
	 * - external: visibility on all FEDERATED Circle if owner is local and:
546
	 *    - with if Circle contains at least one member from the remote instance
547
	 *    - one circle from the remote instance contains the local circle as member, and confirmed (using
548
	 *      sync locally)
549
	 * - passive: like external, but the members list will only contains member from the local instance and
550
	 * from the remote instance.
551
	 *
552
	 * @param string $alias
553
	 * @param bool $sensitive
554
	 * @param string $aliasCircle
555
	 *
556
	 * @throws RequestBuilderException
557
	 */
558
	protected function limitRemoteVisibility(string $alias, bool $sensitive, string $aliasCircle) {
559
		$aliasRemote = $this->generateAlias($alias, self::REMOTE);
560
		$aliasOwner = $this->generateAlias($aliasCircle, self::OWNER);
561
		$aliasRemoteMember = $this->generateAlias($aliasRemote, self::MEMBER);
562
		$aliasRemoteCircle = $this->generateAlias($aliasRemote, self::CIRCLE);
563
		$aliasRemoteCircleOwner = $this->generateAlias($aliasRemoteCircle, self::OWNER);
564
565
		$expr = $this->expr();
566
		$orX = $expr->orX();
567
		$orX->add(
568
			$expr->eq($aliasRemote . '.type', $this->createNamedParameter(RemoteInstance::TYPE_GLOBALSCALE))
569
		);
570
571
		$orExtOrPassive = $expr->orX();
572
		$orExtOrPassive->add(
573
			$expr->eq($aliasRemote . '.type', $this->createNamedParameter(RemoteInstance::TYPE_EXTERNAL))
574
		);
575
		if (!$sensitive) {
576
			$orExtOrPassive->add(
577
				$expr->eq($aliasRemote . '.type', $this->createNamedParameter(RemoteInstance::TYPE_PASSIVE))
578
			);
579
		} else {
580
			if ($this->getDefaultSelectAlias() === CoreQueryBuilder::MEMBER) {
581
				$orExtOrPassive->add($this->limitRemoteVisibility_Sensitive_Members($aliasRemote));
582
			}
583
		}
584
585
		$orInstance = $expr->orX();
586
		$orInstance->add($expr->isNotNull($aliasRemoteMember . '.instance'));
587
		$orInstance->add($expr->isNotNull($aliasRemoteCircleOwner . '.instance'));
588
589
		$andExternal = $expr->andX();
590
		$andExternal->add($orExtOrPassive);
591
		$andExternal->add($orInstance);
592
593
		$orExtOrTrusted = $expr->orX();
594
		$orExtOrTrusted->add($andExternal);
595
		$orExtOrTrusted->add(
596
			$expr->eq($aliasRemote . '.type', $this->createNamedParameter(RemoteInstance::TYPE_TRUSTED))
597
		);
598
599
		$andTrusted = $expr->andX();
600
		$andTrusted->add($orExtOrTrusted);
601
		$andTrusted->add($this->exprLimitBitwise('config', Circle::CFG_FEDERATED, $aliasCircle));
602
		$andTrusted->add($expr->emptyString($aliasOwner . '.instance'));
603
		$orX->add($andTrusted);
604
605
		$this->andWhere($orX);
606
	}
607
608
609
	/**
610
	 * @param string $alias
611
	 * @param Member $member
612
	 *
613
	 * @throws RequestBuilderException
614
	 */
615
	public function limitToDirectMembership(string $alias, Member $member): void {
616
		if ($this->getType() !== QueryBuilder::SELECT) {
617
			return;
618
		}
619
620
		$aliasMember = $this->generateAlias($alias, self::MEMBER, $options);
621
		$getData = $this->getBool('getData', $options, false);
622
623
		$expr = $this->expr();
624
		if ($getData) {
625
			$this->generateMemberSelectAlias($aliasMember);
626
		}
627
		$this->leftJoin(
628
			$this->getDefaultSelectAlias(), CoreRequestBuilder::TABLE_MEMBER, $aliasMember,
629
			$expr->eq($aliasMember . '.circle_id', $alias . '.unique_id')
630
		);
631
632
		$this->filterDirectMembership($aliasMember, $member);
633
	}
634
635
636
	/**
637
	 * @param string $aliasMember
638
	 * @param Member $member
639
	 */
640
	public function filterDirectMembership(string $aliasMember, Member $member): void {
641
		if ($this->getType() !== QueryBuilder::SELECT) {
642
			return;
643
		}
644
645
		$expr = $this->expr();
646
		$andX = $expr->andX();
647
648
		if ($member->getUserId() !== '') {
649
			$andX->add(
650
				$expr->eq($aliasMember . '.user_id', $this->createNamedParameter($member->getUserId()))
651
			);
652
		}
653
654
		if ($member->getSingleId() !== '') {
655
			$andX->add(
656
				$expr->eq($aliasMember . '.single_id', $this->createNamedParameter($member->getSingleId()))
657
			);
658
		}
659
660
		if ($member->getUserType() > 0) {
661
			$andX->add(
662
				$expr->eq($aliasMember . '.user_type', $this->createNamedParameter($member->getUserType()))
663
			);
664
		}
665
666
		$andX->add(
667
			$expr->eq($aliasMember . '.instance', $this->createNamedParameter($this->getInstance($member)))
668
		);
669
670
		if ($member->getLevel() > 0) {
671
			$andX->add(
672
				$expr->gte(
673
					$aliasMember . '.level',
674
					$this->createNamedParameter($member->getLevel(), IQueryBuilder::PARAM_INT)
675
				)
676
			);
677
		}
678
679
		$this->andWhere($andX);
680
	}
681
682
683
	/**
684
	 * @param string $alias
685
	 */
686
	public function countMembers(string $alias): void {
687
		if ($this->getType() !== QueryBuilder::SELECT) {
688
			return;
689
		}
690
691
		try {
692
			$aliasMemberCount = $this->generateAlias($alias, self::MEMBER_COUNT, $options);
693
		} catch (RequestBuilderException $e) {
694
			return;
695
		}
696
697
		$getData = $this->getBool('getData', $options, false);
698
		if (!$getData) {
699
			return;
700
		}
701
702
		$expr = $this->expr();
703
		$this->selectAlias(
704
			$this->createFunction('COUNT(`' . $aliasMemberCount . '`.`member_id`)'),
705
			(($alias !== $this->getDefaultSelectAlias()) ? $alias . '_' : '') . 'population'
706
		);
707
		$this->leftJoin(
708
			$alias, CoreRequestBuilder::TABLE_MEMBER, $aliasMemberCount,
709
			$expr->eq($alias . '.unique_id', $aliasMemberCount . '.circle_id')
710
		);
711
	}
712
713
714
	/**
715
	 * @param string $alias
716
	 * @param IFederatedUser|null $initiator
717
	 * @param string $field
718
	 * @param string $helperAlias
719
	 *
720
	 * @throws RequestBuilderException
721
	 */
722
	public function leftJoinCircle(
723
		string $alias,
724
		?IFederatedUser $initiator = null,
725
		string $field = 'circle_id',
726
		string $helperAlias = ''
727
	): void {
728
		if ($this->getType() !== QueryBuilder::SELECT) {
729
			return;
730
		}
731
732
		$helperAlias = ($helperAlias !== '') ? $helperAlias : $alias;
733
		$aliasCircle = $this->generateAlias($alias, self::CIRCLE, $options);
734
		$getData = $this->getBool('getData', $options, false);
735
		$expr = $this->expr();
736
737
		if ($getData) {
738
			$this->generateCircleSelectAlias($aliasCircle);
739
		}
740
741
		$this->leftJoin(
742
			$helperAlias,
743
			CoreRequestBuilder::TABLE_CIRCLE,
744
			$aliasCircle,
745
			$expr->eq($aliasCircle . '.unique_id', $helperAlias . '.' . $field)
746
		);
747
748
		if (!is_null($initiator)) {
749
			$this->limitToInitiator($aliasCircle, $initiator);
750
		}
751
752
		$this->leftJoinOwner($aliasCircle);
753
	}
754
755
756
	/**
757
	 * @param string $aliasMember
758
	 *
759
	 * @throws RequestBuilderException
760
	 */
761
	public function leftJoinInvitedBy(string $aliasMember): void {
762
		if ($this->getType() !== QueryBuilder::SELECT) {
763
			return;
764
		}
765
766
		try {
767
			$aliasInvitedBy = $this->generateAlias($aliasMember, self::INVITED_BY);
768
		} catch (RequestBuilderException $e) {
769
			return;
770
		}
771
772
		$expr = $this->expr();
773
		$this->generateCircleSelectAlias($aliasInvitedBy)
774
			 ->leftJoin(
775
				 $aliasMember, CoreRequestBuilder::TABLE_CIRCLE, $aliasInvitedBy,
776
				 $expr->eq($aliasMember . '.invited_by', $aliasInvitedBy . '.unique_id')
777
			 );
778
779
		$this->leftJoinOwner($aliasInvitedBy);
780
	}
781
782
783
	/**
784
	 * @param string $aliasMember
785
	 * @param IFederatedUser|null $initiator
786
	 *
787
	 * @throws RequestBuilderException
788
	 */
789
	public function leftJoinBasedOn(
790
		string $aliasMember,
791
		?IFederatedUser $initiator = null
792
	): void {
793
		if ($this->getType() !== QueryBuilder::SELECT) {
794
			return;
795
		}
796
797
		try {
798
			$aliasBasedOn = $this->generateAlias($aliasMember, self::BASED_ON, $options);
799
		} catch (RequestBuilderException $e) {
800
			return;
801
		}
802
803
		$expr = $this->expr();
804
		$this->generateCircleSelectAlias($aliasBasedOn)
805
			 ->leftJoin(
806
				 $aliasMember, CoreRequestBuilder::TABLE_CIRCLE, $aliasBasedOn,
807
				 $expr->eq($aliasBasedOn . '.unique_id', $aliasMember . '.single_id')
808
			 );
809
810
		if (!is_null($initiator)) {
811
			$this->leftJoinInitiator($aliasBasedOn, $initiator);
812
			$this->leftJoinOwner($aliasBasedOn);
813
		}
814
	}
815
816
817
	/**
818
	 * @param string $alias
819
	 * @param string $field
820
	 *
821
	 * @throws RequestBuilderException
822
	 */
823
	public function leftJoinOwner(string $alias, string $field = 'unique_id'): void {
824
		if ($this->getType() !== QueryBuilder::SELECT) {
825
			return;
826
		}
827
828
		try {
829
			$aliasMember = $this->generateAlias($alias, self::OWNER, $options);
830
			$getData = $this->getBool('getData', $options, false);
0 ignored issues
show
Unused Code introduced by
$getData is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
831
		} catch (RequestBuilderException $e) {
832
			return;
833
		}
834
835
		$expr = $this->expr();
836
		$this->generateMemberSelectAlias($aliasMember)
837
			 ->leftJoin(
838
				 $alias, CoreRequestBuilder::TABLE_MEMBER, $aliasMember,
839
				 $expr->andX(
840
					 $expr->eq($aliasMember . '.circle_id', $alias . '.' . $field),
841
					 $expr->eq(
842
						 $aliasMember . '.level',
843
						 $this->createNamedParameter(Member::LEVEL_OWNER, self::PARAM_INT)
844
					 )
845
				 )
846
			 );
847
848
		$this->leftJoinBasedOn($aliasMember);
849
	}
850
851
852
	/**
853
	 * @param string $alias
854
	 * @param string $fieldCircleId
855
	 * @param string $fieldSingleId
856
	 *
857
	 * @throws RequestBuilderException
858
	 */
859
	public function leftJoinMember(
860
		string $alias,
861
		string $fieldCircleId = 'circle_id',
862
		string $fieldSingleId = 'single_id'
863
	): void {
864
		if ($this->getType() !== QueryBuilder::SELECT) {
865
			return;
866
		}
867
868
		try {
869
			$aliasMember = $this->generateAlias($alias, self::MEMBER, $options);
870
			$getData = $this->getBool('getData', $options, false);
0 ignored issues
show
Unused Code introduced by
$getData is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
871
		} catch (RequestBuilderException $e) {
872
			return;
873
		}
874
875
		$expr = $this->expr();
876
		$this->generateMemberSelectAlias($aliasMember)
877
			 ->leftJoin(
878
				 $alias, CoreRequestBuilder::TABLE_MEMBER, $aliasMember,
879
				 $expr->andX(
880
					 $expr->eq($aliasMember . '.circle_id', $alias . '.' . $fieldCircleId),
881
					 $expr->eq($aliasMember . '.single_id', $alias . '.' . $fieldSingleId),
882
					 $expr->gte(
883
						 $aliasMember . '.level',
884
						 $this->createNamedParameter(Member::LEVEL_MEMBER, self::PARAM_INT)
885
					 )
886
				 )
887
			 );
888
889
		$this->leftJoinRemoteInstance($aliasMember);
890
		$this->leftJoinBasedOn($aliasMember);
891
	}
892
893
894
	/**
895
	 * if 'getData' is true, will returns 'inheritanceBy': the Member at the end of a sub-chain of
896
	 * memberships (based on $field for Top Circle's singleId)
897
	 *
898
	 * @param string $alias
899
	 * @param string $field
900
	 * @param string $aliasInheritedBy
901
	 *
902
	 * @throws RequestBuilderException
903
	 */
904
	public function leftJoinInheritedMembers(
905
		string $alias,
906
		string $field = '',
907
		string $aliasInheritedBy = ''
908
	): void {
909
		$expr = $this->expr();
910
911
		$field = ($field === '') ? 'circle_id' : $field;
912
		$aliasMembership = $this->generateAlias($alias, self::MEMBERSHIPS, $options);
913
914
		$this->leftJoin(
915
			$alias, CoreRequestBuilder::TABLE_MEMBERSHIP, $aliasMembership,
916
			$expr->eq($aliasMembership . '.circle_id', $alias . '.' . $field)
917
		);
918
919
//		if (!$this->getBool('getData', $options, false)) {
920
//			return;
921
//		}
922
923
		if ($aliasInheritedBy === '') {
924
			$aliasInheritedBy = $this->generateAlias($alias, self::INHERITED_BY);
925
		}
926
		$this->generateMemberSelectAlias($aliasInheritedBy)
927
			 ->leftJoin(
928
				 $alias, CoreRequestBuilder::TABLE_MEMBER, $aliasInheritedBy,
929
				 $expr->andX(
930
					 $expr->eq($aliasMembership . '.inheritance_last', $aliasInheritedBy . '.circle_id'),
931
					 $expr->eq($aliasMembership . '.single_id', $aliasInheritedBy . '.single_id')
932
				 )
933
			 );
934
935
		$this->leftJoinBasedOn($aliasInheritedBy);
936
	}
937
938
939
	/**
940
	 * @throws RequestBuilderException
941
	 */
942
	public function limitToInheritedMemberships(string $alias, string $singleId, string $field = ''): void {
943
		$expr = $this->expr();
944
		$field = ($field === '') ? 'circle_id' : $field;
945
		$aliasUpstreamMembership = $this->generateAlias($alias, self::UPSTREAM_MEMBERSHIPS, $options);
946
		$this->leftJoin(
947
			$alias, CoreRequestBuilder::TABLE_MEMBERSHIP, $aliasUpstreamMembership,
948
			$expr->eq($aliasUpstreamMembership . '.single_id', $this->createNamedParameter($singleId))
949
		);
950
951
		$orX = $expr->orX(
952
			$expr->eq($aliasUpstreamMembership . '.circle_id', $alias . '.' . $field),
953
			$expr->eq($alias . '.' . $field, $this->createNamedParameter($singleId))
954
		);
955
956
		$this->andWhere($orX);
957
	}
958
959
960
	/**
961
	 * limit the request to Members and Sub Members of a Circle.
962
	 *
963
	 * @param string $alias
964
	 * @param string $singleId
965
	 * @param int $level
966
	 *
967
	 * @throws RequestBuilderException
968
	 */
969
	public function limitToMembersByInheritance(string $alias, string $singleId, int $level = 0): void {
970
		$this->leftJoinMembersByInheritance($alias);
971
972
		$expr = $this->expr();
973
		$aliasMembership = $this->generateAlias($alias, self::MEMBERSHIPS);
974
		$this->andWhere($expr->eq($aliasMembership . '.circle_id', $this->createNamedParameter($singleId)));
975
		if ($level > 1) {
976
			$this->andWhere(
977
				$expr->gte(
978
					$aliasMembership . '.level',
979
					$this->createNamedParameter($level, IQueryBuilder::PARAM_INT)
980
				)
981
			);
982
		}
983
	}
984
985
986
	/**
987
	 * if 'getData' is true, will returns 'inheritanceFrom': the Circle-As-Member of the Top Circle
988
	 * that explain the membership of a Member (based on $field for singleId) to a specific Circle
989
	 *
990
	 * // TODO: returns the link/path ?
991
	 *
992
	 * @param string $alias
993
	 * @param string $field
994
	 *
995
	 * @throws RequestBuilderException
996
	 */
997
	public function leftJoinMembersByInheritance(string $alias, string $field = ''): void {
998
		$expr = $this->expr();
999
1000
		$field = ($field === '') ? 'circle_id' : $field;
1001
		$aliasMembership = $this->generateAlias($alias, self::MEMBERSHIPS, $options);
1002
1003
		$this->leftJoin(
1004
			$alias, CoreRequestBuilder::TABLE_MEMBERSHIP, $aliasMembership,
1005
			$expr->andX(
1006
				$expr->eq($aliasMembership . '.inheritance_last', $alias . '.' . $field),
1007
				$expr->eq($aliasMembership . '.single_id', $alias . '.single_id')
1008
			)
1009
		);
1010
1011
		if (!$this->getBool('getData', $options, false)) {
1012
			return;
1013
		}
1014
1015
		$aliasInheritanceFrom = $this->generateAlias($alias, self::INHERITANCE_FROM);
1016
		$this->generateMemberSelectAlias($aliasInheritanceFrom)
1017
			 ->leftJoin(
1018
				 $aliasMembership, CoreRequestBuilder::TABLE_MEMBER, $aliasInheritanceFrom,
1019
				 $expr->andX(
1020
					 $expr->eq($aliasMembership . '.circle_id', $aliasInheritanceFrom . '.circle_id'),
1021
					 $expr->eq($aliasMembership . '.inheritance_first', $aliasInheritanceFrom . '.single_id')
1022
				 )
1023
			 );
1024
	}
1025
1026
1027
	/**
1028
	 * limit the result to the point of view of a FederatedUser
1029
	 *
1030
	 * @param string $alias
1031
	 * @param IFederatedUser $user
1032
	 * @param string $field
1033
	 * @param string $helperAlias
1034
	 *
1035
	 * @return ICompositeExpression
1036
	 * @throws RequestBuilderException
1037
	 */
1038
	public function limitToInitiator(
1039
		string $alias,
1040
		IFederatedUser $user,
1041
		string $field = '',
1042
		string $helperAlias = ''
1043
	): ICompositeExpression {
1044
		$this->leftJoinInitiator($alias, $user, $field, $helperAlias);
1045
		$where = $this->limitInitiatorVisibility($alias);
1046
1047
		$aliasInitiator = $this->generateAlias($alias, self::INITIATOR, $options);
1048
		if ($this->getBool('getData', $options, false)) {
1049
			$this->leftJoinBasedOn($aliasInitiator);
1050
		}
1051
1052
		return $where;
1053
	}
1054
1055
1056
	/**
1057
	 * Left join members to filter userId as initiator.
1058
	 *
1059
	 * @param string $alias
1060
	 * @param IFederatedUser $initiator
1061
	 * @param string $field
1062
	 * @param string $helperAlias
1063
	 *
1064
	 * @throws RequestBuilderException
1065
	 */
1066
	public function leftJoinInitiator(
1067
		string $alias,
1068
		IFederatedUser $initiator,
1069
		string $field = '',
1070
		string $helperAlias = ''
1071
	): void {
1072
		if ($this->getType() !== QueryBuilder::SELECT) {
1073
			return;
1074
		}
1075
1076
		$expr = $this->expr();
1077
		$field = ($field === '') ? 'unique_id' : $field;
1078
		$helperAlias = ($helperAlias !== '') ? $helperAlias : $alias;
1079
		$aliasMembership = $this->generateAlias($alias, self::MEMBERSHIPS, $options);
1080
1081
		$this->leftJoin(
1082
			$helperAlias,
1083
			CoreRequestBuilder::TABLE_MEMBERSHIP,
1084
			$aliasMembership,
1085
			$expr->andX(
1086
				$this->exprLimit('single_id', $initiator->getSingleId(), $aliasMembership),
1087
				$expr->eq($aliasMembership . '.circle_id', $helperAlias . '.' . $field)
1088
			)
1089
		);
1090
1091
		if (!$this->getBool('getData', $options, false)) {
1092
			return;
1093
		}
1094
1095
		// bypass memberships
1096
		if ($this->getBool('initiatorDirectMember', $options, false)) {
1097
			try {
1098
				$aliasDirectInitiator = $this->generateAlias($alias, self::DIRECT_INITIATOR, $options);
1099
				$this->leftJoin(
1100
					$helperAlias,
1101
					CoreRequestBuilder::TABLE_MEMBER,
1102
					$aliasDirectInitiator,
1103
					$expr->andX(
1104
						$this->exprLimit('single_id', $initiator->getSingleId(), $aliasDirectInitiator),
1105
						$expr->eq($aliasDirectInitiator . '.circle_id', $helperAlias . '.' . $field)
1106
					)
1107
				);
1108
			} catch (RequestBuilderException $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
1109
			}
1110
		}
1111
1112
1113
		try {
1114
			$aliasInitiator = $this->generateAlias($alias, self::INITIATOR, $options);
1115
			$this->leftJoin(
1116
				$aliasMembership, CoreRequestBuilder::TABLE_MEMBER, $aliasInitiator,
1117
				$expr->andX(
1118
					$expr->eq($aliasMembership . '.inheritance_first', $aliasInitiator . '.single_id'),
1119
					$expr->eq($aliasMembership . '.circle_id', $aliasInitiator . '.circle_id')
1120
				)
1121
			);
1122
1123
			$aliasInheritedBy = $this->generateAlias($aliasInitiator, self::INHERITED_BY);
1124
			$this->leftJoin(
1125
				$aliasInitiator, CoreRequestBuilder::TABLE_MEMBER, $aliasInheritedBy,
1126
				$expr->andX(
1127
					$expr->eq($aliasMembership . '.single_id', $aliasInheritedBy . '.single_id'),
1128
					$expr->eq($aliasMembership . '.inheritance_last', $aliasInheritedBy . '.circle_id')
1129
				)
1130
			);
1131
1132
			$default = [];
1133
			if ($this->getBool('canBeVisitor', $options, false)) {
1134
				$default = [
1135
					'user_id'     => $initiator->getUserId(),
1136
					'single_id'   => $initiator->getSingleId(),
1137
					'user_type'   => $initiator->getUserType(),
1138
					'cached_name' => $initiator->getDisplayName(),
1139
					'instance'    => $initiator->getInstance()
1140
				];
1141
			}
1142
			$this->generateMemberSelectAlias($aliasInitiator, $default);
1143
1144
			$this->generateMemberSelectAlias($aliasInheritedBy);
1145
			$aliasInheritedByMembership = $this->generateAlias($aliasInheritedBy, self::MEMBERSHIPS);
1146
			$this->generateMembershipSelectAlias($aliasMembership, $aliasInheritedByMembership);
1147
		} catch (RequestBuilderException $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
1148
		}
1149
	}
1150
1151
1152
	/**
1153
	 * @param string $alias
1154
	 *
1155
	 * @return ICompositeExpression
1156
	 * @throws RequestBuilderException
1157
	 */
1158
	protected function limitInitiatorVisibility(string $alias): ICompositeExpression {
1159
		$aliasMembership = $this->generateAlias($alias, self::MEMBERSHIPS, $options);
1160
		$levelCheck = [$aliasMembership];
1161
1162
		if ($this->getBool('initiatorDirectMember', $options, false)) {
1163
			array_push($levelCheck, $this->generateAlias($alias, self::DIRECT_INITIATOR, $options));
1164
		}
1165
1166
		$getPersonalCircle = $this->getBool('getPersonalCircle', $options, false);
1167
1168
		$expr = $this->expr();
1169
1170
		// Visibility to non-member is
1171
		// - 0 (default), if initiator is member
1172
		// - 2 (Personal), if initiator is owner)
1173
		// - 4 (Visible to everyone)
1174
		$orX = $expr->orX();
1175
1176
		$orXLevel = $expr->orX();
0 ignored issues
show
Unused Code introduced by
$orXLevel is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
1177
		$orX->add(
1178
			$expr->andX(
1179
				$this->orXCheckLevel($levelCheck, Member::LEVEL_MEMBER),
1180
				$this->exprFilterBitwise('config', Circle::CFG_PERSONAL, $alias)
1181
			)
1182
		);
1183
1184
		if ($getPersonalCircle) {
1185
			$orX->add(
1186
				$expr->andX(
1187
					$this->exprLimitBitwise('config', Circle::CFG_PERSONAL, $alias),
1188
					$this->orXCheckLevel($levelCheck, Member::LEVEL_OWNER)
1189
				)
1190
			);
1191
		}
1192
1193
		if (!$this->getBool('mustBeMember', $options, true)) {
1194
			$orX->add($this->exprLimitBitwise('config', Circle::CFG_VISIBLE, $alias));
1195
		}
1196
		if ($this->getBool('canBeVisitor', $options, false)) {
1197
			// TODO: should find a better way, also filter on remote initiator on non-federated ?
1198
			$orX->add($expr->gte($alias . '.config', $this->createNamedParameter(0)));
1199
		}
1200
		if ($this->getBool('canBeVisitorOnOpen', $options, false)) {
1201
			$andOpen = $expr->andX();
1202
			$andOpen->add($this->exprLimitBitwise('config', Circle::CFG_OPEN, $alias));
1203
			$andOpen->add($this->exprFilterBitwise('config', Circle::CFG_REQUEST, $alias));
1204
			$orX->add($andOpen);
1205
		}
1206
1207
		$this->andWhere($orX);
1208
1209
		return $orX;
1210
//		$orTypes = $this->generateLimit($qb, $circleUniqueId, $userId, $type, $name, $forceAll);
1211
//		if (sizeof($orTypes) === 0) {
1212
//			throw new ConfigNoCircleAvailableException(
1213
//				$this->l10n->t(
1214
//					'You cannot use the Circles Application until your administrator has allowed at least one type of circles'
1215
//				)
1216
//			);
1217
//		}
1218
1219
//		$orXTypes = $this->expr()
1220
//						 ->orX();
1221
//		foreach ($orTypes as $orType) {
1222
//			$orXTypes->add($orType);
1223
//		}
1224
//
1225
//		$qb->andWhere($orXTypes);
1226
	}
1227
1228
1229
	/**
1230
	 * CFG_SINGLE, CFG_HIDDEN and CFG_BACKEND means hidden from listing.
1231
	 *
1232
	 * @param string $aliasCircle
1233
	 * @param int $flag
1234
	 */
1235
	public function filterCircles(
1236
		string $aliasCircle,
1237
		int $flag = Circle::CFG_SINGLE | Circle::CFG_HIDDEN | Circle::CFG_BACKEND
1238
	): void {
1239
		if ($flag === 0) {
1240
			return;
1241
		}
1242
1243
		$expr = $this->expr();
1244
		$hide = $expr->andX();
1245
		foreach (Circle::$DEF_CFG as $cfg => $v) {
1246
			if ($flag & $cfg) {
1247
				$hide->add($this->exprFilterBitwise('config', $cfg, $aliasCircle));
1248
			}
1249
		}
1250
1251
		$this->andWhere($hide);
1252
	}
1253
1254
1255
	/**
1256
	 * Limit visibility on Sensitive information when search for members.
1257
	 *
1258
	 * @param string $alias
1259
	 *
1260
	 * @return ICompositeExpression
1261
	 */
1262
	private function limitRemoteVisibility_Sensitive_Members(string $alias): ICompositeExpression {
1263
		$expr = $this->expr();
1264
		$andPassive = $expr->andX();
1265
		$andPassive->add(
1266
			$expr->eq($alias . '.type', $this->createNamedParameter(RemoteInstance::TYPE_PASSIVE))
1267
		);
1268
1269
		$orMemberOrLevel = $expr->orX();
1270
		$orMemberOrLevel->add(
1271
			$expr->eq($this->getDefaultSelectAlias() . '.instance', $alias . '.instance')
1272
		);
1273
		// TODO: do we need this ? (display members from the local instance)
1274
		$orMemberOrLevel->add(
1275
			$expr->emptyString($this->getDefaultSelectAlias() . '.instance')
1276
		);
1277
1278
		$orMemberOrLevel->add(
1279
			$expr->eq(
1280
				$this->getDefaultSelectAlias() . '.level',
1281
				$this->createNamedParameter(Member::LEVEL_OWNER)
1282
			)
1283
		);
1284
		$andPassive->add($orMemberOrLevel);
1285
1286
		return $andPassive;
1287
	}
1288
1289
1290
	/**
1291
	 * Link to storage/filecache
1292
	 *
1293
	 * @param string $aliasShare
1294
	 *
1295
	 * @throws RequestBuilderException
1296
	 */
1297
	public function leftJoinFileCache(string $aliasShare) {
1298
		$expr = $this->expr();
1299
1300
		$aliasFileCache = $this->generateAlias($aliasShare, self::FILE_CACHE);
1301
		$aliasStorages = $this->generateAlias($aliasFileCache, self::STORAGES);
1302
1303
		$this->generateSelectAlias(
1304
			CoreRequestBuilder::$outsideTables[CoreRequestBuilder::TABLE_FILE_CACHE],
0 ignored issues
show
Bug introduced by
The property outsideTables cannot be accessed from this context as it is declared private in class OCA\Circles\Db\CoreRequestBuilder.

This check looks for access to properties that are not accessible from the current context.

If you need to make a property accessible to another context you can either raise its visibility level or provide an accessible getter in the defining class.

Loading history...
1305
			$aliasFileCache,
1306
			$aliasFileCache,
1307
			[]
1308
		)
1309
			 ->generateSelectAlias(
1310
				 CoreRequestBuilder::$outsideTables[CoreRequestBuilder::TABLE_STORAGES],
0 ignored issues
show
Bug introduced by
The property outsideTables cannot be accessed from this context as it is declared private in class OCA\Circles\Db\CoreRequestBuilder.

This check looks for access to properties that are not accessible from the current context.

If you need to make a property accessible to another context you can either raise its visibility level or provide an accessible getter in the defining class.

Loading history...
1311
				 $aliasStorages,
1312
				 $aliasStorages,
1313
				 []
1314
			 )
1315
			 ->leftJoin(
1316
				 $aliasShare, CoreRequestBuilder::TABLE_FILE_CACHE, $aliasFileCache,
1317
				 $expr->eq($aliasShare . '.file_source', $aliasFileCache . '.fileid')
1318
			 )
1319
			 ->leftJoin(
1320
				 $aliasFileCache, CoreRequestBuilder::TABLE_STORAGES, $aliasStorages,
1321
				 $expr->eq($aliasFileCache . '.storage', $aliasStorages . '.numeric_id')
1322
			 );
1323
	}
1324
1325
1326
	/**
1327
	 * @param string $aliasShare
1328
	 * @param string $aliasShareMemberships
1329
	 *
1330
	 * @throws RequestBuilderException
1331
	 */
1332
	public function leftJoinShareChild(string $aliasShare, string $aliasShareMemberships = '') {
1333
		$expr = $this->expr();
1334
1335
		$aliasShareChild = $this->generateAlias($aliasShare, self::SHARE);
1336
		if ($aliasShareMemberships === '') {
1337
			$aliasShareMemberships = $this->generateAlias($aliasShare, self::MEMBERSHIPS, $options);
1338
		}
1339
1340
		$this->leftJoin(
1341
			$aliasShareMemberships, CoreRequestBuilder::TABLE_SHARE, $aliasShareChild,
1342
			$expr->andX(
1343
				$expr->eq($aliasShareChild . '.parent', $aliasShare . '.id'),
1344
				$expr->eq($aliasShareChild . '.share_with', $aliasShareMemberships . '.single_id')
1345
			)
1346
		);
1347
1348
		$this->generateSelectAlias(
1349
			['id', 'file_target', 'permissions'],
1350
			$aliasShareChild,
1351
			'child_',
1352
			[]
1353
		);
1354
1355
//		$this->selectAlias($aliasShareParent . '.permissions', 'parent_perms');
1356
	}
1357
1358
1359
	/**
1360
	 * @param string $alias
1361
	 * @param FederatedUser $federatedUser
1362
	 * @param bool $reshares
1363
	 */
1364
	public function limitToShareOwner(string $alias, FederatedUser $federatedUser, bool $reshares): void {
1365
		$expr = $this->expr();
1366
1367
		$orX = $expr->orX($this->exprLimit('uid_initiator', $federatedUser->getUserId(), $alias));
1368
1369
		if ($reshares) {
1370
			$orX->add($this->exprLimit('uid_owner', $federatedUser->getUserId(), $alias));
1371
		}
1372
1373
		$this->andWhere($orX);
1374
	}
1375
1376
1377
	/**
1378
	 * @param string $aliasMount
1379
	 * @param string $aliasMountMemberships
1380
	 *
1381
	 * @throws RequestBuilderException
1382
	 */
1383
	public function leftJoinMountpoint(string $aliasMount, string $aliasMountMemberships = '') {
1384
		$expr = $this->expr();
1385
1386
		$aliasMountpoint = $this->generateAlias($aliasMount, self::MOUNTPOINT);
1387
		if ($aliasMountMemberships === '') {
1388
			$aliasMountMemberships = $this->generateAlias($aliasMount, self::MEMBERSHIPS, $options);
1389
		}
1390
1391
		$this->leftJoin(
1392
			$aliasMountMemberships, CoreRequestBuilder::TABLE_MOUNTPOINT, $aliasMountpoint,
1393
			$expr->andX(
1394
				$expr->eq($aliasMountpoint . '.mount_id', $aliasMount . '.mount_id'),
1395
				$expr->eq($aliasMountpoint . '.single_id', $aliasMountMemberships . '.single_id')
1396
			)
1397
		);
1398
1399
		$this->selectAlias($aliasMountpoint . '.mountpoint', $aliasMountpoint . '_mountpoint');
1400
		$this->selectAlias($aliasMountpoint . '.mountpoint_hash', $aliasMountpoint . '_mountpoint_hash');
1401
	}
1402
1403
1404
	/**
1405
	 * @param string $alias
1406
	 * @param array $default
1407
	 *
1408
	 * @return CoreQueryBuilder
1409
	 */
1410
	private function generateCircleSelectAlias(string $alias, array $default = []): self {
1411
		$this->generateSelectAlias(
1412
			CoreRequestBuilder::$tables[CoreRequestBuilder::TABLE_CIRCLE],
0 ignored issues
show
Bug introduced by
The property tables cannot be accessed from this context as it is declared private in class OCA\Circles\Db\CoreRequestBuilder.

This check looks for access to properties that are not accessible from the current context.

If you need to make a property accessible to another context you can either raise its visibility level or provide an accessible getter in the defining class.

Loading history...
1413
			$alias,
1414
			$alias,
1415
			$default
1416
		);
1417
1418
		return $this;
1419
	}
1420
1421
	/**
1422
	 * @param string $alias
1423
	 * @param array $default
1424
	 *
1425
	 * @return $this
1426
	 */
1427
	private function generateMemberSelectAlias(string $alias, array $default = []): self {
1428
		$this->generateSelectAlias(
1429
			CoreRequestBuilder::$tables[CoreRequestBuilder::TABLE_MEMBER],
0 ignored issues
show
Bug introduced by
The property tables cannot be accessed from this context as it is declared private in class OCA\Circles\Db\CoreRequestBuilder.

This check looks for access to properties that are not accessible from the current context.

If you need to make a property accessible to another context you can either raise its visibility level or provide an accessible getter in the defining class.

Loading history...
1430
			$alias,
1431
			$alias,
1432
			$default
1433
		);
1434
1435
		return $this;
1436
	}
1437
1438
1439
	/**
1440
	 * @param string $alias
1441
	 * @param array $default
1442
	 * @param string $prefix
1443
	 *
1444
	 * @return $this
1445
	 */
1446
	private function generateMembershipSelectAlias(
1447
		string $alias,
1448
		string $prefix = '',
1449
		array $default = []
1450
	): self {
1451
		$this->generateSelectAlias(
1452
			CoreRequestBuilder::$tables[CoreRequestBuilder::TABLE_MEMBERSHIP],
0 ignored issues
show
Bug introduced by
The property tables cannot be accessed from this context as it is declared private in class OCA\Circles\Db\CoreRequestBuilder.

This check looks for access to properties that are not accessible from the current context.

If you need to make a property accessible to another context you can either raise its visibility level or provide an accessible getter in the defining class.

Loading history...
1453
			$alias,
1454
			($prefix === '') ? $alias : $prefix,
1455
			$default
1456
		);
1457
1458
		return $this;
1459
	}
1460
1461
1462
	/**
1463
	 * @param string $alias
1464
	 * @param array $default
1465
	 *
1466
	 * @return $this
1467
	 */
1468
	private function generateRemoteInstanceSelectAlias(string $alias, array $default = []): self {
1469
		$this->generateSelectAlias(
1470
			CoreRequestBuilder::$tables[CoreRequestBuilder::TABLE_REMOTE],
0 ignored issues
show
Bug introduced by
The property tables cannot be accessed from this context as it is declared private in class OCA\Circles\Db\CoreRequestBuilder.

This check looks for access to properties that are not accessible from the current context.

If you need to make a property accessible to another context you can either raise its visibility level or provide an accessible getter in the defining class.

Loading history...
1471
			$alias,
1472
			$alias,
1473
			$default
1474
		);
1475
1476
		return $this;
1477
	}
1478
1479
1480
	/**
1481
	 * @param array $path
1482
	 * @param array $options
1483
	 */
1484
	public function setOptions(array $path, array $options): void {
1485
		$options = [self::OPTIONS => $options];
1486
		foreach (array_reverse($path) as $item) {
1487
			$options = [$item => $options];
1488
		}
1489
1490
		$this->options = $options;
1491
	}
1492
1493
1494
	/**
1495
	 * @param string $base
1496
	 * @param string $extension
1497
	 * @param array|null $options
1498
	 *
1499
	 * @return string
1500
	 * @throws RequestBuilderException
1501
	 */
1502
	public function generateAlias(string $base, string $extension, ?array &$options = []): string {
1503
		$search = str_replace('_', '.', $base);
1504
		$path = $search . '.' . $extension;
1505
		if (!$this->validKey($path, self::$SQL_PATH)
1506
			&& !in_array($extension, $this->getArray($search, self::$SQL_PATH))) {
1507
			throw new RequestBuilderException($extension . ' not found in ' . $search);
1508
		}
1509
1510
		if (!is_array($options)) {
1511
			$options = [];
1512
		}
1513
1514
		$optionPath = '';
1515
		foreach (explode('.', $path) as $p) {
1516
			$optionPath = trim($optionPath . '.' . $p, '.');
1517
			$options = array_merge(
1518
				$options,
1519
				$this->getArray($optionPath . '.' . self::OPTIONS, self::$SQL_PATH),
1520
				$this->getArray($optionPath . '.' . self::OPTIONS, $this->options)
1521
			);
1522
		}
1523
1524
		return $base . '_' . $extension;
1525
	}
1526
1527
1528
	/**
1529
	 * @param string $prefix
1530
	 *
1531
	 * @return array
1532
	 */
1533
	public function getAvailablePath(string $prefix): array {
1534
		$prefix = trim($prefix, '_');
1535
		$search = str_replace('_', '.', $prefix);
1536
1537
		$path = [];
1538
		foreach ($this->getArray($search, self::$SQL_PATH) as $arr => $item) {
1539
			if (is_numeric($arr)) {
1540
				$k = $item;
1541
			} else {
1542
				$k = $arr;
1543
			}
1544
			$path[$k] = $prefix . '_' . $k . '_';
1545
		}
1546
1547
		return $path;
1548
	}
1549
1550
1551
	/**
1552
	 * @param array $aliases
1553
	 * @param int $level
1554
	 *
1555
	 * @return ICompositeExpression
1556
	 */
1557
	private function orXCheckLevel(array $aliases, int $level): ICompositeExpression {
1558
		$expr = $this->expr();
1559
		$orX = $expr->orX();
1560
		foreach ($aliases as $alias) {
1561
			$orX->add($expr->gte($alias . '.level', $this->createNamedParameter($level)));
1562
		}
1563
1564
		return $orX;
1565
	}
1566
	
1567
}
1568
1569