Completed
Push — master ( 51beeb...bf476c )
by Maxence
02:33
created

CoreQueryBuilder::leftJoinInitiator()   B

Complexity

Conditions 7
Paths 69

Size

Total Lines 67

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 67
rs 7.7866
c 0
b 0
f 0
cc 7
nc 69
nop 4

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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 UPSTREAM_MEMBERSHIPS = 'upstreammemberships';
73
	const INHERITANCE_FROM = 'inheritancefrom';
74
	const INHERITED_BY = 'inheritedby';
75
	const INVITED_BY = 'invitedby';
76
	const MOUNT = 'mount';
77
	const MOUNTPOINT = 'mountpoint';
78
	const SHARE = 'share';
79
	const FILE_CACHE = 'filecache';
80
	const STORAGES = 'storages';
81
	const OPTIONS = 'options';
82
	const HELPER = 'circleshelper';
83
84
85
	public static $SQL_PATH = [
86
		self::SINGLE => [
87
			self::MEMBER
88
		],
89
		self::CIRCLE => [
90
			self::OPTIONS   => [
91
				'getPersonalCircle' => true
92
			],
93
			self::MEMBER,
94
			self::MEMBER_COUNT,
95
			self::OWNER     => [
96
				self::BASED_ON
97
			],
98
			self::MEMBERSHIPS,
99
			self::INITIATOR => [
100
				self::BASED_ON,
101
				self::INHERITED_BY => [
102
					self::MEMBERSHIPS
103
				]
104
			],
105
			self::REMOTE    => [
106
				self::MEMBER,
107
				self::CIRCLE => [
108
					self::OWNER
109
				]
110
			]
111
		],
112
		self::MEMBER => [
113
			self::MEMBERSHIPS,
114
			self::INHERITANCE_FROM,
115
			self::CIRCLE     => [
116
				self::OPTIONS   => [
117
					'getData' => true
118
				],
119
				self::OWNER,
120
				self::MEMBERSHIPS,
121
				self::INITIATOR => [
122
					self::OPTIONS      => [
123
						'mustBeMember' => true,
124
						'canBeVisitor' => false
125
					],
126
					self::BASED_ON,
127
					self::INHERITED_BY => [
128
						self::MEMBERSHIPS
129
					],
130
					self::INVITED_BY   => [
131
						self::OWNER,
132
						self::BASED_ON
133
					]
134
				]
135
			],
136
			self::BASED_ON   => [
137
				self::OWNER,
138
				self::MEMBERSHIPS,
139
				self::INITIATOR => [
140
					self::BASED_ON,
141
					self::INHERITED_BY => [
142
						self::MEMBERSHIPS
143
					]
144
				]
145
			],
146
			self::REMOTE     => [
147
				self::MEMBER,
148
				self::CIRCLE => [
149
					self::OWNER
150
				]
151
			],
152
			self::INVITED_BY => [
153
				self::OWNER,
154
				self::BASED_ON
155
			]
156
		],
157
		self::SHARE  => [
158
			self::SHARE,
159
			self::FILE_CACHE           => [
160
				self::STORAGES
161
			],
162
			self::UPSTREAM_MEMBERSHIPS => [
163
				self::MEMBERSHIPS,
164
				self::INHERITED_BY => [
165
					self::BASED_ON
166
				],
167
				self::SHARE,
168
			],
169
			self::MEMBERSHIPS,
170
			self::INHERITANCE_FROM,
171
			self::INHERITED_BY         => [
172
				self::BASED_ON
173
			],
174
			self::CIRCLE               => [
175
				self::OWNER
176
			],
177
			self::INITIATOR            => [
178
				self::BASED_ON,
179
				self::INHERITED_BY => [
180
					self::MEMBERSHIPS
181
				]
182
			]
183
		],
184
		self::REMOTE => [
185
			self::MEMBER
186
		],
187
		self::MOUNT  => [
188
			self::MEMBER    => [
189
				self::REMOTE
190
			],
191
			self::INITIATOR => [
192
				self::INHERITED_BY => [
193
					self::MEMBERSHIPS
194
				]
195
			],
196
			self::MOUNTPOINT,
197
			self::MEMBERSHIPS
198
		],
199
		self::HELPER => [
200
			self::MEMBERSHIPS,
201
			self::INITIATOR => [
202
				self::INHERITED_BY => [
203
					self::MEMBERSHIPS
204
				]
205
			],
206
			self::CIRCLE    => [
207
				self::OPTIONS => [
208
					'getPersonalCircle' => true
209
				],
210
				self::MEMBER,
211
				self::OWNER   => [
212
					self::BASED_ON
213
				]
214
			]
215
		]
216
	];
217
218
219
	/** @var ConfigService */
220
	private $configService;
221
222
223
	/** @var array */
224
	private $options = [];
225
226
227
	/**
228
	 * CoreQueryBuilder constructor.
229
	 */
230
	public function __construct() {
231
		parent::__construct();
232
233
		$this->configService = OC::$server->get(ConfigService::class);
234
	}
235
236
237
	/**
238
	 * @param IFederatedModel $federatedModel
239
	 *
240
	 * @return string
241
	 */
242
	public function getInstance(IFederatedModel $federatedModel): string {
243
		$instance = $federatedModel->getInstance();
244
245
		return ($this->configService->isLocalInstance($instance)) ? '' : $instance;
246
	}
247
248
249
	/**
250
	 * @param string $id
251
	 */
252
	public function limitToCircleId(string $id): void {
253
		$this->limitToDBField('circle_id', $id, true);
254
	}
255
256
	/**
257
	 * @param string $name
258
	 */
259
	public function limitToName(string $name): void {
260
		$this->limit('name', $name);
261
	}
262
263
	/**
264
	 * @param string $name
265
	 */
266
	public function limitToDisplayName(string $name): void {
267
		$this->limit('display_name', $name, '', false);
268
	}
269
270
	/**
271
	 * @param string $name
272
	 */
273
	public function limitToSanitizedName(string $name): void {
274
		$this->limit('sanitized_name', $name, '', false);
275
	}
276
277
	/**
278
	 * @param int $config
279
	 */
280
	public function limitToConfig(int $config): void {
281
		$this->limitToDBFieldInt('config', $config);
282
	}
283
284
	/**
285
	 * @param int $source
286
	 */
287
	public function limitToSource(int $source): void {
288
		$this->limitToDBFieldInt('source', $source);
289
	}
290
291
	/**
292
	 * @param int $config
293
	 * @param string $alias
294
	 */
295
	public function limitToConfigFlag(int $config, string $alias = ''): void {
296
		$this->limitBitwise('config', $config, $alias);
297
	}
298
299
300
	/**
301
	 * @param string $singleId
302
	 */
