Completed
Pull Request — master (#640)
by Maxence
02:41
created

CoreQueryBuilder::leftJoinMember()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 33

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 33
rs 9.392
c 0
b 0
f 0
cc 3
nc 4
nop 3
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 MEMBERSHIPS = 'memberships';
72
	const CONFIG = 'config';
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
			],
93
			self::MEMBER,
94
			self::MEMBER_COUNT,
95
			self::OWNER       => [
96
				self::BASED_ON
97
			],
98
			self::MEMBERSHIPS => [
99
				self::CONFIG
100
			],
101
			self::INITIATOR   => [
102
				self::BASED_ON,
103
				self::INHERITED_BY => [
104
					self::MEMBERSHIPS
105
				]
106
			],
107
			self::REMOTE      => [
108
				self::MEMBER,
109
				self::CIRCLE => [
110
					self::OWNER
111
				]
112
			]
113
		],
114
		self::MEMBER      => [
115
			self::MEMBERSHIPS => [
116
				self::CONFIG
117
			],
118
			self::INHERITANCE_FROM,
119
			self::CIRCLE      => [
120
				self::OPTIONS     => [
121
					'getData' => true
122
				],
123
				self::OWNER,
124
				self::MEMBERSHIPS => [
125
					self::CONFIG
126
				],
127
				self::INITIATOR   => [
128
					self::OPTIONS      => [
129
						'mustBeMember' => true,
130
						'canBeVisitor' => false
131
					],
132
					self::BASED_ON,
133
					self::INHERITED_BY => [
134
						self::MEMBERSHIPS
135
					],
136
					self::INVITED_BY   => [
137
						self::OWNER,
138
						self::BASED_ON
139
					]
140
				]
141
			],
142
			self::BASED_ON    => [
143
				self::OWNER,
144
				self::MEMBERSHIPS,
145
				self::INITIATOR => [
146
					self::BASED_ON,
147
					self::INHERITED_BY => [
148
						self::MEMBERSHIPS
149
					]
150
				]
151
			],
152
			self::REMOTE      => [
153
				self::MEMBER,
154
				self::CIRCLE => [
155
					self::OWNER
156
				]
157
			],
158
			self::INVITED_BY  => [
159
				self::OWNER,
160
				self::BASED_ON
161
			]
162
		],
163
		self::MEMBERSHIPS => [
164
			self::CONFIG
165
		],
166
		self::SHARE       => [
167
			self::SHARE,
168
			self::FILE_CACHE           => [
169
				self::STORAGES
170
			],
171
			self::UPSTREAM_MEMBERSHIPS => [
172
				self::MEMBERSHIPS,
173
				self::INHERITED_BY => [
174
					self::BASED_ON
175
				],
176
				self::SHARE,
177
			],
178
			self::MEMBERSHIPS          => [
179
				self::CONFIG
180
			],
181
			self::INHERITANCE_FROM,
182
			self::INHERITED_BY         => [
183
				self::BASED_ON
184
			],
185
			self::CIRCLE               => [
186
				self::OWNER
187
			],
188
			self::INITIATOR            => [
189
				self::BASED_ON,
190
				self::INHERITED_BY => [
191
					self::MEMBERSHIPS
192
				]
193
			]
194
		],
195
		self::REMOTE      => [
196
			self::MEMBER
197
		],
198
		self::MOUNT       => [
199
			self::MEMBER      => [
200
				self::REMOTE
201
			],
202
			self::INITIATOR   => [
203
				self::INHERITED_BY => [
204
					self::MEMBERSHIPS
205
				]
206
			],
207
			self::MOUNTPOINT,
208
			self::MEMBERSHIPS => [
209
				self::CONFIG
210
			]
211
		],
212
		self::HELPER      => [
213
			self::MEMBERSHIPS => [
214
				self::CONFIG
215
			],
216
			self::INITIATOR   => [
217
				self::INHERITED_BY => [
218
					self::MEMBERSHIPS
219
				]
220
			],
221
			self::CIRCLE      => [
222
				self::OPTIONS => [
223
				],
224
				self::MEMBER,
225
				self::OWNER   => [
226
					self::BASED_ON
227
				]
228
			]
229
		]
230
	];
231
232
233
	/** @var ConfigService */
234
	private $configService;
235
236
237
	/** @var array */
238
	private $options = [];
239
240
241
	/**
242
	 * CoreQueryBuilder constructor.
243
	 */
244
	public function __construct() {
245
		parent::__construct();
246
247
		$this->configService = OC::$server->get(ConfigService::class);
248
	}
249
250
251
	/**
252
	 * @param IFederatedModel $federatedModel
253
	 *
254
	 * @return string
255
	 */
256
	public function getInstance(IFederatedModel $federatedModel): string {
257
		$instance = $federatedModel->getInstance();
258
259
		return ($this->configService->isLocalInstance($instance)) ? '' : $instance;
260
	}
261
262
263
	/**
264
	 * @param string $id
265
	 */
266
	public function limitToCircleId(string $id): void {
267
		$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...
268
	}
269
270
	/**
271
	 * @param string $name
272
	 */
273
	public function limitToName(string $name): void {
274
		$this->limit('name', $name);
275
	}
276
277
	/**
278
	 * @param string $name
279
	 */
280
	public function limitToDisplayName(string $name): void {
281
		$this->limit('display_name', $name, '', false);
282
	}
283
284
	/**
285
	 * @param string $name
286
	 */
287
	public function limitToSanitizedName(string $name): void {
288
		$this->limit('sanitized_name', $name, '', false);
289
	}
290
291
	/**
292
	 * @param int $config
293
	 */
294
	public function limitToConfig(int $config): void {
295
		$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...
296
	}
297
298
	/**
299
	 * @param int $source
300
	 */
301
	public function limitToSource(int $source): void {
302
		$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...
303
	}
304
305
	/**
306
	 * @param int $config
307
	 * @param string $alias
308
	 */
309
	public function limitToConfigFlag(int $config, string $alias = ''): void {
310
		$this->limitBitwise('config', $config, $alias);
311
	}
312
313
314
	/**
315
	 * @param string $singleId
316
	 */
317
	public function limitToSingleId(string $singleId): void {
318
		$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...
319
	}
320
321
322
	/**
323
	 * @param string $itemId
324
	 */
325
	public function limitToItemId(string $itemId): void {
326
		$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...
327
	}
328
329
330
	/**
331
	 * @param string $host
332
	 */
333
	public function limitToInstance(string $host): void {
334
		$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...
335
	}
336
337
338
	/**
339
	 * @param int $userType
340
	 */
341
	public function limitToUserType(int $userType): void {
342
		$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...
343
	}
344
345
346
	/**
347
	 * @param int $shareType
348
	 */
