Completed
Pull Request — master (#105)
by Maxence
02:19
created

CircleProviderRequestBuilder::limitToShare()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 6
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 6
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 4
nc 2
nop 2
1
<?php
2
3
/**
4
 * Circles - Bring cloud-users closer together.
5
 *
6
 * This file is licensed under the Affero General Public License version 3 or
7
 * later. See the COPYING file.
8
 *
9
 * @author Maxence Lange <[email protected]>
10
 * @copyright 2017
11
 * @license GNU AGPL version 3 or any later version
12
 *
13
 * This program is free software: you can redistribute it and/or modify
14
 * it under the terms of the GNU Affero General Public License as
15
 * published by the Free Software Foundation, either version 3 of the
16
 * License, or (at your option) any later version.
17
 *
18
 * This program is distributed in the hope that it will be useful,
19
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21
 * GNU Affero General Public License for more details.
22
 *
23
 * You should have received a copy of the GNU Affero General Public License
24
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
25
 *
26
 */
27
28
namespace OCA\Circles\Db;
29
30
31
use Doctrine\DBAL\Platforms\PostgreSqlPlatform;
32
use Doctrine\DBAL\Query\QueryBuilder;
33
use OCA\Circles\Model\Circle;
34
use OCA\Circles\Model\Member;
35
use OCP\DB\QueryBuilder\IQueryBuilder;
36
use OCP\IDBConnection;
37
use OCP\Share;
38
use OCP\Share\IShare;
39
40
class CircleProviderRequestBuilder {
41
42
43
	/** @var IDBConnection */
44
	protected $dbConnection;
45
46
47
	/**
48
	 * returns the SQL request to get a specific share from the fileId and circleId
49
	 *
50
	 * @param int $fileId
51
	 * @param int $circleId
52
	 *
53
	 * @return IQueryBuilder
54
	 */
55
	protected function findShareParentSql($fileId, $circleId) {
56
57
		$qb = $this->getBaseSelectSql();
58
		$this->limitToShareParent($qb);
59
		$this->limitToCircle($qb, $circleId);
60
		$this->limitToFiles($qb, $fileId);
61
62
		return $qb;
63
	}
64
65
66
	/**
67
	 * Limit the request to a Circle.
68
	 *
69
	 * @param IQueryBuilder $qb
70
	 * @param int $circleId
71
	 */
72
	protected function limitToCircle(IQueryBuilder &$qb, $circleId) {
73
		$expr = $qb->expr();
74
		$pf = ($qb->getType() === QueryBuilder::SELECT) ? 's.' : '';
75
76
		$qb->andWhere($expr->eq($pf . 'share_with', $qb->createNamedParameter($circleId)));
77
	}
78
79
80
	/**
81
	 * Limit the request to the Share by its Id.
82
	 *
83
	 * @param IQueryBuilder $qb
84
	 * @param $shareId
85
	 */
86
	protected function limitToShare(IQueryBuilder &$qb, $shareId) {
87
		$expr = $qb->expr();
88
		$pf = ($qb->getType() === QueryBuilder::SELECT) ? 's.' : '';
89
90
		$qb->andWhere($expr->eq($pf . 'id', $qb->createNamedParameter($shareId)));
91
	}
92
93
94
	/**
95
	 * Limit the request to the top share (no children)
96
	 *
97
	 * @param IQueryBuilder $qb
98
	 */
99
	protected function limitToShareParent(IQueryBuilder &$qb) {
100
		$expr = $qb->expr();
101
102
		$qb->andWhere($expr->isNull('parent'));
103
	}
104
105
106
	/**
107
	 * limit the request to the children of a share
108
	 *
109
	 * @param IQueryBuilder $qb
110
	 * @param $userId
111
	 * @param int $parentId
112
	 */
113
	protected function limitToShareChildren(IQueryBuilder &$qb, $userId, $parentId = -1) {
114
		$expr = $qb->expr();
115
		$qb->andWhere($expr->eq('share_with', $qb->createNamedParameter($userId)));
116
117
		if ($parentId > -1) {
118
			$qb->andWhere($expr->eq('parent', $qb->createNamedParameter($parentId)));
119
		} else {
120
			$qb->andWhere($expr->isNotNull('parent'));
121
		}
122
	}
123
124
125
	/**
126
	 * limit the request to the share itself AND its children.
127
	 * perfect if you want to delete everything related to a share
128
	 *
129
	 * @param IQueryBuilder $qb
130
	 * @param $circleId
131
	 */
132
	protected function limitToShareAndChildren(IQueryBuilder &$qb, $circleId) {
133
		$expr = $qb->expr();
134
		$pf = ($qb->getType() === QueryBuilder::SELECT) ? 's.' : '';
135
136
		/** @noinspection PhpMethodParametersCountMismatchInspection */
137
		$qb->andWhere(
138
			$expr->orX(
139
				$expr->eq($pf . 'parent', $qb->createNamedParameter($circleId)),
140
				$expr->eq($pf . 'id', $qb->createNamedParameter($circleId))
141
			)
142
		);
143
	}
144
145
146
	/**
147
	 * limit the request to a fileId.
148
	 *
149
	 * @param IQueryBuilder $qb
150
	 * @param $files
151
	 *
152
	 * @internal param $fileId
153
	 */
154
	protected function limitToFiles(IQueryBuilder &$qb, $files) {
155
156
		if (!is_array($files)) {
157
			$files = array($files);
158
		}
159
160
		$expr = $qb->expr();
161
		$pf = ($qb->getType() === QueryBuilder::SELECT) ? 's.' : '';
162
		$qb->andWhere(
163
			$expr->in(
164
				$pf . 'file_source',
165
				$qb->createNamedParameter($files, IQueryBuilder::PARAM_INT_ARRAY)
166
			)
167
		);
168
	}
169
170
171
	/**
172
	 * @param IQueryBuilder $qb
173
	 * @param int $limit
174
	 * @param int $offset
175
	 */
176
	protected function limitToPage(IQueryBuilder &$qb, $limit = -1, $offset = 0) {
177
		if ($limit !== -1) {
178
			$qb->setMaxResults($limit);
179
		}
180
181
		$qb->setFirstResult($offset);
182
	}
183
184
185
	/**
186
	 * limit the request to a userId
187
	 *
188
	 * @param IQueryBuilder $qb
189
	 * @param string $userId
190
	 * @param bool $reShares
191
	 */
192
	protected function limitToShareOwner(IQueryBuilder &$qb, $userId, $reShares = false) {
193
		$expr = $qb->expr();
194
		$pf = ($qb->getType() === QueryBuilder::SELECT) ? 's.' : '';
195
196
		if ($reShares === false) {
197
			$qb->andWhere($expr->eq($pf . 'uid_initiator', $qb->createNamedParameter($userId)));
198
		} else {
199
			/** @noinspection PhpMethodParametersCountMismatchInspection */
200
			$qb->andWhere(
201
				$expr->orX(
202
					$expr->eq($pf . 'uid_owner', $qb->createNamedParameter($userId)),
203
					$expr->eq($pf . 'uid_initiator', $qb->createNamedParameter($userId))
204
				)
205
			);
206
		}
207
	}
208
209
210
	/**
211
	 * link circle field
212
	 *
213
	 * @deprecated
214
	 *
215
	 * @param IQueryBuilder $qb
216
	 * @param int $shareId
217
	 */
218
	protected function linkCircleField(IQueryBuilder &$qb, $shareId = -1) {
219
		$expr = $qb->expr();
220
221
		$qb->from(CoreRequestBuilder::TABLE_CIRCLES, 'c');
222
223
		$tmpOrX = $expr->eq(
224
			's.share_with',
225
			$qb->createFunction('LEFT(c.unique_id, ' . Circle::UNIQUEID_SHORT_LENGTH . ')')
226
		);
227
228
		if ($shareId === -1) {
229
			$qb->andWhere($tmpOrX);
230
231
			return;
232
		}
233
234
		/** @noinspection PhpMethodParametersCountMismatchInspection */
235
		$qb->andWhere(
236
			$expr->orX(
237
				$tmpOrX,
238
				$expr->eq('s.parent', $qb->createNamedParameter($shareId))
239
			)
240
		);
241
	}
242
243
244
	/**
245
	 * @param IQueryBuilder $qb
246
	 */
247
	protected function linkToCircleOwner(IQueryBuilder &$qb) {
248
		$expr = $qb->expr();
249
250
		/** @noinspection PhpMethodParametersCountMismatchInspection */
251
		$qb->leftJoin(
252
			'c', 'circles_members', 'mo', $expr->andX(
253
			$expr->eq(
254
				'mo.circle_id',
255
				$qb->createFunction('LEFT(c.unique_id, ' . Circle::UNIQUEID_SHORT_LENGTH . ')')
256
			),
257
			$expr->eq('mo.level', $qb->createNamedParameter(Member::LEVEL_OWNER))
258
		)
259
		);
260
	}
261
262
263
	/**
264
	 * Link to member (userId) of circle
265
	 *
266
	 * @param IQueryBuilder $qb
267
	 * @param string $userId
268
	 */
269
	protected function linkToMember(IQueryBuilder &$qb, $userId) {
270
		$expr = $qb->expr();
271
272
		$qb->from(CoreRequestBuilder::TABLE_MEMBERS, 'm');
273
		$qb->from(CoreRequestBuilder::TABLE_GROUPS, 'g');
274
		$qb->from(CoreRequestBuilder::NC_TABLE_GROUP_USER, 'ncgu');
275
276
		$orX = $expr->orX();
277
		$orX->add($this->exprLinkToMemberAsCircleMember($qb, $userId));
278
		$orX->add($this->exprLinkToMemberAsGroupMember($qb, $userId));
279
280
		$qb->andWhere($orX);
281
282
	}
283
284
285
	/**
286
	 * generate CompositeExpression to link to a Member as a Real Circle Member
287
	 *
288
	 * @param IQueryBuilder $qb
289
	 * @param string $userId
290
	 *
291
	 * @return \OCP\DB\QueryBuilder\ICompositeExpression
292
	 */
293
	private function exprLinkToMemberAsCircleMember(IQueryBuilder &$qb, $userId) {
294
295
		$expr = $qb->expr();
296
		$andX = $expr->andX();
297
298
		$andX->add($expr->eq('m.user_id', $qb->createNamedParameter($userId)));
299
		$andX->add(
300
			$expr->eq(
301
				'm.circle_id',
302
				$qb->createFunction(
303
					'LEFT(c.unique_id, ' . Circle::UNIQUEID_SHORT_LENGTH . ')'
304
				)
305
			)
306
		);
307
		$andX->add($expr->gte('m.level', $qb->createNamedParameter(Member::LEVEL_MEMBER)));
308
309
		return $andX;
310
	}
311
312
313
	/**
314
	 * generate CompositeExpression to link to a Member as a Group Member (core NC)
315
	 *
316
	 * @param IQueryBuilder $qb
317
	 * @param string $userId
318
	 *
319
	 * @return \OCP\DB\QueryBuilder\ICompositeExpression
320
	 */
321
	private function exprLinkToMemberAsGroupMember(IQueryBuilder &$qb, $userId) {
322
		$expr = $qb->expr();
323
		$andX = $expr->andX();
324
325
		$andX->add($expr->gte('g.level', $qb->createNamedParameter(Member::LEVEL_MEMBER)));
326
		$andX->add($expr->eq('ncgu.gid', 'g.group_id'));
327
		$andX->add($expr->eq('ncgu.uid', $qb->createNamedParameter($userId)));
328
		$andX->add(
329
			$expr->eq(
330
				'g.circle_id',
331
				$qb->createFunction(
332
					'LEFT(c.unique_id, ' . Circle::UNIQUEID_SHORT_LENGTH . ')'
333
				)
334
			)
335
		);
336
337
		return $andX;
338
	}
339
340
341
	/**
342
	 * left join to get more data about the initiator of the share
343
	 *
344
	 * @param IQueryBuilder $qb
345
	 */
346
	protected function leftJoinShareInitiator(IQueryBuilder &$qb) {
347
		$expr = $qb->expr();
348
349
350
		// Circle member
351 View Code Duplication
		if ($qb->getConnection()
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
352
			   ->getDatabasePlatform() instanceof PostgreSqlPlatform
353
		) {
354
			$req = $expr->eq('s.share_with', $qb->createFunction('CAST(fo.circle_id AS TEXT)'));
355
		} else {
356
			$req = $expr->eq(
357
				's.share_with', $expr->castColumn('src_m.circle_id', IQueryBuilder::PARAM_STR)
358
			);
359
		}
360
361
		$qb->selectAlias('src_m.level', 'initiator_circle_level');
362
		/** @noinspection PhpMethodParametersCountMismatchInspection */
363
		$qb->leftJoin(
364
			's', CoreRequestBuilder::TABLE_MEMBERS, 'src_m',
365
			$expr->andX(
366
				$expr->eq('s.uid_initiator', 'src_m.user_id'),
367
				$req
368
			)
369
		);
370
371
372
		// group member
373 View Code Duplication
		if ($qb->getConnection()
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
374
			   ->getDatabasePlatform() instanceof PostgreSqlPlatform
375
		) {
376
			$req = $expr->eq('s.share_with', $qb->createFunction('CAST(src_g.circle_id AS TEXT)'));
377
		} else {
378
			$req = $expr->eq(
379
				's.share_with', $expr->castColumn('src_g.circle_id', IQueryBuilder::PARAM_STR)
380
			);
381
		}
382
383
		$qb->selectAlias('src_g.level', 'initiator_group_level');
384
		$qb->leftJoin(
385
			's', CoreRequestBuilder::NC_TABLE_GROUP_USER, 'src_ncgu',
386
			$expr->eq('s.uid_initiator', 'src_ncgu.uid')
387
		);
388
		/** @noinspection PhpMethodParametersCountMismatchInspection */
389
		$qb->leftJoin(
390
			's', 'circles_groups', 'src_g',
391
			$expr->andX(
392
				$expr->gte('src_g.level', $qb->createNamedParameter(Member::LEVEL_MEMBER)),
393
				$expr->eq('src_ncgu.gid', 'src_g.group_id'),
394
				$req
395
			)
396
		);
397
	}
398
399
400
	/**
401
	 * Link to all members of circle
402
	 *
403
	 * @param IQueryBuilder $qb
404
	 */
405
	protected function joinCircleMembers(IQueryBuilder &$qb) {
406
		$expr = $qb->expr();
407
408
		$qb->from(CoreRequestBuilder::TABLE_MEMBERS, 'm');
409
410
		// TODO - Remove this in 12.0.1
411 View Code Duplication
		if ($qb->getConnection()
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
412
			   ->getDatabasePlatform() instanceof PostgreSqlPlatform
0 ignored issues
show
Bug introduced by
The class Doctrine\DBAL\Platforms\PostgreSqlPlatform does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
413
		) {
414
			$qb->andWhere(
415
				$expr->eq('s.share_with', $qb->createFunction('CAST(m.circle_id AS TEXT)'))
416
			);
417
		} else {
418
419
			$qb->andWhere(
420
				$expr->eq(
421
					's.share_with', $expr->castColumn('m.circle_id', IQueryBuilder::PARAM_STR)
422
				)
423
			);
424
		}
425
	}
426
427
428
	/**
429
	 * Link to storage/filecache
430
	 *
431
	 * @param IQueryBuilder $qb
432
	 * @param string $userId
433
	 */
434
	protected function linkToFileCache(IQueryBuilder &$qb, $userId) {
435
		$expr = $qb->expr();
436
437
		/** @noinspection PhpMethodParametersCountMismatchInspection */
438
		$qb->leftJoin('s', 'filecache', 'f', $expr->eq('s.file_source', 'f.fileid'))
439
		   ->leftJoin('f', 'storages', 'st', $expr->eq('f.storage', 'st.numeric_id'))
440
		   ->leftJoin(
441
			   's', 'share', 's2', $expr->andX(
442
			   $expr->eq('s.id', 's2.parent'),
443
			   $expr->eq('s2.share_with', $qb->createNamedParameter($userId))
444
		   )
445
		   );
446
447
		$qb->selectAlias('s2.id', 'parent_id');
448
		$qb->selectAlias('s2.file_target', 'parent_target');
449
		$qb->selectAlias('s2.permissions', 'parent_perms');
450
451
	}
452
453
454
	/**
455
	 * add share to the database and return the ID
456
	 *
457
	 * @param IShare $share
458
	 *
459
	 * @return IQueryBuilder
460
	 */
461
	protected function getBaseInsertSql($share) {
462
		$qb = $this->dbConnection->getQueryBuilder();
463
		$qb->insert('share')
464
		   ->setValue('share_type', $qb->createNamedParameter(Share::SHARE_TYPE_CIRCLE))
465
		   ->setValue('item_type', $qb->createNamedParameter($share->getNodeType()))
466
		   ->setValue('item_source', $qb->createNamedParameter($share->getNodeId()))
467
		   ->setValue('file_source', $qb->createNamedParameter($share->getNodeId()))
468
		   ->setValue('file_target', $qb->createNamedParameter($share->getTarget()))
469
		   ->setValue('share_with', $qb->createNamedParameter($share->getSharedWith()))
470
		   ->setValue('uid_owner', $qb->createNamedParameter($share->getShareOwner()))
471
		   ->setValue('uid_initiator', $qb->createNamedParameter($share->getSharedBy()))
472
		   ->setValue('permissions', $qb->createNamedParameter($share->getPermissions()))
473
		   ->setValue('token', $qb->createNamedParameter($share->getToken()))
474
		   ->setValue('stime', $qb->createFunction('UNIX_TIMESTAMP()'));
475
476
		return $qb;
477
	}
478
479
480
	/**
481
	 * generate and return a base sql request.
482
	 *
483
	 * @param int $shareId
484
	 *
485
	 * @return IQueryBuilder
486
	 */
487
	protected function getBaseSelectSql($shareId = -1) {
488
		$qb = $this->dbConnection->getQueryBuilder();
489
490
		/** @noinspection PhpMethodParametersCountMismatchInspection */
491
		$qb->select(
492
			's.id', 's.share_type', 's.share_with', 's.uid_owner', 's.uid_initiator',
493
			's.parent', 's.item_type', 's.item_source', 's.item_target', 's.file_source',
494
			's.file_target', 's.permissions', 's.stime', 's.accepted', 's.expiration',
495
			's.token', 's.mail_send', 'c.type AS circle_type', 'c.name AS circle_name',
496
			'mo.user_id AS circle_owner'
497
		);
498
		$this->linkToCircleOwner($qb);
499
		$this->joinShare($qb);
500
501
		// TODO: Left-join circle and REMOVE this line
502
		$this->linkCircleField($qb, $shareId);
0 ignored issues
show
Deprecated Code introduced by
The method OCA\Circles\Db\CirclePro...lder::linkCircleField() has been deprecated.

This method has been deprecated.

Loading history...
503
504
		return $qb;
505
	}
506
507
508
	/**
509
	 * Generate and return a base sql request
510
	 * This one should be used to retrieve a complete list of users (ie. access list).
511
	 *
512
	 * @return IQueryBuilder
513
	 */
514
	protected function getAccessListBaseSelectSql() {
515
		$qb = $this->dbConnection->getQueryBuilder();
516
517
		/** @noinspection PhpMethodParametersCountMismatchInspection */
518
		$qb->select(
519
			'm.user_id', 's.file_source', 's.file_target'
520
		);
521
		$this->joinCircleMembers($qb);
522
		$this->joinShare($qb);
523
524
		return $qb;
525
	}
526
527
528
	protected function getCompleteSelectSql() {
529
		$qb = $this->dbConnection->getQueryBuilder();
530
531
		/** @noinspection PhpMethodParametersCountMismatchInspection */
532
		$qb->selectDistinct('s.id')
533
		   ->addSelect(
534
			   's.*', 'f.fileid', 'f.path', 'f.permissions AS f_permissions', 'f.storage',
535
			   'f.path_hash', 'f.parent AS f_parent', 'f.name', 'f.mimetype', 'f.mimepart',
536
			   'f.size', 'f.mtime', 'f.storage_mtime', 'f.encrypted', 'f.unencrypted_size',
537
			   'f.etag', 'f.checksum', 'c.type AS circle_type', 'c.name AS circle_name',
538
			   'mo.user_id AS circle_owner'
539
		   )
540
		   ->selectAlias('st.id', 'storage_string_id');
541
542
543
		$this->linkToCircleOwner($qb);
544
		$this->joinShare($qb);
545
		$this->linkCircleField($qb);
0 ignored issues
show
Deprecated Code introduced by
The method OCA\Circles\Db\CirclePro...lder::linkCircleField() has been deprecated.

This method has been deprecated.

Loading history...
546
547
548
		return $qb;
549
	}
550
551
552
	/**
553
	 * @param IQueryBuilder $qb
554
	 */
555
	private function joinShare(IQueryBuilder &$qb) {
556
		$expr = $qb->expr();
557
558
		/** @noinspection PhpMethodParametersCountMismatchInspection */
559
		$qb->from('share', 's')
560
		   ->where($expr->eq('s.share_type', $qb->createNamedParameter(Share::SHARE_TYPE_CIRCLE)))
561
		   ->andWhere(
562
			   $expr->orX(
563
				   $expr->eq('s.item_type', $qb->createNamedParameter('file')),
564
				   $expr->eq('s.item_type', $qb->createNamedParameter('folder'))
565
			   )
566
		   );
567
	}
568
569
570
	/**
571
	 * generate and return a base sql request.
572
	 *
573
	 * @return \OCP\DB\QueryBuilder\IQueryBuilder
574
	 */
575 View Code Duplication
	protected function getBaseDeleteSql() {
576
		$qb = $this->dbConnection->getQueryBuilder();
577
		$expr = $qb->expr();
578
579
		$qb->delete('share')
580
		   ->where($expr->eq('share_type', $qb->createNamedParameter(Share::SHARE_TYPE_CIRCLE)));
581
582
		return $qb;
583
	}
584
585
586
	/**
587
	 * generate and return a base sql request.
588
	 *
589
	 * @return \OCP\DB\QueryBuilder\IQueryBuilder
590
	 */
591 View Code Duplication
	protected function getBaseUpdateSql() {
592
		$qb = $this->dbConnection->getQueryBuilder();
593
		$expr = $qb->expr();
594
595
		$qb->update('share')
596
		   ->where($expr->eq('share_type', $qb->createNamedParameter(Share::SHARE_TYPE_CIRCLE)));
597
598
		return $qb;
599
	}
600
}
601