303
	public function limitToSingleId(string $singleId): void {
304
		$this->limitToDBField('single_id', $singleId, true);
305
	}
306
307
308
	/**
309
	 * @param string $itemId
310
	 */
311
	public function limitToItemId(string $itemId): void {
312
		$this->limitToDBField('item_id', $itemId, true);
313
	}
314
315
316
	/**
317
	 * @param string $host
318
	 */
319
	public function limitToInstance(string $host): void {
320
		$this->limitToDBField('instance', $host, false);
321
	}
322
323
324
	/**
325
	 * @param int $userType
326
	 */
327
	public function limitToUserType(int $userType): void {
328
		$this->limitToDBFieldInt('user_type', $userType);
329
	}
330
331
332
	/**
333
	 * @param int $shareType
334
	 */
335
	public function limitToShareType(int $shareType): void {
336
		$this->limitToDBFieldInt('share_type', $shareType);
337
	}
338
339
340
	/**
341
	 * @param string $shareWith
342
	 */
343
	public function limitToShareWith(string $shareWith): void {
344
		$this->limitToDBField('share_with', $shareWith);
345
	}
346
347
348
	/**
349
	 * @param int $nodeId
350
	 */
351
	public function limitToFileSource(int $nodeId): void {
352
		$this->limitToDBFieldInt('file_source', $nodeId);
353
	}
354
355
	/**
356
	 * @param array $files
357
	 */
358
	public function limitToFileSourceArray(array $files): void {
359
		$this->limitToDBFieldInArray('file_source', $files);
360
	}
361
362
363
	/**
364
	 * @param int $shareId
365
	 */
366
	public function limitToShareParent(int $shareId): void {
367
		$this->limitToDBFieldInt('parent', $shareId);
368
	}
369
370
371
	/**
372
	 * @param Circle $circle
373
	 */
374
	public function filterCircle(Circle $circle): void {
375
		if ($this->getType() !== QueryBuilder::SELECT) {
376
			return;
377
		}
378
379
		if ($circle->getDisplayName() !== '') {
380
			$this->searchInDBField('display_name', '%' . $circle->getDisplayName() . '%');
381
		}
382
		if ($circle->getConfig() > 0) {
383
			$this->limitBitwise('config', $circle->getConfig());
384
		}
385
	}
386
387
388
	/**
389
	 * left join RemoteInstance based on a Member
390
	 */
391
	public function leftJoinRemoteInstance(string $alias): void {
392
		$expr = $this->expr();
393
394
		try {
395
			$aliasRemoteInstance = $this->generateAlias($alias, self::REMOTE);
396
			$this->generateRemoteInstanceSelectAlias($aliasRemoteInstance)
397
				 ->leftJoin(
398
					 $alias, CoreRequestBuilder::TABLE_REMOTE, $aliasRemoteInstance,
399
					 $expr->eq($alias . '.instance', $aliasRemoteInstance . '.instance')
400
				 );
401
		} catch (RequestBuilderException $e) {
402
		}
403
	}
404
405
406
	/**
407
	 * @param string $alias
408
	 * @param RemoteInstance $remoteInstance
409
	 * @param bool $filterSensitiveData
410
	 * @param string $aliasCircle
411
	 *
412
	 * @throws RequestBuilderException
413
	 */
414
	public function limitToRemoteInstance(
415
		string $alias,
416
		RemoteInstance $remoteInstance,
417
		bool $filterSensitiveData = true,
418
		string $aliasCircle = ''
419
	): void {
420
421
		if ($aliasCircle === '') {
422
			$aliasCircle = $alias;
423
		}
424
425
		$this->leftJoinRemoteInstanceIncomingRequest($alias, $remoteInstance);
426
		$this->leftJoinMemberFromInstance($alias, $remoteInstance, $aliasCircle);
427
		$this->leftJoinMemberFromRemoteCircle($alias, $remoteInstance, $aliasCircle);
428
		$this->limitRemoteVisibility($alias, $filterSensitiveData, $aliasCircle);
429
	}
430
431
432
	/**
433
	 * Left join RemoteInstance based on an incoming request
434
	 *
435
	 * @param string $alias
436
	 * @param RemoteInstance $remoteInstance
437
	 *
438
	 * @throws RequestBuilderException
439
	 */
440
	public function leftJoinRemoteInstanceIncomingRequest(
441
		string $alias,
442
		RemoteInstance $remoteInstance
443
	): void {
444
		if ($this->getType() !== QueryBuilder::SELECT) {
445
			return;
446
		}
447
448
		$aliasRemote = $this->generateAlias($alias, self::REMOTE);
449
		$expr = $this->expr();
450
		$this->leftJoin(
451
			$this->getDefaultSelectAlias(), CoreRequestBuilder::TABLE_REMOTE, $aliasRemote,
452
			$expr->eq($aliasRemote . '.instance', $this->createNamedParameter($remoteInstance->getInstance()))
453
		);
454
	}
455
456
457
	/**
458
	 * left join members to check memberships of someone from instance
459
	 *
460
	 * @param string $alias
461
	 * @param RemoteInstance $remoteInstance
462
	 * @param string $aliasCircle
463
	 *
464
	 * @throws RequestBuilderException
465
	 */
466
	private function leftJoinMemberFromInstance(
467
		string $alias, RemoteInstance $remoteInstance, string $aliasCircle
468
	) {
469
		if ($this->getType() !== QueryBuilder::SELECT) {
470
			return;
471
		}
472
473
		$aliasRemote = $this->generateAlias($alias, self::REMOTE);
474
		$aliasRemoteMember = $this->generateAlias($aliasRemote, self::MEMBER);
475
476
		$expr = $this->expr();
477
		$this->leftJoin(
478
			$this->getDefaultSelectAlias(), CoreRequestBuilder::TABLE_MEMBER, $aliasRemoteMember,
479
			$expr->andX(
480
				$expr->eq($aliasRemoteMember . '.circle_id', $aliasCircle . '.unique_id'),
481
				$expr->eq(
482
					$aliasRemoteMember . '.instance',
483
					$this->createNamedParameter($remoteInstance->getInstance())
484
				),
485
				$expr->gte($aliasRemoteMember . '.level', $this->createNamedParameter(Member::LEVEL_MEMBER))
486
			)
487
		);
488
	}
489
490
491
	/**
492
	 * left join circle is member of a circle from remote instance
493
	 *
494
	 * @param string $alias
495
	 * @param RemoteInstance $remoteInstance
496
	 * @param string $aliasCircle
497
	 *
498
	 * @throws RequestBuilderException
499
	 */