349
	public function limitToShareType(int $shareType): void {
350
		$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...
351
	}
352
353
354
	/**
355
	 * @param string $shareWith
356
	 */
357
	public function limitToShareWith(string $shareWith): void {
358
		$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...
359
	}
360
361
362
	/**
363
	 * @param int $nodeId
364
	 */
365
	public function limitToFileSource(int $nodeId): void {
366
		$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...
367
	}
368
369
	/**
370
	 * @param array $files
371
	 */
372
	public function limitToFileSourceArray(array $files): void {
373
		$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...
374
	}
375
376
377
	/**
378
	 * @param int $shareId
379
	 */
380
	public function limitToShareParent(int $shareId): void {
381
		$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...
382
	}
383
384
385
	/**
386
	 * @param Circle $circle
387
	 */
388
	public function filterCircle(Circle $circle): void {
389
		if ($this->getType() !== QueryBuilder::SELECT) {
390
			return;
391
		}
392
393
		if ($circle->getDisplayName() !== '') {
394
			$this->searchInDBField('display_name', '%' . $circle->getDisplayName() . '%');
395
		}
396
		if ($circle->getConfig() > 0) {
397
			$this->limitBitwise('config', $circle->getConfig());
398
		}
399
	}
400
401
402
	/**
403
	 * left join RemoteInstance based on a Member
404
	 */
405
	public function leftJoinRemoteInstance(string $alias): void {
406
		$expr = $this->expr();
407
408
		try {
409
			$aliasRemoteInstance = $this->generateAlias($alias, self::REMOTE);
410
			$this->generateRemoteInstanceSelectAlias($aliasRemoteInstance)
411
				 ->leftJoin(
412
					 $alias, CoreRequestBuilder::TABLE_REMOTE, $aliasRemoteInstance,
413
					 $expr->eq($alias . '.instance', $aliasRemoteInstance . '.instance')
414
				 );
415
		} catch (RequestBuilderException $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
416
		}
417
	}
418
419
420
	/**
421
	 * @param string $alias
422
	 * @param RemoteInstance $remoteInstance
423
	 * @param bool $filterSensitiveData
424
	 * @param string $aliasCircle
425
	 *
426
	 * @throws RequestBuilderException
427
	 */
428
	public function limitToRemoteInstance(
429
		string $alias,
430
		RemoteInstance $remoteInstance,
431
		bool $filterSensitiveData = true,
432
		string $aliasCircle = ''
433
	): void {
434
435
		if ($aliasCircle === '') {
436
			$aliasCircle = $alias;
437
		}
438
439
		$this->leftJoinRemoteInstanceIncomingRequest($alias, $remoteInstance);
440
		$this->leftJoinMemberFromInstance($alias, $remoteInstance, $aliasCircle);
441
		$this->leftJoinMemberFromRemoteCircle($alias, $remoteInstance, $aliasCircle);
442
		$this->limitRemoteVisibility($alias, $filterSensitiveData, $aliasCircle);
443
	}
444
445
446
	/**
447
	 * Left join RemoteInstance based on an incoming request
448
	 *
449
	 * @param string $alias
450
	 * @param RemoteInstance $remoteInstance
451
	 *
452
	 * @throws RequestBuilderException
453
	 */
454
	public function leftJoinRemoteInstanceIncomingRequest(
455
		string $alias,
456
		RemoteInstance $remoteInstance
457
	): void {
458
		if ($this->getType() !== QueryBuilder::SELECT) {
459
			return;
460
		}
461
462
		$aliasRemote = $this->generateAlias($alias, self::REMOTE);
463
		$expr = $this->expr();
464
		$this->leftJoin(
465
			$this->getDefaultSelectAlias(), CoreRequestBuilder::TABLE_REMOTE, $aliasRemote,
466
			$expr->eq($aliasRemote . '.instance', $this->createNamedParameter($remoteInstance->getInstance()))
467
		);
468
	}
469
470
471
	/**
472
	 * left join members to check memberships of someone from instance
473
	 *
474
	 * @param string $alias
475
	 * @param RemoteInstance $remoteInstance
476
	 * @param string $aliasCircle
477
	 *
478
	 * @throws RequestBuilderException
479
	 */
480
	private function leftJoinMemberFromInstance(
481
		string $alias, RemoteInstance $remoteInstance, string $aliasCircle
482
	) {
483
		if ($this->getType() !== QueryBuilder::SELECT) {
484
			return;
485
		}
486
487
		$aliasRemote = $this->generateAlias($alias, self::REMOTE);
488
		$aliasRemoteMember = $this->generateAlias($aliasRemote, self::MEMBER);
489
490
		$expr = $this->expr();
491
		$this->leftJoin(
492
			$this->getDefaultSelectAlias(), CoreRequestBuilder::TABLE_MEMBER, $aliasRemoteMember,
493
			$expr->andX(
494
				$expr->eq($aliasRemoteMember . '.circle_id', $aliasCircle . '.unique_id'),
495
				$expr->eq(
496
					$aliasRemoteMember . '.instance',
497
					$this->createNamedParameter($remoteInstance->getInstance())
498
				),
499
				$expr->gte($aliasRemoteMember . '.level', $this->createNamedParameter(Member::LEVEL_MEMBER))
500
			)
501
		);
502
	}
503
504
505
	/**
506
	 * left join circle is member of a circle from remote instance
507
	 *
508
	 * @param string $alias
509
	 * @param RemoteInstance $remoteInstance
510
	 * @param string $aliasCircle
511
	 *
512
	 * @throws RequestBuilderException
513
	 */
514
	private function leftJoinMemberFromRemoteCircle(
515
		string $alias,
516
		RemoteInstance $remoteInstance,
517
		string $aliasCircle
518
	) {
519
		if ($this->getType() !== QueryBuilder::SELECT) {
520
			return;
521
		}
522
523
		$aliasRemote = $this->generateAlias($alias, self::REMOTE);
524
		$aliasRemoteCircle = $this->generateAlias($aliasRemote, self::CIRCLE);
525
		$aliasRemoteCircleOwner = $this->generateAlias($aliasRemoteCircle, self::OWNER);
526
527
		$expr = $this->expr();
528
		$this->leftJoin(
529
			$this->getDefaultSelectAlias(), CoreRequestBuilder::TABLE_MEMBER, $aliasRemoteCircle,
530
			$expr->andX(
531
				$expr->eq($aliasRemoteCircle . '.single_id', $aliasCircle . '.unique_id'),
532
				$expr->emptyString($aliasRemoteCircle . '.instance'),
533
				$expr->gte($aliasRemoteCircle . '.level', $this->createNamedParameter(Member::LEVEL_MEMBER))
534
			)
535
		);
536
		$this->leftJoin(
537
			$this->getDefaultSelectAlias(), CoreRequestBuilder::TABLE_MEMBER, $aliasRemoteCircleOwner,
538
			$expr->andX(
539
				$expr->eq($aliasRemoteCircle . '.circle_id', $aliasRemoteCircleOwner . '.circle_id'),
540
				$expr->eq(
541
					$aliasRemoteCircleOwner . '.instance',
542
					$this->createNamedParameter($remoteInstance->getInstance())
543
				),
544
				$expr->eq(
545
					$aliasRemoteCircleOwner . '.level', $this->createNamedParameter(Member::LEVEL_OWNER)
546
				)
547
			)
548
		);
549
	}
