Completed
Pull Request — master (#551)
by Maxence
02:15
created

CoreQueryBuilder::limitToRemoteInstance()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
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
 * Circles - Bring cloud-users closer together.
7
 *
8
 * This file is licensed under the Affero General Public License version 3 or
9
 * later. See the COPYING file.
10
 *
11
 * @author Maxence Lange <[email protected]>
12
 * @copyright 2017
13
 * @license GNU AGPL version 3 or any later version
14
 *
15
 * This program is free software: you can redistribute it and/or modify
16
 * it under the terms of the GNU Affero General Public License as
17
 * published by the Free Software Foundation, either version 3 of the
18
 * License, or (at your option) any later version.
19
 *
20
 * This program is distributed in the hope that it will be useful,
21
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
23
 * GNU Affero General Public License for more details.
24
 *
25
 * You should have received a copy of the GNU Affero General Public License
26
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
27
 *
28
 */
29
30
31
namespace OCA\Circles\Db;
32
33
34
use daita\MySmallPhpTools\Db\Nextcloud\nc21\NC21ExtendedQueryBuilder;
35
use Doctrine\DBAL\Query\QueryBuilder;
36
use OC;
37
use OCA\Circles\IFederatedUser;
38
use OCA\Circles\Model\Circle;
39
use OCA\Circles\Model\Federated\RemoteInstance;
40
use OCA\Circles\Model\FederatedUser;
41
use OCA\Circles\Model\Member;
42
use OCA\Circles\Service\ConfigService;
43
44
/**
45
 * Class CoreQueryBuilder
46
 *
47
 * @package OCA\Circles\Db
48
 */
49
class CoreQueryBuilder extends NC21ExtendedQueryBuilder {
50
51
52
	const PREFIX_MEMBER = 'member_';
53
	const PREFIX_OWNER = 'owner_';
54
	const PREFIX_INITIATOR = 'initiator_';
55
	const PREFIX_CIRCLE = 'circle_';
56
57
58
	/** @var ConfigService */
59
	private $configService;
60
61
62
	/**
63
	 * CoreQueryBuilder constructor.
64
	 */
65
	public function __construct() {
66
		parent::__construct();
67
68
		$this->configService = OC::$server->get(ConfigService::class);
69
	}
70
71
72
	/**
73
	 * @param IFederatedUser $member
74
	 *
75
	 * @return string
76
	 */
77
	public function getInstance(IFederatedUser $member): string {
78
		$instance = $member->getInstance();
79
80
		return ($this->configService->isLocalInstance($instance)) ? '' : $instance;
81
	}
82
83
84
	/**
85
	 * @param string $id
86
	 */
87
	public function limitToCircleId(string $id): void {
88
		$this->limitToDBField('circle_id', $id, true);
89
	}
90
91
	/**
92
	 * @param int $config
93
	 */
94
	public function limitToConfig(int $config): void {
95
		$this->limitToDBFieldInt('config', $config);
96
	}
97
98
99
	/**
100
	 * @param string $singleId
101
	 */
102
	public function limitToSingleId(string $singleId): void {
103
		$this->limitToDBField('single_id', $singleId, true);
104
	}
105
106
	/**
107
	 * @param string $host
108
	 */
109
	public function limitToInstance(string $host): void {
110
		$this->limitToDBField('instance', $host, false);
111
	}
112
113
114
	/**
115
	 * @param int $userType
116
	 */
117
	public function limitToUserType(int $userType): void {
118
		$this->limitToDBFieldInt('user_type', $userType);
119
	}
120
121
122
	/**
123
	 * @param IFederatedUser $initiator
124
	 * @param string $alias
125
	 */
126
	public function limitToInitiator(IFederatedUser $initiator, string $alias = ''): void {
127
		$this->leftJoinInitiator($initiator, 'init', $alias);
128
		$this->limitVisibility('init', $alias);
129
	}
130
131
132
	/**
133
	 * @param string $instance
134
	 */
135
	public function limitToRemoteInstance(string $instance): void {
136
		$this->leftJoinRemoteInstance($instance, 'ri');
137
		$this->limitRemoteVisibility('ri');
138
	}
139
140
141
	/**
142
	 * @param IFederatedUser $member
143
	 * @param int $level
144
	 */
145
	public function limitToMembership(IFederatedUser $member, int $level = Member::LEVEL_MEMBER): void {
146
		if ($this->getType() !== QueryBuilder::SELECT) {
147
			return;
148
		}
149
150
		$expr = $this->expr();
151
152
		$alias = 'm';
153
		$this->generateMemberSelectAlias($alias, self::PREFIX_MEMBER)
154
			 ->leftJoin(
155
				 $this->getDefaultSelectAlias(), CoreRequestBuilder::TABLE_MEMBER, $alias,
156
				 $expr->eq($alias . '.circle_id', $this->getDefaultSelectAlias() . '.unique_id')
157
			 );
158
159
		// TODO: Check in big table if it is better to put condition in andWhere() or in LeftJoin()
160
		$this->andWhere(
161
			$expr->andX(
162
				$expr->eq($alias . '.user_id', $this->createNamedParameter($member->getUserId())),
163
				$expr->eq($alias . '.user_type', $this->createNamedParameter($member->getUserType())),
164
				$expr->eq($alias . '.instance', $this->createNamedParameter($this->getInstance($member))),
165
				$expr->gte($alias . '.level', $this->createNamedParameter($level))
166
			)
167
		);
168
	}
169
170
171
	/**
172
	 * @param FederatedUser|null $initiator
173
	 */
174
	public function leftJoinCircle(?FederatedUser $initiator = null) {
175
		if ($this->getType() !== QueryBuilder::SELECT) {
176
			return;
177
		}
178
179
		$expr = $this->expr();
180
181
		$alias = 'c';
182
		$this->generateCircleSelectAlias($alias, self::PREFIX_CIRCLE)
183
			 ->leftJoin(
184
				 $this->getDefaultSelectAlias(), CoreRequestBuilder::TABLE_CIRCLE, $alias,
185
				 $expr->andX(
186
					 $expr->eq($alias . '.unique_id', $this->getDefaultSelectAlias() . '.circle_id')
187
				 )
188
			 );
189
190
		if (!is_null($initiator)) {
191
			$this->leftJoinOwner($alias);
192
			$this->limitToInitiator($initiator, $alias);
193
		}
194
195
	}
196
197
198
	/**
199
	 * @param string $circleTableAlias
200
	 */
201
	public function leftJoinOwner(string $circleTableAlias = '') {
202
		if ($this->getType() !== QueryBuilder::SELECT) {
203
			return;
204
		}
205
206
		if ($circleTableAlias === '') {
207
			$circleTableAlias = $this->getDefaultSelectAlias();
208
		}
209
		$expr = $this->expr();
210
211
		$alias = 'o';
212
		$this->generateMemberSelectAlias($alias, self::PREFIX_OWNER)
213
			 ->leftJoin(
214
				 $circleTableAlias, CoreRequestBuilder::TABLE_MEMBER, $alias,
215
				 $expr->andX(
216
					 $expr->eq($alias . '.circle_id', $circleTableAlias . '.unique_id'),
217
					 $expr->eq($alias . '.level', $this->createNamedParameter(Member::LEVEL_OWNER))
218
				 )
219
			 );
220
	}
221
222
223
	/**
224
	 * Left join members to filter userId as initiator.
225
	 *
226
	 * @param IFederatedUser $initiator
227
	 * @param string $alias
228
	 * @param string $aliasCircle
229
	 */
230
	public function leftJoinInitiator(
231
		IFederatedUser $initiator, string $alias = 'init', string $aliasCircle = ''
232
	): void {
233
		if ($this->getType() !== QueryBuilder::SELECT) {
234
			return;
235
		}
236
237
		$expr = $this->expr();
238
		$aliasCircle = ($aliasCircle === '') ? $this->getDefaultSelectAlias() : $aliasCircle;
239
		$this->generateMemberSelectAlias($alias, self::PREFIX_INITIATOR)
240
			 ->leftJoin(
241
				 $aliasCircle, CoreRequestBuilder::TABLE_MEMBER, $alias,
242
				 $expr->andX(
243
					 $expr->eq($alias . '.circle_id', $aliasCircle . '.unique_id'),
244
					 $expr->eq($alias . '.user_id', $this->createNamedParameter($initiator->getUserId())),
245
					 $expr->eq($alias . '.user_type', $this->createNamedParameter($initiator->getUserType())),
246
					 $expr->eq(
247
						 $alias . '.instance', $this->createNamedParameter($this->getInstance($initiator))
248
					 )
249
				 )
250
			 );
251
	}
252
253
254
	/**
255
	 * Left join remotes to filter visibility based on RemoteInstance.
256
	 *
257
	 * @param string $instance
258
	 * @param string $alias
259
	 */
260
	public function leftJoinRemoteInstance(string $instance, string $alias = 'ri'): void {
261
		if ($this->getType() !== QueryBuilder::SELECT) {
262
			return;
263
		}
264
265
		$expr = $this->expr();
266
		$this->leftJoin(
267
			$this->getDefaultSelectAlias(), CoreRequestBuilder::TABLE_REMOTE, $alias,
268
			$expr->eq($alias . '.instance', $this->createNamedParameter($instance))
269
		);
270
	}
271
272
273
	/**
274
	 * @param string $alias
275
	 * @param string $aliasCircle
276
	 */
277
	protected function limitVisibility(string $alias = 'init', string $aliasCircle = '') {
278
		$expr = $this->expr();
279
		$aliasCircle = ($aliasCircle === '') ? $this->getDefaultSelectAlias() : $aliasCircle;
280
281
		// Visibility to non-member is
282
		// - 0 (default), if initiator is member
283
		// - 2 (Personal), if initiator is owner)
284
		// - 4 (Visible to everyone)
285
		$orX = $expr->orX();
286
		$orX->add(
287
			$expr->andX($expr->gte($alias . '.level', $this->createNamedParameter(Member::LEVEL_MEMBER)))
288
		);
289
		$orX->add(
290
			$expr->andX(
291
				$expr->bitwiseAnd($aliasCircle . '.config', Circle::CFG_PERSONAL),
292
				$expr->eq($alias . '.level', $this->createNamedParameter(Member::LEVEL_OWNER))
293
			)
294
		);
295
		$orX->add($expr->bitwiseAnd($aliasCircle . '.config', Circle::CFG_VISIBLE));
296
		$this->andWhere($orX);
297
298
299
		// TODO: add a filter for allowing those circles in some request
300
		// - CFG_SINGLE, CFG_HIDDEN and CFG_BACKEND means hidden from listing, filtering
301
		$orHidden = $expr->orX();
302
		$orHidden->add($expr->bitwiseAnd($aliasCircle . '.config', Circle::CFG_SINGLE));
303
		$orHidden->add($expr->bitwiseAnd($aliasCircle . '.config', Circle::CFG_HIDDEN));
304
		$orHidden->add($expr->bitwiseAnd($aliasCircle . '.config', Circle::CFG_BACKEND));
305
		$this->andWhere($this->createFunction('NOT') . $orHidden);
306
307
308
//		$orTypes = $this->generateLimit($qb, $circleUniqueId, $userId, $type, $name, $forceAll);
309
//		if (sizeof($orTypes) === 0) {
310
//			throw new ConfigNoCircleAvailableException(
311
//				$this->l10n->t(
312
//					'You cannot use the Circles Application until your administrator has allowed at least one type of circles'
313
//				)
314
//			);
315
//		}
316
317
//		$orXTypes = $this->expr()
318
//						 ->orX();
319
//		foreach ($orTypes as $orType) {
320
//			$orXTypes->add($orType);
321
//		}
322
//
323
//		$qb->andWhere($orXTypes);
324
	}
325
326
327
	/**
328
	 * - global_scale: visibility on all Circles
329
	 * - trusted: visibility on all FEDERATED Circle if owner is local
330
	 * - external: visibility on all FEDERATED Circle if owner is local and with at least one member from
331
	 * this instance, or if forced
332
	 *             (searching for a specific circle ?)
333
	 *
334
	 * @param string $alias
335
	 * @param string $aliasCircle
336
	 */
337
	protected function limitRemoteVisibility(string $alias = 'ri', string $aliasCircle = 'c') {
338
		$expr = $this->expr();
339
340
		$orX = $expr->orX();
341
		$orX->add(
342
			$expr->eq($alias . '.type', $this->createNamedParameter(RemoteInstance::TYPE_GLOBAL_SCALE))
343
		);
344
345
		$andTrusted = $expr->andX();
346
		$andTrusted->add(
347
			$expr->eq($alias . '.type', $this->createNamedParameter(RemoteInstance::TYPE_TRUSTED))
348
		);
349
		$andTrusted->add($expr->bitwiseAnd($aliasCircle . '.config', Circle::CFG_FEDERATED));
350
		$andTrusted->add($expr->emptyString('o' . '.instance'));
351
		$orX->add($andTrusted);
352
353
		$this->andWhere($orX);
354
	}
355
356
357
	/**
358
	 * @param int $flag
359
	 */
360
	public function filterConfig(int $flag): void {
361
		$this->andWhere($this->expr()->bitwiseAnd($this->getDefaultSelectAlias() . '.config', $flag));
362
	}
363
364
365
	/**
366
	 * @param string $alias
367
	 * @param string $prefix
368
	 *
369
	 * @return $this
370
	 */
371
	private function generateCircleSelectAlias(string $alias, string $prefix): self {
372
		$this->selectAlias($alias . '.unique_id', $prefix . 'unique_id')
373
			 ->selectAlias($alias . '.name', $prefix . 'name')
374
			 ->selectAlias($alias . '.alt_name', $prefix . 'alt_name')
375
			 ->selectAlias($alias . '.description', $prefix . 'description')
376
			 ->selectAlias($alias . '.settings', $prefix . 'settings')
377
			 ->selectAlias($alias . '.config', $prefix . 'config')
378
			 ->selectAlias($alias . '.contact_addressbook', $prefix . 'contact_addressbook')
379
			 ->selectAlias($alias . '.contact_groupname', $prefix . 'contact_groupname')
380
			 ->selectAlias($alias . '.creation', $prefix . 'creation');
381
382
		return $this;
383
	}
384
385
	/**
386
	 * @param string $alias
387
	 * @param string $prefix
388
	 *
389
	 * @return $this
390
	 */
391
	private function generateMemberSelectAlias(string $alias, string $prefix): self {
392
		$this->selectAlias($alias . '.circle_id', $prefix . 'circle_id')
393
			 ->selectAlias($alias . '.single_id', $prefix . 'single_id')
394
			 ->selectAlias($alias . '.user_id', $prefix . 'user_id')
395
			 ->selectAlias($alias . '.user_type', $prefix . 'user_type')
396
			 ->selectAlias($alias . '.member_id', $prefix . 'member_id')
397
			 ->selectAlias($alias . '.instance', $prefix . 'instance')
398
			 ->selectAlias($alias . '.cached_name', $prefix . 'cached_name')
399
			 ->selectAlias($alias . '.cached_update', $prefix . 'cached_update')
400
			 ->selectAlias($alias . '.status', $prefix . 'status')
401
			 ->selectAlias($alias . '.level', $prefix . 'level')
402
			 ->selectAlias($alias . '.note', $prefix . 'note')
403
			 ->selectAlias($alias . '.contact_id', $prefix . 'contact_id')
404
			 ->selectAlias($alias . '.contact_meta', $prefix . 'contact_meta')
405
			 ->selectAlias($alias . '.joined', $prefix . 'joined');
406
407
		return $this;
408
	}
409
410
}
411
412