500
	private function leftJoinMemberFromRemoteCircle(
501
		string $alias,
502
		RemoteInstance $remoteInstance,
503
		string $aliasCircle
504
	) {
505
		if ($this->getType() !== QueryBuilder::SELECT) {
506
			return;
507
		}
508
509
		$aliasRemote = $this->generateAlias($alias, self::REMOTE);
510
		$aliasRemoteCircle = $this->generateAlias($aliasRemote, self::CIRCLE);
511
		$aliasRemoteCircleOwner = $this->generateAlias($aliasRemoteCircle, self::OWNER);
512
513
		$expr = $this->expr();
514
		$this->leftJoin(
515
			$this->getDefaultSelectAlias(), CoreRequestBuilder::TABLE_MEMBER, $aliasRemoteCircle,
516
			$expr->andX(
517
				$expr->eq($aliasRemoteCircle . '.single_id', $aliasCircle . '.unique_id'),
518
				$expr->emptyString($aliasRemoteCircle . '.instance'),
519
				$expr->gte($aliasRemoteCircle . '.level', $this->createNamedParameter(Member::LEVEL_MEMBER))
520
			)
521
		);
522
		$this->leftJoin(
523
			$this->getDefaultSelectAlias(), CoreRequestBuilder::TABLE_MEMBER, $aliasRemoteCircleOwner,
524
			$expr->andX(
525
				$expr->eq($aliasRemoteCircle . '.circle_id', $aliasRemoteCircleOwner . '.circle_id'),
526
				$expr->eq(
527
					$aliasRemoteCircleOwner . '.instance',
528
					$this->createNamedParameter($remoteInstance->getInstance())
529
				),
530
				$expr->eq(
531
					$aliasRemoteCircleOwner . '.level', $this->createNamedParameter(Member::LEVEL_OWNER)
532
				)
533
			)
534
		);
535
	}
536
537
538
	/**
539
	 * - global_scale: visibility on all Circles
540
	 * - trusted: visibility on all FEDERATED Circle if owner is local
541
	 * - external: visibility on all FEDERATED Circle if owner is local and:
542
	 *    - with if Circle contains at least one member from the remote instance
543
	 *    - one circle from the remote instance contains the local circle as member, and confirmed (using
544
	 *      sync locally)
545
	 * - passive: like external, but the members list will only contains member from the local instance and
546
	 * from the remote instance.
547
	 *
548
	 * @param string $alias
549
	 * @param bool $sensitive
550
	 * @param string $aliasCircle
551
	 *
552
	 * @throws RequestBuilderException
553
	 */
554
	protected function limitRemoteVisibility(string $alias, bool $sensitive, string $aliasCircle) {
555
		$aliasRemote = $this->generateAlias($alias, self::REMOTE);
556
		$aliasOwner = $this->generateAlias($aliasCircle, self::OWNER);
557
		$aliasRemoteMember = $this->generateAlias($aliasRemote, self::MEMBER);
558
		$aliasRemoteCircle = $this->generateAlias($aliasRemote, self::CIRCLE);
559
		$aliasRemoteCircleOwner = $this->generateAlias($aliasRemoteCircle, self::OWNER);
560
561
		$expr = $this->expr();
562
		$orX = $expr->orX();
563
		$orX->add(
564
			$expr->eq($aliasRemote . '.type', $this->createNamedParameter(RemoteInstance::TYPE_GLOBALSCALE))
565
		);
566
567
		$orExtOrPassive = $expr->orX();
568
		$orExtOrPassive->add(
569
			$expr->eq($aliasRemote . '.type', $this->createNamedParameter(RemoteInstance::TYPE_EXTERNAL))
570
		);
571
		if (!$sensitive) {
572
			$orExtOrPassive->add(
573
				$expr->eq($aliasRemote . '.type', $this->createNamedParameter(RemoteInstance::TYPE_PASSIVE))
574
			);
575
		} else {
576
			if ($this->getDefaultSelectAlias() === CoreQueryBuilder::MEMBER) {
577
				$orExtOrPassive->add($this->limitRemoteVisibility_Sensitive_Members($aliasRemote));
578
			}
579
		}
580
581
		$orInstance = $expr->orX();
582
		$orInstance->add($expr->isNotNull($aliasRemoteMember . '.instance'));
583
		$orInstance->add($expr->isNotNull($aliasRemoteCircleOwner . '.instance'));
584
585
		$andExternal = $expr->andX();
586
		$andExternal->add($orExtOrPassive);
587
		$andExternal->add($orInstance);
588
589
		$orExtOrTrusted = $expr->orX();
590
		$orExtOrTrusted->add($andExternal);
591
		$orExtOrTrusted->add(
592
			$expr->eq($aliasRemote . '.type', $this->createNamedParameter(RemoteInstance::TYPE_TRUSTED))
593
		);
594
595
		$andTrusted = $expr->andX();
596
		$andTrusted->add($orExtOrTrusted);
597
		$andTrusted->add($this->exprLimitBitwise('config', Circle::CFG_FEDERATED, $aliasCircle));
598
		$andTrusted->add($expr->emptyString($aliasOwner . '.instance'));
599
		$orX->add($andTrusted);
600
601
		$this->andWhere($orX);
602
	}
603
604
605
	/**
606
	 * @param string $alias
607
	 * @param Member $member
608
	 *
609
	 * @throws RequestBuilderException
610
	 */
611
	public function limitToDirectMembership(string $alias, Member $member): void {
612
		if ($this->getType() !== QueryBuilder::SELECT) {
613
			return;
614
		}
615
616
		$aliasMember = $this->generateAlias($alias, self::MEMBER, $options);
617
		$getData = $this->getBool('getData', $options, false);
618
619
		$expr = $this->expr();
620
		if ($getData) {
621
			$this->generateMemberSelectAlias($aliasMember);
622
		}
623
		$this->leftJoin(
624
			$this->getDefaultSelectAlias(), CoreRequestBuilder::TABLE_MEMBER, $aliasMember,
625
			$expr->eq($aliasMember . '.circle_id', $alias . '.unique_id')
626
		);
627
628
		$this->filterDirectMembership($aliasMember, $member);
629
	}
630
631
632
	/**
633
	 * @param string $aliasMember
634
	 * @param Member $member
635
	 */