550
551
552
	/**
553
	 * - global_scale: visibility on all Circles
554
	 * - trusted: visibility on all FEDERATED Circle if owner is local
555
	 * - external: visibility on all FEDERATED Circle if owner is local and:
556
	 *    - with if Circle contains at least one member from the remote instance
557
	 *    - one circle from the remote instance contains the local circle as member, and confirmed (using
558
	 *      sync locally)
559
	 * - passive: like external, but the members list will only contains member from the local instance and
560
	 * from the remote instance.
561
	 *
562
	 * @param string $alias
563
	 * @param bool $sensitive
564
	 * @param string $aliasCircle
565
	 *
566
	 * @throws RequestBuilderException
567
	 */
568
	protected function limitRemoteVisibility(string $alias, bool $sensitive, string $aliasCircle) {
569
		$aliasRemote = $this->generateAlias($alias, self::REMOTE);
570
		$aliasOwner = $this->generateAlias($aliasCircle, self::OWNER);
571
		$aliasRemoteMember = $this->generateAlias($aliasRemote, self::MEMBER);
572
		$aliasRemoteCircle = $this->generateAlias($aliasRemote, self::CIRCLE);
573
		$aliasRemoteCircleOwner = $this->generateAlias($aliasRemoteCircle, self::OWNER);
574
575
		$expr = $this->expr();
576
		$orX = $expr->orX();
577
		$orX->add(
578
			$expr->eq($aliasRemote . '.type', $this->createNamedParameter(RemoteInstance::TYPE_GLOBALSCALE))
579
		);
580
581
		$orExtOrPassive = $expr->orX();
582
		$orExtOrPassive->add(
583
			$expr->eq($aliasRemote . '.type', $this->createNamedParameter(RemoteInstance::TYPE_EXTERNAL))
584
		);
585
		if (!$sensitive) {
586
			$orExtOrPassive->add(
587
				$expr->eq($aliasRemote . '.type', $this->createNamedParameter(RemoteInstance::TYPE_PASSIVE))
588
			);
589
		} else {
590
			if ($this->getDefaultSelectAlias() === CoreQueryBuilder::MEMBER) {
591
				$orExtOrPassive->add($this->limitRemoteVisibility_Sensitive_Members($aliasRemote));
592
			}
593
		}
594
595
		$orInstance = $expr->orX();
596
		$orInstance->add($expr->isNotNull($aliasRemoteMember . '.instance'));
597
		$orInstance->add($expr->isNotNull($aliasRemoteCircleOwner . '.instance'));
598
599
		$andExternal = $expr->andX();
600
		$andExternal->add($orExtOrPassive);
601
		$andExternal->add($orInstance);
602
603
		$orExtOrTrusted = $expr->orX();
604
		$orExtOrTrusted->add($andExternal);
605
		$orExtOrTrusted->add(
606
			$expr->eq($aliasRemote . '.type', $this->createNamedParameter(RemoteInstance::TYPE_TRUSTED))
607
		);
608
609
		$andTrusted = $expr->andX();
610
		$andTrusted->add($orExtOrTrusted);
611
		$andTrusted->add($this->exprLimitBitwise('config', Circle::CFG_FEDERATED, $aliasCircle));
612
		$andTrusted->add($expr->emptyString($aliasOwner . '.instance'));
613
		$orX->add($andTrusted);
614
615
		$this->andWhere($orX);
616
	}
617
618
619
	/**
620
	 * @param string $alias
621
	 * @param Member $member
622
	 *
623
	 * @throws RequestBuilderException
624
	 */
625
	public function limitToDirectMembership(string $alias, Member $member): void {
626
		if ($this->getType() !== QueryBuilder::SELECT) {
627
			return;
628
		}
629
630
		$aliasMember = $this->generateAlias($alias, self::MEMBER, $options);
631
		$getData = $this->getBool('getData', $options, false);
632
633
		$expr = $this->expr();
634
		if ($getData) {
635
			$this->generateMemberSelectAlias($aliasMember);
636
		}
637
		$this->leftJoin(
638
			$this->getDefaultSelectAlias(), CoreRequestBuilder::TABLE_MEMBER, $aliasMember,
639
			$expr->eq($aliasMember . '.circle_id', $alias . '.unique_id')
640
		);
641
642
		$this->filterDirectMembership($aliasMember, $member);
643
	}
644
645
646
	/**
647
	 * @param string $aliasMember
648
	 * @param Member $member
649
	 */
650
	public function filterDirectMembership(string $aliasMember, Member $member): void {
651
		if ($this->getType() !== QueryBuilder::SELECT) {
652
			return;
653
		}
654
655
		$expr = $this->expr();
656
		$andX = $expr->andX();
657
658
		if ($member->getUserId() !== '') {
659
			$andX->add(
660
				$expr->eq($aliasMember . '.user_id', $this->createNamedParameter($member->getUserId()))
661
			);
662
		}
663
664
		if ($member->getSingleId() !== '') {
665
			$andX->add(
666
				$expr->eq($aliasMember . '.single_id', $this->createNamedParameter($member->getSingleId()))
667
			);
668
		}
669
670
		if ($member->getUserType() > 0) {
671
			$andX->add(
672
				$expr->eq($aliasMember . '.user_type', $this->createNamedParameter($member->getUserType()))
673
			);
674
		}
675
676
		$andX->add(
677
			$expr->eq($aliasMember . '.instance', $this->createNamedParameter($this->getInstance($member)))
678
		);
679
680
		if ($member->getLevel() > 0) {
681
			$andX->add(
682
				$expr->gte(
683
					$aliasMember . '.level',
684
					$this->createNamedParameter($member->getLevel(), IQueryBuilder::PARAM_INT)
685
				)
686
			);
687
		}
688
689
		$this->andWhere($andX);
690
	}
691
692
693
	/**
694
	 * @param string $alias
695
	 */
