Completed
Pull Request — master (#633)
by Maxence
03:06
created

CoreQueryBuilder::generateCircleSelectAlias()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 10
rs 9.9332
c 0
b 0
f 0
cc 1
nc 1
nop 2
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
	 * @throws RequestBuilderException
683
	 */
684
	public function countMembers(string $alias): void {
685
		if ($this->getType() !== QueryBuilder::SELECT) {
686
			return;
687
		}
688
689
		$aliasMemberCount = $this->generateAlias($alias, self::MEMBER_COUNT, $options);
690
		$getData = $this->getBool('getData', $options, false);
691
		if (!$getData) {
692
			return;
693
		}
694
695
		$expr = $this->expr();
696
		$this->selectAlias(
697
			$this->createFunction('COUNT(`' . $aliasMemberCount . '`.`member_id`)'),
698
			(($alias !== $this->getDefaultSelectAlias()) ? $alias . '_' : '') . 'population'
699
		);
700
		$this->leftJoin(
701
			$alias, CoreRequestBuilder::TABLE_MEMBER, $aliasMemberCount,
702
			$expr->eq($alias . '.unique_id', $aliasMemberCount . '.circle_id')
703
		);
704
	}
705
706
707
	/**
708
	 * @param string $alias
709
	 * @param IFederatedUser|null $initiator
710
	 * @param string $field
711
	 * @param string $helpedAlias
0 ignored issues
show
Documentation introduced by
There is no parameter named $helpedAlias. Did you maybe mean $helperAlias?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function. It has, however, found a similar but not annotated parameter which might be a good fit.

Consider the following example. The parameter $ireland is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $ireland
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was changed, but the annotation was not.

Loading history...
712
	 *
713
	 * @throws RequestBuilderException
714
	 */
715
	public function leftJoinCircle(
716
		string $alias,
717
		?IFederatedUser $initiator = null,
718
		string $field = 'circle_id',
719
		string $helperAlias = ''
720
	): void {
721
		if ($this->getType() !== QueryBuilder::SELECT) {
722
			return;
723
		}
724
725
		$helperAlias = ($helperAlias !== '') ? $helperAlias : $alias;
726
		$aliasCircle = $this->generateAlias($alias, self::CIRCLE, $options);
727
		$getData = $this->getBool('getData', $options, false);
728
		$expr = $this->expr();
729
730
		if ($getData) {
731
			$this->generateCircleSelectAlias($aliasCircle);
732
		}
733
734
		$this->leftJoin(
735
			$helperAlias,
736
			CoreRequestBuilder::TABLE_CIRCLE,
737
			$aliasCircle,
738
			$expr->eq($aliasCircle . '.unique_id', $helperAlias . '.' . $field)
739
		);
740
741
		if (!is_null($initiator)) {
742
			$this->limitToInitiator($aliasCircle, $initiator);
743
		}
744
745
		$this->leftJoinOwner($aliasCircle);
746
	}
747
748
749
	/**
750
	 * @param string $aliasMember
751
	 *
752
	 * @throws RequestBuilderException
753
	 */
754
	public function leftJoinInvitedBy(string $aliasMember): void {
755
		if ($this->getType() !== QueryBuilder::SELECT) {
756
			return;
757
		}
758
759
		try {
760
			$aliasInvitedBy = $this->generateAlias($aliasMember, self::INVITED_BY);
761
		} catch (RequestBuilderException $e) {
762
			return;
763
		}
764
765
		$expr = $this->expr();
766
		$this->generateCircleSelectAlias($aliasInvitedBy)
767
			 ->leftJoin(
768
				 $aliasMember, CoreRequestBuilder::TABLE_CIRCLE, $aliasInvitedBy,
769
				 $expr->eq($aliasMember . '.invited_by', $aliasInvitedBy . '.unique_id')
770
			 );
771
772
		$this->leftJoinOwner($aliasInvitedBy);
773
	}
774
775
776
	/**
777
	 * @param string $aliasMember
778
	 * @param IFederatedUser|null $initiator
779
	 *
780
	 * @throws RequestBuilderException
781
	 */
782
	public function leftJoinBasedOn(
783
		string $aliasMember,
784
		?IFederatedUser $initiator = null
785
	): void {
786
		if ($this->getType() !== QueryBuilder::SELECT) {
787
			return;
788
		}
789
790
		try {
791
			$aliasBasedOn = $this->generateAlias($aliasMember, self::BASED_ON, $options);
792
		} catch (RequestBuilderException $e) {
793
			return;
794
		}
795
796
		$expr = $this->expr();
797
		$this->generateCircleSelectAlias($aliasBasedOn)
798
			 ->leftJoin(
799
				 $aliasMember, CoreRequestBuilder::TABLE_CIRCLE, $aliasBasedOn,
800
				 $expr->eq($aliasBasedOn . '.unique_id', $aliasMember . '.single_id')
801
			 );
802
803
		if (!is_null($initiator)) {
804
			$this->leftJoinInitiator($aliasBasedOn, $initiator);
805
			$this->leftJoinOwner($aliasBasedOn);
806
		}
807
	}
808
809
810
	/**
811
	 * @param string $alias
812
	 * @param string $field
813
	 *
814
	 * @throws RequestBuilderException
815
	 */
816
	public function leftJoinOwner(string $alias, string $field = 'unique_id'): void {
817
		if ($this->getType() !== QueryBuilder::SELECT) {
818
			return;
819
		}
820
821
		try {
822
			$aliasMember = $this->generateAlias($alias, self::OWNER, $options);
823
			$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...
824
		} catch (RequestBuilderException $e) {
825
			return;
826
		}
827
828
		$expr = $this->expr();
829
		$this->generateMemberSelectAlias($aliasMember)
830
			 ->leftJoin(
831
				 $alias, CoreRequestBuilder::TABLE_MEMBER, $aliasMember,
832
				 $expr->andX(
833
					 $expr->eq($aliasMember . '.circle_id', $alias . '.' . $field),
834
					 $expr->eq(
835
						 $aliasMember . '.level',
836
						 $this->createNamedParameter(Member::LEVEL_OWNER, self::PARAM_INT)
837
					 )
838
				 )
839
			 );
840
841
		$this->leftJoinBasedOn($aliasMember);
842
	}
843
844
845
	/**
846
	 * @param string $alias
847
	 * @param string $fieldCircleId
848
	 * @param string $fieldSingleId
849
	 *
850
	 * @throws RequestBuilderException
851
	 */
852
	public function leftJoinMember(
853
		string $alias,
854
		string $fieldCircleId = 'circle_id',
855
		string $fieldSingleId = 'single_id'
856
	): void {
857
		if ($this->getType() !== QueryBuilder::SELECT) {
858
			return;
859
		}
860
861
		try {
862
			$aliasMember = $this->generateAlias($alias, self::MEMBER, $options);
863
			$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...
864
		} catch (RequestBuilderException $e) {
865
			return;
866
		}
867
868
		$expr = $this->expr();
869
		$this->generateMemberSelectAlias($aliasMember)
870
			 ->leftJoin(
871
				 $alias, CoreRequestBuilder::TABLE_MEMBER, $aliasMember,
872
				 $expr->andX(
873
					 $expr->eq($aliasMember . '.circle_id', $alias . '.' . $fieldCircleId),
874
					 $expr->eq($aliasMember . '.single_id', $alias . '.' . $fieldSingleId),
875
					 $expr->gte(
876
						 $aliasMember . '.level',
877
						 $this->createNamedParameter(Member::LEVEL_MEMBER, self::PARAM_INT)
878
					 )
879
				 )
880
			 );
881
882
		$this->leftJoinRemoteInstance($aliasMember);
883
		$this->leftJoinBasedOn($aliasMember);
884
	}
885
886
887
	/**
888
	 * if 'getData' is true, will returns 'inheritanceBy': the Member at the end of a sub-chain of
889
	 * memberships (based on $field for Top Circle's singleId)
890
	 *
891
	 * @param string $alias
892
	 * @param string $field
893
	 * @param string $aliasInheritedBy
894
	 *
895
	 * @throws RequestBuilderException
896
	 */
897
	public function leftJoinInheritedMembers(
898
		string $alias,
899
		string $field = '',
900
		string $aliasInheritedBy = ''
901
	): void {
902
		$expr = $this->expr();
903
904
		$field = ($field === '') ? 'circle_id' : $field;
905
		$aliasMembership = $this->generateAlias($alias, self::MEMBERSHIPS, $options);
906
907
		$this->leftJoin(
908
			$alias, CoreRequestBuilder::TABLE_MEMBERSHIP, $aliasMembership,
909
			$expr->eq($aliasMembership . '.circle_id', $alias . '.' . $field)
910
		);
911
912
//		if (!$this->getBool('getData', $options, false)) {
913
//			return;
914
//		}
915
916
		if ($aliasInheritedBy === '') {
917
			$aliasInheritedBy = $this->generateAlias($alias, self::INHERITED_BY);
918
		}
919
		$this->generateMemberSelectAlias($aliasInheritedBy)
920
			 ->leftJoin(
921
				 $alias, CoreRequestBuilder::TABLE_MEMBER, $aliasInheritedBy,
922
				 $expr->andX(
923
					 $expr->eq($aliasMembership . '.inheritance_last', $aliasInheritedBy . '.circle_id'),
924
					 $expr->eq($aliasMembership . '.single_id', $aliasInheritedBy . '.single_id')
925
				 )
926
			 );
927
928
		$this->leftJoinBasedOn($aliasInheritedBy);
929
	}
930
931
932
	/**
933
	 * @throws RequestBuilderException
934
	 */
935
	public function limitToInheritedMemberships(string $alias, string $singleId, string $field = ''): void {
936
		$expr = $this->expr();
937
		$field = ($field === '') ? 'circle_id' : $field;
938
		$aliasUpstreamMembership = $this->generateAlias($alias, self::UPSTREAM_MEMBERSHIPS, $options);
939
		$this->leftJoin(
940
			$alias, CoreRequestBuilder::TABLE_MEMBERSHIP, $aliasUpstreamMembership,
941
			$expr->eq($aliasUpstreamMembership . '.single_id', $this->createNamedParameter($singleId))
942
		);
943
944
		$orX = $expr->orX(
945
			$expr->eq($aliasUpstreamMembership . '.circle_id', $alias . '.' . $field),
946
			$expr->eq($alias . '.' . $field, $this->createNamedParameter($singleId))
947
		);
948
949
		$this->andWhere($orX);
950
	}
951
952
953
	/**
954
	 * limit the request to Members and Sub Members of a Circle.
955
	 *
956
	 * @param string $alias
957
	 * @param string $singleId
958
	 * @param int $level
959
	 *
960
	 * @throws RequestBuilderException
961
	 */
962
	public function limitToMembersByInheritance(string $alias, string $singleId, int $level = 0): void {
963
		$this->leftJoinMembersByInheritance($alias);
964
965
		$expr = $this->expr();
966
		$aliasMembership = $this->generateAlias($alias, self::MEMBERSHIPS);
967
		$this->andWhere($expr->eq($aliasMembership . '.circle_id', $this->createNamedParameter($singleId)));
968
		if ($level > 1) {
969
			$this->andWhere(
970
				$expr->gte(
971
					$aliasMembership . '.level',
972
					$this->createNamedParameter($level, IQueryBuilder::PARAM_INT)
973
				)
974
			);
975
		}
976
	}
977
978
979
	/**
980
	 * if 'getData' is true, will returns 'inheritanceFrom': the Circle-As-Member of the Top Circle
981
	 * that explain the membership of a Member (based on $field for singleId) to a specific Circle
982
	 *
983
	 * // TODO: returns the link/path ?
984
	 *
985
	 * @param string $alias
986
	 * @param string $field
987
	 *
988
	 * @throws RequestBuilderException
989
	 */
990
	public function leftJoinMembersByInheritance(string $alias, string $field = ''): void {
991
		$expr = $this->expr();
992
993
		$field = ($field === '') ? 'circle_id' : $field;
994
		$aliasMembership = $this->generateAlias($alias, self::MEMBERSHIPS, $options);
995
996
		$this->leftJoin(
997
			$alias, CoreRequestBuilder::TABLE_MEMBERSHIP, $aliasMembership,
998
			$expr->andX(
999
				$expr->eq($aliasMembership . '.inheritance_last', $alias . '.' . $field),
1000
				$expr->eq($aliasMembership . '.single_id', $alias . '.single_id')
1001
			)
1002
		);
1003
1004
		if (!$this->getBool('getData', $options, false)) {
1005
			return;
1006
		}
1007
1008
		$aliasInheritanceFrom = $this->generateAlias($alias, self::INHERITANCE_FROM);
1009
		$this->generateMemberSelectAlias($aliasInheritanceFrom)
1010
			 ->leftJoin(
1011
				 $aliasMembership, CoreRequestBuilder::TABLE_MEMBER, $aliasInheritanceFrom,
1012
				 $expr->andX(
1013
					 $expr->eq($aliasMembership . '.circle_id', $aliasInheritanceFrom . '.circle_id'),
1014
					 $expr->eq($aliasMembership . '.inheritance_first', $aliasInheritanceFrom . '.single_id')
1015
				 )
1016
			 );
1017
	}
1018
1019
1020
	/**
1021
	 * limit the result to the point of view of a FederatedUser
1022
	 *
1023
	 * @param string $alias
1024
	 * @param IFederatedUser $user
1025
	 * @param string $field
1026
	 * @param string $helperAlias
1027
	 *
1028
	 * @return ICompositeExpression
1029
	 * @throws RequestBuilderException
1030
	 */
1031
	public function limitToInitiator(
1032
		string $alias,
1033
		IFederatedUser $user,
1034
		string $field = '',
1035
		string $helperAlias = ''
1036
	): ICompositeExpression {
1037
		$this->leftJoinInitiator($alias, $user, $field, $helperAlias);
1038
		$where = $this->limitInitiatorVisibility($alias);
1039
1040
		$aliasInitiator = $this->generateAlias($alias, self::INITIATOR, $options);
1041
		if ($this->getBool('getData', $options, false)) {
1042
			$this->leftJoinBasedOn($aliasInitiator);
1043
		}
1044
1045
		return $where;
1046
	}
1047
1048
1049
	/**
1050
	 * Left join members to filter userId as initiator.
1051
	 *
1052
	 * @param string $alias
1053
	 * @param IFederatedUser $initiator
1054
	 * @param string $field
1055
	 * @param string $helperAlias
1056
	 *
1057
	 * @throws RequestBuilderException
1058
	 */
1059
	public function leftJoinInitiator(
1060
		string $alias,
1061
		IFederatedUser $initiator,
1062
		string $field = '',
1063
		string $helperAlias = ''
1064
	): void {
1065
		if ($this->getType() !== QueryBuilder::SELECT) {
1066
			return;
1067
		}
1068
1069
		$expr = $this->expr();
1070
		$field = ($field === '') ? 'unique_id' : $field;
1071
		$helperAlias = ($helperAlias !== '') ? $helperAlias : $alias;
1072
		$aliasMembership = $this->generateAlias($alias, self::MEMBERSHIPS, $options);
1073
1074
		$this->leftJoin(
1075
			$helperAlias,
1076
			CoreRequestBuilder::TABLE_MEMBERSHIP,
1077
			$aliasMembership,
1078
			$expr->andX(
1079
				$this->exprLimit('single_id', $initiator->getSingleId(), $aliasMembership),
1080
				$expr->eq($aliasMembership . '.circle_id', $helperAlias . '.' . $field)
1081
			)
1082
		);
1083
1084
		if (!$this->getBool('getData', $options, false)) {
1085
			return;
1086
		}
1087
1088
		try {
1089
			$aliasInitiator = $this->generateAlias($alias, self::INITIATOR, $options);
1090
			$this->leftJoin(
1091
				$aliasMembership, CoreRequestBuilder::TABLE_MEMBER, $aliasInitiator,
1092
				$expr->andX(
1093
					$expr->eq($aliasMembership . '.inheritance_first', $aliasInitiator . '.single_id'),
1094
					$expr->eq($aliasMembership . '.circle_id', $aliasInitiator . '.circle_id')
1095
				)
1096
			);
1097
1098
			$aliasInheritedBy = $this->generateAlias($aliasInitiator, self::INHERITED_BY);
1099
			$this->leftJoin(
1100
				$aliasInitiator, CoreRequestBuilder::TABLE_MEMBER, $aliasInheritedBy,
1101
				$expr->andX(
1102
					$expr->eq($aliasMembership . '.single_id', $aliasInheritedBy . '.single_id'),
1103
					$expr->eq($aliasMembership . '.inheritance_last', $aliasInheritedBy . '.circle_id')
1104
				)
1105
			);
1106
1107
			$default = [];
1108
			if ($this->getBool('canBeVisitor', $options, false)) {
1109
				$default = [
1110
					'user_id'     => $initiator->getUserId(),
1111
					'single_id'   => $initiator->getSingleId(),
1112
					'user_type'   => $initiator->getUserType(),
1113
					'cached_name' => $initiator->getDisplayName(),
1114
					'instance'    => $initiator->getInstance()
1115
				];
1116
			}
1117
			$this->generateMemberSelectAlias($aliasInitiator, $default);
1118
1119
			$this->generateMemberSelectAlias($aliasInheritedBy);
1120
			$aliasInheritedByMembership = $this->generateAlias($aliasInheritedBy, self::MEMBERSHIPS);
1121
			$this->generateMembershipSelectAlias($aliasMembership, $aliasInheritedByMembership);
1122
		} catch (RequestBuilderException $e) {
1123
			\OC::$server->getLogger()->log(3, '-- ' . $e->getMessage());
1124
		}
1125
	}
1126
1127
1128
	/**
1129
	 * @param string $alias
1130
	 *
1131
	 * @return ICompositeExpression
1132
	 * @throws RequestBuilderException
1133
	 */
1134
	protected function limitInitiatorVisibility(string $alias): ICompositeExpression {
1135
		$aliasMembership = $this->generateAlias($alias, self::MEMBERSHIPS, $options);
1136
		$getPersonalCircle = $this->getBool('getPersonalCircle', $options, false);
1137
1138
		$expr = $this->expr();
1139
1140
		// Visibility to non-member is
1141
		// - 0 (default), if initiator is member
1142
		// - 2 (Personal), if initiator is owner)
1143
		// - 4 (Visible to everyone)
1144
		$orX = $expr->orX();
1145
		$orX->add(
1146
			$expr->andX(
1147
				$expr->gte($aliasMembership . '.level', $this->createNamedParameter(Member::LEVEL_MEMBER))
1148
			)
1149
		);
1150
1151
		if ($getPersonalCircle) {
1152
			$orX->add(
1153
				$expr->andX(
1154
					$this->exprLimitBitwise('config', Circle::CFG_PERSONAL, $alias),
1155
					$expr->eq($aliasMembership . '.level', $this->createNamedParameter(Member::LEVEL_OWNER))
1156
				)
1157
			);
1158
		}
1159
		if (!$this->getBool('mustBeMember', $options, true)) {
1160
			$orX->add($this->exprLimitBitwise('config', Circle::CFG_VISIBLE, $alias));
1161
		}
1162
		if ($this->getBool('canBeVisitor', $options, false)) {
1163
			// TODO: should find a better way, also filter on remote initiator on non-federated ?
1164
			$orX->add($expr->gte($alias . '.config', $this->createNamedParameter(0)));
1165
		}
1166
		if ($this->getBool('canBeVisitorOnOpen', $options, false)) {
1167
			$andOpen = $expr->andX();
1168
			$andOpen->add($this->exprLimitBitwise('config', Circle::CFG_OPEN, $alias));
1169
			$andOpen->add($this->exprFilterBitwise('config', Circle::CFG_REQUEST, $alias));
1170
			$orX->add($andOpen);
1171
		}
1172
1173
		$this->andWhere($orX);
1174
1175
		return $orX;
1176
//		$orTypes = $this->generateLimit($qb, $circleUniqueId, $userId, $type, $name, $forceAll);
1177
//		if (sizeof($orTypes) === 0) {
1178
//			throw new ConfigNoCircleAvailableException(
1179
//				$this->l10n->t(
1180
//					'You cannot use the Circles Application until your administrator has allowed at least one type of circles'
1181
//				)
1182
//			);
1183
//		}
1184
1185
//		$orXTypes = $this->expr()
1186
//						 ->orX();
1187
//		foreach ($orTypes as $orType) {
1188
//			$orXTypes->add($orType);
1189
//		}
1190
//
1191
//		$qb->andWhere($orXTypes);
1192
	}
1193
1194
1195
	/**
1196
	 * CFG_SINGLE, CFG_HIDDEN and CFG_BACKEND means hidden from listing.
1197
	 *
1198
	 * @param string $aliasCircle
1199
	 * @param int $flag
1200
	 */
1201
	public function filterCircles(
1202
		string $aliasCircle,
1203
		int $flag = Circle::CFG_SINGLE | Circle::CFG_HIDDEN | Circle::CFG_BACKEND
1204
	): void {
1205
		if ($flag === 0) {
1206
			return;
1207
		}
1208
1209
		$expr = $this->expr();
1210
		$hide = $expr->andX();
1211
		foreach (Circle::$DEF_CFG as $cfg => $v) {
1212
			if ($flag & $cfg) {
1213
				$hide->add($this->exprFilterBitwise('config', $cfg, $aliasCircle));
1214
			}
1215
		}
1216
1217
		$this->andWhere($hide);
1218
	}
1219
1220
1221
	/**
1222
	 * Limit visibility on Sensitive information when search for members.
1223
	 *
1224
	 * @param string $alias
1225
	 *
1226
	 * @return ICompositeExpression
1227
	 */
1228
	private function limitRemoteVisibility_Sensitive_Members(string $alias): ICompositeExpression {
1229
		$expr = $this->expr();
1230
		$andPassive = $expr->andX();
1231
		$andPassive->add(
1232
			$expr->eq($alias . '.type', $this->createNamedParameter(RemoteInstance::TYPE_PASSIVE))
1233
		);
1234
1235
		$orMemberOrLevel = $expr->orX();
1236
		$orMemberOrLevel->add(
1237
			$expr->eq($this->getDefaultSelectAlias() . '.instance', $alias . '.instance')
1238
		);
1239
		// TODO: do we need this ? (display members from the local instance)
1240
		$orMemberOrLevel->add(
1241
			$expr->emptyString($this->getDefaultSelectAlias() . '.instance')
1242
		);
1243
1244
		$orMemberOrLevel->add(
1245
			$expr->eq(
1246
				$this->getDefaultSelectAlias() . '.level',
1247
				$this->createNamedParameter(Member::LEVEL_OWNER)
1248
			)
1249
		);
1250
		$andPassive->add($orMemberOrLevel);
1251
1252
		return $andPassive;
1253
	}
1254
1255
1256
	/**
1257
	 * Link to storage/filecache
1258
	 *
1259
	 * @param string $aliasShare
1260
	 *
1261
	 * @throws RequestBuilderException
1262
	 */
1263
	public function leftJoinFileCache(string $aliasShare) {
1264
		$expr = $this->expr();
1265
1266
		$aliasFileCache = $this->generateAlias($aliasShare, self::FILE_CACHE);
1267
		$aliasStorages = $this->generateAlias($aliasFileCache, self::STORAGES);
1268
1269
		$this->generateSelectAlias(
1270
			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...
1271
			$aliasFileCache,
1272
			$aliasFileCache,
1273
			[]
1274
		)
1275
			 ->generateSelectAlias(
1276
				 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...
1277
				 $aliasStorages,
1278
				 $aliasStorages,
1279
				 []
1280
			 )
1281
			 ->leftJoin(
1282
				 $aliasShare, CoreRequestBuilder::TABLE_FILE_CACHE, $aliasFileCache,
1283
				 $expr->eq($aliasShare . '.file_source', $aliasFileCache . '.fileid')
1284
			 )
1285
			 ->leftJoin(
1286
				 $aliasFileCache, CoreRequestBuilder::TABLE_STORAGES, $aliasStorages,
1287
				 $expr->eq($aliasFileCache . '.storage', $aliasStorages . '.numeric_id')
1288
			 );
1289
	}
1290
1291
1292
	/**
1293
	 * @param string $aliasShare
1294
	 * @param string $aliasShareMemberships
1295
	 *
1296
	 * @throws RequestBuilderException
1297
	 */
1298
	public function leftJoinShareChild(string $aliasShare, string $aliasShareMemberships = '') {
1299
		$expr = $this->expr();
1300
1301
		$aliasShareChild = $this->generateAlias($aliasShare, self::SHARE);
1302
		if ($aliasShareMemberships === '') {
1303
			$aliasShareMemberships = $this->generateAlias($aliasShare, self::MEMBERSHIPS, $options);
1304
		}
1305
1306
		$this->leftJoin(
1307
			$aliasShareMemberships, CoreRequestBuilder::TABLE_SHARE, $aliasShareChild,
1308
			$expr->andX(
1309
				$expr->eq($aliasShareChild . '.parent', $aliasShare . '.id'),
1310
				$expr->eq($aliasShareChild . '.share_with', $aliasShareMemberships . '.single_id')
1311
			)
1312
		);
1313
1314
		$this->generateSelectAlias(
1315
			['id', 'file_target', 'permissions'],
1316
			$aliasShareChild,
1317
			'child_',
1318
			[]
1319
		);
1320
1321
//		$this->selectAlias($aliasShareParent . '.permissions', 'parent_perms');
1322
	}
1323
1324
1325
	/**
1326
	 * @param string $alias
1327
	 * @param FederatedUser $federatedUser
1328
	 * @param bool $reshares
1329
	 */
1330
	public function limitToShareOwner(string $alias, FederatedUser $federatedUser, bool $reshares): void {
1331
		$expr = $this->expr();
1332
1333
		$orX = $expr->orX($this->exprLimit('uid_initiator', $federatedUser->getUserId(), $alias));
1334
1335
		if ($reshares) {
1336
			$orX->add($this->exprLimit('uid_owner', $federatedUser->getUserId(), $alias));
1337
		}
1338
1339
		$this->andWhere($orX);
1340
	}
1341
1342
1343
	/**
1344
	 * @param string $aliasMount
1345
	 * @param string $aliasMountMemberships
1346
	 *
1347
	 * @throws RequestBuilderException
1348
	 */
1349
	public function leftJoinMountpoint(string $aliasMount, string $aliasMountMemberships = '') {
1350
		$expr = $this->expr();
1351
1352
		$aliasMountpoint = $this->generateAlias($aliasMount, self::MOUNTPOINT);
1353
		if ($aliasMountMemberships === '') {
1354
			$aliasMountMemberships = $this->generateAlias($aliasMount, self::MEMBERSHIPS, $options);
1355
		}
1356
1357
		$this->leftJoin(
1358
			$aliasMountMemberships, CoreRequestBuilder::TABLE_MOUNTPOINT, $aliasMountpoint,
1359
			$expr->andX(
1360
				$expr->eq($aliasMountpoint . '.mount_id', $aliasMount . '.mount_id'),
1361
				$expr->eq($aliasMountpoint . '.single_id', $aliasMountMemberships . '.single_id')
1362
			)
1363
		);
1364
1365
		$this->selectAlias($aliasMountpoint . '.mountpoint', $aliasMountpoint . '_mountpoint');
1366
		$this->selectAlias($aliasMountpoint . '.mountpoint_hash', $aliasMountpoint . '_mountpoint_hash');
1367
	}
1368
1369
1370
	/**
1371
	 * @param string $alias
1372
	 * @param array $default
1373
	 *
1374
	 * @return CoreQueryBuilder
1375
	 */
1376
	private function generateCircleSelectAlias(string $alias, array $default = []): self {
1377
		$this->generateSelectAlias(
1378
			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...
1379
			$alias,
1380
			$alias,
1381
			$default
1382
		);
1383
1384
		return $this;
1385
	}
1386
1387
	/**
1388
	 * @param string $alias
1389
	 * @param array $default
1390
	 *
1391
	 * @return $this
1392
	 */
1393
	private function generateMemberSelectAlias(string $alias, array $default = []): self {
1394
		$this->generateSelectAlias(
1395
			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...
1396
			$alias,
1397
			$alias,
1398
			$default
1399
		);
1400
1401
		return $this;
1402
	}
1403
1404
1405
	/**
1406
	 * @param string $alias
1407
	 * @param array $default
1408
	 * @param string $prefix
1409
	 *
1410
	 * @return $this
1411
	 */
1412
	private function generateMembershipSelectAlias(
1413
		string $alias,
1414
		string $prefix = '',
1415
		array $default = []
1416
	): self {
1417
		$this->generateSelectAlias(
1418
			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...
1419
			$alias,
1420
			($prefix === '') ? $alias : $prefix,
1421
			$default
1422
		);
1423
1424
		return $this;
1425
	}
1426
1427
1428
	/**
1429
	 * @param string $alias
1430
	 * @param array $default
1431
	 *
1432
	 * @return $this
1433
	 */
1434
	private function generateRemoteInstanceSelectAlias(string $alias, array $default = []): self {
1435
		$this->generateSelectAlias(
1436
			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...
1437
			$alias,
1438
			$alias,
1439
			$default
1440
		);
1441
1442
		return $this;
1443
	}
1444
1445
1446
	/**
1447
	 * @param array $path
1448
	 * @param array $options
1449
	 */
1450
	public function setOptions(array $path, array $options): void {
1451
		$options = [self::OPTIONS => $options];
1452
		foreach (array_reverse($path) as $item) {
1453
			$options = [$item => $options];
1454
		}
1455
1456
		$this->options = $options;
1457
	}
1458
1459
1460
	/**
1461
	 * @param string $base
1462
	 * @param string $extension
1463
	 * @param array|null $options
1464
	 *
1465
	 * @return string
1466
	 * @throws RequestBuilderException
1467
	 */
1468
	public function generateAlias(string $base, string $extension, ?array &$options = []): string {
1469
		$search = str_replace('_', '.', $base);
1470
		$path = $search . '.' . $extension;
1471
		if (!$this->validKey($path, self::$SQL_PATH)
1472
			&& !in_array($extension, $this->getArray($search, self::$SQL_PATH))) {
1473
			throw new RequestBuilderException($extension . ' not found in ' . $search);
1474
		}
1475
1476
		if (!is_array($options)) {
1477
			$options = [];
1478
		}
1479
1480
		$optionPath = '';
1481
		foreach (explode('.', $path) as $p) {
1482
			$optionPath = trim($optionPath . '.' . $p, '.');
1483
			$options = array_merge(
1484
				$options,
1485
				$this->getArray($optionPath . '.' . self::OPTIONS, self::$SQL_PATH),
1486
				$this->getArray($optionPath . '.' . self::OPTIONS, $this->options)
1487
			);
1488
		}
1489
1490
		return $base . '_' . $extension;
1491
	}
1492
1493
1494
	/**
1495
	 * @param string $prefix
1496
	 *
1497
	 * @return array
1498
	 */
1499
	public function getAvailablePath(string $prefix): array {
1500
		$prefix = trim($prefix, '_');
1501
		$search = str_replace('_', '.', $prefix);
1502
1503
		$path = [];
1504
		foreach ($this->getArray($search, self::$SQL_PATH) as $arr => $item) {
1505
			if (is_numeric($arr)) {
1506
				$k = $item;
1507
			} else {
1508
				$k = $arr;
1509
			}
1510
			$path[$k] = $prefix . '_' . $k . '_';
1511
		}
1512
1513
		return $path;
1514
	}
1515
1516
}
1517
1518