636
	public function filterDirectMembership(string $aliasMember, Member $member): void {
637
		if ($this->getType() !== QueryBuilder::SELECT) {
638
			return;
639
		}
640
641
		$expr = $this->expr();
642
		$andX = $expr->andX();
643
644
		if ($member->getUserId() !== '') {
645
			$andX->add(
646
				$expr->eq($aliasMember . '.user_id', $this->createNamedParameter($member->getUserId()))
647
			);
648
		}
649
650
		if ($member->getSingleId() !== '') {
651
			$andX->add(
652
				$expr->eq($aliasMember . '.single_id', $this->createNamedParameter($member->getSingleId()))
653
			);
654
		}
655
656
		if ($member->getUserType() > 0) {
657
			$andX->add(
658
				$expr->eq($aliasMember . '.user_type', $this->createNamedParameter($member->getUserType()))
659
			);
660
		}
661
662
		$andX->add(
663
			$expr->eq($aliasMember . '.instance', $this->createNamedParameter($this->getInstance($member)))
664
		);
665
666
		if ($member->getLevel() > 0) {
667
			$andX->add(
668
				$expr->gte(
669
					$aliasMember . '.level',
670
					$this->createNamedParameter($member->getLevel(), IQueryBuilder::PARAM_INT)
671
				)
672
			);
673
		}
674
675
		$this->andWhere($andX);
676
	}
677
678
679
	/**
680
	 * @param string $alias
681
	 */
682
	public function countMembers(string $alias): void {
683
		if ($this->getType() !== QueryBuilder::SELECT) {
684
			return;
685
		}
686
687
		try {
688
			$aliasMemberCount = $this->generateAlias($alias, self::MEMBER_COUNT, $options);
689
		} catch (RequestBuilderException $e) {
690
			return;
691
		}
692
693
		$getData = $this->getBool('getData', $options, false);
694
		if (!$getData) {
695
			return;
696
		}
697
698
		$expr = $this->expr();
699
		$this->selectAlias(
700
			$this->createFunction('COUNT(`' . $aliasMemberCount . '`.`member_id`)'),
701
			(($alias !== $this->getDefaultSelectAlias()) ? $alias . '_' : '') . 'population'
702
		);
703
		$this->leftJoin(
704
			$alias, CoreRequestBuilder::TABLE_MEMBER, $aliasMemberCount,
705
			$expr->eq($alias . '.unique_id', $aliasMemberCount . '.circle_id')
706
		);
707
	}
708
709
710
	/**
711
	 * @param string $alias
712
	 * @param IFederatedUser|null $initiator
713
	 * @param string $field
714
	 * @param string $helperAlias
715
	 *
716
	 * @throws RequestBuilderException
717
	 */
718
	public function leftJoinCircle(
719
		string $alias,
720
		?IFederatedUser $initiator = null,
721
		string $field = 'circle_id',
722
		string $helperAlias = ''
723
	): void {
724
		if ($this->getType() !== QueryBuilder::SELECT) {
725
			return;
726
		}
727
728
		$helperAlias = ($helperAlias !== '') ? $helperAlias : $alias;
729
		$aliasCircle = $this->generateAlias($alias, self::CIRCLE, $options);
730
		$getData = $this->getBool('getData', $options, false);
731
		$expr = $this->expr();
732
733
		if ($getData) {
734
			$this->generateCircleSelectAlias($aliasCircle);
735
		}
736
737
		$this->leftJoin(
738
			$helperAlias,
739
			CoreRequestBuilder::TABLE_CIRCLE,
740
			$aliasCircle,
741
			$expr->eq($aliasCircle . '.unique_id', $helperAlias . '.' . $field)
742
		);
743
744
		if (!is_null($initiator)) {
745
			$this->limitToInitiator($aliasCircle, $initiator);
746
		}
747
748
		$this->leftJoinOwner($aliasCircle);
749
	}
750
751
752
	/**
753
	 * @param string $aliasMember
754
	 *
755
	 * @throws RequestBuilderException
756
	 */
757
	public function leftJoinInvitedBy(string $aliasMember): void {
758
		if ($this->getType() !== QueryBuilder::SELECT) {
759
			return;
760
		}
761
762
		try {
763
			$aliasInvitedBy = $this->generateAlias($aliasMember, self::INVITED_BY);
764
		} catch (RequestBuilderException $e) {
765
			return;
766
		}
767
768
		$expr = $this->expr();
769
		$this->generateCircleSelectAlias($aliasInvitedBy)
770
			 ->leftJoin(
771
				 $aliasMember, CoreRequestBuilder::TABLE_CIRCLE, $aliasInvitedBy,
772
				 $expr->eq($aliasMember . '.invited_by', $aliasInvitedBy . '.unique_id')
773
			 );
774
775
		$this->leftJoinOwner($aliasInvitedBy);
776
	}
777
778
779
	/**
780
	 * @param string $aliasMember
781
	 * @param IFederatedUser|null $initiator
782
	 *
783
	 * @throws RequestBuilderException
784
	 */
785
	public function leftJoinBasedOn(
786
		string $aliasMember,
787
		?IFederatedUser $initiator = null
788
	): void {
789
		if ($this->getType() !== QueryBuilder::SELECT) {
790
			return;
791
		}
792
793
		try {
794
			$aliasBasedOn = $this->generateAlias($aliasMember, self::BASED_ON, $options);
795
		} catch (RequestBuilderException $e) {
796
			return;
797
		}
798
799
		$expr = $this->expr();
800
		$this->generateCircleSelectAlias($aliasBasedOn)
801
			 ->leftJoin(
802
				 $aliasMember, CoreRequestBuilder::TABLE_CIRCLE, $aliasBasedOn,
803
				 $expr->eq($aliasBasedOn . '.unique_id', $aliasMember . '.single_id')
804
			 );
805
806
		if (!is_null($initiator)) {
807
			$this->leftJoinInitiator($aliasBasedOn, $initiator);
808
			$this->leftJoinOwner($aliasBasedOn);
809
		}
810
	}
811
812
813
	/**
814
	 * @param string $alias
815
	 * @param string $field
816
	 *
817
	 * @throws RequestBuilderException
818
	 */
819
	public function leftJoinOwner(string $alias, string $field = 'unique_id'): void {
820
		if ($this->getType() !== QueryBuilder::SELECT) {
821
			return;
822
		}
823
824
		try {
825
			$aliasMember = $this->generateAlias($alias, self::OWNER, $options);
826
			$getData = $this->getBool('getData', $options, false);
827
		} catch (RequestBuilderException $e) {
828
			return;
829
		}
830
831
		$expr = $this->expr();
832
		$this->generateMemberSelectAlias($aliasMember)
833
			 ->leftJoin(
834
				 $alias, CoreRequestBuilder::TABLE_MEMBER, $aliasMember,
835
				 $expr->andX(
836
					 $expr->eq($aliasMember . '.circle_id', $alias . '.' . $field),
837
					 $expr->eq(
838
						 $aliasMember . '.level',
839
						 $this->createNamedParameter(Member::LEVEL_OWNER, self::PARAM_INT)
840
					 )
841
				 )
842
			 );
843
844
		$this->leftJoinBasedOn($aliasMember);
845
	}