696
	public function countMembers(string $alias): void {
697
		if ($this->getType() !== QueryBuilder::SELECT) {
698
			return;
699
		}
700
701
		try {
702
			$aliasMemberCount = $this->generateAlias($alias, self::MEMBER_COUNT, $options);
703
		} catch (RequestBuilderException $e) {
704
			return;
705
		}
706
707
		$getData = $this->getBool('getData', $options, false);
708
		if (!$getData) {
709
			return;
710
		}
711
712
		$expr = $this->expr();
713
		$this->selectAlias(
714
			$this->createFunction('COUNT(`' . $aliasMemberCount . '`.`member_id`)'),
715
			(($alias !== $this->getDefaultSelectAlias()) ? $alias . '_' : '') . 'population'
716
		);
717
		$this->leftJoin(
718
			$alias, CoreRequestBuilder::TABLE_MEMBER, $aliasMemberCount,
719
			$expr->eq($alias . '.unique_id', $aliasMemberCount . '.circle_id')
720
		);
721
	}
722
723
724
	/**
725
	 * @param string $alias
726
	 * @param IFederatedUser|null $initiator
727
	 * @param string $field
728
	 * @param string $helperAlias
729
	 *
730
	 * @throws RequestBuilderException
731
	 */
732
	public function leftJoinCircle(
733
		string $alias,
734
		?IFederatedUser $initiator = null,
735
		string $field = 'circle_id',
736
		string $helperAlias = ''
737
	): void {
738
		if ($this->getType() !== QueryBuilder::SELECT) {
739
			return;
740
		}
741
742
		$helperAlias = ($helperAlias !== '') ? $helperAlias : $alias;
743
		$aliasCircle = $this->generateAlias($alias, self::CIRCLE, $options);
744
		$getData = $this->getBool('getData', $options, false);
745
		$expr = $this->expr();
746
747
		if ($getData) {
748
			$this->generateCircleSelectAlias($aliasCircle);
749
		}
750
751
		$this->leftJoin(
752
			$helperAlias,
753
			CoreRequestBuilder::TABLE_CIRCLE,
754
			$aliasCircle,
755
			$expr->eq($aliasCircle . '.unique_id', $helperAlias . '.' . $field)
756
		);
757
758
		if (!is_null($initiator)) {
759
			$this->limitToInitiator($aliasCircle, $initiator);
760
		}
761
762
		$this->leftJoinOwner($aliasCircle);
763
	}
764
765
766
	/**
767
	 * @param string $aliasMember
768
	 *
769
	 * @throws RequestBuilderException
770
	 */
771
	public function leftJoinInvitedBy(string $aliasMember): void {
772
		if ($this->getType() !== QueryBuilder::SELECT) {
773
			return;
774
		}
775
776
		try {
777
			$aliasInvitedBy = $this->generateAlias($aliasMember, self::INVITED_BY);
778
		} catch (RequestBuilderException $e) {
779
			return;
780
		}
781
782
		$expr = $this->expr();
783
		$this->generateCircleSelectAlias($aliasInvitedBy)
784
			 ->leftJoin(
785
				 $aliasMember, CoreRequestBuilder::TABLE_CIRCLE, $aliasInvitedBy,
786
				 $expr->eq($aliasMember . '.invited_by', $aliasInvitedBy . '.unique_id')
787
			 );
788
789
		$this->leftJoinOwner($aliasInvitedBy);
790
	}
791
792
793
	/**
794
	 * @param string $aliasMember
795
	 * @param IFederatedUser|null $initiator
796
	 *
797
	 * @throws RequestBuilderException
798
	 */
799
	public function leftJoinBasedOn(
800
		string $aliasMember,
801
		?IFederatedUser $initiator = null
802
	): void {
803
		if ($this->getType() !== QueryBuilder::SELECT) {
804
			return;
805
		}
806
807
		try {
808
			$aliasBasedOn = $this->generateAlias($aliasMember, self::BASED_ON, $options);
809
		} catch (RequestBuilderException $e) {
810
			return;
811
		}
812
813
		$expr = $this->expr();
814
		$this->generateCircleSelectAlias($aliasBasedOn)
815
			 ->leftJoin(
816
				 $aliasMember, CoreRequestBuilder::TABLE_CIRCLE, $aliasBasedOn,
817
				 $expr->eq($aliasBasedOn . '.unique_id', $aliasMember . '.single_id')
818
			 );
819
820
		if (!is_null($initiator)) {
821
			$this->leftJoinInitiator($aliasBasedOn, $initiator);
822
			$this->leftJoinOwner($aliasBasedOn);
823
		}
824
	}
825
826
827
	/**
828
	 * @param string $alias
829
	 * @param string $field
830
	 *
831
	 * @throws RequestBuilderException
832
	 */
833
	public function leftJoinOwner(string $alias, string $field = 'unique_id'): void {
834
		if ($this->getType() !== QueryBuilder::SELECT) {
835
			return;
836
		}
837
838
		try {
839
			$aliasMember = $this->generateAlias($alias, self::OWNER, $options);
840
			$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...
841
		} catch (RequestBuilderException $e) {
842
			return;
843
		}
844
845
		$expr = $this->expr();
846
		$this->generateMemberSelectAlias($aliasMember)
847
			 ->leftJoin(
848
				 $alias, CoreRequestBuilder::TABLE_MEMBER, $aliasMember,
849
				 $expr->andX(
850
					 $expr->eq($aliasMember . '.circle_id', $alias . '.' . $field),
851
					 $expr->eq(
852
						 $aliasMember . '.level',
853
						 $this->createNamedParameter(Member::LEVEL_OWNER, self::PARAM_INT)
854
					 )
855
				 )
856
			 );
857
858
		$this->leftJoinBasedOn($aliasMember);
859
	}
860
861
862
	/**
863
	 * @param string $alias
864
	 * @param string $fieldCircleId
865
	 * @param string $fieldSingleId
866
	 *
867
	 * @throws RequestBuilderException
868
	 */
869
	public function leftJoinMember(
870
		string $alias,
871
		string $fieldCircleId = 'circle_id',
872
		string $fieldSingleId = 'single_id'
873
	): void {
874
		if ($this->getType() !== QueryBuilder::SELECT) {
875
			return;
876
		}
877
878
		try {
879
			$aliasMember = $this->generateAlias($alias, self::MEMBER, $options);
880
			$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...
881
		} catch (RequestBuilderException $e) {
882
			return;
883
		}
884
885
		$expr = $this->expr();
886
		$this->generateMemberSelectAlias($aliasMember)
887
			 ->leftJoin(
888
				 $alias, CoreRequestBuilder::TABLE_MEMBER, $aliasMember,
889
				 $expr->andX(
890
					 $expr->eq($aliasMember . '.circle_id', $alias . '.' . $fieldCircleId),
891
					 $expr->eq($aliasMember . '.single_id', $alias . '.' . $fieldSingleId),
892
					 $expr->gte(
893
						 $aliasMember . '.level',
894
						 $this->createNamedParameter(Member::LEVEL_MEMBER, self::PARAM_INT)
895
					 )
896
				 )
897
			 );
898
899
		$this->leftJoinRemoteInstance($aliasMember);
900
		$this->leftJoinBasedOn($aliasMember);
901
	}
