Completed
Push — master ( bf476c...b09631 )
by Maxence
02:44
created

CoreQueryBuilder::limitToName()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 3
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 1
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);
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...
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);
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...
282
	}
283
284
	/**
285
	 * @param int $source
286
	 */
287
	public function limitToSource(int $source): void {
288
		$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...
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);
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...
305
	}
306
307
308
	/**
309
	 * @param string $itemId
310
	 */
311
	public function limitToItemId(string $itemId): void {
312
		$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...
313
	}
314
315
316
	/**
317
	 * @param string $host
318
	 */
319
	public function limitToInstance(string $host): void {
320
		$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...
321
	}
322
323
324
	/**
325
	 * @param int $userType
326
	 */
327
	public function limitToUserType(int $userType): void {
328
		$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...
329
	}
330
331
332
	/**
333
	 * @param int $shareType
334
	 */
335
	public function limitToShareType(int $shareType): void {
336
		$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...
337
	}
338
339
340
	/**
341
	 * @param string $shareWith
342
	 */
343
	public function limitToShareWith(string $shareWith): void {
344
		$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...
345
	}
346
347
348
	/**
349
	 * @param int $nodeId
350
	 */
351
	public function limitToFileSource(int $nodeId): void {
352
		$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...
353
	}
354
355
	/**
356
	 * @param array $files
357
	 */
358
	public function limitToFileSourceArray(array $files): void {
359
		$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...
360
	}
361
362
363
	/**
364
	 * @param int $shareId
365
	 */
366
	public function limitToShareParent(int $shareId): void {
367
		$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...
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) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
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);
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...
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);
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...
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) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
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)
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],
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...
1274
			$aliasFileCache,
1275
			$aliasFileCache,
1276
			[]
1277
		)
1278
			 ->generateSelectAlias(
1279
				 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...
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],
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...
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],
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...
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],
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...
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],
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...
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