846
847
848
	/**
849
	 * @param string $alias
850
	 * @param string $fieldCircleId
851
	 * @param string $fieldSingleId
852
	 *
853
	 * @throws RequestBuilderException
854
	 */
855
	public function leftJoinMember(
856
		string $alias,
857
		string $fieldCircleId = 'circle_id',
858
		string $fieldSingleId = 'single_id'
859
	): void {
860
		if ($this->getType() !== QueryBuilder::SELECT) {
861
			return;
862
		}
863
864
		try {
865
			$aliasMember = $this->generateAlias($alias, self::MEMBER, $options);
866
			$getData = $this->getBool('getData', $options, false);
867
		} catch (RequestBuilderException $e) {
868
			return;
869
		}
870
871
		$expr = $this->expr();
872
		$this->generateMemberSelectAlias($aliasMember)
873
			 ->leftJoin(
874
				 $alias, CoreRequestBuilder::TABLE_MEMBER, $aliasMember,
875
				 $expr->andX(
876
					 $expr->eq($aliasMember . '.circle_id', $alias . '.' . $fieldCircleId),
877
					 $expr->eq($aliasMember . '.single_id', $alias . '.' . $fieldSingleId),
878
					 $expr->gte(
879
						 $aliasMember . '.level',
880
						 $this->createNamedParameter(Member::LEVEL_MEMBER, self::PARAM_INT)
881
					 )
882
				 )
883
			 );
884
885
		$this->leftJoinRemoteInstance($aliasMember);
886
		$this->leftJoinBasedOn($aliasMember);
887
	}
888
889
890
	/**
891
	 * if 'getData' is true, will returns 'inheritanceBy': the Member at the end of a sub-chain of
892
	 * memberships (based on $field for Top Circle's singleId)
893
	 *
894
	 * @param string $alias
895
	 * @param string $field
896
	 * @param string $aliasInheritedBy
897
	 *
898
	 * @throws RequestBuilderException
899
	 */
900
	public function leftJoinInheritedMembers(
901
		string $alias,
902
		string $field = '',
903
		string $aliasInheritedBy = ''
904
	): void {
905
		$expr = $this->expr();
906
907
		$field = ($field === '') ? 'circle_id' : $field;
908
		$aliasMembership = $this->generateAlias($alias, self::MEMBERSHIPS, $options);
909
910
		$this->leftJoin(
911
			$alias, CoreRequestBuilder::TABLE_MEMBERSHIP, $aliasMembership,
912
			$expr->eq($aliasMembership . '.circle_id', $alias . '.' . $field)
913
		);
914
915
//		if (!$this->getBool('getData', $options, false)) {
916
//			return;
917
//		}
918
919
		if ($aliasInheritedBy === '') {
920
			$aliasInheritedBy = $this->generateAlias($alias, self::INHERITED_BY);
921
		}
922
		$this->generateMemberSelectAlias($aliasInheritedBy)
923
			 ->leftJoin(
924
				 $alias, CoreRequestBuilder::TABLE_MEMBER, $aliasInheritedBy,
925
				 $expr->andX(
926
					 $expr->eq($aliasMembership . '.inheritance_last', $aliasInheritedBy . '.circle_id'),
927
					 $expr->eq($aliasMembership . '.single_id', $aliasInheritedBy . '.single_id')
928
				 )
929
			 );
930
931
		$this->leftJoinBasedOn($aliasInheritedBy);
932
	}
933
934
935
	/**
936
	 * @throws RequestBuilderException
937
	 */
938
	public function limitToInheritedMemberships(string $alias, string $singleId, string $field = ''): void {
939
		$expr = $this->expr();
940
		$field = ($field === '') ? 'circle_id' : $field;
941
		$aliasUpstreamMembership = $this->generateAlias($alias, self::UPSTREAM_MEMBERSHIPS, $options);
942
		$this->leftJoin(
943
			$alias, CoreRequestBuilder::TABLE_MEMBERSHIP, $aliasUpstreamMembership,
944
			$expr->eq($aliasUpstreamMembership . '.single_id', $this->createNamedParameter($singleId))
945
		);
946
947
		$orX = $expr->orX(
948
			$expr->eq($aliasUpstreamMembership . '.circle_id', $alias . '.' . $field),
949
			$expr->eq($alias . '.' . $field, $this->createNamedParameter($singleId))
950
		);
951
952
		$this->andWhere($orX);
953
	}
954
955
956
	/**
957
	 * limit the request to Members and Sub Members of a Circle.
958
	 *
959
	 * @param string $alias
960
	 * @param string $singleId
961
	 * @param int $level
962
	 *
963
	 * @throws RequestBuilderException
964
	 */
965
	public function limitToMembersByInheritance(string $alias, string $singleId, int $level = 0): void {
966
		$this->leftJoinMembersByInheritance($alias);
967
968
		$expr = $this->expr();
969
		$aliasMembership = $this->generateAlias($alias, self::MEMBERSHIPS);
970
		$this->andWhere($expr->eq($aliasMembership . '.circle_id', $this->createNamedParameter($singleId)));
971
		if ($level > 1) {
972
			$this->andWhere(
973
				$expr->gte(
974
					$aliasMembership . '.level',
975
					$this->createNamedParameter($level, IQueryBuilder::PARAM_INT)
976
				)
977
			);
978
		}
979
	}
980
981
982
	/**
983
	 * if 'getData' is true, will returns 'inheritanceFrom': the Circle-As-Member of the Top Circle
984
	 * that explain the membership of a Member (based on $field for singleId) to a specific Circle
985
	 *
986
	 * // TODO: returns the link/path ?
987
	 *
988
	 * @param string $alias
989
	 * @param string $field
990
	 *
991
	 * @throws RequestBuilderException
992
	 */
993
	public function leftJoinMembersByInheritance(string $alias, string $field = ''): void {
994
		$expr = $this->expr();
995
996
		$field = ($field === '') ? 'circle_id' : $field;
997
		$aliasMembership = $this->generateAlias($alias, self::MEMBERSHIPS, $options);
998
999
		$this->leftJoin(
1000
			$alias, CoreRequestBuilder::TABLE_MEMBERSHIP, $aliasMembership,
1001
			$expr->andX(
1002
				$expr->eq($aliasMembership . '.inheritance_last', $alias . '.' . $field),
1003
				$expr->eq($aliasMembership . '.single_id', $alias . '.single_id')
1004
			)
1005
		);
1006
1007
		if (!$this->getBool('getData', $options, false)) {
1008
			return;
1009
		}
1010
1011
		$aliasInheritanceFrom = $this->generateAlias($alias, self::INHERITANCE_FROM);
1012
		$this->generateMemberSelectAlias($aliasInheritanceFrom)
1013
			 ->leftJoin(
1014
				 $aliasMembership, CoreRequestBuilder::TABLE_MEMBER, $aliasInheritanceFrom,
1015
				 $expr->andX(
1016
					 $expr->eq($aliasMembership . '.circle_id', $aliasInheritanceFrom . '.circle_id'),
1017
					 $expr->eq($aliasMembership . '.inheritance_first', $aliasInheritanceFrom . '.single_id')
1018
				 )
1019
			 );
1020
	}