902
903
904
	/**
905
	 * if 'getData' is true, will returns 'inheritanceBy': the Member at the end of a sub-chain of
906
	 * memberships (based on $field for Top Circle's singleId)
907
	 *
908
	 * @param string $alias
909
	 * @param string $field
910
	 * @param string $aliasInheritedBy
911
	 *
912
	 * @throws RequestBuilderException
913
	 */
914
	public function leftJoinInheritedMembers(
915
		string $alias,
916
		string $field = '',
917
		string $aliasInheritedBy = ''
918
	): void {
919
		$expr = $this->expr();
920
921
		$field = ($field === '') ? 'circle_id' : $field;
922
		$aliasMembership = $this->generateAlias($alias, self::MEMBERSHIPS, $options);
923
924
		$this->leftJoin(
925
			$alias, CoreRequestBuilder::TABLE_MEMBERSHIP, $aliasMembership,
926
			$expr->eq($aliasMembership . '.circle_id', $alias . '.' . $field)
927
		);
928
929
//		if (!$this->getBool('getData', $options, false)) {
930
//			return;
931
//		}
932
933
		if ($aliasInheritedBy === '') {
934
			$aliasInheritedBy = $this->generateAlias($alias, self::INHERITED_BY);
935
		}
936
		$this->generateMemberSelectAlias($aliasInheritedBy)
937
			 ->leftJoin(
938
				 $alias, CoreRequestBuilder::TABLE_MEMBER, $aliasInheritedBy,
939
				 $expr->andX(
940
					 $expr->eq($aliasMembership . '.inheritance_last', $aliasInheritedBy . '.circle_id'),
941
					 $expr->eq($aliasMembership . '.single_id', $aliasInheritedBy . '.single_id')
942
				 )
943
			 );
944
945
		$this->leftJoinBasedOn($aliasInheritedBy);
946
	}
947
948
949
	/**
950
	 * @throws RequestBuilderException
951
	 */
952
	public function limitToInheritedMemberships(string $alias, string $singleId, string $field = ''): void {
953
		$expr = $this->expr();
954
		$field = ($field === '') ? 'circle_id' : $field;
955
		$aliasUpstreamMembership = $this->generateAlias($alias, self::UPSTREAM_MEMBERSHIPS, $options);
956
		$this->leftJoin(
957
			$alias, CoreRequestBuilder::TABLE_MEMBERSHIP, $aliasUpstreamMembership,
958
			$expr->eq($aliasUpstreamMembership . '.single_id', $this->createNamedParameter($singleId))
959
		);
960
961
		$orX = $expr->orX(
962
			$expr->eq($aliasUpstreamMembership . '.circle_id', $alias . '.' . $field),
963
			$expr->eq($alias . '.' . $field, $this->createNamedParameter($singleId))
964
		);
965
966
		$this->andWhere($orX);
967
	}
968
969
970
	/**
971
	 * limit the request to Members and Sub Members of a Circle.
972
	 *
973
	 * @param string $alias
974
	 * @param string $singleId
975
	 * @param int $level
976
	 *
977
	 * @throws RequestBuilderException
978
	 */
979
	public function limitToMembersByInheritance(string $alias, string $singleId, int $level = 0): void {
980
		$this->leftJoinMembersByInheritance($alias);
981
982
		$expr = $this->expr();
983
		$aliasMembership = $this->generateAlias($alias, self::MEMBERSHIPS);
984
		$this->andWhere($expr->eq($aliasMembership . '.circle_id', $this->createNamedParameter($singleId)));
985
		if ($level > 1) {
986
			$this->andWhere(
987
				$expr->gte(
988
					$aliasMembership . '.level',
989
					$this->createNamedParameter($level, IQueryBuilder::PARAM_INT)
990
				)
991
			);
992
		}
993
	}
994
995
996
	/**
997
	 * if 'getData' is true, will returns 'inheritanceFrom': the Circle-As-Member of the Top Circle
998
	 * that explain the membership of a Member (based on $field for singleId) to a specific Circle
999
	 *
1000
	 * // TODO: returns the link/path ?
1001
	 *
1002
	 * @param string $alias
1003
	 * @param string $field
1004
	 *
1005
	 * @throws RequestBuilderException
1006
	 */
1007
	public function leftJoinMembersByInheritance(string $alias, string $field = ''): void {
1008
		$expr = $this->expr();
1009
1010
		$field = ($field === '') ? 'circle_id' : $field;
1011
		$aliasMembership = $this->generateAlias($alias, self::MEMBERSHIPS, $options);
1012
1013
		$this->leftJoin(
1014
			$alias, CoreRequestBuilder::TABLE_MEMBERSHIP, $aliasMembership,
1015
			$expr->andX(
1016
				$expr->eq($aliasMembership . '.inheritance_last', $alias . '.' . $field),
1017
				$expr->eq($aliasMembership . '.single_id', $alias . '.single_id')
1018
			)
1019
		);
1020
1021
		if (!$this->getBool('getData', $options, false)) {
1022
			return;
1023
		}
1024
1025
		$aliasInheritanceFrom = $this->generateAlias($alias, self::INHERITANCE_FROM);
1026
		$this->generateMemberSelectAlias($aliasInheritanceFrom)
1027
			 ->leftJoin(
1028
				 $aliasMembership, CoreRequestBuilder::TABLE_MEMBER, $aliasInheritanceFrom,
1029
				 $expr->andX(
1030
					 $expr->eq($aliasMembership . '.circle_id', $aliasInheritanceFrom . '.circle_id'),
1031
					 $expr->eq($aliasMembership . '.inheritance_first', $aliasInheritanceFrom . '.single_id')
1032
				 )
1033
			 );
1034
	}
1035
1036
1037
	/**
1038
	 * limit the result to the point of view of a FederatedUser
1039
	 *
1040
	 * @param string $alias
1041
	 * @param IFederatedUser $user
1042
	 * @param string $field
1043
	 * @param string $helperAlias
1044
	 *
1045
	 * @return ICompositeExpression
1046
	 * @throws RequestBuilderException
1047
	 */
1048
	public function limitToInitiator(
1049
		string $alias,
1050
		IFederatedUser $user,
1051
		string $field = '',
1052
		string $helperAlias = ''
1053
	): ICompositeExpression {
1054
		$this->leftJoinInitiator($alias, $user, $field, $helperAlias);
1055
		$where = $this->limitInitiatorVisibility($alias);
1056
1057
		$aliasInitiator = $this->generateAlias($alias, self::INITIATOR, $options);
1058
		if ($this->getBool('getData', $options, false)) {
1059
			$this->leftJoinBasedOn($aliasInitiator);
1060
		}
1061
1062
		return $where;
1063
	}
1064
1065
1066
	/**
1067
	 * @param string $alias
1068
	 */
1069
	public function leftJoinCircleConfig(string $alias): void {
1070
		$expr = $this->expr();
1071
		try {
1072
			$aliasConfig = $this->generateAlias($alias, self::CONFIG, $options);
1073
			$this->selectAlias(
1074
				$aliasConfig . '.config',
1075
				(($alias !== $this->getDefaultSelectAlias()) ? $alias . '_' : '') . 'circle_config'
1076
			);
1077
			$this->leftJoin(
1078
				$alias,
1079
				CoreRequestBuilder::TABLE_CIRCLE,
1080
				$aliasConfig,
1081
				$expr->eq($alias . '.circle_id', $aliasConfig . '.unique_id')
1082
			);
1083
		} catch (RequestBuilderException $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
1084
		}
1085
	}
1086
1087
1088
	/**
1089
	 * Left join members to filter userId as initiator.
1090
	 *
1091
	 * @param string $alias
1092
	 * @param IFederatedUser $initiator
1093
	 * @param string $field
1094
	 * @param string $helperAlias
1095
	 *
1096
	 * @throws RequestBuilderException
1097
	 */
1098
	public function leftJoinInitiator(
1099
		string $alias,
1100
		IFederatedUser $initiator,
1101
		string $field = '',
1102
		string $helperAlias = ''
1103
	): void {
1104
		if ($this->getType() !== QueryBuilder::SELECT) {
1105
			return;
1106
		}
1107
1108
		$expr = $this->expr();
1109
		$field = ($field === '') ? 'unique_id' : $field;
1110
		$helperAlias = ($helperAlias !== '') ? $helperAlias : $alias;
1111
		$aliasMembership = $this->generateAlias($alias, self::MEMBERSHIPS, $options);
1112
1113
		$this->leftJoin(
1114
			$helperAlias,
1115
			CoreRequestBuilder::TABLE_MEMBERSHIP,
1116
			$aliasMembership,
1117
			$expr->andX(
1118
				$this->exprLimit('single_id', $initiator->getSingleId(), $aliasMembership),
1119
				$expr->eq($aliasMembership . '.circle_id', $helperAlias . '.' . $field)
1120
			)
1121
		);
1122
1123
		try {
1124
			$aliasMembershipCircle = $this->generateAlias($aliasMembership, self::CONFIG, $options);
1125
			$this->leftJoin(
1126
				$aliasMembership,
1127
				CoreRequestBuilder::TABLE_CIRCLE,
1128
				$aliasMembershipCircle,
1129
				$expr->eq($aliasMembership . '.circle_id', $aliasMembershipCircle . '.unique_id')
1130
			);
1131
		} catch (RequestBuilderException $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
1132
		}
1133
1134
		if (!$this->getBool('getData', $options, false)) {
1135
			return;
1136
		}
1137
1138
		try {
1139
			$aliasInitiator = $this->generateAlias($alias, self::INITIATOR, $options);
1140
			$this->leftJoin(
1141
				$aliasMembership, CoreRequestBuilder::TABLE_MEMBER, $aliasInitiator,
1142
				$expr->andX(
1143
					$expr->eq($aliasMembership . '.inheritance_first', $aliasInitiator . '.single_id'),
1144
					$expr->eq($aliasMembership . '.circle_id', $aliasInitiator . '.circle_id')
1145
				)
1146
			);
1147
1148
			$aliasInheritedBy = $this->generateAlias($aliasInitiator, self::INHERITED_BY);
1149
			$this->leftJoin(
1150
				$aliasInitiator, CoreRequestBuilder::TABLE_MEMBER, $aliasInheritedBy,
1151
				$expr->andX(
1152
					$expr->eq($aliasMembership . '.single_id', $aliasInheritedBy . '.single_id'),
1153
					$expr->eq($aliasMembership . '.inheritance_last', $aliasInheritedBy . '.circle_id')
1154
				)
1155
			);
1156
1157
			$default = [];
1158
			if ($this->getBool('canBeVisitor', $options, false)) {
1159
				$default = [
1160
					'user_id'     => $initiator->getUserId(),
1161
					'single_id'   => $initiator->getSingleId(),
1162
					'user_type'   => $initiator->getUserType(),
1163
					'cached_name' => $initiator->getDisplayName(),
1164
					'instance'    => $initiator->getInstance()
1165
				];
1166
			}
1167
			$this->generateMemberSelectAlias($aliasInitiator, $default);
1168
1169
			$this->generateMemberSelectAlias($aliasInheritedBy);
1170
			$aliasInheritedByMembership = $this->generateAlias($aliasInheritedBy, self::MEMBERSHIPS);
1171
			$this->generateMembershipSelectAlias($aliasMembership, $aliasInheritedByMembership);
1172
		} catch (RequestBuilderException $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
1173
		}
1174
	}
1175
1176
1177
	/**
1178
	 * @param string $alias
1179
	 *
1180
	 * @return ICompositeExpression
1181
	 * @throws RequestBuilderException
1182
	 */