1021
1022
1023
	/**
1024
	 * limit the result to the point of view of a FederatedUser
1025
	 *
1026
	 * @param string $alias
1027
	 * @param IFederatedUser $user
1028
	 * @param string $field
1029
	 * @param string $helperAlias
1030
	 *
1031
	 * @return ICompositeExpression
1032
	 * @throws RequestBuilderException
1033
	 */
1034
	public function limitToInitiator(
1035
		string $alias,
1036
		IFederatedUser $user,
1037
		string $field = '',
1038
		string $helperAlias = ''
1039
	): ICompositeExpression {
1040
		$this->leftJoinInitiator($alias, $user, $field, $helperAlias);
1041
		$where = $this->limitInitiatorVisibility($alias);
1042
1043
		$aliasInitiator = $this->generateAlias($alias, self::INITIATOR, $options);
1044
		if ($this->getBool('getData', $options, false)) {
1045
			$this->leftJoinBasedOn($aliasInitiator);
1046
		}
1047
1048
		return $where;
1049
	}
1050
1051
1052
	/**
1053
	 * Left join members to filter userId as initiator.
1054
	 *
1055
	 * @param string $alias
1056
	 * @param IFederatedUser $initiator
1057
	 * @param string $field
1058
	 * @param string $helperAlias
1059
	 *
1060
	 * @throws RequestBuilderException
1061
	 */
1062
	public function leftJoinInitiator(
1063
		string $alias,
1064
		IFederatedUser $initiator,
1065
		string $field = '',
1066
		string $helperAlias = ''
1067
	): void {
1068
		if ($this->getType() !== QueryBuilder::SELECT) {
1069
			return;
1070
		}
1071
1072
		$expr = $this->expr();
1073
		$field = ($field === '') ? 'unique_id' : $field;
1074
		$helperAlias = ($helperAlias !== '') ? $helperAlias : $alias;
1075
		$aliasMembership = $this->generateAlias($alias, self::MEMBERSHIPS, $options);
1076
1077
		$this->leftJoin(
1078
			$helperAlias,
1079
			CoreRequestBuilder::TABLE_MEMBERSHIP,
1080
			$aliasMembership,
1081
			$expr->andX(
1082
				$this->exprLimit('single_id', $initiator->getSingleId(), $aliasMembership),
1083
				$expr->eq($aliasMembership . '.circle_id', $helperAlias . '.' . $field)
1084
			)
1085
		);
1086
1087
		if (!$this->getBool('getData', $options, false)) {
1088
			return;
1089
		}
1090
1091
		try {
1092
			$aliasInitiator = $this->generateAlias($alias, self::INITIATOR, $options);
1093
			$this->leftJoin(
1094
				$aliasMembership, CoreRequestBuilder::TABLE_MEMBER, $aliasInitiator,
1095
				$expr->andX(
1096
					$expr->eq($aliasMembership . '.inheritance_first', $aliasInitiator . '.single_id'),
1097
					$expr->eq($aliasMembership . '.circle_id', $aliasInitiator . '.circle_id')
1098
				)
1099
			);
1100
1101
			$aliasInheritedBy = $this->generateAlias($aliasInitiator, self::INHERITED_BY);
1102
			$this->leftJoin(
1103
				$aliasInitiator, CoreRequestBuilder::TABLE_MEMBER, $aliasInheritedBy,
1104
				$expr->andX(
1105
					$expr->eq($aliasMembership . '.single_id', $aliasInheritedBy . '.single_id'),
1106
					$expr->eq($aliasMembership . '.inheritance_last', $aliasInheritedBy . '.circle_id')
1107
				)
1108
			);
1109
1110
			$default = [];
1111
			if ($this->getBool('canBeVisitor', $options, false)) {
1112
				$default = [
1113
					'user_id'     => $initiator->getUserId(),
1114
					'single_id'   => $initiator->getSingleId(),
1115
					'user_type'   => $initiator->getUserType(),
1116
					'cached_name' => $initiator->getDisplayName(),
1117
					'instance'    => $initiator->getInstance()
1118
				];
1119
			}
1120
			$this->generateMemberSelectAlias($aliasInitiator, $default);
1121
1122
			$this->generateMemberSelectAlias($aliasInheritedBy);
1123
			$aliasInheritedByMembership = $this->generateAlias($aliasInheritedBy, self::MEMBERSHIPS);
1124
			$this->generateMembershipSelectAlias($aliasMembership, $aliasInheritedByMembership);
1125
		} catch (RequestBuilderException $e) {
1126
		}
1127
	}
1128
1129
1130
	/**
1131
	 * @param string $alias
1132
	 *
1133
	 * @return ICompositeExpression
1134
	 * @throws RequestBuilderException
1135
	 */
1136
	protected function limitInitiatorVisibility(string $alias): ICompositeExpression {
1137
		$aliasMembership = $this->generateAlias($alias, self::MEMBERSHIPS, $options);
1138
		$getPersonalCircle = $this->getBool('getPersonalCircle', $options, false);
1139
1140
		$expr = $this->expr();
1141
1142
		// Visibility to non-member is
1143
		// - 0 (default), if initiator is member
1144
		// - 2 (Personal), if initiator is owner)
1145
		// - 4 (Visible to everyone)
1146
		$orX = $expr->orX();
1147
		$orX->add(
1148
			$expr->andX(
1149
				$expr->gte($aliasMembership . '.level', $this->createNamedParameter(Member::LEVEL_MEMBER))
1150
				$this->exprFilterBitwise('config', Circle::CFG_PERSONAL, $alias)
0 ignored issues
show
Bug introduced by
This code did not parse for me. Apparently, there is an error somewhere around this line:

Syntax error, unexpected T_VARIABLE, expecting ',' or ')'
Loading history...
1151
			)
1152
		);
1153
1154
		if ($getPersonalCircle) {
1155
			$orX->add(
1156
				$expr->andX(
1157
					$this->exprLimitBitwise('config', Circle::CFG_PERSONAL, $alias),
1158
					$expr->eq($aliasMembership . '.level', $this->createNamedParameter(Member::LEVEL_OWNER))
1159
				)
1160
			);
1161
		}
1162
		if (!$this->getBool('mustBeMember', $options, true)) {
1163
			$orX->add($this->exprLimitBitwise('config', Circle::CFG_VISIBLE, $alias));
1164
		}
1165
		if ($this->getBool('canBeVisitor', $options, false)) {
1166
			// TODO: should find a better way, also filter on remote initiator on non-federated ?
1167
			$orX->add($expr->gte($alias . '.config', $this->createNamedParameter(0)));
1168
		}
1169
		if ($this->getBool('canBeVisitorOnOpen', $options, false)) {
1170
			$andOpen = $expr->andX();
1171
			$andOpen->add($this->exprLimitBitwise('config', Circle::CFG_OPEN, $alias));
1172
			$andOpen->add($this->exprFilterBitwise('config', Circle::CFG_REQUEST, $alias));
1173
			$orX->add($andOpen);
1174
		}
1175
1176
		$this->andWhere($orX);
1177
1178
		return $orX;
1179
//		$orTypes = $this->generateLimit($qb, $circleUniqueId, $userId, $type, $name, $forceAll);
1180
//		if (sizeof($orTypes) === 0) {
1181
//			throw new ConfigNoCircleAvailableException(
1182
//				$this->l10n->t(
1183
//					'You cannot use the Circles Application until your administrator has allowed at least one type of circles'
1184
//				)
1185
//			);
1186
//		}
1187
1188
//		$orXTypes = $this->expr()
1189
//						 ->orX();
1190
//		foreach ($orTypes as $orType) {
1191
//			$orXTypes->add($orType);
1192
//		}
1193
//
1194
//		$qb->andWhere($orXTypes);
1195
	}