1183
	protected function limitInitiatorVisibility(string $alias): ICompositeExpression {
1184
		$aliasMembership = $this->generateAlias($alias, self::MEMBERSHIPS, $options);
1185
		$aliasMembershipCircle = $this->generateAlias($aliasMembership, self::CONFIG, $options);
1186
1187
		$filterPersonalCircle = $this->getBool('filterPersonalCircle', $options, true);
1188
1189
		$expr = $this->expr();
1190
1191
		// Visibility to non-member is
1192
		// - 0 (default), if initiator is member
1193
		// - 2 (Personal), if initiator is owner)
1194
		// - 4 (Visible to everyone)
1195
		$orX = $expr->orX();
1196
		if ($filterPersonalCircle) {
1197
			$orX->add(
1198
				$expr->andX(
1199
					$this->exprLimitBitwise('config', Circle::CFG_PERSONAL, $aliasMembershipCircle),
1200
					$expr->eq($aliasMembership . '.level', $this->createNamedParameter(Member::LEVEL_OWNER))
1201
				)
1202
			);
1203
		}
1204
1205
		$andXMember = $expr->andX();
1206
		$andXMember->add(
1207
			$expr->gte($aliasMembership . '.level', $this->createNamedParameter(Member::LEVEL_MEMBER))
1208
		);
1209
		if ($filterPersonalCircle) {
1210
			$andXMember->add(
1211
				$this->exprFilterBitwise('config', Circle::CFG_PERSONAL, $aliasMembershipCircle)
1212
			);
1213
		}
1214
		$orX->add($andXMember);
1215
1216
		if (!$this->getBool('mustBeMember', $options, true)) {
1217
			$orX->add($this->exprLimitBitwise('config', Circle::CFG_VISIBLE, $alias));
1218
		}
1219
		if ($this->getBool('canBeVisitor', $options, false)) {
1220
			// TODO: should find a better way, also filter on remote initiator on non-federated ?
1221
			$andXVisitor = $expr->andX();
1222
			$andXVisitor->add($expr->gte($alias . '.config', $this->createNamedParameter(0)));
1223
			if ($filterPersonalCircle) {
1224
				$andXVisitor->add(
1225
					$this->exprFilterBitwise('config', Circle::CFG_PERSONAL, $aliasMembershipCircle)
1226
				);
1227
			}
1228
			$orX->add($andXVisitor);
1229
		}
1230
		if ($this->getBool('canBeVisitorOnOpen', $options, false)) {
1231
			$andOpen = $expr->andX();
1232
			$andOpen->add($this->exprLimitBitwise('config', Circle::CFG_OPEN, $alias));
1233
			$andOpen->add($this->exprFilterBitwise('config', Circle::CFG_REQUEST, $alias));
1234
			$orX->add($andOpen);
1235
		}
1236
1237
		$this->andWhere($orX);
1238
1239
		return $orX;
1240
//		$orTypes = $this->generateLimit($qb, $circleUniqueId, $userId, $type, $name, $forceAll);
1241
//		if (sizeof($orTypes) === 0) {
1242
//			throw new ConfigNoCircleAvailableException(
1243
//				$this->l10n->t(
1244
//					'You cannot use the Circles Application until your administrator has allowed at least one type of circles'
1245
//				)
1246
//			);
1247
//		}
1248
1249
//		$orXTypes = $this->expr()
1250
//						 ->orX();
1251
//		foreach ($orTypes as $orType) {
1252
//			$orXTypes->add($orType);
1253
//		}
1254
//
1255
//		$qb->andWhere($orXTypes);
1256
	}
1257
1258
1259
	/**
1260
	 * CFG_SINGLE, CFG_HIDDEN and CFG_BACKEND means hidden from listing.
1261
	 *
1262
	 * @param string $aliasCircle
1263
	 * @param int $flag
1264
	 */
1265
	public function filterCircles(
1266
		string $aliasCircle,
1267
		int $flag = Circle::CFG_SINGLE | Circle::CFG_HIDDEN | Circle::CFG_BACKEND
1268
	): void {
1269
		if ($flag === 0) {
1270
			return;
1271
		}
1272
1273
		$expr = $this->expr();
1274
		$hide = $expr->andX();
1275
		foreach (Circle::$DEF_CFG as $cfg => $v) {
1276
			if ($flag & $cfg) {
1277
				$hide->add($this->exprFilterBitwise('config', $cfg, $aliasCircle));
1278
			}
1279
		}
1280
1281
		$this->andWhere($hide);
1282
	}
1283
1284
1285
	/**
1286
	 * Limit visibility on Sensitive information when search for members.
1287
	 *
1288
	 * @param string $alias
1289
	 *
1290
	 * @return ICompositeExpression
1291
	 */
1292
	private function limitRemoteVisibility_Sensitive_Members(string $alias): ICompositeExpression {
1293
		$expr = $this->expr();
1294
		$andPassive = $expr->andX();
1295
		$andPassive->add(
1296
			$expr->eq($alias . '.type', $this->createNamedParameter(RemoteInstance::TYPE_PASSIVE))
1297
		);
1298
1299
		$orMemberOrLevel = $expr->orX();
1300
		$orMemberOrLevel->add(
1301
			$expr->eq($this->getDefaultSelectAlias() . '.instance', $alias . '.instance')
1302
		);
1303
		// TODO: do we need this ? (display members from the local instance)
1304
		$orMemberOrLevel->add(
1305
			$expr->emptyString($this->getDefaultSelectAlias() . '.instance')
1306
		);
1307
1308
		$orMemberOrLevel->add(
1309
			$expr->eq(
1310
				$this->getDefaultSelectAlias() . '.level',
1311
				$this->createNamedParameter(Member::LEVEL_OWNER)
1312
			)
1313
		);
1314
		$andPassive->add($orMemberOrLevel);
1315
1316
		return $andPassive;
1317
	}
1318
1319
1320
	/**
1321
	 * Link to storage/filecache
1322
	 *
1323
	 * @param string $aliasShare
1324
	 *
1325
	 * @throws RequestBuilderException
1326
	 */
1327
	public function leftJoinFileCache(string $aliasShare) {
1328
		$expr = $this->expr();
1329
1330
		$aliasFileCache = $this->generateAlias($aliasShare, self::FILE_CACHE);
1331
		$aliasStorages = $this->generateAlias($aliasFileCache, self::STORAGES);
1332
1333
		$this->generateSelectAlias(
1334
			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...
1335
			$aliasFileCache,
1336
			$aliasFileCache,
1337
			[]
1338
		)
1339
			 ->generateSelectAlias(
1340
				 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...
1341
				 $aliasStorages,
1342
				 $aliasStorages,
1343
				 []
1344
			 )
1345
			 ->leftJoin(
1346
				 $aliasShare, CoreRequestBuilder::TABLE_FILE_CACHE, $aliasFileCache,
1347
				 $expr->eq($aliasShare . '.file_source', $aliasFileCache . '.fileid')
1348
			 )
1349
			 ->leftJoin(
1350
				 $aliasFileCache, CoreRequestBuilder::TABLE_STORAGES, $aliasStorages,
1351
				 $expr->eq($aliasFileCache . '.storage', $aliasStorages . '.numeric_id')
1352
			 );
1353
	}
1354
1355
1356
	/**
1357
	 * @param string $aliasShare
1358
	 * @param string $aliasShareMemberships
1359
	 *
1360
	 * @throws RequestBuilderException
1361
	 */
1362
	public function leftJoinShareChild(string $aliasShare, string $aliasShareMemberships = '') {
1363
		$expr = $this->expr();
1364
1365
		$aliasShareChild = $this->generateAlias($aliasShare, self::SHARE);
1366
		if ($aliasShareMemberships === '') {
1367
			$aliasShareMemberships = $this->generateAlias($aliasShare, self::MEMBERSHIPS, $options);
1368
		}
1369
1370
		$this->leftJoin(
1371
			$aliasShareMemberships, CoreRequestBuilder::TABLE_SHARE, $aliasShareChild,
1372
			$expr->andX(
1373
				$expr->eq($aliasShareChild . '.parent', $aliasShare . '.id'),
1374
				$expr->eq($aliasShareChild . '.share_with', $aliasShareMemberships . '.single_id')
1375
			)
1376
		);
1377
1378
		$this->generateSelectAlias(
1379
			['id', 'file_target', 'permissions'],
1380
			$aliasShareChild,
1381
			'child_',
1382
			[]
1383
		);
1384
1385
//		$this->selectAlias($aliasShareParent . '.permissions', 'parent_perms');
1386
	}