1196
1197
1198
	/**
1199
	 * CFG_SINGLE, CFG_HIDDEN and CFG_BACKEND means hidden from listing.
1200
	 *
1201
	 * @param string $aliasCircle
1202
	 * @param int $flag
1203
	 */
1204
	public function filterCircles(
1205
		string $aliasCircle,
1206
		int $flag = Circle::CFG_SINGLE | Circle::CFG_HIDDEN | Circle::CFG_BACKEND
1207
	): void {
1208
		if ($flag === 0) {
1209
			return;
1210
		}
1211
1212
		$expr = $this->expr();
1213
		$hide = $expr->andX();
1214
		foreach (Circle::$DEF_CFG as $cfg => $v) {
1215
			if ($flag & $cfg) {
1216
				$hide->add($this->exprFilterBitwise('config', $cfg, $aliasCircle));
1217
			}
1218
		}
1219
1220
		$this->andWhere($hide);
1221
	}
1222
1223
1224
	/**
1225
	 * Limit visibility on Sensitive information when search for members.
1226
	 *
1227
	 * @param string $alias
1228
	 *
1229
	 * @return ICompositeExpression
1230
	 */
1231
	private function limitRemoteVisibility_Sensitive_Members(string $alias): ICompositeExpression {
1232
		$expr = $this->expr();
1233
		$andPassive = $expr->andX();
1234
		$andPassive->add(
1235
			$expr->eq($alias . '.type', $this->createNamedParameter(RemoteInstance::TYPE_PASSIVE))
1236
		);
1237
1238
		$orMemberOrLevel = $expr->orX();
1239
		$orMemberOrLevel->add(
1240
			$expr->eq($this->getDefaultSelectAlias() . '.instance', $alias . '.instance')
1241
		);
1242
		// TODO: do we need this ? (display members from the local instance)
1243
		$orMemberOrLevel->add(
1244
			$expr->emptyString($this->getDefaultSelectAlias() . '.instance')
1245
		);
1246
1247
		$orMemberOrLevel->add(
1248
			$expr->eq(
1249
				$this->getDefaultSelectAlias() . '.level',
1250
				$this->createNamedParameter(Member::LEVEL_OWNER)
1251
			)
1252
		);
1253
		$andPassive->add($orMemberOrLevel);
1254
1255
		return $andPassive;
1256
	}
1257
1258
1259
	/**
1260
	 * Link to storage/filecache
1261
	 *
1262
	 * @param string $aliasShare
1263
	 *
1264
	 * @throws RequestBuilderException
1265
	 */
1266
	public function leftJoinFileCache(string $aliasShare) {
1267
		$expr = $this->expr();
1268
1269
		$aliasFileCache = $this->generateAlias($aliasShare, self::FILE_CACHE);
1270
		$aliasStorages = $this->generateAlias($aliasFileCache, self::STORAGES);
1271
1272
		$this->generateSelectAlias(
1273
			CoreRequestBuilder::$outsideTables[CoreRequestBuilder::TABLE_FILE_CACHE],
1274
			$aliasFileCache,
1275
			$aliasFileCache,
1276
			[]
1277
		)
1278
			 ->generateSelectAlias(
1279
				 CoreRequestBuilder::$outsideTables[CoreRequestBuilder::TABLE_STORAGES],
1280
				 $aliasStorages,
1281
				 $aliasStorages,
1282
				 []
1283
			 )
1284
			 ->leftJoin(
1285
				 $aliasShare, CoreRequestBuilder::TABLE_FILE_CACHE, $aliasFileCache,
1286
				 $expr->eq($aliasShare . '.file_source', $aliasFileCache . '.fileid')
1287
			 )
1288
			 ->leftJoin(
1289
				 $aliasFileCache, CoreRequestBuilder::TABLE_STORAGES, $aliasStorages,
1290
				 $expr->eq($aliasFileCache . '.storage', $aliasStorages . '.numeric_id')
1291
			 );
1292
	}
1293
1294
1295
	/**
1296
	 * @param string $aliasShare
1297
	 * @param string $aliasShareMemberships
1298
	 *
1299
	 * @throws RequestBuilderException
1300
	 */
1301
	public function leftJoinShareChild(string $aliasShare, string $aliasShareMemberships = '') {
1302
		$expr = $this->expr();
1303
1304
		$aliasShareChild = $this->generateAlias($aliasShare, self::SHARE);
1305
		if ($aliasShareMemberships === '') {
1306
			$aliasShareMemberships = $this->generateAlias($aliasShare, self::MEMBERSHIPS, $options);
1307
		}
1308
1309
		$this->leftJoin(
1310
			$aliasShareMemberships, CoreRequestBuilder::TABLE_SHARE, $aliasShareChild,
1311
			$expr->andX(
1312
				$expr->eq($aliasShareChild . '.parent', $aliasShare . '.id'),
1313
				$expr->eq($aliasShareChild . '.share_with', $aliasShareMemberships . '.single_id')
1314
			)
1315
		);
1316
1317
		$this->generateSelectAlias(
1318
			['id', 'file_target', 'permissions'],
1319
			$aliasShareChild,
1320
			'child_',
1321
			[]
1322
		);
1323
1324
//		$this->selectAlias($aliasShareParent . '.permissions', 'parent_perms');
1325
	}
1326
1327
1328
	/**
1329
	 * @param string $alias
1330
	 * @param FederatedUser $federatedUser
1331
	 * @param bool $reshares
1332
	 */
1333
	public function limitToShareOwner(string $alias, FederatedUser $federatedUser, bool $reshares): void {
1334
		$expr = $this->expr();
1335
1336
		$orX = $expr->orX($this->exprLimit('uid_initiator', $federatedUser->getUserId(), $alias));
1337
1338
		if ($reshares) {
1339
			$orX->add($this->exprLimit('uid_owner', $federatedUser->getUserId(), $alias));
1340
		}
1341
1342
		$this->andWhere($orX);
1343
	}
1344
1345
1346
	/**
1347
	 * @param string $aliasMount
1348
	 * @param string $aliasMountMemberships
1349
	 *
1350
	 * @throws RequestBuilderException
1351
	 */
1352
	public function leftJoinMountpoint(string $aliasMount, string $aliasMountMemberships = '') {
1353
		$expr = $this->expr();
1354
1355
		$aliasMountpoint = $this->generateAlias($aliasMount, self::MOUNTPOINT);
1356
		if ($aliasMountMemberships === '') {
1357
			$aliasMountMemberships = $this->generateAlias($aliasMount, self::MEMBERSHIPS, $options);
1358
		}
1359
1360
		$this->leftJoin(
1361
			$aliasMountMemberships, CoreRequestBuilder::TABLE_MOUNTPOINT, $aliasMountpoint,
1362
			$expr->andX(
1363
				$expr->eq($aliasMountpoint . '.mount_id', $aliasMount . '.mount_id'),
1364
				$expr->eq($aliasMountpoint . '.single_id', $aliasMountMemberships . '.single_id')
1365
			)
1366
		);
1367
1368
		$this->selectAlias($aliasMountpoint . '.mountpoint', $aliasMountpoint . '_mountpoint');
1369
		$this->selectAlias($aliasMountpoint . '.mountpoint_hash', $aliasMountpoint . '_mountpoint_hash');
1370
	}
1371
1372
1373
	/**
1374
	 * @param string $alias
1375
	 * @param array $default
1376
	 *
1377
	 * @return CoreQueryBuilder
1378
	 */
1379
	private function generateCircleSelectAlias(string $alias, array $default = []): self {
1380
		$this->generateSelectAlias(
1381
			CoreRequestBuilder::$tables[CoreRequestBuilder::TABLE_CIRCLE],
1382
			$alias,
1383
			$alias,
1384
			$default
1385
		);
1386
1387
		return $this;
1388
	}
1389
1390
	/**
1391
	 * @param string $alias
1392
	 * @param array $default
1393
	 *
1394
	 * @return $this
1395
	 */
1396
	private function generateMemberSelectAlias(string $alias, array $default = []): self {
1397
		$this->generateSelectAlias(
1398
			CoreRequestBuilder::$tables[CoreRequestBuilder::TABLE_MEMBER],
1399
			$alias,
1400
			$alias,
1401
			$default
1402
		);
1403
1404
		return $this;
1405
	}
1406
1407
1408
	/**
1409
	 * @param string $alias
1410
	 * @param array $default
1411
	 * @param string $prefix
1412
	 *
1413
	 * @return $this
1414
	 */
1415
	private function generateMembershipSelectAlias(
1416
		string $alias,
1417
		string $prefix = '',
1418
		array $default = []
1419
	): self {
1420
		$this->generateSelectAlias(
1421
			CoreRequestBuilder::$tables[CoreRequestBuilder::TABLE_MEMBERSHIP],
1422
			$alias,
1423
			($prefix === '') ? $alias : $prefix,
1424
			$default
1425
		);
1426
1427
		return $this;
1428
	}
1429
1430
1431
	/**
1432
	 * @param string $alias
1433
	 * @param array $default
1434
	 *
1435
	 * @return $this
1436
	 */
1437
	private function generateRemoteInstanceSelectAlias(string $alias, array $default = []): self {
1438
		$this->generateSelectAlias(
1439
			CoreRequestBuilder::$tables[CoreRequestBuilder::TABLE_REMOTE],
1440
			$alias,
1441
			$alias,
1442
			$default
1443
		);
1444
1445
		return $this;
1446
	}
1447
1448
1449
	/**
1450
	 * @param array $path
1451
	 * @param array $options
1452
	 */
1453
	public function setOptions(array $path, array $options): void {
1454
		$options = [self::OPTIONS => $options];
1455
		foreach (array_reverse($path) as $item) {
1456
			$options = [$item => $options];
1457
		}
1458
1459
		$this->options = $options;
1460
	}
1461
1462
1463
	/**
1464
	 * @param string $base
1465
	 * @param string $extension
1466
	 * @param array|null $options
1467
	 *
1468
	 * @return string
1469
	 * @throws RequestBuilderException
1470
	 */
1471
	public function generateAlias(string $base, string $extension, ?array &$options = []): string {
1472
		$search = str_replace('_', '.', $base);
1473
		$path = $search . '.' . $extension;
1474
		if (!$this->validKey($path, self::$SQL_PATH)
1475
			&& !in_array($extension, $this->getArray($search, self::$SQL_PATH))) {
1476
			throw new RequestBuilderException($extension . ' not found in ' . $search);
1477
		}
1478
1479
		if (!is_array($options)) {
1480
			$options = [];
1481
		}
1482
1483
		$optionPath = '';
1484
		foreach (explode('.', $path) as $p) {
1485
			$optionPath = trim($optionPath . '.' . $p, '.');
1486
			$options = array_merge(
1487
				$options,
1488
				$this->getArray($optionPath . '.' . self::OPTIONS, self::$SQL_PATH),
1489
				$this->getArray($optionPath . '.' . self::OPTIONS, $this->options)
1490
			);
1491
		}
1492
1493
		return $base . '_' . $extension;
1494
	}
1495
1496
1497
	/**
1498
	 * @param string $prefix
1499
	 *
1500
	 * @return array
1501
	 */
1502
	public function getAvailablePath(string $prefix): array {
1503
		$prefix = trim($prefix, '_');
1504
		$search = str_replace('_', '.', $prefix);
1505
1506
		$path = [];
1507
		foreach ($this->getArray($search, self::$SQL_PATH) as $arr => $item) {
1508
			if (is_numeric($arr)) {
1509
				$k = $item;
1510
			} else {
1511
				$k = $arr;
1512
			}
1513
			$path[$k] = $prefix . '_' . $k . '_';
1514
		}
1515
1516
		return $path;
1517
	}
1518
1519
}
1520
1521