1387
1388
1389
	/**
1390
	 * @param string $alias
1391
	 * @param FederatedUser $federatedUser
1392
	 * @param bool $reshares
1393
	 */
1394
	public function limitToShareOwner(string $alias, FederatedUser $federatedUser, bool $reshares): void {
1395
		$expr = $this->expr();
1396
1397
		$orX = $expr->orX($this->exprLimit('uid_initiator', $federatedUser->getUserId(), $alias));
1398
1399
		if ($reshares) {
1400
			$orX->add($this->exprLimit('uid_owner', $federatedUser->getUserId(), $alias));
1401
		}
1402
1403
		$this->andWhere($orX);
1404
	}
1405
1406
1407
	/**
1408
	 * @param string $aliasMount
1409
	 * @param string $aliasMountMemberships
1410
	 *
1411
	 * @throws RequestBuilderException
1412
	 */
1413
	public function leftJoinMountpoint(string $aliasMount, string $aliasMountMemberships = '') {
1414
		$expr = $this->expr();
1415
1416
		$aliasMountpoint = $this->generateAlias($aliasMount, self::MOUNTPOINT);
1417
		if ($aliasMountMemberships === '') {
1418
			$aliasMountMemberships = $this->generateAlias($aliasMount, self::MEMBERSHIPS, $options);
1419
		}
1420
1421
		$this->leftJoin(
1422
			$aliasMountMemberships, CoreRequestBuilder::TABLE_MOUNTPOINT, $aliasMountpoint,
1423
			$expr->andX(
1424
				$expr->eq($aliasMountpoint . '.mount_id', $aliasMount . '.mount_id'),
1425
				$expr->eq($aliasMountpoint . '.single_id', $aliasMountMemberships . '.single_id')
1426
			)
1427
		);
1428
1429
		$this->selectAlias($aliasMountpoint . '.mountpoint', $aliasMountpoint . '_mountpoint');
1430
		$this->selectAlias($aliasMountpoint . '.mountpoint_hash', $aliasMountpoint . '_mountpoint_hash');
1431
	}
1432
1433
1434
	/**
1435
	 * @param string $alias
1436
	 * @param array $default
1437
	 *
1438
	 * @return CoreQueryBuilder
1439
	 */
1440
	private function generateCircleSelectAlias(string $alias, array $default = []): self {
1441
		$this->generateSelectAlias(
1442
			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...
1443
			$alias,
1444
			$alias,
1445
			$default
1446
		);
1447
1448
		return $this;
1449
	}
1450
1451
	/**
1452
	 * @param string $alias
1453
	 * @param array $default
1454
	 *
1455
	 * @return $this
1456
	 */
1457
	private function generateMemberSelectAlias(string $alias, array $default = []): self {
1458
		$this->generateSelectAlias(
1459
			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...
1460
			$alias,
1461
			$alias,
1462
			$default
1463
		);
1464
1465
		return $this;
1466
	}
1467
1468
1469
	/**
1470
	 * @param string $alias
1471
	 * @param array $default
1472
	 * @param string $prefix
1473
	 *
1474
	 * @return $this
1475
	 */
1476
	private function generateMembershipSelectAlias(
1477
		string $alias,
1478
		string $prefix = '',
1479
		array $default = []
1480
	): self {
1481
		$this->generateSelectAlias(
1482
			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...
1483
			$alias,
1484
			($prefix === '') ? $alias : $prefix,
1485
			$default
1486
		);
1487
1488
		return $this;
1489
	}
1490
1491
1492
	/**
1493
	 * @param string $alias
1494
	 * @param array $default
1495
	 *
1496
	 * @return $this
1497
	 */
1498
	private function generateRemoteInstanceSelectAlias(string $alias, array $default = []): self {
1499
		$this->generateSelectAlias(
1500
			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...
1501
			$alias,
1502
			$alias,
1503
			$default
1504
		);
1505
1506
		return $this;
1507
	}
1508
1509
1510
	/**
1511
	 * @param array $path
1512
	 * @param array $options
1513
	 */
1514
	public function setOptions(array $path, array $options): void {
1515
		$options = [self::OPTIONS => $options];
1516
		foreach (array_reverse($path) as $item) {
1517
			$options = [$item => $options];
1518
		}
1519
1520
		$this->options = $options;
1521
	}
1522
1523
1524
	/**
1525
	 * @param string $base
1526
	 * @param string $extension
1527
	 * @param array|null $options
1528
	 *
1529
	 * @return string
1530
	 * @throws RequestBuilderException
1531
	 */
1532
	public function generateAlias(string $base, string $extension, ?array &$options = []): string {
1533
		$search = str_replace('_', '.', $base);
1534
		$path = $search . '.' . $extension;
1535
		if (!$this->validKey($path, self::$SQL_PATH)
1536
			&& !in_array($extension, $this->getArray($search, self::$SQL_PATH))) {
1537
			throw new RequestBuilderException($extension . ' not found in ' . $search);
1538
		}
1539
1540
		if (!is_array($options)) {
1541
			$options = [];
1542
		}
1543
1544
		$optionPath = '';
1545
		foreach (explode('.', $path) as $p) {
1546
			$optionPath = trim($optionPath . '.' . $p, '.');
1547
			$options = array_merge(
1548
				$options,
1549
				$this->getArray($optionPath . '.' . self::OPTIONS, self::$SQL_PATH),
1550
				$this->getArray($optionPath . '.' . self::OPTIONS, $this->options)
1551
			);
1552
		}
1553
1554
		return $base . '_' . $extension;
1555
	}
1556
1557
1558
	/**
1559
	 * @param string $prefix
1560
	 *
1561
	 * @return array
1562
	 */
1563
	public function getAvailablePath(string $prefix): array {
1564
		$prefix = trim($prefix, '_');
1565
		$search = str_replace('_', '.', $prefix);
1566
1567
		$path = [];
1568
		foreach ($this->getArray($search, self::$SQL_PATH) as $arr => $item) {
1569
			if (is_numeric($arr)) {
1570
				$k = $item;
1571
			} else {
1572
				$k = $arr;
1573
			}
1574
			$path[$k] = $prefix . '_' . $k . '_';
1575
		}
1576
1577
		return $path;
1578
	}
1579
1580
}
1581
1582